From e4a830e9b7ca039c7c70697786d29f5b6679d775 Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 9 Feb 2021 16:06:38 +0000 Subject: [PATCH 01/13] Squashed 'lib/vscode/' changes from e5a624b788..3e344b17b7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3e344b17b7 Remove extrenuous file. (#113825) 36f9eaf1e7 Merge pull request #113596 from Ry0taK/release/1.52 e73a6b082c Create ryotak.txt ea3859d4ba Bump version number to 1.52.1 (#112659) 608dfc1363 Catch all localhost duplicates when forwarding a port (#112575) 8b5ece7c20 Merge pull request #112431 from microsoft/misolori/notebook-color-icons c11e9282d8 Merge pull request #112565 from microsoft/isidorn/enableBreakpointsFor 2b6973a35d debug: check for existence of enableBreakpointsFor.languageIds 374d05c17c Revert "debug: stop supporting enableBreakpointsFor" f93c8a0386 Fix notebook status bar icon colors (fixes #112323) 940b5f4bb5 chore: fix run-on values for snap build (#112245) dbbf7079b2 Make sure that tunnel information isn't set on desktop (#112228) 99edf4ff0f fix linux deb repo pointer (#112226) 91a50e3022 Set tunnel information for embedders (#112215) c6cb541539 Fix port forwarded nofication showing for the wrong port (#112160) 6caba06920 Merge pull request #112141 from microsoft/joh/fix/111913 cb971b8ffd Preserve whitespace in tree hover (#112133) e4af2d8ad6 disable proposed API checks on top-level getters 6f933020e8 Just set textContent for custom hover (#112075) d66e3740a4 Merge pull request #112054 from microsoft/connor4312/js-debug-1.52.2 b4ec131cff retry all cosmosdb ops (#112072) 5e585c5fb4 use proper repos for linux arm (#112026) 37c2de252e debug: bump js-debug bc13785d3d Retry createAsset sproc due to ECONNRESET (#112020) 559a78eaaa Mitigate #109728 (#112008) 62d6b82a6f Revert "build: create asset should still try to add asset" (#112009) 30e0c5784b fix #111898 (#111928) 532ba43c78 Remove console.log 39f33b588a wrap font faimly with quotes dc8ac78031 fix #111910. Avoid notebook save race condition. 97e4af4d20 Fixes #111899: Maintain compatibility for context keys like `vim.use` acbe8f273e Only render `vscode-remote://` extensions as remote 9254ec3b28 fixes #111577 7413cc2493 yarn gulp vscode 528ca4c9ea fix duplicate imports 8e5a1da05e Update doc comment for resolveTreeItem Related to #111715 478c7b633f Make welcome buttons look better in wide pane Fixes #111744 d298a8d5d6 Fix timing issue with environment tunnels Fixes microsoft/vscode-remote-release#4112 7fcdba7fea Add heuristic for when to notify about forwarded port Fixes microsoft/vscode-remote-release#4133 2e3236827e suppress refactor preview from onWill-handler, workaround for https://github.com/microsoft/vscode/issues/111873 de3dd1383c Active indent lines do not render in High Contrast theme. Fixes #111137 8f410da37f Revert "fixes #110353" 6ed64df492 repl: font family 0c947cb4ee fixes #96264 6a1ad5b9b8 :lipstick: code lens font fix 60bf040b98 workaround for https://github.com/microsoft/vscode/issues/111871, fixes https://github.com/microsoft/vscode/issues/111666 5e350b1b79 build: create asset should still try to add asset 7256c3ab26 :lipstick: code lens font 6a58335d02 explorerViewer drop await c2f68baa78 search: fix handle empty results from ripgrep 63137db51c debug: fix auto attach not turning on if reselecting current state c70d984fa2 debug: bump js-debug ff66544ada Allow using upper case hex characters for color ff824f2776 Bump actions b617b725a0 update notebook execute kb shortcut tooltip for windows. 1aa26c3775 Fixes #105808 9d5c351c71 notebook list view scrollbar should be below cell toolbar 53eac52308 Fixes #111499 1c1c59fd0f Kill code processes after each integration test suite run (#111844) 39fb92c6c5 Remove redundant when clauses 62b119aa22 Fixes bad has implementation 370e092880 extJupyter 41eb987d09 add log for save/saveas 7b1154d447 update resolve time stats even if they are the same. 7ecce71a48 Fixes #110376 450ae69a33 Fix #111835 2964a69479 Fix #111798: Getting Started: capitalized "Editor" looks weird 061e84830d fixes #110775 a82ae0c9b7 Fix #108266 564ff91763 fixes #111832 95f6cfa0ae More 💅: adjust overall padding & sizing (refs #111753) ae93d48902 fixes #111583 15f38fe338 Fix ports view order in remote explorer Fixes #111830 a63786db23 Add paren, bracket, and brace to autoclosing pairs for make Fixes #89191 3c4bbf6da2 Fix ports view listener leak Same kind of cause as https://github.com/microsoft/vscode/issues/107903 Fixes #111772 6d222ba785 Fixes #97196: Clear out semantic tokens when there are no more providers 0447415697 Hide the editor's cursor when doing composition (fixes #106663) 79fb30f5b6 Fix resolveTreeItem called twice Fixes #111749 07333dec48 node-debug@1.44.15 87e43299ad update DAP to 1.43 09a7ad2ae7 fix raw timers telemetry event 4095701c70 Exclude endgame-plan issues from query 0ea221b325 fix query, remove commas 4f33022639 add verification query for issues from non-vscode team members c0ad6a56c0 Slightly darken list active bg on light theme (fixes #111750) 92192baf76 Bring content closer to center vertical when space permitting Ref #111753 f603b548c5 Polish getting started styles (#111774) af63e8263a Fix #111706: Getting started: images need alt text 2012817c0d Fix #111699: workbench.startupEditor setting not showing Getting Started tab 5215161c88 fixes #108712 5b121903fc Fix issue preventing cells from leaving "running" state Fix #110973 253844006a fix #111735. 1fc36c35bb remove extrenouus log fixes #111759 b122603b86 Fix comment decoration weirdness, fixes https://github.com/microsoft/vscode-pull-request-github/issues/2309 9d9ae54aa8 Add jpg to vscodeResources in order to bring gettingStarted images into the bundle 44dafcc840 Allow an `EncodedTokensProvider` to also provide a tokenize method a9100d686c Update verification notebook milestone 4884986dd6 tweak smart select test a2193cb827 fix #111714 efd8b0141e explorer: update creation labels acaecbdc86 fixes #111602 0db0fa8fc9 Prompt when hitting a different UndoRedoSrouce when undoing (#111640) 2b179bff4a Extract `_undo`, `_redo` 0ac5e95251 Fix #109644 df2c328e59 string literal does not work with status bar item [background] color (fix #111687) d758a74bf7 Merge pull request #110902 from Wscats/patch-5 d79110a329 Avoid layer breaker bbdbcd333b Update contextkey.ts 9cd45f96de fixes #111710 383dc83e7f Fix the standalone editor build 97646e138a workbench.tree.expandMode 49ef641300 fixes #111639 af29768f18 Fix #110844 e16d1f06d7 Add default URI if not provided in dialog API Fixes #111585 f2ae4927f9 update distro 980ef5cb52 Merge pull request #110846 from Wscats/patch-3 94dd681d16 more fixes for #95697 16ffa1deff Add GreaterEquals and SmallerEquals and fix implementations 65c70884c1 Fix #106989 763b155fe9 Fix #111574 51b1029e98 Check detected tunnels before auto forwarding Fixes microsoft/vscode-remote-release#4112 87538e9cea Merge remote-tracking branch 'origin/master' into pr/Wscats/110846 2ba6946272 fixes #111581 8d171e6492 Improve multiple lock files notification Fixes #111589 0be03ecc6d fixes #111693 5d591624ae add install in browser action 16e91f1011 fix https://github.com/microsoft/vscode/issues/111741 f622d7b6f1 fixes #111657 9a9c6b5557 Extra character in icon hover. Fixes #111681 b9b92e3152 Fix tree item not getting resolved after data change Fixes #111711 f4187f2202 fixes #111732 #111733 806eaddcbe Fix resolveTreeItem getting called too early Fixes #111613 0587f828d6 fixes #111617 399be0e564 fixes #111671 3730238647 fixes #111601 e024fd8b96 add cancel-option to continue bisect model dialog, https://github.com/microsoft/vscode/issues/111667 914b5633f5 fixes #111593 2388291cbc start extension bisect with all extensions disabled and stop early if problem reproduces, https://github.com/microsoft/vscode/issues/111672 a1e5a1a593 Small typo in notebook mime type icon description. Fixes #111674 6d4532c484 align continue bisect messages, https://github.com/microsoft/vscode/issues/111667 1cad5ab365 Prepend `vs/nls` to `workerMain.js` (fixes #111599) 00bbeb3973 Fixes #111649: Have the diff editor control `wordWrapOverride1` and the toggle word wrap action `wordWrapOverride2` such that they can function independently 8041440e5b linux: disable integration tests af848910f7 Merge pull request #111661 from dataleaky/master c44732014d windows - prefer to focus window where files open in 14bf080705 Keep Editors Open menu choice in the editor '...' menu should be a checked option (fix #111668) b2ef118857 window.restoreWindows: preserve description polish (fix #111607) c76a42acc6 Bad rendering of status bar item with error background color (fix #111618) 1e0094545c Merge branch 'master' into master d9ea0ced98 build: fix config 60f5e6ee9a build: disable exploration sync 647cecb29d build: exclude electron-11.x.y branch from continuous build 5e54da666c fix #111675 and #111673 9dba02c075 re #109594. b2c2c20fe7 fix #109626. 8fa61d9652 Fix a dependency URL a0aeb3a559 Update package.json 4d9277a23a Optimize autosave message when changing settings. Fixes #111648 c96643feab Fix #111598 0e8f9d143b Merge pull request #111541 from mdesrosiers/handle-find-widget-history-delayer-promise-rejection f89bb0d54a Tweak verification needed query c7e849f9be fix https://github.com/microsoft/vscode/issues/111604 af5c09a10c Remove console.log from hoverWidget Fixes #111609 3689660fcc Add descriptions for npm.packageManager options Fixes #111628 72cce5194c fixes #110511 f67050c029 Missing jsdoc in LinkedEditingRanges. Fixes #111590 e0f804f483 fixes #111496 55489fdd47 Move ports veiw into remote explorer by default 4ca1a30f5d Update distro commit 9395c9403b ignore already verified items d531b6dfe9 proxy - various login dialog fixes c5ffc57ee6 Get ready for ports view experiment 20bf09bc66 [linked editing] finalize LinkedEditingRangeProvider. Fixes #109923 ec2bcdbb57 #111573 also check for setting value type 6e7d1fa680 Fix #111573 bf21395291 column options in openWith-command can be undefined, fyi @mjbvz 3472159cb1 Merge pull request #111408 from microsoft/aeschli/notebookIcons 7536644522 Fixes #111569 648ed9f9d1 unblock build f019356d4d Revert "Make users save new file (#110330)" e3105b9453 Add jpg exclude to hygene bd627caa54 Add actual images for content e428ce2d26 Dont let product icon overflow 5a3c6cb697 Fix offcenter footer 35e9278e50 Fix scrolling on getting staarted 562f909902 Revert "fix: c++ exception in keytar module" b0fba33756 Styling tweaks to getting started 8baa2cdaaf Better shape images in getting started d95abcca66 Prevent error when reopening markdown preview. (#107205) (#111449) 1c4cc602c6 Fix merge conflict 8b59a9ce1a Focus on the content window for iframe based webviews b854c0bffd Make vscode.openWith a proper api command (#111006) 6062ebe318 Bump highlight.js in /extensions/markdown-language-features (#111262) 9e5f14c1bc Default strict null checks to off for implicit projects 304d3d7e36 Handle rejected promise in history delayer 18ecb87939 fix html yarn.lock b03cdca765 icon doc 15b05ff05a fix #108788. 68eaa95360 Bumps version of github-browser c358910b01 Fixes #110880 - sets useConfigOnly=true on commit 634fb13135 [css][html] update dependencies 8448b1680c [html] update dependencies c941749f5b Enable persistance of getting started ui state dceba9ebb7 [css][html] Option to disable MDN Reference popups only (without effecting other functionalities). For #97979 c6e90c5ea3 Update 'statusBarItem.errorBackground' default color token (refs #110214) ec13471816 [json] Inconsistency between files.insertFinalNewline and Format Document. Fixes #17359 d132d93aeb Register `DeleteInsideWord` as an editor action 0b97b6b160 [json] update dependencies 025952c4d5 Rename `editor.atomicSoftTabs` to `editor.stickyTabStops` bb33ef06bf Fix #99530 de36470f39 Fixes #110897: Always disable word wrapping in left hand side editor of inline diff 833231811f Define a label for `deleteInsideWord` 3fb2f6f976 chore: bump distro 47ce2a8d6d #111845 fix localization tests 155691d0be chore: bump electron@9.3.5 (#111530) 5c71b5425a Fix #111357 b548ac2211 more build fixes 6dfdbe24d9 update distro 8cb4a6907b support passing log level using query param ac165d7f97 bump cache salt 8c2a384efa :lipstick: format file 858817eb3c Update description of `remote.autoForwardPorts` e7f19cf61a don't validate MessagePort inside iframe as safari seems to have trouble with that bf24f189f1 Clean up defaultFolderPath 9689508fa5 Run OSS Tool and make necessary updates 2afcea867a Use argparse 1.0.9 in npm extension 648090440f caching is HARD aa36c6211b move duplicated code into central place 2c5901053a spelling error in command name (fix #111455) f5fb75d829 fix tests 0f1492d6da chore - remove unneccessary non null assertions 8ce7356104 Fix defaultFolderPath so that userhome is correct 2890fd321f Fix #111485 a5a37240bc fixes #108001 eee332ba85 fix misuse of VSCODE_ARCH in builds 2018df47d8 support font features, like ligatures, in code lens, https://github.com/microsoft/vscode/issues/16038 b7211aa875 fixes #107651 9172ecfad8 :up: distro a4054cda6d update references viewlet, also no more proposed API usage required cedc2850ec Exclude testplan-item label f47aae014c Merge pull request #111441 from microsoft/ben/native-tests 673c1adcb0 fix layer breaker 1efcfbf242 api - fix exception in status bar ef2a900dec sandbox - add SH1 to HEX utility using crypto.subtle with fallback to our own solution fbf7566d39 tests - add test suite for native modules 7723f2548c Fix #109406 8ee75c19bb Escape more white space in appendText Fixes #110464 c5ab321020 fix https://github.com/microsoft/vscode/issues/110554 444f79cc3a Improve quiet light diff syntax highlighting. For #107926 07224f0681 update to latest DAP e24ea5ef61 Merge pull request #107926 from alisonnoyes/master 1a6d7f3118 [json] performance warning message can't be ignored. Fixes #105988 e6d6661247 fixes #111410 cfa02997d2 fixes #111413 e2dd774fa4 fix yarn retry logic 627ad0b4ee rename OnTypeRename -> LinkedEditing (for #109923) f13720627f Improve task dependency cycle check Fixes #111369 cd9be282b7 Add condition editing UI to breakpoint filters 6795d766a0 remove old logging API proposal, fixes https://github.com/microsoft/vscode/issues/85992 79b6d35bd6 remove TreeItem2 01bbb81052 Finalize markdown tree tooltip and resolveTreeItem Fixes #100741 016655c546 fixes #104629 8891878a3b bump cache 210a1a1327 Merge pull request #111405 from microsoft/joao/trusted-extension-uris 3495d2fd76 :lipstick: 6391d710cf adopt latest loader, pass trusted types policy to loader, adopt in loader-usages 483e81f3b8 Fix duplicate port forwards for ipv4 vs ipv6 Fixes https://github.com/microsoft/vscode/issues/111400 f8d1c07e16 use createChannelReceiver, createChannelSender 995983da54 github: get all branches c3414f3cf3 Always show the open view option in the open in browser command cae277a203 Add a command for open port in browser Part of https://github.com/microsoft/vscode/issues/111402 2fb5b5b696 fixes #44542 0321ca5d96 fixes #106664 6c8f62432f Merge branch 'master' into aeschli/notebookIcons 8012c255e5 fix keybinding smoke test after icon name changes 4e45d27a72 fix stopIcon name 35a8955dc1 Merge branch 'master' into aeschli/notebookIcons 4ae452929e Merge pull request #111383 from microsoft/aeschli/extensionsIcons 3a92391c20 Merge pull request #111377 from microsoft/aeschli/preferencesIcons fddc9bb221 success/error icons f6249641f2 externalize notebook items bafc05ff0a fix retry 71836f95dd fixes #111366 6253e47773 #44542 ability to reveal setting 42bb62a06f Exclude endgame-plan label 1e0af43774 trusted ext urls: use 1 hour instead of 10 mins c471973938 Merge branch 'master' into aeschli/preferencesIcons 44ddde116f adopt checkProposedApiEnabled 14d59bef92 Allow status items with error background color (fix #110214) (#111353) 14415847e1 trusted extension urls 2cd7a70d62 original urls get passed along with openUrl ac9e250cce support node 15 cfe2e99ad8 debug: load all stack frames text shuold have initial color when selected so it is readable 4cb5bb656a Merge branch 'master' into aeschli/preferencesIcons 8e6517069a Merge branch 'master' into aeschli/extensionsIcons 98f80c4047 Fix #110698 8784b77bfd fix #110699 1e77bd81d8 registerCodicon 8234453386 update tests 83f8b96a94 add extension icons 43f5beb307 fix terminal icons 09f281dff6 dirty dff icons 2f0552737c terminal icons 5c2127ec74 Fix #110964 f70430eae4 download to temp location first and rename 26a0c24c6c fixes #111218 818174d4e8 bulk edit service: Show infinte progress when there is only 1 item since we do not know how long it takes 4ac4cf3dff some more cleanup, enfore proposed API checks, fyi @RMacfarlane c84fa206d4 add preferences icons c219b0673c :lipstick: discover and mark more proposed types 44af5d2af2 marker view icons f0bb23ca02 more icon work 58a90729c4 externalize activitybar icons 1e6e371d3b :liptsick: sort API types and namespaces, enforce proposed API for types a94217739a Don't auto forward ports in WSL Fixes #111375 a5bda3ee95 support later node for building f81e91d1c9 Merge pull request #111363 from microsoft/joh/playwright cb8259c7b7 Fix forwarded ports restore race e5111fc439 Only show "Not Forwarded" when auto forward is disabled Part of microsoft/vscode-remote-release#4021 58fe1b9dfa Add badge back to ports view Part of microsoft/vscode-remote-release#4021 3018240290 add user data init markers 047063458b Merge branch 'master' into joh/playwright b2b5647451 disable two comparer test that fail in latest safari, https://github.com/microsoft/vscode/issues/111368 fyi @bpasero 6a974d536b Change port language to "forwarded" and add icon Part of microsoft/vscode-remote-release#4021 b269cd9464 Add open pull request query 2d4ec09c1d add yarn lock file 9ee2a1ee1e :up: update playwright 3c3ed7fb58 Fix #111351 b01183bda2 Add extension source to forwarded ports Part of microsoft/vscode-remote-release#4021 2f8f00d622 add dropDownButton-icon description 1aba2b87c6 fix unnecessary import 8648658c44 use codicon for openEditorWith picker a6a53198f6 make viewPaneContainer twisties themable 32a18e3f5b add Codicon.dropDownButton 20b649729e button: use CSSIcon 75c71b49cc Fix #107152 6692bf17e8 find completion item color in detail and at the start/end of documentation, fixes https://github.com/microsoft/vscode/issues/109794 d9c33ab941 Fix port nofication cooldown c13542b7ca Remove heading for forwarded ports Part of microsoft/vscode-remote-release#4021 ebaf0a2ef0 read line preview when having symbol results, https://github.com/microsoft/vscode/issues/109523 dcf4cd2a50 Improve tunnelFactory doc and remove an escaping undefined 8ba0fd356e IEnvironmentVariableInfo: Use ThemeIcon b4912314fc move file decoration API to stable, https://github.com/microsoft/vscode/issues/54938 38a200c746 :lipstick: c6be304059 Ignore flush errors during dispose since the underlying socket might be already closed 2d2a9e7527 Prevent unwanted concatenation of "null" to feedback URL (#111325) 4031280b29 Go to symbol in editor, open to side should reset scroll position in original editor (fix #111346) 5e6a2779f6 add try/catch for #111177 d49955575e :lipstick: relative pattern a5e20f89e4 :lipstick: imports 392b6a94e0 perf - remove redundant performance usages d547170675 sandbox - move some changes from PR over to master 0a80aacc7b chore: update cache 764620efae fix: c++ exception in keytar module 5f569de4cd more removal of non-null 0921f711c3 Add GettingStartedService/Registry and initial getting started UI (#111175) ef03adf3a0 notebook editor widget always creates list in ctor. 3f0ada11fd core action active editor context should always have a view model. f24f7be7f0 less non-null operator in notebookEditorWidget 76882e4eb4 avoid memory leak of large text buffer from notebook cell text model. 2c20676f5c update distro (and hopefully unstuck builds) 2ecb47d4ec Use ThemeIcon & IconRegistry for custom icons c59ddc6a1f update distro cad45e538a Add `ProtocolMessageType.ReplayRequest` such that missing messages can be requested again 6edf58b9ae Avoid layer breaker (using `process` from `/browser/`) 3cd515d33e support installing web preferred extensions on web server af52f2cb8c `TextModel` should not dispose its `TextBuffer` 8dce3899da avoid unnecessary deepclone 5b243ab54b don't leak env vars into process.env; fixes #83187 b51a171a75 Update codicons https://github.com/microsoft/vscode-codicons/commit/cbe2a17f2965d0f3ff363830fee4ebae5fee7c4d e2c9d1a7a3 Avoid using `innerHTML` (#108400) d7bdbe118e Allow running `yarn tsec-compile-check` also on Windows 84cf12f40b Add trusted types policies where `.innerHTML` needs to be used (#108400) 89c255a523 generate icon-css from theme.comon.IconRegistry 71462d03fe Fixes #111309 0ab259ff27 remove unsued code, fyi @roblourens, https://github.com/microsoft/vscode/issues/106741 37e9cceddc Enable sandbox, contextIsolation and vscode-file for process explorer and issue reporter (#111304) 545332f793 explorer: polish progress e287b8c9d3 explorer: hook in cancellation bef58ff84a forgot! 5cb2ee7b8b confirmed extension -> user trusted extension cab737e707 Make `extensionKind: ['ui', 'web', 'workspace']` pick the web worker extension host if possible b909e20513 progress: Increment by percentage points since progress API expects that 5a0ab56492 use native performance.mark when avialable e3e2837476 Bring Running Extensions to the web aa91d04552 add cancellation token to working copy service and hook it up with file operation participants a500715422 fix terrapin order a59aa579b6 fix debug toolbar colors 37274abda9 update distro 043c17ef7d update azure cosmos :shrug: d387fa978a Extract `AbstractRuntimeExtensionsEditor` to separate file 97f01b33e9 Move profiling related state down from `AbstractRuntimeExtensionsEditor` ba867135c5 Extract `AbstractRuntimeExtensionsEditor` 97a896091d remove startupPerf experiement, https://github.com/microsoft/vscode-internalbacklog/issues/1620 a8b6afefab process explorer - prevent process loops d189ecf936 issues - check for sender being destroyed before reply cda7b564e8 fixes #111191 182fe687b7 #111291 fix tests d63fee4308 Fix #111291 2864ebf134 Do not include iteration plans into the open issues ad8b6baef6 Extract `SaveExtensionHostProfileAction` to a separate file 6dc1a9540c "undo delete" operation recreates file contents when intital create had contents, fixes https://github.com/microsoft/vscode/issues/111162 72f4a2a7de Fixed $REPOS macro 897d2ec9a4 Merge remote-tracking branch 'origin/master' 3de0ea500a Merge branch 'joao/snap-arm' a15493cf90 Extract `DebugExtensionHostAction` to its own file 3ceea17846 npm: use project dir when using npm 84ce131781 Emoji characters in col 40 of an empty file causes crash (fix #111235) bb60e21a36 fix esrp for arm64 799e72cc2f Add tunnel creation options to web api 221a5570b6 Fixing how escape key works in listWidget (#110760) 7bbf45c14e env - only show one notifications not two (fix #111246) ce6f5e0f8f Make ports view flat when there're only forwarded ports Part of microsoft/vscode-remote-release#4021 6e631518ab Extract `ReportExtensionIssueAction` to a separate file fb54cd1cd7 Move inline actions when ports view is in the panel Part of microsoft/vscode-remote-release#4021 9abd43315c Manually release reference to piece tree when disposing (#107999) 48f4109f3d unify win32 build templates e6d2bd326d fix darwin build 97a7e63de8 also check cancellation token in outer perform function, https://github.com/microsoft/vscode/issues/111281 7b1da3a3d9 add IBulkEditOptions#token, https://github.com/microsoft/vscode/issues/111281 927290270a build: - ENABLE_TERRAPIN env - .build/terrapin cache flag - fix alpine cache - enable web cache 96b2c670d7 debt - extensions path is always defined ffec932655 add timeout to terrapin step 236298851a :lipstick: use linked list instead of array-remove gymnastics, fyi @bpasero 2ea9ecd6da increate file participant default time to 1 minute, show notification progress which allows for cancellation, https://github.com/microsoft/vscode/issues/111208 00eea33b23 More cleanup for armhfp and aarch64 rpm hardcoded dependencies (#111253) 41ef01de56 Fix #111212 1460908e18 bootstrap - move portable mode into bootstrap-node 2433e29383 sandbox - use IPC communication to read bundles ec5da2bdc1 build: rebuild keytar for arm64 c6e908997b jsdoc :lipstick: for relative pattern f37ca74411 Merge pull request #111269 from dsanders11/patch-1 614a865d1d Fix error message 3898e2ced3 Update extension install button to match default button style 8c9dfcbcb4 fix: appName in upload-vscode-configuration task 093acbdcbf timeline icons: use codicon registry for now 5a04894296 update distro 357fc9d16b Use codicons instead of string literals 0728b59411 fix: upload-vscode-configuration task for macOS f645a8d8c8 Link to ThemeIcon id reference 6a41811e10 Merge pull request #111183 from microsoft/aeschli/themableDebugIcons 3cfc3ac372 remove duplicate 'codicon' class (for #111240) 668b1d2439 explorer file operations: show progress 24e9b29b39 wording for output show more. fd15b93261 remove layer breaker 40a81495f2 fix: removed plugin helper entitlement 07bd0e3c35 :lipstick: 23e7e2fef0 showNotebookDocument. 160baa3b8d :lipstick: f9aa23fb6c relative pattern - update JSDoc comment 2e8bbdb41e api - test relative pattern with URI 2be5cc1845 #107999 Use disposable store and add onWillDispose listener 562d9451bc Fix #109114 66cb21b0b1 Fix #107999 5987e40765 Add vscode.Uri to types for vscode.RelativePattern's base parameter (#111155) c37ffd83ba fixes #111240 bd40e2555b remove TS on-type-rename feature, https://github.com/microsoft/vscode/issues/109923, fyi @mjbvz a5344096a2 raceTimeout test failed (fix #111233) 950c942b7c TunnelCreationInformation -> TunnelCreationOptions Fixes #110795 c0ecf176e7 Move ports view into the panel Part of microsoft/vscode-remote-release#4021 17e9e4ca1a Merge branch 'joao/retry-yarn' c4b7d10912 Use property bag for tunnel creation elevation Fixes #110795 e052c8e045 move down common icons 197f1341e6 explorerService: consolidate bulkFileService apply in one location 2a8ee68eaa #110721 pass action runner afce5c425d Add simple check for task dependency cycles Fixes #111186 cf4f2fa6c8 #11123 more fixes ac21b369b3 Merge branch 'master' into aeschli/themableDebugIcons c2d3d7f857 bump distro 8df4bc17da Merge .nativeignore and .moduleignore. Fixes #111220 fed34f982e startup util fixes 95e32fc67e update cache salt ff0785571f explorer: limit undo file content to 5mb a4e4e7ede3 Merge pull request #111013 from microsoft/isidor/doNotUseTrashFlag b5861b0330 update the monaco.d.ts 4563ba1f94 add start function to explicitly start performance observer b7851bfc80 :chore: bump distro 050963b8d1 missing terrapin usage ad3adfa28e retry yarn install 900e4474e9 missing terrapin usage 63efefb897 Merge pull request #111182 from microsoft/sandy081/notifications/dropdown 1ff7e57724 Merge branch 'master' into sandy081/notifications/dropdown 875ad7bdd7 Fix #111168 7e4f90277d run prettier over yml files 9a657db088 add tiny util to allow performance mark collection (via perf_hooks) independent of amd or commonjs usage 384d7b5e9e implement review comments a327c55c85 Merge branch 'master' into aeschli/themableDebugIcons 4ddf7bc0a7 Fix null characters showing in forwarded ports d970e263c7 more fixes for #111221 08d9b15245 fix dropdown position a486099d30 fixes #111221 723ee37c02 bootstrap - expose preload process to base lib d2365da65c rename doNotUseTrash to skipTrashBin 4d58b56772 Merge branch 'master' into isidor/doNotUseTrashFlag 0ec40638a8 Merge pull request #111042 from microsoft/isidorn/bulkFileEditsMaxSize 41025928ad bootstrap - fix more compile errors ae64039e43 Move `runtimeExtensionsInput` to `/common/` de85ef3a20 Fix hardcoded rpm dependencies for armv7hl and aarch64 (#111198) e8ba7e51ee bootstrap - fix compile errors 6038823879 add doc 2efde187ac HTML Semantic Tokens test failures. Fixes #111214 44281ba1d5 do not support secondary option on menu ff281d39f3 fix - pass the action runner 155b218dcf web - home indicator :lipstick: 2046205637 debt - fix layer break with dependency to v8-inspect-profiler 4aae851063 Update Codicons: Add 'circle-large-outline' and 'circle-large-filled' https://github.com/microsoft/vscode-codicons/commit/7ddcbfb5f46e97691028f19b475d9183f43ac245 abf3964921 Updating Codicons: Add 'pass-filled' https://github.com/microsoft/vscode-codicons/ c3e1f0e02a re #102503. allow open notebook on the side. 9ce365f085 chore: bump distro 0b7158b190 :lipstick: e66547a1d9 ensure the editor has enough top padding when there is decoration ::after: { top } 0baf6bb9cc chore: update to electron 11 (#110759) 10d92e5efa Prompt user with info about terminal config, add settings search by @id, and add sendToShell setting (#110987) 6f87c1cf7f Removed "duplicate" label 8e76838da5 Tweaked endgame notebook, added my endgame notebook 2a1c8bbaf2 re #108464. 1732cb9540 DAP: add support for conditional exceptions 4c55c7264f Revert "Fix #58600, Format Emmet package.json (#110891)" 40a902c9cf Multiline markdownEnumDescription should not break split json settings editor Fix #110840 a7b1267006 both custom and native hover showing (#111178) 542de8a8d7 Fix bug where tasks.json tasks aren't resolved eecbbcd3e3 fixes #110720 34b0cedeb7 fixes #108073 ef5a912876 fixes #109097 0223a22d87 externalize debug icons 535943628c revert a2ca2bbb1c revert onCancel 5e82d27fa7 support menu with prompt choices a85b3391ef Consider to remove migrateFromOldCachedViewContainersValue (fix #109726) 6c415c2486 Merge branch 'joao/terrapin' ea989da383 Feedback from resolveTreeItem API proposal review Part of #100741 21a42246d3 fix compile 4e1eff7083 web - turn the home indicator into a menu f19b19018c Automatic port forwarding polish Part of microsoft/vscode-remote-release#4021 e9fc5e147d Fixes microsoft/monaco-editor#2192 9a07d50f52 Delegate to the command service as a fallback (microsoft/monaco-editor#2195) 951509368c Add terminal hack for task line data back in Fixes #105159 93534b15da fixes #104793 12983c8175 Merge remote-tracking branch 'origin/master' 93f8adf263 :lipstick: 07d680b7c8 semantic highligting: deprecate member, add method. Fixes microsoft/language-server-protocol#1087 e81eb57671 Merge branch 'master' into pr/107705 ee6e905a8a cache OpenURLOptions in ExtensionUrlBootstrapHandler (#110725) eaf5eaa29f Allow name to be updated on extension forwarded ports Fixes microsoft/vscode-remote-release#4028 4b6b2bc6c2 update cache salt 3a37613eb5 Automatically forward candidate ports Part of microsoft/vscode-remote-release#4021 ab6878688f fix terrapin usage d632381e71 Add running process info to tunnels Part of microsoft/vscode-remote-release#4021 1436b523d4 [themes] Generate Color Theme From Current Settings doesn't have all colors. Fixes #111147 0a78fe165a Merge pull request #111110 from chenjigeng/master 6026ab576d fix #109104, #105884. 986e1248f6 bump distro 4ae26a1563 Update milestone 52098eaeb0 env - tweak shell resolve experience on startup eae037b391 Fixed Hover style incorrect when writing javascript in HTML fb80c0e44a Merge pull request #107126 from Timmmm/atomic_tabs 88856f1a1c Simplify changes from #108193 a194746aa3 auto attach: allow temporarily disabling 45ec698b12 Avoid walking whitespace twice when going left & small stylistic changes f1cfe2d3a6 Update CodeActionOnSaveParticipant (#108193) 2b75c3d5ac updated javascript for in snippet (#111062) 9195c9ab14 Expose TS server tracing (#110534) 06be9f4029 testing: tests and speed improvements 3880463037 Temporarily only warn on duplicate scoped context 93e7dda5a1 Error if a new ScopedContextKeyService conflicts with an existing ScopedContextKeyService (#110363) c9d89dd5ce `editor.atomicSoftTabs` can be a simple editor option 25e8ca8e18 Merge remote-tracking branch 'origin/master' into pr/Timmmm/107126 b2bd3540b0 Merge pull request #108448 from xndcn/patch-1 7265dfb6d7 testing: smarter change event 87b8061711 Merge pull request #110917 from dsanders11/patch-2 0ffe32409d Don't show complete command arguments in the native tooltips 786eca5cd6 Merge branch 'master' into patch-2 a0b7c2310f support dropdown actions in notifications cbeaf4fc55 Merge pull request #108379 from KapitanOczywisty/patch-2 b2b3b015d5 Sort line decorations 796a38f930 Merge remote-tracking branch 'origin/master' into pr/KapitanOczywisty/108379 f25f1abfaf give the option completely to notebook extension for now. d5a74a912b add trusted status bar item. 6b19da7102 update execution button when trusted notebook metadata changes. 1b3b40265b trusted notebook metadata 4d4f3660a0 opacity for notebook cell status 33c058d042 fix mouse click on text status item d23c882470 use status bar item for execute cell placeholder fc98345ba1 Extract output container renderer efea77ab7c move cell output element to its own module da00706deb cellDnd 57ca27e5f5 merge cell action view 8cf97fefd9 trust notebook prep 4b5dc2fcbc WorkspaceFileEditOptions add maxSize d1280418d7 testing: initial api implementation ff1887be3e Preserve per-window environment variables between workspace changes (fix #108571) 7d1cd1ff33 Fixes #99313 690d0e33c1 window - cleanup reload() method 1568c0ca45 :up: distro fb277edc0f env - take window configuration into account when resolving shellEnv 542a82754c yarn watch: save errors in separate files, make error regex multiline aware 1273299f3d :lipstick: bootstrap-window 6f3fcd2ce0 env - move shell env into bootstrap-window and let user env win over shell env 69de6464ce explorer: pass the folder flag when deleting directories fe65b26426 perf - add ellapsedWaitForShellEnv to measure blocked time on startup 5f8aa18104 Merge commit 'a36c68b9ec3d6a0aca9799d7a10be741a6658a51' into joao/terrapin bffd7a6ad8 Fix #111014 185212db53 Revisit explicit use of ConfigurationTarget.USER when updating config (#109373) 7207ee201f env - also surface shell env resolve errors to users 8a6fea66c0 Fixes #107636 c8e59100c4 Validate line numbers bb7b7dc7f2 Fix doc comment for CustomExecution api Fixes #110981 d84b5eb2da bulkFileEdits: do not use file trash flag 936048e028 :up: distro a81ea8c4fa fix tests 9103955a6d [themes] wait applying settings until user data initialization is completed. Fixes #111009 6fdc79d81a env - set timeout back to 10s but show warning when it takes long 6541e52b5b icons: remote polish c89f783319 Fix installing forever bug in server acfd724aac Pick up latest TS 4.2 for building VS Code caf01baa45 Copy createSettingMatchRegExp locally to fix cycle b4c0cfb513 Better note current version in TS version UI 67b0e2d72d Pick up TS 4.1 final release ea4d99921c update distro 1dcbd67f00 Load `applicationinsights` lazily 5d75857c59 Update marked cgmanifest entry 59428623f9 Cancel encodedSemanticClassifications-full on resource change c833899a84 adds @features: settings search type and dropdown option to Configure Terminal Settings (#110874) ffacef4727 Fix #58600, Format Emmet package.json (#110891) 8b720d6740 chore: bump keytar@7.2.0 (#110977) f0ed8aa2e9 named codicons for views (for #92791) 5ebe7eb5f6 fix #110750. 195779a054 Fix #110870 2fb9c3d5ce Fix #110702 692e61eda1 Fix #110702 145c6e5beb #110905 also optimise for builtin extensions arg 3946a0a314 fix another variation of the "palette" typo... d72f3fba3b Move LoaderStats down to `/base/` 508ef0928c Better loader configuration for nodejs processes d99a9ade8a Update to latest loader a245552aa9 update distro 5876a5e4ae Merge pull request #110930 from microsoft/joh/undoOpts 83246704d4 update distro b0d577c1b7 update doc 22895e9856 Introduce a server method to check if extensions are ready on server 835ce347fe bump distro 76e1b21f4f fixes #110800 624aca5bf5 Merge pull request #110855 from microsoft/joh/pureoutput 56c3caa680 fix https://github.com/microsoft/vscode/issues/110666 4a1cea9306 fix suggest explain mode for complex labels 3665005621 add undo options (undo group id, isUndoing) to working copy service and bulk edit logic 37f44b2e30 Merge pull request #109511 from chanmaoooo/deleteWordEntire 8374d6a4c7 Detect ports to forward more often Part of microsoft/vscode-remote-release#4021 cf3194d184 Add tests and improve the implementation 71fce013c9 Weird on-type-rename-decoration. Fixes #110878 06bf8e6539 editors - update readonly options when file providers change (fix #110854) 2f05337db0 Merge commit 'a36c68b9ec3d6a0aca9799d7a10be741a6658a51' into joao/snap-arm 990ca74d22 Docs for FileSystemWatcher limitations (#110858) 23a826ff3c more jsdoc for #54938 eb6356cfd7 debt - properly implement interface 47eb468acb sandbox - fix process in sandboxed environment (allow to run on Windows) d04f3178dc proper name for register function: registerFileDecorationProvider, #54938 710c859af0 Use label for "Follow link" command's tooltip a36c68b9ec Fix #110905 c11ef72380 Merge branch 'master' into patch-3 a89864b295 fix tests 268091616c Merge branch 'master' into patch-3 b1bc453c55 Fix notebook focus treatment in HC a5c190e1ad fix #110894 7077e069cf :lipstick: 1d42a1c1c3 debounce editor blur focus state update. 113e0428f6 Fix markdown cell focus treatment 3598f9714d Add new codespaces extension id to allow list ab4cab8ac5 rename to `DeleteInsideWord`, remove keybinding ddff206d37 Simplifications: substitute constant arguments 59fa30c069 Scope toolbar visibility to when notebook is in focus (refs #110883) 36a6f53480 Make notebook cell toolbar appear when not hovering over notebook (refs #110883) 334ede72c6 Fixes #97906: Do not classify user installed extensions as built-in extensions d265071a78 Merge branch 'master' into joao/snap-arm 4f61f6d59e damn builds 7e0e697dc8 nbdiff. add menu to disposable store. 80169ca843 leaking disposables in notebook diff editor. 5ac643def7 re #109549. Reveal the first change in notebook diff editor on open. c983b2ae4b explorer actions: use bulk edit service 21c0490036 explorerService: add hasViewFocus and undoRedoSource 49a1f40890 update my-work notebook f4b9edc587 take local value when merging with conflicts 7dbdcf19a8 Merge branch 'master' into joao/snap-arm 8f1f1fda1b what the ad6f4faafb use better name for async markdown rendering, https://github.com/microsoft/vscode/issues/67806 f5a7eb3848 markdown renderer listens to img load and sends resize events, https://github.com/microsoft/vscode/issues/67806 cacb0c90d9 Add `UndoRedoSource` as a means to mark and retrieve undo/redo elements based on their source 9e704c93be Merge pull request #110867 from microsoft/isidorn/bulkEditServiceSupportFolder 63f04d87da bulkEditService: support folder creation 25a5576bbe improve browser extensions view 5cca5c598c empty commit cbfd4d5a9f fix snapcraft x64 6d439732d7 Reduce diff 2a499549ce add deleteWordEntire command 0718c3415f fix test 4ef843ec02 Merge pull request #97203 from bzarco/launch-multi-root-args 54a9df4857 debug console: increase maring right of count badge 78908e7eb4 bump distro 8513f398c4 command for open external 02ecc884d7 enable web extensions in web + server 671f169516 #100415 :lipstick: ff468f6536 #100415 have fixed height only for initial empty installed view 8d25d42560 Merge pull request #110856 from microsoft/isidorn/addCopyOperation 28d71bdbab fix snap target-arch 11644ab3f8 Merge branch 'master' into joh/pureoutput 20d2857142 fix wrong file decoration check 74cefb3f13 bulkEdits: add copy to WorkspaceFileEditOptions 93a0ad81c3 Merge branch 'master' into launch-multi-root-args ca20be531e simplify ITransformedDisplayOutputDto type 3d708b3c64 #100415 show installed extensions view always 7ec1a6899c remove transformXYZOutput 5dd81b4077 move orderedMimeTypes and pickedMimeTypeIndex out of the domain model into the UX (its sole user) 3d013dc76f move git rename to command palette 363534db96 Fix #100415 f6f12c1bcf linux build: fix step condition 11d9d4da8f fix compile error edf43e529a build snap packages for linux arm 12b808cf08 make event optional, event naming, relax badge length a little, https://github.com/microsoft/vscode/issues/54938 f9c3f0dda2 Codicons in prompt (#96430) a34cad5c6b Codicons in prompt (#96430) 72defdc77c Support greater than and less than 896848239d fix typos: pallete -> palette !!! b80117cc34 web - enable dnd to download in pwa (fix #83517) f11635ef11 :up: distro 23ed0649cb storage - remove deprecated methods (fix #109967) d21a51c233 re #109549. undo redo should edit markdown cell. 0109e4d504 fix #110826. e7ce16ceb0 Preserve old showEmmetcommands ID #58600 33e7e030d2 Move Show Emmet Commands command to extension #58600 9641ad2253 Don't use local server for signin in remote scenarios, fixes #104628 f3db091486 re #109549. hook notebook cell language picker into Change Language Mode. 9ecb7e4039 re #109549. click on status bar focus the editor 11341a0d37 Merge pull request #109987 from lf-/patch-3 e0eb2e6e75 Fixes #110468 9fbf87e38a Move management of `inDiffEditor` to the other editor context keys ba5371a252 Render "Extension" in Source column when the keybinding comes from a user installed extension (fixes #110653) 596a96ec7a Add `ResolvedKeybindingItem.isBuiltinExtension` 22b4c45bc1 update distro 58be98a0aa `native-keymap@2.2.1` 8a11346573 re #110581. 3184dca0bc cli server: add OpenExternalCommandPipeArgs 965c8c4cfd chore: bump distro fb6a2a3258 Bump vscode-ripgrep Now has pcre2 in arm linux build 82b485f805 chore: bump keytar@7.1.0 (#110815) 8edc150fe7 Try resolving URIs immediately from the tunnelFactory Part of https://github.com/microsoft/vscode-remote-release/issues/4012 05606c1e09 reuse terminals only for unchanged config; fixes #81345 87050c7fb5 re #109967. 0593b3eb54 Backup location can change in same session (fix #109019) d3057c54c8 remove console.time 7bf226cf50 add console log service to cli cd3caa2d7d provide a promise to wait until initialization is finished 221b8289c4 Merge pull request #110792 from microsoft/alex/native-keymap 8c93891a4d Minor renames ee4071ccbe Simplify handling of `diffEditor.codeLens` f405a3dcde editors - stop trying to be smart about default editor options in `vscode.diff` command f9f2637f79 debt - onDidBeforeRun => onBeforeRun 9c4c195980 Make users save new file (#110330) 442b1d1b61 Move all tree view out of contrib Fixes #110374 c4d2181da6 Clone (#110333) 540cb21d92 Add an elevate parameter to tunnelFactory Part of #110795 171a21b631 Add doc to resolveTreeItem Part of #100741 0c2c8dd8b8 Inline allOf clauses for additionalProperties: false (fixes microsoft/vscode-remote-release#2967) 60c625e9f0 fixes #110768 f255e3e00e Merge pull request #110573 from microsoft/joh/tsQuickRename 28b16c26d8 Move usage of `native-keymap` to the main process 8b192ae90f Move `IKeyboardLayoutService` to `/platform/` d8e40aeedf fix tests on linux 1b7d1e21ca fix tests - dispose 793b679ebc adopt latest API 0cd3c4c994 fixes #110775 fd610cfa5f Merge branch 'master' into joh/tsQuickRename 8667e71704 #109019 remove dependency on backup location 6479a9c46a add node task to fix build breaks (#110762) b25867b709 Support --diff parameter with reading from stdin (fix #110426) 2feb009c8e Editors: consider to allow name/description for file based editors (fix #110738) d19c7c52fd fix #110740. c4f50933f7 Remove some unused issue reporter code c839f49d8d Inline DeferredPromise Noticed that this was imported from /test/. Not going to make it a nice async helper because this isn't generally a great pattern. It's used in SearchService right now sort of as part of a workaround for issues dealing with EH search providers. When we straighten that code out we'll be able to just delete this. Fix #110610 566917c00b :lipstick: 0408c0010f use piece tree for size and line limit calculation. 6327e4b8da re #110581. adopt text truncation in text/plain. 09b47ace4a re #110581. truncate large simple text output. d878434d63 prevent keyup of action baritem without keydown fixes #110745 4fb3e8e6c1 set tabindex on submenu entry fixes #110765 9c3522ec80 Include experiment info in issue reporter data 274bf78da4 Merge pull request #110748 from microsoft/misolori/notebook-styles cabfaa63d0 Merge branch 'master' into misolori/notebook-styles 00fa5d3884 [typescript-language-features] Update importModuleSpecifierPreference values (#110536) 96949c76e5 Merge `IKeymapService` and `IKeyboardLayoutService` 8f32cbb1d6 Align `IKeyboardLayoutService` and `IKeymapService` c2f89b213a Move keyboard mapper instantiation to KeyboardLayoutService c0d53daa9e Emit `IKeyboardLayoutService.onDidChangeKeyboardLayout` only in case of a change 3224201b07 expose current flights on exp service 771f9f9a48 Update distro 744694ed31 Extract usage of `native-keymap` into IKeyboardLayoutService e7fe18ddf8 Update default styles and polish focus treatment cce8f8bc69 Fix #72878 6b85b9ff0e fixes Title: custom title zooms in #110716 fdd103cae2 Fix #100498 15353669a6 Merge branch 'master' into joh/tsQuickRename 8134d8c9e7 rename to OnTypeRenameRangeProvider. For #109923 0b8a242be0 surface latest jsbeatify settings. Fixes #84606. Fixes #79334 ec8e86a03e fixes #109603 for macOS web e62a2054a1 Merge pull request #108451 from justjosias/patch-1 ce864da274 fix showing remote extensions f663779523 Merge pull request #110572 from Wscats/patch-2 00525e9f7c Fixes #110603: Add `diffEditor.wordWrap` c704881abb Validate dynamic configurations only on launch 62e830be77 Git/SCM: Use vscode.open and vscode.diff for a better editor opening experience (#110733) da9a12b837 Fix #104055 9ba554073b debug: add unit tests for repl output count 11d835b65d debug hover polish the tip 97b9ca6028 debug console: respect element.count in aria label b39c9d3cc9 assign extension to menu actions 0454fdfcd7 fix tests cda2529387 debug: collapse identical lines in the debug console 876d1f09cc introduce new variable ${fileWorkspaceFolder}; fixes #84162 ea3b8da874 also quick rename type parameters, ignore declare only things e6bb245b6c use predefined constants f9224001d5 Merge branch 'master' into joh/tsQuickRename b04e9c8f9d :lipstick: generate command docs 504b891937 move commands to their locations (#110714) 8f9aeb83da fix invalid command converter behaviour 2b06004f0d :lipstick: 402e7afde7 https -> http in port forwarding view 85714e1007 clamp suggest line height between 8 and 100, https://github.com/microsoft/vscode/issues/110078 e416cba1b3 fix https://github.com/microsoft/vscode/issues/110707 46ff33b6b6 Subpixel antialiasing in some setting item labels Fix #86336 b4f09c5de3 Change search.actionsPosition default, fix #107163 2f34433853 Tweak settings tree sizing Fix #109095 4618539da2 💄 af6843bacf Convert terminal ext api null args to undefined Fix #110253 0180ce8eac Bump distro for merge resolve 89a418cc43 Add vscode-regexpp to remote/package.json 5afc5cd160 search: intelligently normalize crlf in regex search 7dde16206f Merge pull request #110094 from olivercoad/conflicting-autoclose-pairs 5d2ae44c01 Small style changes d1d59f8b74 Fix #27716 5da5495a7f search: fix copy all containing extraneous crlf line endings fbaf7b1187 fix #110427. 7a1ee06e24 increase titlebar height for macos big sur (#110592) 7a5fa10646 Merge remote-tracking branch 'origin/master' into pr/olivercoad/110094 8c76afad6c Fixes #108160: Handle padding in mouse target computation 9d389f0364 Handle error case for candidate ports Part of microsoft/vscode-remote-release#2776 f2ea8849e4 Merge branch 'master' into patch-2 c6efea07b0 :lipstick: c5e6489b3d fixes #110430 f3efe70c9a don't attempt to convert API types inside the renderer, break up mainThread-api arguments (and plan future removal) addaad3f99 Don't have 'show local' button in open dialog when remote Fixes #110264 043d8e2086 #109019 make fileSystemBackupsHome updatable 19c93615dd Properly call dispose on extension created tunnels Part of microsoft/vscode-remote-release#3961 0ec4ce387d Fix #110574 04be17a373 Fix #45244 17813ce44e Open command options are not mixed-in in the right order eea8648466 Use vscode.open in NPM scripts view Part of #110497 a9837acc6e Optimize code readability b851bf9ca0 Merge branch 'master' into joh/tsQuickRename 2ac7284762 add api argument stubs for strings and numbers 5c1789667c debug: make exception widget accessible 63ac48bf6d debt - move api command things into extHostCommand so that they can be contributed from anywhere 01dd9f0307 :lipstick: 71e24bc99b Fix #39543 70061a8b22 modernize vscode.resolveNotebookContentProviders-command, fyi @rebornix didn't find a unit-test for this! 443c0db7c9 modernize vscode.executeColorPresentationProvider-command e25b8819f7 modernize vscode.executeCodeActionProvider-command f3439ece09 modernize vscode.executeCodeLensProvider-command 94a57406aa deprecate old api command story 74703883e3 modernize vscode.executeSignatureHelpProvider-command 883936de2e modernize vscode.executeCompletionItemProvider-command d310cba731 Handle conflicting multi-char auto closing pairs (fixes #72177) 4e4f02f632 Allow ms-vscode.github-richnav to access proposed API c5ee747748 Merge pull request #110541 from loganrosen/ember-cli 29e7305d9d progress bar: report progress 39bea31ae8 Update classifier.json 1f11563975 debt - move contrib only service to contrib (output) 5b5e79558a debt - lift checkbox to workbench 76a8ea640e Add undefined to TaskProcessEndedDTO.exitCode (#110508) 0998e4ccbb respect user settings when creating terminals; fixes #109111 5d7ca3a879 debug hover: show tip to show language hover 45ce70792d fix tests b5cd02f178 #106348 Adopt codicon for npm view 3ee6fc3f68 Fix showing newly enabled extension in enabled section 3d8ad7d74e tweak setting for #5312, fyi @usernamehw 336b58c8a2 Fix #106348 f1560e9a19 Merge pull request #110494 from usernamehw/word_suggestion_any_language b07d19a768 storage - remove store2 (#109967) 8f82b02a4c Merge pull request #109842 from microsoft/joh/98228 20b49a4a9c storage - more target adoption (#109967) d9893b5d64 :lipstick: editor memento 38f55ece05 environment - add OS info (#106528) 710da39135 Mark .ember-cli as jsonc instead of json ee46c1f89e Adopt Action2 for more search editor commands to fix #110407 bf6776b980 fix #87730. 293af2074e handle last line of copying lines. 63e42d414b re #97497. f47b588736 :lipstick: 629cf65721 fix #97497. 591039e34c debug: fix compound integrated terminal launches breaking cb7297a10a :lipstick: 2b900dcf11 fix #110503. 4358d8bf30 rename 'dirSep' to 'pathSeparator' b155f9757a Update seti df143f19b7 no double spread 8d73ce7ed2 :lipstick: no more homemade iteration 2fdc607f19 Merge pull request #110273 from microsoft/connor4312/fix-search-freeze-on-long-lines 217ad5ac66 Typo: Uncommited → Uncommitted 7f6ea40bc2 terminal: typeahead state management for verified lines 4c46272797 Stores the actual config object in the cache e96cfbccbc 💄 576d8af2df Changes to use TernarySearchTree 8a5605bd21 Splits out loop & uses regex to improve perf Addresses PR feedback d21cbabe6b Adds default context caching for objects Clears all cached config context values for objects 165e49bfe8 use rename info instead of highlight info ce7bd67d46 explore on-type-rename for TypeScript 16ffcb50fa adopt latest references viewlet, https://github.com/microsoft/vscode/issues/85636 611f28952f trustedDomains: loosen restrictions on what a * can be fixes #110501 b0ce55403a Merge pull request #105887 from nrayburn-tech/dirSep edaf854a74 Remove "files.autoSaveDelay" from Commonly Used 753a1c095a Adjust debug toolbar shadow (fixes #110422) 561501492a Remove unneeded constructor from TreeItem2 0286c4f793 Add missing bit of TreeItemLabel API 4dae492ac7 Merge pull request #110428 from microsoft/connor4312/disallow-nested-config-resolver-keys 31b0068af0 Make TaskProcessEndEvent's exitCode reflect the truth Fixes #110185 630e706ad2 Add open context to certain commands (#110475) e526f16131 Prioritize configured tasks in `getTask` Fixes #109939 2e15bd0592 Add a setting to include word based suggestions regardless of the language 7b5849b123 Fix build ea5e971611 Tolerate strings again (fixes #110432) b785be4c73 Finalize TreeItemLabel API Fixes #61579 5758f6fcd4 fix cmd + down in open editors view fa856cf019 #67603 Add new extensions automatically to the list 7bfc7edadb open editors: introduce sorting 37103467dc dispose list elements on list dispose dc74330d39 fixes #110429 690db82ef2 tweak fix for #29210 and thereby fix https://github.com/microsoft/vscode/issues/106090 52e9e02e0a Fixes #110141: Do not push undo stack elements for a no-op EOL change 549da12a6d fixes #104061 717a730f3a window - fix undefined access e4da961570 Merge pull request #110455 from microsoft/joh/open 94d3497dac :lipstick: a9c04968f3 Merge pull request #110423 from dsanders11/patch-1 1cfb74c330 windows restore - add a new setting choice to restore the previous session even when opening a file or folder cc9616baf8 use 'enablement' instead of 'preconditions' e275af7e79 render activation events as code fb0128c7ec Fix progress bit overflow bug (#110411) b321d3c364 Merge branch 'master' into joh/open 33a49a9c63 Fix #67603 02316b635d editors - fix listener leak (#110336) 050a123750 Fix F6 in extension page webviews 4da8e50404 Don't show users a notification if a save is cancelled (#110344) 17c29f0b99 Add support for web TS Server logging over postMessage afcfc97316 Fix method spelling 7c6994d1b5 Use toOpenedFilePath to ensure we don't ask TS server for projectInfo if a file is not open fb93010aa2 Make sure we use the same path normalizer in a few additional places in buffer sync 805aa7e8f4 Make ResourceMap treat the same file with different scheme as different 67817c33a1 fix #40713. bb8d5f1842 terminal: update typeahead tests 0ef0d2d65a fix #109765. 97664e1452 fix #74622. b2eca1fd4d Fix InMemoryDocument to support both OS line endings Co-authored-by: mjbvz Co-authored-by: meganrogge bd98013973 insert a 1 sec delay before using a newly created integr. terminal; a companion fix for #38578 56947b3a75 terminal: only start typeahead predictions after the first prediction for a line is valid 65c3fb0a08 [css] add ':' as trigger character. Fixes microsoft/vscode-css-languageservice#108 13669e6bbb fix https://github.com/microsoft/vscode/issues/110420 02f6319ca1 Fix autoSaveDelay missing from Commonly Used, add warnings for settings patterns d5febf9628 config: disallow nested config resolver keys fac5cc3a54 #102906 add tests df171cfcab #102906 tweak wording f086ee122c Set StorageTarget in auth service, #109967 dea609b7ba #102906 wording tweaks f1c6ce7c9e clarify effect of command enablement b3c2cc3a7f debug: fix integrated terminal not changing drive 0e708ea0f7 fix #90897. 776d80f470 Merge pull request #104997 from rotem-bar/jensui/102906 0d23be5d74 Merge branch 'master' into jensui/102906 a48ad3b1d8 clear find widget reveal timeout d55e884024 Merge pull request #110370 from jeanp413/fix-107104 166f7a4cb0 Merge branch 'master' into fix-107104 f066dfcda8 Fix #67905 4ef91c2e40 Fix #94416 593d3dd94d debug: normalize drive letter in loaded sources view f2ccceef3d fix #84772. 49d1396aeb update pattern and add to grammar 900bcb7632 Merge remote-tracking branch 'origin/master' into connor4312/fix-search-freeze-on-long-lines 3e797ae1aa Update drop shadows (refs #92301) a986b578a2 fix #106570 4d6c4ae0b7 insert a 1 sec delay before using a newly created terminal; fixes #38578 6a8fe0a8da let CommandsConverter actually convert commands, make sure "magic" commands handle uri components b794105cb6 Fix #90548 0bda02ba0d tweak throttle based on actual numbers 40558fc8e1 report `suggest.durations.json`-even only every 500th time 2b21aab9e2 fix #110410 8367f475e8 explorer: use EditorResourceAccessor 5bf6d0cabc fixes #65188 d7b84b8fd7 update monaco.d.ts e69d768e53 adopt feedback for #109923 776193fdc0 explorer: reveal diff editor ab7c9d605a fix reading store from config 740087a3e0 #109056 remove hack to use dialog service 61970cb6f3 preview - fix italic for description fb5e7bae09 do not remove from remote if key is not registered - compute unregistered keys from last sync state 86621ccdee do not remove key in cloud if it is not registered 87fd567da0 add commit characters to explain mode, https://github.com/microsoft/vscode/issues/110382 efef7c1d15 layout code lens content widget when adding commands, fixes https://github.com/microsoft/vscode/issues/110332 bcf2f23cef Can't save workspace locally when using remote extension. Fixes #109713 49342801cd [semantic highlighting] improve fallback scope for macro. Fixes #110150 14075af62e don't resolve code lens when typing, immediately resolve code lens after receiving them, fixes https://github.com/microsoft/vscode/issues/96783 49bd4e4b34 Merge branch 'master' into dirSep 82c65e753f Add Refresh context menu action to candidate ports Fixes microsoft/vscode-remote-release#2927 d4e1ae56ed fix alpine build 5e5ce9ec53 Better compacting of URLs in ports view 8dc55cf219 fix sandbox 8c8e2a888c debt - make dialog handlers a workbench core piece c58384b87b distro eba1343a10 cleanup linux alpine build b7cb5e2e1a Merge pull request #109643 from eltociear/patch-1 f27d73be72 fixes #110353 14140d2a19 Revert "Merge pull request #109049 from digeff/searchView/context/extensionPoints" 31c034604e remove old forUris, rename forUris2 to forUris, https://github.com/microsoft/vscode/issues/110241 a19b26ef69 Fixes #107104 7a322c44db Try to fix the build f5898a0e59 surround match in zero-width spaces to avoid clobbering 3f62d10652 Removes deprecated IDefaultLayout interfaces 35f9bac07d Disable markdown smart select tests #110365 0572704226 Make settingLayout patterns strict Fix #110129 7ba372c80c Fix tests f23e47b9a2 Extract getParentFlowToElement c8877809c9 Pass webview editors an explicit scoped context key service aab1c59227 Use UriIdentityService for #110241 a28b99d78b notebook: echo complete error object on preload function exception 49695de8a1 notebook: execute kernel preloads in series 2b9e62fcf0 Merge remote-tracking branch 'origin/master' into connor4312/fix-search-freeze-on-long-lines 00f32f9a26 make search editor ranges work for hidden text 046654ae65 make search editor ranges work for hidden text a856e60a0c Better support dragging and dropping with webview views 10514bf423 Fix webview scrollbar colors for firefox 2b820b4bff Hook custom editor backup cancellation into the extension layer 7a4e81a674 fix catastrophic backtracking when parsing markdown files (#109964) a1bb5ac207 Merge pull request #109049 from digeff/searchView/context/extensionPoints 9a08a10708 Support ctrl+v PS override on Firefox too 982bfc2334 Update decorations synchronously when the wrapping info changes 86f773db82 Merge remote-tracking branch 'origin/master' into searchView/context/extensionPoints 559f9b60d1 Applied feedback 809db2993b fixup! 63d15a97c7 Support character markup in mardown smart select (#110195) 4bfa0a0aca fix build db6fbd581a Merge branch 'master' into connor4312/fix-search-freeze-on-long-lines d2701267df Potential fix for microsoft/vscode-internalbacklog#1604 038835d88c Merge pull request #109551 from maldahleh/109255-support-detail-text-settings-dropdown 1ffb469a15 add no-drag to context view css in menu.ts fixes #110282 1ce5b5773e fix #105757. 3030d4f380 Remove registered color for selectBoxCustom detail 414c387375 fix #108950. 0dafeae79f :lipstick: 636c588eda Refactor DialogService to use model and contributions (#109980) 57203b243d debug: add ability to close exception widget 92b9426701 Remove unused variable 014cca917e Merge branch 'master' into 109255-support-detail-text-settings-dropdown cb23c0892e quote powershell arguments properly; fixes #68151 5184dff52f terminal: fix typeahead edge case fcfc8531aa #110241 fix tests 96acd5f37f Merge pull request #108401 from turara:resolve-78733 3ef3247e85 Remove participants list from comment headers 140ce6130d adopt latest references viewlet extensions, https://github.com/microsoft/vscode/issues/85636 e7a598f648 Allow context view to align with anchor (#110277) 50416be1c6 #110241 Adopt TernarySearchTree.forUris2 00c4572909 Merge branch 'master' into resolve-78733 7065675056 Fix #109177 77154ae5c2 Merge pull request #110272 from microsoft/misolori/shadows 0a7f58fbf7 Merge branch 'master' into misolori/shadows 5fbd3b43bb Style terminal scroll bar on Firefox 3ab3514bd2 Force ctrl+v to not be sent to shell in Firefox 3d61a39074 fixes #103031 d34ba652f3 clarify explorer.enableDragAndDrop setting 02bd5f139b fix focus issue on list e4cbfba51b Don't change LANG vars containing 'euc' 09ab4faf90 :lipstick: remove LinkedList#toArray which became obsolete with iterators d10b4cc81e polish when selecting config ab6a005750 Fix #86077 d26f927791 Merge pull request #110268 from microsoft/alex/diff-word-wrap 415955a5cd Allow word wrapping in diff editor cd395984ca Make inline lightbulb aware of wrapping of changed or deleted lines 982689e007 added GH PR extension to list of tracked repos 912e8f7e99 Merge pull request #110306 from microsoft/joh/wordBasedAll e8b69537cd fixes #40295 74b2510985 word based completion includes words from other files of same language ad88db1031 fixes #110304 76abce66de Merge branch 'pr/107958' ddefc990cd correct codicon size for code lens placeholders 62877b66d8 Merge branch 'master' into pr/107958 0e4b1fec5d Render char diffs in the wrapped deleted or change lines in the inline diff 13f9f73353 :lipstick: 1d18ebe47e Merge branch 'master' into pr/107074 ecfb8771e4 Merge remote-tracking branch 'origin/master' 6a2da6d725 :lipstick: 7200f73de2 Merge branch 'master' into pr/106368 77a57165e4 debt - race more minimal edits computation against 1sec timeout 2c1272b794 fixes #104945 64ef9869b6 Fix filtering on * for simple file dialog Fixes #110265 55fdf94d67 fix https://github.com/microsoft/vscode/issues/109776 cb63744475 Adopt TernarySearchTree#forUris2 (#110241) a56c7406d7 Fix issue where toggling between inline and side-by-side was forcing word wrap off 3f8e8a1733 Push view zones in the original editor when the original lines are wrapping in the inline diff case 438f610860 strict path casing in extension host profiler, https://github.com/microsoft/vscode/issues/110241 156d702f5a Diff editor: show path as description (fix #109224) d751b6bcd2 add IExtUri#ignorePathCasing, use forUris2 in decorations service, https://github.com/microsoft/vscode/issues/110241 f2a9b63c64 Do not show hover on icon label after click Fixes #110284 f5e9a01715 make default not ignore path casing, https://github.com/microsoft/vscode/issues/110241 3bf2afdc01 add TernarySearchTree#forUri2 which allow correct path casing handling, https://github.com/microsoft/vscode/issues/110241 21c84a4351 restore old case ignore behaviour unless explicitly set 9b0522f3dc scmViewPane: do not render whitespace in commit message input field (#107913) b3cc19b819 :lipstick: use async-await for code lens provider logic c0c4ceed44 fixes #110290 351e6172e1 update code lens when editor gains focus, https://github.com/microsoft/vscode/issues/83363 0216c71896 Revert "Use editor base weight when contributing peek commands, fixes https://github.com/microsoft/vscode/issues/109727#issuecomment-720986472" 1357f6d494 editor preview - ensure simple file dialog opens pinned too 65f805d98e fix build 6c32ada9b5 show extension editor manage action always 2815e732f7 Fix refocusing clearing existing forcus for iframe based webviews 4247b296eb Including ` as part of foldEndPairCharacters (#110108) d04956f6a7 Switches the 'Fix all' quick fix provider to use resolveCodeAction (#107853) ffb8c08dd9 Enable IgnoreMenuShortcuts for iframe based webviews df7fdd6515 search: fix freezing ui on long lines c9bebe2cc9 Fix #98041 37a01430cc Tweak shadow colors 840bc2ef1c Set explicit StorageTarget for webviews 0efbfd9190 fix #108765. c067c9b947 Improve rendering of view zones with changed or deleted text (inline diff editor) 59b020a362 Merge pull request #110255 from microsoft/rzhao271/format-uri 8248e19817 Merge branch 'master' into rzhao271/format-uri 5ce31a6e8b fixes build break b473bc55de Clean up drop shadows 2f7f100e44 Render inline diff view zones in batch 701e6713f9 Merge pull request #110112 from microsoft/misolori/squiggles-bg-theme 93a7a07559 Do not store the original content in the diff information for inline diff margin actions 2b9ae05554 Merge branch 'master' into misolori/squiggles-bg-theme 51eb0901d4 add setting for code lens font family and size, https://github.com/microsoft/vscode/issues/16038 5e8f9cf1d2 :lipstick: code lens fcef0e3db4 Use Array.flat() (#110189) 4e0c77f8aa Add `IViewModel.createLineBreaksComputer()` e300dfcdd2 terminal: allow excluding programs by name from typeahead b5f6a521e0 Fix vscode-emmet-helper issue #1 d29487617b update language e5530fc4f0 fix #105920. 62d39a7dba Adopt storagetarget in Exp services refs #109967 98a3c4fe64 reduce work when menu is not visible fixes #108712 ef6b438769 Fix #88703 a45abdbd1b Update seti 87d49a7b66 Fix #108300 226503ba0a Merge pull request #110213 from jeanp413/fix-110212 ea6ee515f1 Add color tokens for warning/info bg f1ebde547c fixes #109781 876af4ccfd no max width for welcome buttons 1c7d982b93 fixes #105201 Co-authored-by: rebornix 6e6654a679 Fix #91534 f0580d497e Fix #101441 d6fb7989f5 Push view zones to accomodate equal but differently wrapped lines 1256b16ee1 Merge remote-tracking branch 'origin/master' 7509a0103e Fix #103941 91aa548b05 Merge branch 'master' into pr/104614 4ecf3f8f02 Merge remote-tracking branch 'origin/master' 98e0e93ae0 :lipstick: 61500a468c Fix #94289 c96f362934 Merge branch 'master' into pr/99324 f18700d036 Merge remote-tracking branch 'origin/master' 309cabdf51 :lipstick: 961cbd66b7 CLI help: consider to separate --file-uri and --folder-uri. Fixes #110206 978d39324a Merge branch 'master' into pr/104312 e7e38c161a Be aware of line mapping when rendering diff view zones and diff overview decorations 7e5609afa9 feat: implement Git: Push Tags command (#110096) e3754e6f8d group uninstall and install another version actions into same group 294406d7a1 Fix #110121 23579d815f [file icon theme] icon match the last word of folder name not the whole name. Fixes #110183 ec084a25bd :lipstick: df4524f713 Merge branch 'master' into pr/97525 86d848d8e7 :lipstick: 592fa5cdaf Merge branch 'master' into pr/97486 fd306e8cb4 Merge branch 'pr/97322' dd97a9d335 :lipstick: d40499936f Merge pull request #108682 from Siegrift/enable-tsec-language-service-plugin 551db7ec94 Add new fileDirnameBasename variable Fixes #78316 8cfcd9a3fe Merge pull request #110178 from vfcp/master 2720a8022e Allow user tasks to run when no folder is open 66ea9c5ca1 Pass in actual editors a0031aa6ac Small refactorings 07f0bade37 remove duplicated context key expressions, fixes https://github.com/microsoft/vscode/issues/97381 5290baabac improve message when files is too large for formatting, fixes https://github.com/microsoft/vscode/issues/105986 8cebd10782 set line height for suggest details, fixes https://github.com/microsoft/vscode/issues/110172 3ed1d0050c Merge branch 'master' into enable-tsec-language-service-plugin 14767349ab window - some renames :lipstick: 147f623629 Merge branch 'master' into pr/97322 4439de9a3b tweak setting name `editor.suggest.showInlineDetails`, #109690 7efc22cc85 git.publish: do not await notification resolution related to #109977 776541c380 Setting to Disable Split Editor on Drag and Drop (fix #71016) c73c0b30f2 :lipstick: let -> const 35ff2c0df8 fixes #110012 85958bcecd fix https://github.com/microsoft/vscode/issues/97451 aede2434b0 fixes microsoft/vscode-remote-release#3180 5a534883c7 Fixes #110212 875c2ced66 Finalize FoldingRangeProvider.onDidChangeFoldingRanges. Fixes #108929 1fab0083f9 Renames 7ca4a47ba5 add vscode-test 4e24bef648 :lipstick: 54ba2bad67 Merge pull request #104923 from AE1020/scrollbar-paging abcc621872 Rename option to `scrollByPage` 3517c02303 files - stop checking for BOM before writing 7bfd7fb685 Keyboard shortcuts for switching focus between left & right sides of diff view (fix #95068) eb8c718e81 debt - fix compile errors in bootstrap files 3154dd4f69 editors - add workbench.editor.enablePreview to most commonly used settings 59a1893d6d Log number of loaded certificates (#91794) f9e19c9ad1 only hide outputs when output is transient. 148b85862a fix #89250. 9ad6e7edf9 Correct themeLabel in package.nls.json for "Tomorrow Night Blue" theme 7095ef144b Revert "Revert "Fix #99971"" a2963771d1 hover info should not be broken within word (#106885) 024df33553 feat: add setting for default stash message 109d9984c1 fix: commit template appears as default stash message 19c0c60064 feat: detect default stash message use commit message as default stash message if commit message box is populated 03c38e6702 Add more logging to github auth provider 1d5854f611 Unassign myself from the classifier 3338ff4e18 Fixes navigation commands for webviews 6e2aa0bfb7 clean up code and remove unused methods ce7e6d9978 Bump actions & fix #109699 153ba443d5 Use string.matchAll (#110074) b419f2f169 Adopt StorageTarget in searchEditorInput Ref #109967 5961799c53 Escape backslashes in issue reporter data, fixes #105494 be410d1a3d explain why we modify textarea input handler in firefox. fc07b2f170 add comments to clarify code 83f000df03 Merge pull request #106873 from belcherj/patch-1 cb044cec21 Merge remote-tracking branch 'origin/master' into pr/belcherj/106873 339681f631 Use excludive selectors for search editor contributions to fix #91453. cc @jrieken fca7107884 fix #105901. 35bad7021d disable submenus rendered as dropdowns (#109934) c8cfffe09f Add vscode-encrypt to nativeignore df9cf627b1 Merge pull request #110095 from microsoft/rzhao271/webgl-description 9d5dbc6a1a Bump vscode-ripgrep 9d0346ad2f Fix #107858 c91facce0a Add an action to remove recently used tasks Fixes #93403 8335fcc3fc debug: do not auto pick dynamic launch configs 779a171f9d Align configure task quick pick closer to terminal Part of https://github.com/microsoft/vscode/issues/93864 ad19579a44 Don't clear filter for Show All Tasks Fixes #97467 15afc78232 Fix exthosttree tests a1025c64c1 fixes #110077 9f2ce53867 debug: stop supporting enableBreakpointsFor 081632d225 debug: remove legacy adapterExecutableCommand e95c40c1e4 API proposal for revealing tree view when no elements Fixes #90005 ce4fb6e323 fixes #96264 0cc93f5c48 debug: split ConfigurationManager into ConfigurationManager and AdapterManager 6ce30f1dce remove deprecated debugAdapterExectuable debug proposed api f53678cb4b Update branch for sql grammar Fixes https://github.com/microsoft/vscode/issues/109251 85534c004c Only reset theme icon color in treeview once 4b0855f292 Improve the dynamic launch config UI f63d695254 Fallback to userhome when no "file" scheme folders open (#110066) 0302b84221 web - set version to 1.52.0-dev 63ce8b4732 fixes #92146 d03925a88c Merge branch 'master' into pr/95915 e380037a06 [themes] Use light theme for --disable-extensions. Fixes #97058 168ebd1e66 fixes #95599 256066f94c Fix preffered pm when pm can't be determined 57ec100886 fixes #40548 1593959b5e In case of multiple possible longest common substrings, try to give preference to the ones containing the most ammount of text 24b28f57be Always use `vscode.open` to open markdown links 7f7307b89a Fix command for renabling extensions when running with cli flag, fixes #94532 4059ff428f Allow cancelling GitHub sign in, fixes #109101 93d736db23 fix #93326. e026e9a77e Update JS/TS grammars 194068f693 adopt StorageTarget in SCM #109967 48ebbe9ffa Merge pull request #110038 from aasimkhan30/aasim/fix/checkboxOutline 663a049f0a Merge branch 'master' into aasim/fix/checkboxOutline 25c6703f39 debug: fix debugees blocking when writing stderr 024368a638 removed unnecessary important from outline-offset in checkbox c258fff193 Fix compile error 80a9ecff3e debug: cancel hover evaluate requests when hovering off dialog 451c2b61d3 Specify type of CodeLensProvider instead of using casts 9f4737fa3f Exclude definitions from js/ts references code lens d88ac6f164 Finalize the isWritableFileSystem API d54c3c67dc fix #74353 18902550f7 fix #70306. 11faf89369 added outline offset to checkbox to make focus visible. 05049f7fbb Add Definition list to Markdown Snippets (#110026) 8a7189599e Add color token for error bg 0440be9aa4 Fixes #110033 c2fafb10e3 fix #57197. a58d816580 Don't remove -l from tasks on Mac if setting is set Fixes #107563 5a73a68e1f Revert "Fix #99971" faf36c2f47 Fix #99971 fd951da3ae remote indicator :lipstick: 736a46fc8b Merge remote-tracking branch 'origin/master' 49990bebe4 add git CommandErrorOutputTextDocumentContentProvider 2eb805ad75 Mention Inherit Env setting (fixes microsoft/vscode-remote-release#3030) cd95d90589 Use correct context key service in custom trees Fixes #99767 ec471da613 Merge branch 'master' into pr/95354 cd6466909b fixes #92034 e3ace1fe1f Merge branch 'master' into pr/94369 7b6561ff5b Merge remote-tracking branch 'origin/master' 08d271585d :lipstick: eb1cea5432 Merge branch 'master' into pr/94358 8fe4a348ec Remove focus when scrolling (#94280) 544c986e0a Added a warning before dropping a stash. (#94267) 5afaec5c21 Merge remote-tracking branch 'origin/master' 7328c3ccb0 Merge branch 'master' into pr/91838 a089e24fed fix typo 818d57db15 Better diff for deleted file in merge conflict. Fixes #88973 (#91245) 36e2868372 preview - pin an editor when a navigation starts to another editor (#109779) d6af4893ed Update grammars 1dbff8bdd1 Remote Host status bar entry has no max width (fix #107451) 47a956eb2e Fix #109023 9832b90fde explorer: Revisit explicit use of ConfigurationTarget.USER when updating config 520a050cd3 Add quotes to autoClosingPairs for make Fixes #89191 d43e45b796 updates 578d7381da fix #7989 b0a7c8496d IconLabel markdown title debt (#109914) 786f507696 Fix #107320 f021eee884 layout - write to correct config target (#109373) bfb73347bc Added a grooming delta notebook a69a01af9e Remove unnecessary argument (#109373) fa4f11cf16 fix stray console.log aaf17b5d9f Ignore dev container logs in url finder Fixes microsoft/vscode-remote-release#3953 ac2ca0b539 typescript - fix nls issue 1cbdea3da0 Revisit explicit use of ConfigurationTarget.USER when updating config. For #109373 a69f07abf0 Revisit use of ConfigurationTarget.User in tasks Part of #109373 22c2bad746 Merge pull request #109966 from microsoft/aeschli/remoteThemeFix 4f88f448a0 Adopt StorageTarget. For #109967 c486bbf493 editors - add a menu item to keep editors open 1082f3e58c Adopt storage target in tasks and remote explorer Part of #109967 a3658a2fee Merge branch 'master' into scrollbar-paging 1c0d056915 editors - some preview tweaks 19044c847c #109373 do not pass user target 92b6b9c80a storage - adopt more storage targets (#109967) 805ba83b59 Storage service error in shared process console (fix #109985) eae74f5125 Loc comment #100941 e57af55eaa Pick up latest TS nightly for building VS Code 4a54027a82 Disable rename after refactoring in interactive playground e6079bd127 fix #105614. 373ea1b969 Fix build 6eeaab8e5d Handle cancelled sign in when publishing to GitHub, fixes #96502 2e6d3bdf6c fix #27836. 0f623956c2 Show dialog when there are no trusted extensions for account, fixes #96359 5463322d19 Clear trusted extensions on sign out, fixes #96616 f4e49a5f3a php: revert break deindent behavior 6bbb17d318 terminal: unregister csi handler when not in use for performance 81a25a894c Rename implicit project settings and deprecate old ones ff7cc33236 Use objects.equals for comparing configuration objects 05fee0cf3d Extract implictProjectConfiguration class 64705a07dc Add two new setting for strict null and strict funtion in js/ts that are implicit projects c38a12c26c Quote jsconfig and tsconfig in setting 341124ff39 #109896 Remove IStorageKeysSyncRegistryService and introduce new service for syncing extensions storage 4a6a5ab1f8 Explicit fontLigatures description 05602b399f terminal: clear typeahead after timeout 8141dcf29a Extract adjustFoldingEnd 97db458fa4 Show closing ) while folding JS/TS 0608e3f5bd Pick up TS 4.1-rc for insiders a7cd9408a9 add comment 6d5736aa3c #109967 Adopt to new storage API c2dafddbae Add splitLines helper function (#109869) 73eb361f29 notebooks: expose scriptUrl global to renderers and kernels 4286c603d6 Remove unused variable bb8e52bca6 Update comments panel message text, fixes #91623 7183777033 fix: run to cursor with multiple paused sessions d11872b127 Fix comments panel focus, fixes #103466 b35ea7e5a0 fix #54829 08e556dc5d fix #109486 and #109489 f8a91e4210 Adopt IStorageService.store2 in terminal 320602f7b6 Fix #109860 660222bdec fix https://github.com/microsoft/vscode/issues/103818 267817cf46 debug: adopt storageService.store2 bc5c7923f8 Merge branch 'master' into aeschli/remoteThemeFix 7ae6a45879 adopt StorageTarget, https://github.com/microsoft/vscode/issues/109967 35026262bc storage - avoid Object.entries in platform 2c292d3af4 fix tests 92314d61a5 reverse the eye open/closed icons for hiding snippets 2db5a355bc storage - adopt some StorageTargets f5f4af6db3 Update C++ grammar 5e4c53cffc add another test for https://github.com/microsoft/vscode/issues/96545 c8d123ded5 Explorer: move off iterating over file changes 4b9e0bba6e Merge pull request #108964 from turara/fix-96545 3c33f50f39 Merge branch 'master' into aeschli/remoteThemeFix d0343e68c0 sequencer for set and reload theme operations 9db1b1492a Merge remote-tracking branch 'origin/master' c3b5f2bf1c cleanup for #89313 09829053d1 adopt storage changes in extension bisect fa61ee7caf fix flawed comparison, increase threshold ea84245395 Lint Sprüngli e3a79e9e44 [css/html/json] Randomize inspect ports to avoid conflicts 1b9cd42b9b remove unused import 0382313947 Merge branch 'master' into pr/89313 4cd46e32b2 Merge remote-tracking branch 'origin/master' e5285908ca :lipstick: f923ac98e9 storage - document enum better 90aa30e660 Merge branch 'master' into pr/89249 fe458b4aa2 Merge remote-tracking branch 'origin/master' 6029cd4e3a Merge branch 'refs/heads/pr/88772' 163bd1dcb3 storage - rename storage change event e216a598d3 don't presist little size changes ea14fc2c2f add command to reset suggest widget sizes d562ace335 adopt to new storage api 8f5311c771 Merge pull request #109960 from microsoft/ben/storage-target 4f038dba60 :lipstick: e6b53ff8da adopt using new storage service to sync storage 744eed0a96 Add icon in Windows explorer window (#86283) e61be087c6 Merge branch 'master' into pr/88772 a665899915 fix compile and tests 194c7ea237 test name dfb74a6c97 some comments and more tests 1f7234c0ee workspace color theme setting not picked up 214125ebcb fix https://github.com/microsoft/vscode/issues/101870 c2944c6f22 another failing/skipped test for https://github.com/microsoft/vscode/issues/101870 130aa96b42 add to #109818 0feb41eed0 test machine storage is not synced b561be9e4c Add url finding for python 2.7 simple server Fixes microsoft/vscode-remote-release#3949 59bfe65e18 store synced state in user scope 8145e9f37a Fix local scheme for registered file systems in file picker Part of #https://github.com/microsoft/vscode/issues/109345 c49bc9b1cb fix tests a0ce43416d make snippet prefix optional and hide snippets without prefix from IntelliSense and TabCompletion, fixes https://github.com/microsoft/vscode/issues/73635 8b712ee66d - Use global keys with user scope from storage service to sync - Remove versioning of keys b449c95a84 Merge pull request #109916 from microsoft/joh/snippetIgnore 1f7528568e storage - add target to change event and cache targets bd7a83c110 tweak wording 41d6a0a560 Merge branch 'master' into joh/snippetIgnore 21cfbaeeee Merge commit '265a2f6424dfbd3a9788652c7d376a7991d049a3' into joao/terrapin 57c7e40a50 bulk edit - respect sideBySide options from event 7a00f134f7 do not show sync icon when extensio is not installed 265a2f6424 Supress marked logging for using sanitize 983ced6961 Changes to const enum cddb1b75f1 Moves newline handling into the hover Adds enum for parameter e06d374787 Avoids paragraphs for newlines in string tooltips This better matches the native tooltip styling of newlines 1c2005331c [json] use links for $ref, not goto definition (fixes #101474) e934668bd0 Merge pull request #109833 from fleon/patch-1 c68b238af3 Use `self` directly 51a4bbdfa5 1.10 is bad on macOS, revert this temporarily Revert "chore: bump vscode-ripgrep@1.10.0" ab9bb823ea debug: allow serverReadyAction to run a launch config by name 57340b736b Warning when invoking explorer.newFile (fix #100604) (#109905) 7831795dcb Backups: remove legacy migration code path. Fixes #101484 9c00847244 OnTypeRenameProvider API wording 529388e524 :lipstick: d88f099116 Remember History on Debug Console filter between vscode sessions b9833bb962 Fix #99105 7278373f3d fixes #99709 bafccc8ebb format f17f51878e update milestones in notebooks ffd61f2917 fix markdown string integration test 0e43c7a495 Include description in custom tree aria label Part of #108480 7dca88de88 fixes #64188 503135e9b8 expose snippet enablement inside "Insert Snippet" picker fc37a9d0d3 Move tasks onBeforeShutdown to electron-browser Fixes #108649 e52382a0b3 findExecutable should check PATH in a case-insensitive way Fix #109863 e01e0ebe39 [theme] Git:Input Validation Subject Length - not working on Monokai Theme.Fixes #101081 aba089cf11 Fix #95268 bd0c897c90 Fix #95211 20b534d41e add persisted snippet enablement 70227b45b5 remove unused label ef941c2c3c :lipstick: more async-await f5490aed3d Fix #108023 7936093c33 git: merge resources should return undefined original resource 5b3b6b8026 :lipstick: async-await a3a5d6a6e1 Fix tasks test 796db55744 Remove iframe around web worker in codespace case 56b686f9fd :lipstick: snippets 80d59864b1 appendText should escape leading whitespace, should escape leading quote character, also remove duplicated implementations, fixes https://github.com/microsoft/vscode/issues/68983, fixes https://github.com/microsoft/vscode/issues/109040 7d1affcf90 Throw useful error if task execution is undefined https://github.com/microsoft/vscode/issues/109861 5905725b5c Locally, but exported, constants hurt minification (a little) (fix #109899) eeaa42d650 storage - let flush return a promise when done 2dce4b75e6 Merge branch 'master' into joao/terrapin 1703e7e71e fix bad JSON a8036f5d27 Show current debug line in overview ruler 4ec0cd5f29 fixes #92709 153835e0f5 Fix #99963 e720530819 Open to side: do not first use fileService for resolving, since item is already part of explorer model 7601718ccc Use editor base weight when contributing peek commands, fixes https://github.com/microsoft/vscode/issues/109727#issuecomment-720986472 82ff980017 use item labels for themes dfaf4ef080 Merge branch 'master' into joao/terrapin 4d9e54c0b8 increase job timeout 37e16f845b storage - adopt new API in one place b64171bd0b storage - introduce onDidChangeTarget event 6f26a40b76 Fix #101612 183b2a2892 [themes] theme names are all not localized. Fixes #109840 c8b48d0d99 Fix #103438 8d429660a9 fix message cecd55133f Fix #106989 6a242516e4 storage - first cut target support (user, machine) 1d98078624 fix #106857 ad1dc7f87a Strip newlines from the code actions title 296aa89012 debug: improve attach to vscode config 6acc9ff1ee editor cmd+e find with selection. 90b17ed115 Make it easier to override markdown table stylings 699e3e5afa Update marked 49873aa674 Bump actions 1ca6360d5e terminal: fix typeahead eol naming 1351f36293 :lipstick: 06c401b358 terminal: cleanup typeahead tests 25e70bb455 terminal: update typeahead tests 5e636afbcf Remove unused imports 4e15db0b49 Fixes always white comment body border f25cb53571 Bump distro 4f1f830735 Merge remote-tracking branch 'origin/master' into searchView/context/extensionPoints bce86a137e Fixed other context menus using this context bf161eb5cd use mouse events for iPadOS instead of pointer events as iOS sometimes does not emit them 16d8eb9c57 Merge pull request #109630 from microsoft/eagerTerminalReconnection c1a6bc16b8 terminal: capture cursor position correctly in typeahead backspace 220a067d8e Merge pull request #109850 from kena0ki/issue##109636 3258a28683 clarify activity bar action menu layout refs #40262 fd9ace8458 terminal: fix typeahead leaving stray characters with backspaces cbb21a30dd terminal: restore old cursor style after failed backspace 933d888b2a Merge branch 'master' into eagerTerminalReconnection efb8cba23a Fix #108817 e6e9d6c36b add report issue to extension bisect, move issue service interface to workbench/services, allow to file issue against disabled extension if explicitly names, fyi @RMacfarlane 6c5cc99c77 Merge branch 'master' into issue##109636 a9dfd7e0ed fixes #109849 a0012e5f63 fix terrapin step 3b3b94525f fixes #95697 6e9fe5a638 fixes #71994 a25c5efdf3 :lipstick: fd8411e75e Fix #89559 4b49bae903 Update xterm.css c110357096 fixes #67685 a5d719c570 Try new Rust grammar (#108254) 6d20077821 fixes #67315 7964dfd6e8 :lipstick: async-await 8600d333fc debt - make editor smart select a "real" editor option 9d936d51f3 support ThemeIcon#color in workspace edit metadata, https://github.com/microsoft/vscode/issues/109460 4e198a4b4b :lipstsick: use underscore for privates bea76730f1 feat: Autodetect npm.packageManager #102050 (#102494) 1bd7cf9e41 use gh username for todo-tags, nuke some todo-tags ab94234299 Merge pull request #109740 from microsoft/joh/extbisect 0729278ad1 use label2 when CompletionEntry#source is a path inside the workspace 441698a621 do not allow editor override for walkthough inputs, fixes https://github.com/microsoft/vscode/issues/109595 98262ef050 fixes #109818 7901aa87e4 wording, show number of disabled extensions, option to keep bad extension disabled, towards filing issue 7cce3438d1 #107657 skip it unitl fixed 4198638fac Fix #109755 744f2ab731 web - use unbuffered upload/download for small files to speed things up f3a23697a9 Editors: revisit "preview" editors default behaviour (#109779) d6c2656f19 [regression] Files served by readonly filesystem show double '(read-only)' suffix in titlebar again (fix #109733) 2d7aacfa92 notifications - reduce weight of ESC for closing if toast has no focus d56b608506 sandbox - bring in browser based keyboard service to enable basic keyboard usage 0268b334e5 enable enableExperimentalProxyLoginDialog per default 5956564b72 storage - IWorkspaceStorageChangeEvent => IStorageChangeEvent 87f7879528 flush storage, some wording tweaks 44e28513b1 Merge branch 'master' into joh/extbisect 3e8c2e036d add "editor.suggest.showStatusDetailsInline"-setting to disable/enable inline details, https://github.com/microsoft/vscode/issues/109690 10a98746f2 Use `var` instead of `const` for the `global` variable 7e25008c60 Merge pull request #109750 from microsoft/sandy081/fix109709 5264ab94a3 chore: bump vscode-ripgrep@1.10.0 8abfa9bc03 Bump version to 1.52.0 580ac9765d rename variable to MatchRange to avoid argumentProcessor 75894bf0a6 Applied feedback bb1be0326d Added extensibility points for searchView/context 5662d49333 Revert "Revert "Fix #109709"" bba3e33b7c fix compilo a77dcf3cb1 tweak wordings bc957f402a add commands and logic to bisect extensions 7cedcfa44f Merge branch 'master' into eagerTerminalReconnection 485badfd8d Fire onDidChangeConnectionState at the right time 574ee2032c Set initial terminal focus based on connection state a607b1437b Fixed typo 'sequece' to 'sequence' bb4b04f206 Terminal reconnection - Get rid of awkward "empty tab" concept 5d0525b50d Reconnect terminals at startup instead of waiting for the terminal view to become visible 994b071092 #109255 Add encoding description to settings file encoding drop-down 7f5714beec chore(extensions): incorrect localize key c79c26babe Merge branch 'master' into resolve-78733 9dffbf40b6 Update SearchView#updateTextFromFindWidgetOrSelection to check seedWithNearestWord 18b30c6a8c Fix SearchView#updateTextFromSelection method to return correct value 10f7c0573e Update OneSnippet#move method to fix #96545. 8d535e661b Update matchCase and wholeWord options when updating text from find widget in SearchView c9b479c4a1 use terrapin 720cdd5e1b Merge branch 'master' into patch-1 2f232ccbca Enable tsec language service plugin 5b5431d552 Merge branch 'master' into scrollbar-paging 36446b1b97 Merge branch 'master' into atomic_tabs 3064431dc5 Fix remaining hightlights d3a3f39dbf Fix overlay area for minimap slider e6cddb065a Update regular expression option when updating text from find widget in SearchView f0583c0db2 Extract updateText method in SearchView d000256957 Add atomicSoftTabs in one place that I missed 3ca4059ea5 Move mouse handling to a lower level to handle more cases d119c92489 Add atomic tabs option bb42ea52e0 Typo hightlight -> highlight 66160a8417 Add updateTextFromFindWidgetOrSelection method to SearchView c9fb066990 Ignore LineDecoration order when comparing e51cc0eae7 Move changes to scmViewPane b08e733365 Merge branch 'master' into fix-89145 02cfa0b9a8 Create empty commit with changed files #107753 b9b6241354 Merge remote-tracking branch 'upstream/master' Merging bug fix with original VSCode repo ff2a9a476e Fixed issue 104346 ceeb974856 Wire up `preconditions` for viewsWelcome buttons, and use it af4fb00687 Initial 'Git extension is activating...' in SCM view 4e00a9b1a5 fix #106487 Contribute an "activating" placeholder message and button 822ca5f07b don't declare a variable that isn't used 2f1c0213d3 Fix composition logic for Firefox 3cc907a220 Fix return 3890d7fba9 Formatting 22ca0c1e8a Add providerName option to git.api.getRemoteSources fa3c6026e2 Merge branch 'master' into dirSep b955b58eee Add dirSep variable launch.json 16d89b0694 Merge branch 'master' into scrollbar-paging 6089fec6a4 Merge branch 'master' into scrollbar-paging aca9ae3288 Option: editor.scrollbar.gutterClickMovesByPage 8b21b331fa Amend scrollbarState.test.ts for new positions 2fa97c8794 feat(extensions): added an option to disable related dependencies when disabling an extension with enabled dependencies 11664e62a3 Make Clicking in Scrollbars Move By Page ff8d4feeb4 Git: Add cherryPick command b4c528cbfd fixes #103281 663ecdbb2a Merge branch 'master' into better-checkout-type d9d1be4e49 Avoid debug ae540536b4 Rewrite checkout items 3f585d7400 Add better support for checkout type config df3af97279 fix fetchOnPull behavior for Sync fc797d2430 fix 97472 bed300cd4f Don't fail when resolving config variables without a `folderUri`. 8561cbb8ae Add force checkout and smart checkout 31ee5b9644 fix something b24cb8b47d Add rename by git context menu 1531898fdb avoid loading and twisty set at the same time 1243ff76e4 Changed name of the setting to openAfterClone b64cb1ecf4 Merge branch 'master' of https://github.com/microsoft/vscode 4420bbfbcf Offer to show git command output on failure 64b8e933ba Merge branch 'master' into fix-89145 5a9b210d91 Merge branch 'master' of https://github.com/microsoft/vscode 26e1217ce9 Merge branch 'master' of https://github.com/microsoft/vscode 156d5ab281 Added setting for opening cloned repository without prompt. #93300 412a44e9bc Git: ask to save unsaved files before stashing bc85a9ffdb Added user choice for opening the folder always. 725985401d Merge branch 'master' into amend-message 37bca69ff1 :lipstick: 3b9dca805c Merge branch 'master' into fix-89145 f23fcb72f5 allow git amend message only 33b5a9ba25 Merge branch 'master' into ignore_sub cdc6c051e5 Persist scm tree view state between sessions 712ceb8279 Fixes #89145 2abdb90472 git.pruneOnFetch setting implemented d7ed37e864 add ignoreSubmodules option REVERT: e5a624b788 Bump version number to 1.51.1 (#110355) REVERT: 02f4e3563b reset icon color once REVERT: 314b97acc6 Fixes #110033 REVERT: 67a784d64f Merge pull request #110247 from microsoft/joh/fix110188 REVERT: b0f1494e96 restore old case ignore behaviour unless explicitly set REVERT: fcac248b07 Merge pull request #109961 from microsoft/joh/fix/109906 REVERT: 9a44531938 fix flawed comparison, increase threshold REVERT: f918d52947 don't presist little size changes REVERT: f37df00d4b add command to reset suggest widget sizes REVERT: 271026ee11 Avoids paragraphs for newlines in string tooltips This better matches the native tooltip styling of newlines REVERT: a5dfd9dcf8 Merge pull request #109882 from microsoft/roblou/fix109863 REVERT: a46559b134 findExecutable should check PATH in a case-insensitive way Fix #109863 REVERT: c57a3408d8 Merge pull request #109859 from microsoft/connor4312/typeahead-151-candidates REVERT: c82d350c6c terminal: cleanup typeahead tests REVERT: 54cadfb2cd terminal: update typeahead tests REVERT: 192fd218cc terminal: fix typeahead leaving stray characters with backspaces REVERT: 3d37590985 terminal: restore old cursor style after failed backspace REVERT: fce8fc82ca Merge pull request #109829 from microsoft/sandy081/fix109709 REVERT: 0bc9b1de71 Revert "Revert "Fix #109709"" git-subtree-dir: lib/vscode git-subtree-split: 3e344b17b7b63b23379a023d575a54ab3a838b6b --- .eslintrc.json | 1 + .github/ISSUE_TEMPLATE/config.yml | 2 +- .github/calendar.yml | 68 +- .github/classifier.json | 12 +- .github/commands.json | 13 + .github/commands.yml | 21 +- .github/endgame/insiders.yml | 10 +- .github/insiders.yml | 10 +- .github/similarity.yml | 6 +- .github/workflows/author-verified.yml | 6 +- .github/workflows/ci.yml | 38 +- .github/workflows/codeql.yml | 61 +- .github/workflows/commands.yml | 4 +- .github/workflows/deep-classifier-monitor.yml | 4 +- .github/workflows/deep-classifier-runner.yml | 4 +- .github/workflows/deep-classifier-scraper.yml | 4 +- .github/workflows/devcontainer-cache.yml | 7 +- .github/workflows/english-please.yml | 4 +- .github/workflows/feature-request.yml | 4 +- .github/workflows/latest-release-monitor.yml | 4 +- .github/workflows/locker.yml | 6 +- .github/workflows/needs-more-info-closer.yml | 6 +- .github/workflows/on-label.yml | 6 +- .github/workflows/on-open.yml | 4 +- .../workflows/release-pipeline-labeler.yml | 4 +- .github/workflows/rich-navigation.yml | 40 +- .../workflows/test-plan-item-validator.yml | 4 +- .vscode/launch.json | 56 +- .vscode/notebooks/api.github-issues | 2 +- .vscode/notebooks/endgame.github-issues | 146 +- .../notebooks/grooming-delta.github-issues | 767 + .vscode/notebooks/grooming.github-issues | 2 +- .vscode/notebooks/my-endgame.github-issues | 218 + .vscode/notebooks/my-work.github-issues | 2 +- .vscode/notebooks/verification.github-issues | 2 +- .yarnrc | 2 +- ThirdPartyNotices.txt | 137 +- azure-pipelines.yml | 5 + build/.cachesalt | 2 +- build/.moduleignore | 114 +- build/.nativeignore | 102 - build/azure-pipelines/common/createAsset.ts | 3 +- build/azure-pipelines/common/createBuild.ts | 3 +- build/azure-pipelines/common/releaseBuild.ts | 3 +- build/azure-pipelines/common/retry.ts | 26 + build/azure-pipelines/common/sync-mooncake.ts | 5 +- .../darwin/continuous-build-darwin.yml | 126 +- .../darwin/product-build-darwin.yml | 537 +- build/azure-pipelines/darwin/publish.sh | 12 +- build/azure-pipelines/distro-build.yml | 54 +- build/azure-pipelines/exploration-build.yml | 53 +- .../linux/alpine/install-dependencies.sh | 5 + build/azure-pipelines/linux/alpine/publish.sh | 28 + .../linux/continuous-build-linux.yml | 148 +- .../linux/multiarch/alpine/build.sh | 3 - .../linux/multiarch/alpine/prebuild.sh | 3 - .../linux/multiarch/alpine/publish.sh | 3 - .../linux/multiarch/arm64/build.sh | 3 - .../linux/multiarch/arm64/prebuild.sh | 3 - .../linux/multiarch/arm64/publish.sh | 3 - .../linux/multiarch/armhf/build.sh | 3 - .../linux/multiarch/armhf/prebuild.sh | 3 - .../linux/multiarch/armhf/publish.sh | 3 - .../linux/product-build-alpine.yml | 135 + .../linux/product-build-linux-multiarch.yml | 115 - .../linux/product-build-linux.yml | 388 +- build/azure-pipelines/linux/publish.sh | 14 +- .../linux/snap-build-linux.yml | 84 +- build/azure-pipelines/product-build.yml | 327 +- build/azure-pipelines/product-compile.yml | 265 +- .../publish-types/publish-types.yml | 128 +- build/azure-pipelines/release.yml | 32 +- build/azure-pipelines/sync-mooncake.yml | 36 +- .../azure-pipelines/web/product-build-web.yml | 209 +- .../win32/continuous-build-win32.yml | 140 +- .../win32/product-build-win32-arm64.yml | 192 - .../win32/product-build-win32.yml | 466 +- build/azure-pipelines/win32/retry.ps1 | 19 + build/darwin/sign.ts | 3 +- build/gulpfile.editor.js | 2 +- build/gulpfile.extensions.js | 2 +- build/gulpfile.vscode.js | 21 +- build/gulpfile.vscode.linux.js | 3 + build/hygiene.js | 3 +- .../eslint/code-no-unexternalized-strings.ts | 5 +- build/lib/i18n.resources.json | 12 + build/lib/reporter.ts | 113 +- build/npm/preinstall.js | 4 +- build/package.json | 4 +- build/win32/code.iss | 124 + build/yarn.lock | 206 +- cglicenses.json | 32 +- cgmanifest.json | 4 +- .../configuration-editing/.vscodeignore | 2 + .../build/inline-allOf.ts | 103 + .../configuration-editing/build/tsconfig.json | 7 + extensions/configuration-editing/package.json | 4 +- .../schemas/attachContainer.schema.json | 2 +- .../devContainer.schema.generated.json | 832 + ...hema.json => devContainer.schema.src.json} | 4 +- extensions/cpp/build/update-grammars.js | 6 +- extensions/cpp/cgmanifest.json | 4 +- extensions/cpp/syntaxes/c.tmLanguage.json | 300 +- .../cpp.embedded.macro.tmLanguage.json | 13421 ++++--------- extensions/cpp/syntaxes/cpp.tmLanguage.json | 15766 ++++++++++------ .../test/colorize-results/test-78769_cpp.json | 14 +- .../test/colorize-results/test-80644_cpp.json | 80 +- .../test/colorize-results/test-92369_cpp.json | 1907 +- .../cpp/test/colorize-results/test_cc.json | 26 +- .../cpp/test/colorize-results/test_cpp.json | 34 +- .../css-language-features/.vscode/launch.json | 24 +- .../client/src/node/cssClientMain.ts | 3 +- .../css-language-features/server/package.json | 2 +- .../server/src/cssServer.ts | 2 +- .../css-language-features/server/yarn.lock | 8 +- extensions/debug-auto-launch/src/extension.ts | 117 +- extensions/debug-server-ready/package.json | 37 +- .../debug-server-ready/package.nls.json | 4 +- .../debug-server-ready/src/extension.ts | 7 +- extensions/emmet/package.json | 7 +- extensions/emmet/package.nls.json | 1 + extensions/emmet/src/abbreviationActions.ts | 1 + extensions/emmet/src/emmetCommon.ts | 4 + .../emmet/src/evaluateMathExpression.ts | 35 +- .../src/test/evaluateMathExpression.test.ts | 52 + .../src/typings/emmetio__math-expression.d.ts | 13 - extensions/emmet/yarn.lock | 62 +- extensions/git/package.json | 145 +- extensions/git/package.nls.json | 29 +- extensions/git/src/api/api1.ts | 2 +- extensions/git/src/api/git.d.ts | 1 + extensions/git/src/commands.ts | 567 +- extensions/git/src/decorationProvider.ts | 10 +- extensions/git/src/git.ts | 78 +- extensions/git/src/remoteSource.ts | 73 +- extensions/git/src/repository.ts | 306 +- extensions/git/src/timelineProvider.ts | 2 +- .../github-authentication/src/extension.ts | 9 + .../github-authentication/src/github.ts | 3 + .../github-authentication/src/githubServer.ts | 10 +- extensions/github/src/publish.ts | 28 +- extensions/github/src/pushErrorHandler.ts | 10 +- extensions/github/src/remoteSourceProvider.ts | 60 +- extensions/github/src/typings/git.d.ts | 2 + .../client/src/htmlClient.ts | 18 +- .../client/src/node/htmlClientMain.ts | 2 +- .../html-language-features/package.json | 39 +- .../html-language-features/package.nls.json | 7 +- .../server/package.json | 4 +- .../server/src/htmlServer.ts | 18 +- .../server/src/modes/cssMode.ts | 6 +- .../server/src/modes/htmlMode.ts | 10 +- .../server/src/modes/javascriptMode.ts | 6 +- .../src/modes/javascriptSemanticTokens.ts | 8 +- .../server/src/modes/languageModes.ts | 4 +- .../server/src/test/semanticTokens.test.ts | 14 +- .../html-language-features/server/yarn.lock | 16 +- extensions/html-language-features/yarn.lock | 8 +- .../snippets/javascript.code-snippets | 2 +- .../syntaxes/JavaScript.tmLanguage.json | 53 +- .../syntaxes/JavaScriptReact.tmLanguage.json | 53 +- .../test/colorize-results/test_jsx.json | 2 +- .../client/src/jsonClient.ts | 21 +- .../client/src/node/jsonClientMain.ts | 2 +- .../json-language-features/package.json | 4 +- .../json-language-features/package.nls.json | 1 + .../server/package.json | 6 +- .../server/src/jsonServer.ts | 8 +- .../json-language-features/server/yarn.lock | 41 +- extensions/json-language-features/yarn.lock | 20 +- extensions/json/cgmanifest.json | 2 +- extensions/json/package.json | 6 +- extensions/json/syntaxes/JSON.tmLanguage.json | 2 +- .../json/syntaxes/JSONC.tmLanguage.json | 2 +- extensions/make/language-configuration.json | 47 +- extensions/markdown-basics/cgmanifest.json | 14 +- .../snippets/markdown.code-snippets | 7 +- .../syntaxes/markdown.tmLanguage.json | 35 +- .../media/markdown.css | 16 +- .../markdown-language-features/package.json | 2 +- .../src/commands/openDocumentLink.ts | 84 +- .../src/features/documentLinkProvider.ts | 21 +- .../src/features/foldingProvider.ts | 3 +- .../src/features/preview.ts | 2 +- .../src/features/smartSelect.ts | 373 +- .../src/features/workspaceSymbolProvider.ts | 3 +- .../src/test/inMemoryDocument.ts | 10 +- .../src/test/smartSelect.test.ts | 351 +- .../src/util/arrays.ts | 4 - .../test-workspace/a.md | 12 +- .../test-workspace/b.md | 4 +- .../markdown-language-features/tsconfig.json | 2 + .../markdown-language-features/yarn.lock | 8 +- .../microsoft-authentication/src/AADHelper.ts | 2 +- extensions/npm/README.md | 2 +- extensions/npm/package.json | 19 +- extensions/npm/package.nls.json | 4 + extensions/npm/src/commands.ts | 8 +- .../npm/src/features/bowerJSONContribution.ts | 12 +- .../npm/src/features/jsonContributions.ts | 28 +- .../src/features/packageJSONContribution.ts | 28 +- extensions/npm/src/npmMain.ts | 11 +- extensions/npm/src/npmView.ts | 47 +- extensions/npm/src/preferred-pm.ts | 80 + extensions/npm/src/scriptHover.ts | 10 +- extensions/npm/src/tasks.ts | 142 +- extensions/npm/tsconfig.json | 2 +- extensions/npm/yarn.lock | 143 + extensions/package.json | 2 +- extensions/php/.vscode/launch.json | 3 +- extensions/php/language-configuration.json | 2 +- extensions/rust/cgmanifest.json | 10 +- extensions/rust/package.json | 39 +- extensions/rust/syntaxes/rust.tmLanguage.json | 1589 +- .../test/colorize-results/test-6611_rs.json | 1094 +- .../rust/test/colorize-results/test_rs.json | 721 +- extensions/search-result/package.json | 1 + extensions/search-result/src/extension.ts | 55 +- .../syntaxes/generateTMLanguage.js | 5 + .../syntaxes/searchResult.tmLanguage.json | 4 + extensions/sql/build/update-grammar.js | 10 + extensions/sql/cgmanifest.json | 2 +- extensions/sql/package.json | 37 +- extensions/sql/syntaxes/sql.tmLanguage.json | 10 +- extensions/theme-abyss/package.json | 5 +- extensions/theme-abyss/package.nls.json | 5 +- .../theme-abyss/themes/abyss-color-theme.json | 4 +- extensions/theme-defaults/package.json | 14 +- extensions/theme-defaults/package.nls.json | 10 +- .../themes/hc_black_defaults.json | 11 +- .../theme-defaults/themes/light_defaults.json | 5 +- extensions/theme-kimbie-dark/package.json | 5 +- extensions/theme-kimbie-dark/package.nls.json | 5 +- .../themes/kimbie-dark-color-theme.json | 2 +- extensions/theme-monokai-dimmed/package.json | 5 +- .../theme-monokai-dimmed/package.nls.json | 5 +- extensions/theme-monokai/package.json | 5 +- extensions/theme-monokai/package.nls.json | 5 +- .../themes/monokai-color-theme.json | 6 +- extensions/theme-quietlight/package.json | 5 +- extensions/theme-quietlight/package.nls.json | 5 +- .../themes/quietlight-color-theme.json | 36 +- extensions/theme-red/package.json | 5 +- extensions/theme-red/package.nls.json | 5 +- extensions/theme-seti/cgmanifest.json | 2 +- extensions/theme-seti/icons/seti.woff | Bin 35436 -> 35676 bytes .../theme-seti/icons/vs-seti-icon-theme.json | 292 +- extensions/theme-seti/package.json | 2 +- extensions/theme-seti/package.nls.json | 5 +- extensions/theme-solarized-dark/package.json | 5 +- .../theme-solarized-dark/package.nls.json | 5 +- .../themes/solarized-dark-color-theme.json | 4 +- extensions/theme-solarized-light/package.json | 5 +- .../theme-solarized-light/package.nls.json | 5 +- .../themes/solarized-light-color-theme.json | 4 +- .../theme-tomorrow-night-blue/package.json | 7 +- .../package.nls.json | 5 +- ...n => tomorrow-night-blue-color-theme.json} | 16 +- extensions/typescript-basics/cgmanifest.json | 2 +- .../syntaxes/TypeScript.tmLanguage.json | 51 +- .../syntaxes/TypeScriptReact.tmLanguage.json | 51 +- .../typescript-language-features/package.json | 60 +- .../package.nls.json | 16 +- .../codeLens/baseCodeLensProvider.ts | 4 +- .../codeLens/implementationsCodeLens.ts | 4 +- .../codeLens/referencesCodeLens.ts | 10 +- .../src/languageFeatures/completions.ts | 9 +- .../fileConfigurationManager.ts | 18 +- .../src/languageFeatures/folding.ts | 20 +- .../src/languageFeatures/quickFix.ts | 85 +- .../src/languageFeatures/refactor.ts | 18 +- .../src/languageFeatures/semanticTokens.ts | 6 +- .../src/tsServer/bufferSyncSupport.ts | 9 +- .../src/tsServer/serverProcess.browser.ts | 16 + .../src/tsServer/spawner.ts | 22 +- .../src/tsServer/versionStatus.ts | 5 +- .../src/typeScriptServiceClientHost.ts | 8 +- .../src/typescriptService.ts | 4 +- .../src/typescriptServiceClient.ts | 12 +- .../src/utils/configuration.ts | 74 +- .../src/utils/resourceMap.ts | 13 +- .../src/utils/tsconfig.ts | 12 +- .../src/singlefolder-tests/workspace.test.ts | 34 + extensions/yarn.lock | 8 +- package.json | 25 +- product.json | 12 +- remote/package.json | 3 +- remote/yarn.lock | 27 +- resources/linux/debian/postinst.template | 2 +- resources/linux/rpm/dependencies.json | 239 +- resources/linux/snap/snapcraft.yaml | 4 + resources/web/code-web.js | 4 + scripts/code.bat | 1 - scripts/code.sh | 1 - scripts/test-integration.sh | 25 + src/bootstrap-amd.js | 6 +- src/bootstrap-fork.js | 10 +- src/bootstrap-node.js | 70 + src/bootstrap-window.js | 110 +- src/bootstrap.js | 121 +- src/buildfile.js | 2 +- src/cli.js | 5 +- src/main.js | 34 +- src/paths.js | 45 +- src/vs/base/browser/contextmenu.ts | 3 +- src/vs/base/browser/dom.ts | 46 +- src/vs/base/browser/globalMouseMoveMonitor.ts | 12 +- src/vs/base/browser/hash.ts | 25 + src/vs/base/browser/markdownRenderer.ts | 94 +- src/vs/base/browser/ui/actionbar/actionbar.ts | 15 +- .../ui/breadcrumbs/breadcrumbsWidget.ts | 4 +- src/vs/base/browser/ui/button/button.css | 11 +- src/vs/base/browser/ui/button/button.ts | 167 +- src/vs/base/browser/ui/checkbox/checkbox.ts | 4 +- .../browser/ui/codicons/codicon/codicon.ttf | Bin 62324 -> 62836 bytes .../base/browser/ui/codicons/codiconStyles.ts | 13 +- .../browser/ui/contextview/contextview.ts | 70 +- src/vs/base/browser/ui/dialog/dialog.css | 3 +- src/vs/base/browser/ui/dialog/dialog.ts | 29 +- src/vs/base/browser/ui/dropdown/dropdown.css | 4 + .../ui/dropdown/dropdownActionViewItem.ts | 14 +- src/vs/base/browser/ui/grid/grid.ts | 4 +- src/vs/base/browser/ui/grid/gridview.ts | 40 +- src/vs/base/browser/ui/hover/hover.css | 1 - src/vs/base/browser/ui/iconLabel/iconLabel.ts | 66 +- .../base/browser/ui/iconLabel/iconlabel.css | 4 +- src/vs/base/browser/ui/list/listPaging.ts | 4 + src/vs/base/browser/ui/list/listView.ts | 4 +- src/vs/base/browser/ui/list/listWidget.ts | 14 +- src/vs/base/browser/ui/menu/menu.ts | 14 +- src/vs/base/browser/ui/menu/menubar.ts | 6 +- .../browser/ui/progressbar/progressbar.css | 9 +- .../browser/ui/progressbar/progressbar.ts | 1 + src/vs/base/browser/ui/sash/sash.css | 43 +- src/vs/base/browser/ui/sash/sash.ts | 12 + .../browser/ui/scrollbar/abstractScrollbar.ts | 12 +- .../ui/scrollbar/horizontalScrollbar.ts | 9 +- .../browser/ui/scrollbar/scrollableElement.ts | 11 +- .../ui/scrollbar/scrollableElementOptions.ts | 6 + .../browser/ui/scrollbar/scrollbarState.ts | 22 + .../browser/ui/scrollbar/verticalScrollbar.ts | 9 +- src/vs/base/browser/ui/selectBox/selectBox.ts | 1 + .../browser/ui/selectBox/selectBoxCustom.css | 9 + .../browser/ui/selectBox/selectBoxCustom.ts | 17 +- src/vs/base/browser/ui/splitview/paneview.css | 19 +- .../base/browser/ui/splitview/splitview.css | 21 +- src/vs/base/browser/ui/splitview/splitview.ts | 36 +- src/vs/base/browser/ui/toolbar/toolbar.ts | 7 +- src/vs/base/browser/ui/tree/abstractTree.ts | 20 +- src/vs/base/browser/ui/tree/asyncDataTree.ts | 6 +- .../ui/tree/compressedObjectTreeModel.ts | 9 +- src/vs/base/browser/ui/tree/objectTree.ts | 5 +- src/vs/base/browser/ui/tree/tree.ts | 2 +- src/vs/base/browser/ui/tree/treeIcons.ts | 12 +- src/vs/base/common/actions.ts | 21 +- src/vs/base/common/amd.ts | 131 + src/vs/base/common/arrays.ts | 14 + src/vs/base/common/async.ts | 39 + src/vs/base/common/codicons.ts | 25 +- src/vs/base/common/collections.ts | 30 + src/vs/base/common/diff/diff.ts | 72 + src/vs/base/common/event.ts | 2 +- src/vs/base/common/hash.ts | 10 +- src/vs/base/common/htmlContent.ts | 40 +- src/vs/base/common/json.ts | 2 +- src/vs/base/common/labels.ts | 4 + src/vs/base/common/linkedList.ts | 8 - src/vs/base/common/map.ts | 100 +- src/vs/base/common/marked/cgmanifest.json | 4 +- src/vs/base/common/marked/marked.js | 109 +- src/vs/base/common/network.ts | 51 + src/vs/base/common/performance.js | 4 +- src/vs/base/common/platform.ts | 9 +- src/vs/base/common/process.ts | 14 +- src/vs/base/common/resourceTree.ts | 12 +- src/vs/base/common/resources.ts | 38 +- src/vs/base/common/strings.ts | 24 +- src/vs/base/common/uri.ts | 2 +- src/vs/base/common/uuid.ts | 5 +- src/vs/base/common/worker/simpleWorker.ts | 4 + src/vs/base/node/paths.ts | 9 +- src/vs/base/parts/ipc/common/ipc.net.ts | 39 +- .../quickinput/browser/media/quickInput.css | 5 + .../parts/quickinput/browser/quickInput.ts | 35 +- .../parts/sandbox/electron-browser/preload.js | 132 +- .../parts/sandbox/electron-sandbox/globals.ts | 42 +- src/vs/base/parts/storage/common/storage.ts | 32 +- .../parts/storage/test/node/storage.test.ts | 14 + src/vs/base/test/browser/comparers.test.ts | 4 +- .../test/{common => browser}/hash.test.ts | 24 +- .../test/browser/markdownRenderer.test.ts | 22 +- .../ui/scrollbar/scrollbarState.test.ts | 10 +- .../browser/ui/splitview/splitview.test.ts | 8 +- src/vs/base/test/common/async.test.ts | 79 +- src/vs/base/test/common/linkedList.test.ts | 2 +- src/vs/base/test/common/map.test.ts | 153 +- .../base/test/common/markdownString.test.ts | 18 +- src/vs/base/test/common/network.test.ts | 70 + src/vs/base/test/common/processes.test.ts | 1 - src/vs/base/test/common/strings.test.ts | 5 + src/vs/base/worker/workerMain.ts | 10 +- .../code/browser/workbench/workbench-dev.html | 9 +- src/vs/code/browser/workbench/workbench.html | 9 +- src/vs/code/browser/workbench/workbench.ts | 8 + .../sharedProcess/sharedProcessMain.ts | 12 +- .../electron-browser/workbench/workbench.js | 31 +- src/vs/code/electron-main/app.ts | 269 +- src/vs/code/electron-main/auth2.ts | 3 +- src/vs/code/electron-main/main.ts | 12 +- src/vs/code/electron-main/protocol.ts | 108 + src/vs/code/electron-main/sharedProcess.ts | 5 +- src/vs/code/electron-main/window.ts | 81 +- .../issue/issueReporterMain.ts | 19 +- .../issue/issueReporterModel.ts | 27 +- .../issue/issueReporterPage.ts | 26 +- .../issue/test/testReporterModel.test.ts | 94 +- .../processExplorer/processExplorerMain.ts | 16 +- .../electron-sandbox/workbench/workbench.js | 29 +- src/vs/code/node/cli.ts | 62 +- src/vs/code/node/cliProcessMain.ts | 84 +- src/vs/code/node/shellEnv.ts | 84 +- .../test/electron-main/nativeHelpers.test.ts | 40 - .../editor/browser/controller/mouseHandler.ts | 15 +- .../editor/browser/controller/mouseTarget.ts | 58 +- .../browser/controller/pointerHandler.ts | 2 +- .../browser/controller/textAreaHandler.ts | 2 + .../browser/controller/textAreaInput.ts | 7 +- .../editor/browser/core/markdownRenderer.ts | 8 +- src/vs/editor/browser/editorBrowser.ts | 13 +- src/vs/editor/browser/editorDom.ts | 8 +- src/vs/editor/browser/editorExtensions.ts | 43 - .../services/abstractCodeEditorService.ts | 3 + .../browser/services/bulkEditService.ts | 6 + .../browser/services/codeEditorService.ts | 2 + .../browser/services/codeEditorServiceImpl.ts | 30 +- .../editor/browser/services/openerService.ts | 26 +- .../browser/view/domLineBreaksComputer.ts | 9 +- src/vs/editor/browser/view/viewController.ts | 4 + .../browser/view/viewUserInputEvents.ts | 7 + .../currentLineHighlight.ts | 23 +- .../editorScrollbar/editorScrollbar.ts | 1 + .../browser/viewParts/minimap/minimap.ts | 11 +- .../viewParts/viewCursors/viewCursors.ts | 17 +- .../editor/browser/widget/codeEditorWidget.ts | 35 +- .../editor/browser/widget/diffEditorWidget.ts | 1098 +- src/vs/editor/browser/widget/diffReview.ts | 17 +- .../editor/browser/widget/inlineDiffMargin.ts | 38 +- .../common/config/commonEditorConfig.ts | 20 + src/vs/editor/common/config/editorOptions.ts | 355 +- src/vs/editor/common/controller/cursor.ts | 2 +- .../controller/cursorAtomicMoveOperations.ts | 160 + .../editor/common/controller/cursorCommon.ts | 15 +- .../controller/cursorDeleteOperations.ts | 2 +- .../common/controller/cursorMoveCommands.ts | 12 +- .../common/controller/cursorMoveOperations.ts | 29 +- .../common/controller/cursorTypeOperations.ts | 91 +- .../common/controller/cursorWordOperations.ts | 118 +- src/vs/editor/common/editorContextKeys.ts | 1 + src/vs/editor/common/model.ts | 27 +- src/vs/editor/common/model/editStack.ts | 17 + src/vs/editor/common/model/mirrorTextModel.ts | 3 +- .../pieceTreeTextBuffer.ts | 12 +- src/vs/editor/common/model/textModel.ts | 24 +- src/vs/editor/common/model/tokensStore.ts | 4 + src/vs/editor/common/modes.ts | 42 +- .../common/modes/languageConfiguration.ts | 29 +- .../modes/languageConfigurationRegistry.ts | 6 + .../common/modes/supports/indentRules.ts | 16 +- .../common/modes/textToHtmlTokenizer.ts | 2 +- .../common/services/editorSimpleWorker.ts | 38 +- .../services/editorWorkerServiceImpl.ts | 59 +- .../editor/common/services/getIconClasses.ts | 4 +- .../common/services/modelServiceImpl.ts | 4 + .../common/standalone/standaloneEnums.ts | 220 +- src/vs/editor/common/view/viewEvents.ts | 16 +- .../common/viewLayout/lineDecorations.ts | 18 + .../editor/common/viewLayout/linesLayout.ts | 17 + src/vs/editor/common/viewLayout/viewLayout.ts | 7 + .../common/viewLayout/viewLineRenderer.ts | 2 +- .../viewModel/monospaceLineBreaksComputer.ts | 3 +- .../common/viewModel/splitLinesCollection.ts | 75 +- .../common/viewModel/viewEventHandler.ts | 19 +- src/vs/editor/common/viewModel/viewModel.ts | 64 + .../editor/common/viewModel/viewModelImpl.ts | 14 +- .../editor/contrib/codeAction/codeAction.ts | 6 +- .../contrib/codeAction/codeActionMenu.ts | 8 +- .../test/codeActionKeybindingResolver.test.ts | 3 +- .../editor/contrib/codelens/codeLensCache.ts | 4 +- src/vs/editor/contrib/codelens/codelens.ts | 17 +- .../contrib/codelens/codelensController.ts | 51 +- .../editor/contrib/codelens/codelensWidget.ts | 31 +- src/vs/editor/contrib/colorPicker/color.ts | 15 +- .../contrib/colorPicker/colorContributions.ts | 57 + .../contrib/colorPicker/colorDetector.ts | 10 +- .../contrib/colorPicker/colorPickerWidget.ts | 2 +- src/vs/editor/contrib/comment/comment.ts | 28 +- .../contrib/comment/lineCommentCommand.ts | 26 +- .../comment/test/lineCommentCommand.test.ts | 4 +- src/vs/editor/contrib/dnd/dnd.ts | 11 + src/vs/editor/contrib/find/findController.ts | 79 +- .../editor/contrib/find/findOptionsWidget.ts | 2 +- src/vs/editor/contrib/find/findWidget.ts | 115 +- .../contrib/find/test/findController.test.ts | 97 +- src/vs/editor/contrib/folding/folding.ts | 6 +- .../contrib/folding/foldingDecorations.ts | 18 +- src/vs/editor/contrib/format/formatActions.ts | 7 +- src/vs/editor/contrib/gotoError/gotoError.ts | 7 +- .../contrib/gotoError/gotoErrorWidget.ts | 5 +- .../editor/contrib/gotoSymbol/goToCommands.ts | 3 + .../gotoSymbol/peek/referencesController.ts | 18 +- .../contrib/gotoSymbol/referencesModel.ts | 18 +- src/vs/editor/contrib/hover/hover.ts | 9 + .../editor/contrib/hover/modesContentHover.ts | 58 +- .../editor/contrib/indentation/indentation.ts | 14 +- .../linesOperations/copyLinesCommand.ts | 14 +- .../linesOperations/linesOperations.ts | 5 +- .../linesOperations/moveLinesCommand.ts | 97 +- .../test/moveLinesCommand.test.ts | 53 + .../linkedEditing.ts} | 96 +- .../test/linkedEditing.test..ts} | 47 +- src/vs/editor/contrib/links/links.ts | 12 +- .../contrib/message/messageController.css | 16 + .../contrib/message/messageController.ts | 35 +- .../editor/contrib/multicursor/multicursor.ts | 5 + .../multicursor/test/multicursor.test.ts | 8 +- .../parameterHints/parameterHintsWidget.ts | 13 +- .../parameterHints/provideSignatureHelp.ts | 41 +- .../editorNavigationQuickAccess.ts | 27 +- .../quickAccess/gotoLineQuickAccess.ts | 7 +- .../quickAccess/gotoSymbolQuickAccess.ts | 22 +- src/vs/editor/contrib/rename/rename.ts | 2 +- .../editor/contrib/rename/renameInputField.ts | 2 +- .../editor/contrib/smartSelect/smartSelect.ts | 180 +- .../editor/contrib/snippet/snippetSession.ts | 81 +- .../contrib/snippet/snippetVariables.ts | 4 +- .../snippet/test/snippetController2.test.ts | 9 +- .../snippet/test/snippetSession.test.ts | 78 + .../snippet/test/snippetVariables.test.ts | 3 +- src/vs/editor/contrib/suggest/resizable.ts | 6 +- src/vs/editor/contrib/suggest/suggest.ts | 58 +- .../editor/contrib/suggest/suggestMemory.ts | 9 +- src/vs/editor/contrib/suggest/suggestModel.ts | 11 +- .../editor/contrib/suggest/suggestWidget.ts | 444 +- .../contrib/suggest/suggestWidgetDetails.ts | 6 +- .../contrib/suggest/suggestWidgetRenderer.ts | 52 +- .../viewportSemanticTokens.ts | 8 +- .../test/wordOperations.test.ts | 121 +- .../contrib/wordOperations/wordOperations.ts | 34 +- src/vs/editor/editor.all.ts | 4 +- src/vs/editor/editor.api.ts | 8 +- src/vs/editor/standalone/browser/colorizer.ts | 7 +- .../standalone/browser/simpleServices.ts | 7 +- .../browser/standaloneCodeEditor.ts | 4 + .../standalone/browser/standaloneEditor.ts | 3 +- .../standalone/browser/standaloneLanguages.ts | 33 +- .../standalone/browser/standaloneServices.ts | 3 - .../browser/standaloneThemeServiceImpl.ts | 11 +- .../test/browser/controller/cursor.test.ts | 122 +- .../test/browser/controller/imeTester.ts | 19 +- .../editor/test/browser/editorTestServices.ts | 1 + .../services/decorationRenderOptions.test.ts | 16 +- .../cursorAtomicMoveOperations.test.ts | 152 + .../test/common/diff/diffComputer.test.ts | 27 + .../textBufferAutoTestUtils.ts | 7 +- .../pieceTreeTextBuffer.test.ts | 15 +- .../services/editorSimpleWorker.test.ts | 2 +- .../viewLayout/editorLayoutProvider.test.ts | 4 +- .../monospaceLineBreaksComputer.test.ts | 3 +- .../viewModel/splitLinesCollection.test.ts | 4 +- src/vs/loader.js | 88 +- src/vs/monaco.d.ts | 366 +- .../browser/menuEntryActionViewItem.ts | 8 +- src/vs/platform/actions/common/actions.ts | 2 +- .../backup/electron-main/backupMainService.ts | 41 +- src/vs/platform/backup/node/backup.ts | 5 - .../electron-main/backupMainService.test.ts | 70 +- .../configuration/common/configuration.ts | 2 +- .../common/configurationRegistry.ts | 3 + .../test/common/configurationModels.test.ts | 3 +- .../contextkey/browser/contextKeyService.ts | 22 +- .../platform/contextkey/common/contextkey.ts | 334 +- .../contextkey/test/common/contextkey.test.ts | 80 + .../contextview/browser/contextMenuHandler.ts | 3 +- .../contextview/browser/contextView.ts | 3 +- .../diagnostics/node/diagnosticsService.ts | 14 +- src/vs/platform/dialogs/common/dialogs.ts | 66 +- .../display/common/displayMainService.ts} | 9 +- .../electron-main/displayMainService.ts | 47 + src/vs/platform/environment/common/argv.ts | 3 +- .../environment/common/environment.ts | 2 +- .../electron-main/environmentMainService.ts | 8 +- src/vs/platform/environment/node/argv.ts | 10 +- .../platform/environment/node/argvHelper.ts | 8 +- .../environment/node/environmentService.ts | 11 +- .../environment}/test/node/argv.test.ts | 1 + .../test/node/nativeModules.test.ts | 105 + .../common/extensionEnablementService.ts | 23 +- .../common/extensionGalleryService.ts | 4 +- .../common/extensionManagement.ts | 5 +- .../common/extensionManagementIpc.ts | 36 +- .../common/extensionUrlTrust.ts | 13 + .../electron-sandbox/extensionTipsService.ts | 6 +- .../node/extensionDownloader.ts | 31 +- .../node/extensionManagementService.ts | 37 +- .../node/extensionUrlTrustService.ts | 97 + .../node/extensionsScanner.ts | 2 +- src/vs/platform/files/common/fileService.ts | 3 +- src/vs/platform/files/common/files.ts | 30 +- .../nsfw/test/nsfwWatcherService.test.ts | 24 +- .../watcher/unix/chokidarWatcherService.ts | 2 +- .../unix/test/chockidarWatcherService.test.ts | 44 +- src/vs/platform/issue/common/issue.ts | 1 + .../issue/electron-main/issueMainService.ts | 48 +- .../keybinding/common/keybindingResolver.ts | 15 +- .../keybinding/common/keybindingsRegistry.ts | 8 +- .../common/resolvedKeybindingItem.ts | 4 +- .../common/abstractKeybindingService.test.ts | 3 +- .../test/common/keybindingResolver.test.ts | 3 +- .../keyboardLayout}/common/dispatchConfig.ts | 0 .../keyboardLayout/common/keyboardLayout.ts | 260 + .../common/keyboardLayoutMainService.ts | 18 + .../keyboardLayout}/common/keyboardMapper.ts | 0 .../keyboardLayoutMainService.ts | 59 + .../launch/electron-main/launchMainService.ts | 16 +- .../electron-main/lifecycleMainService.ts | 10 +- src/vs/platform/list/browser/listService.ts | 91 +- src/vs/platform/log/common/fileLogService.ts | 4 +- src/vs/platform/log/common/log.ts | 52 +- src/vs/platform/log/node/spdlogService.ts | 3 +- .../platform/menubar/electron-main/menubar.ts | 9 +- .../electron-main/nativeHostMainService.ts | 12 +- .../notification/common/notification.ts | 34 +- src/vs/platform/opener/common/opener.ts | 6 + src/vs/platform/product/common/product.ts | 3 +- .../platform/product/common/productService.ts | 1 + .../quickinput/browser/commandsQuickAccess.ts | 12 +- src/vs/platform/remote/common/tunnel.ts | 23 +- src/vs/platform/remote/node/tunnelService.ts | 5 +- .../common/serviceMachineId.ts | 6 +- .../storage/browser/storageService.ts | 43 +- src/vs/platform/storage/common/storage.ts | 348 +- .../platform/storage/node/storageService.ts | 50 +- .../test/common/storageService.test.ts | 198 + .../storage/test/node/storageService.test.ts | 75 +- .../telemetry/browser/errorTelemetry.ts | 2 +- .../telemetry/node/appInsightsAppender.ts | 58 +- .../platform/telemetry/node/errorTelemetry.ts | 2 +- src/vs/platform/theme/browser/checkbox.ts | 26 - src/vs/platform/theme/common/colorRegistry.ts | 9 +- src/vs/platform/theme/common/iconRegistry.ts | 82 +- src/vs/platform/theme/common/themeService.ts | 68 +- .../common/tokenClassificationRegistry.ts | 5 +- .../theme/electron-main/themeMainService.ts | 4 +- src/vs/platform/undoRedo/common/undoRedo.ts | 31 +- .../undoRedo/common/undoRedoService.ts | 161 +- .../electron-main/updateService.darwin.ts | 3 +- src/vs/platform/url/common/url.ts | 2 + src/vs/platform/url/common/urlIpc.ts | 4 +- .../url/electron-main/electronUrlListener.ts | 23 +- .../common/extensionsStorageSync.ts | 97 + .../userDataSync/common/extensionsSync.ts | 67 +- .../userDataSync/common/globalStateMerge.ts | 119 +- .../userDataSync/common/globalStateSync.ts | 67 +- .../userDataSync/common/keybindingsSync.ts | 39 +- .../userDataSync/common/settingsSync.ts | 3 +- .../userDataSync/common/storageKeys.ts | 134 - .../common/userDataAutoSyncService.ts | 18 +- .../userDataSync/common/userDataSync.ts | 6 +- .../userDataSync/common/userDataSyncIpc.ts | 64 - .../common/userDataSyncMachines.ts | 4 +- .../userDataSyncResourceEnablementService.ts | 12 +- .../common/userDataSyncService.ts | 30 +- .../common/userDataSyncStoreService.ts | 83 +- .../test/common/globalStateMerge.test.ts | 199 +- .../test/common/globalStateSync.test.ts | 45 +- .../test/common/userDataSyncClient.ts | 4 +- .../common/userDataSyncStoreService.test.ts | 14 +- .../webview/common/webviewManagerService.ts | 10 +- .../electron-main/webviewMainService.ts | 27 +- src/vs/platform/windows/common/windows.ts | 22 +- .../platform/windows/electron-main/windows.ts | 13 +- .../electron-main/windowsMainService.ts | 346 +- .../electron-main/windowsStateStorage.test.ts | 0 src/vs/platform/workspace/common/workspace.ts | 10 +- .../workspace/test/common/testWorkspace.ts | 15 +- .../workspace/test/common/workspace.test.ts | 12 +- .../workspacesHistoryMainService.ts | 92 +- src/vs/vscode.d.ts | 253 +- src/vs/vscode.proposed.d.ts | 550 +- .../api/browser/extensionHost.contribution.ts | 1 + .../api/browser/mainThreadAuthentication.ts | 35 +- .../api/browser/mainThreadBulkEdits.ts | 10 +- .../api/browser/mainThreadCodeInsets.ts | 4 +- .../api/browser/mainThreadCommands.ts | 30 +- .../api/browser/mainThreadComments.ts | 9 +- .../api/browser/mainThreadCustomEditors.ts | 7 +- .../api/browser/mainThreadDebugService.ts | 14 +- .../api/browser/mainThreadDialogs.ts | 19 +- .../browser/mainThreadDocumentsAndEditors.ts | 44 +- .../api/browser/mainThreadEditors.ts | 63 +- .../mainThreadFileSystemEventService.ts | 12 +- .../api/browser/mainThreadLanguageFeatures.ts | 31 +- .../api/browser/mainThreadNotebook.ts | 111 +- src/vs/workbench/api/browser/mainThreadSCM.ts | 8 +- .../api/browser/mainThreadStatusBar.ts | 4 +- .../api/browser/mainThreadStorage.ts | 17 +- .../workbench/api/browser/mainThreadTask.ts | 4 +- .../api/browser/mainThreadTesting.ts | 77 + .../api/browser/mainThreadTreeViews.ts | 14 +- .../api/browser/mainThreadTunnelService.ts | 40 +- .../api/browser/mainThreadWebviewPanels.ts | 5 +- .../api/browser/viewsExtensionPoint.ts | 21 +- src/vs/workbench/api/common/apiCommands.ts | 175 +- .../api/common/configurationExtensionPoint.ts | 4 + .../workbench/api/common/extHost.api.impl.ts | 231 +- .../workbench/api/common/extHost.protocol.ts | 97 +- .../api/common/extHostApiCommands.ts | 451 +- .../workbench/api/common/extHostCommands.ts | 127 +- .../api/common/extHostCustomEditors.ts | 4 +- .../api/common/extHostDebugService.ts | 82 +- .../api/common/extHostDecorations.ts | 6 +- .../common/extHostDocumentContentProviders.ts | 3 +- .../common/extHostFileSystemEventService.ts | 12 +- .../api/common/extHostLanguageFeatures.ts | 115 +- .../workbench/api/common/extHostNotebook.ts | 54 +- .../workbench/api/common/extHostQuickOpen.ts | 6 +- src/vs/workbench/api/common/extHostSCM.ts | 82 +- .../workbench/api/common/extHostStatusBar.ts | 51 +- src/vs/workbench/api/common/extHostStorage.ts | 2 +- src/vs/workbench/api/common/extHostTask.ts | 27 +- src/vs/workbench/api/common/extHostTesting.ts | 794 + .../workbench/api/common/extHostTreeViews.ts | 26 +- .../api/common/extHostTunnelService.ts | 15 +- .../api/common/extHostTypeConverters.ts | 123 +- src/vs/workbench/api/common/extHostTypes.ts | 193 +- .../api/common/extHostWebviewPanels.ts | 4 +- .../workbench/api/common/extHostWorkspace.ts | 44 +- .../api/common/menusExtensionPoint.ts | 36 +- src/vs/workbench/api/common/shared/editor.ts | 39 - src/vs/workbench/api/common/shared/tasks.ts | 2 +- src/vs/workbench/api/node/extHostCLIServer.ts | 22 +- .../workbench/api/node/extHostDebugService.ts | 90 +- src/vs/workbench/api/node/extHostTask.ts | 4 + .../api/node/extHostTerminalService.ts | 11 +- .../api/node/extHostTunnelService.ts | 70 +- .../browser/actions/layoutActions.ts | 14 +- .../workbench/browser/actions/listCommands.ts | 20 +- .../browser/actions/navigationActions.ts | 31 +- .../browser/actions/workspaceActions.ts | 2 +- .../browser/actions/workspaceCommands.ts | 51 +- src/vs/workbench/browser/dnd.ts | 6 +- src/vs/workbench/browser/layout.ts | 231 +- src/vs/workbench/browser/media/style.css | 4 + .../parts/activitybar/activitybarActions.ts | 396 +- .../parts/activitybar/activitybarPart.ts | 279 +- .../activitybar/media/activityaction.css | 4 - .../activitybar/media/activitybarpart.css | 8 - .../workbench/browser/parts/compositePart.ts | 4 +- .../parts/dialogs/dialog.web.contribution.ts | 76 + .../parts/dialogs/dialogHandler.ts} | 11 +- .../browser/parts/editor/binaryEditor.ts | 8 +- .../parts/editor/breadcrumbsControl.ts | 8 +- .../browser/parts/editor/breadcrumbsPicker.ts | 3 +- .../parts/editor/editor.contribution.ts | 50 +- .../workbench/browser/parts/editor/editor.ts | 7 +- .../browser/parts/editor/editorCommands.ts | 242 +- .../browser/parts/editor/editorDropTarget.ts | 49 +- .../browser/parts/editor/editorGroupView.ts | 23 +- .../browser/parts/editor/editorPane.ts | 40 +- .../browser/parts/editor/editorPart.ts | 6 +- .../browser/parts/editor/editorStatus.ts | 19 +- .../browser/parts/editor/editorWidgets.ts | 2 + .../browser/parts/editor/editorsObserver.ts | 4 +- .../parts/editor/media/breadcrumbscontrol.css | 2 +- .../browser/parts/editor/tabsTitleControl.ts | 4 +- .../browser/parts/editor/textDiffEditor.ts | 47 +- .../browser/parts/editor/textEditor.ts | 30 +- .../parts/editor/textResourceEditor.ts | 8 +- .../notifications/media/notificationsList.css | 15 +- .../media/notificationsToasts.css | 2 +- .../notifications/notificationsActions.ts | 28 +- .../notifications/notificationsCenter.ts | 2 +- .../notifications/notificationsCommands.ts | 32 +- .../notifications/notificationsToasts.ts | 2 +- .../notifications/notificationsViewer.ts | 44 +- .../browser/parts/panel/media/panelpart.css | 4 - .../browser/parts/panel/panelActions.ts | 16 +- .../browser/parts/panel/panelPart.ts | 13 +- .../browser/parts/statusbar/statusbarPart.ts | 12 +- .../browser/parts/titlebar/menubarControl.ts | 101 +- .../browser/parts/titlebar/titlebarPart.ts | 10 +- .../parts/views}/media/views.css | 6 +- .../workbench/browser/parts/views/treeView.ts | 1092 +- .../browser/parts/views/viewPaneContainer.ts | 59 +- src/vs/workbench/browser/web.main.ts | 12 +- .../browser/workbench.contribution.ts | 11 +- src/vs/workbench/browser/workbench.ts | 7 +- src/vs/workbench/common/component.ts | 6 +- src/vs/workbench/common/dialogs.ts | 50 + src/vs/workbench/common/editor.ts | 74 +- .../common/editor/diffEditorInput.ts | 52 +- .../common/editor/resourceEditorInput.ts | 1 + .../common/editor/textResourceEditorInput.ts | 32 +- src/vs/workbench/common/memento.ts | 12 +- src/vs/workbench/common/notifications.ts | 8 +- src/vs/workbench/common/theme.ts | 14 +- src/vs/workbench/common/views.ts | 38 +- .../electron-browser/backupRestorer.test.ts | 2 +- .../electron-browser/backupTracker.test.ts | 5 +- .../contrib/bulkEdit/browser/bulkCellEdits.ts | 13 +- .../bulkEdit/browser/bulkEditService.ts | 68 +- .../contrib/bulkEdit/browser/bulkFileEdits.ts | 110 +- .../contrib/bulkEdit/browser/bulkTextEdits.ts | 32 +- .../browser/preview/bulkEdit.contribution.ts | 17 +- .../bulkEdit/browser/preview/bulkEditPane.ts | 15 +- .../browser/preview/bulkEditPreview.ts | 3 +- .../bulkEdit/browser/preview/bulkEditTree.ts | 15 +- .../browser/callHierarchy.contribution.ts | 11 +- .../browser/callHierarchyPeek.ts | 8 +- .../contrib/cli/node/cli.contribution.ts | 192 +- .../browser/accessibility/accessibility.ts | 4 +- .../codeEditor/browser/diffEditorHelper.ts | 2 +- .../browser/find/simpleFindReplaceWidget.ts | 15 +- .../browser/find/simpleFindWidget.ts | 11 +- .../browser/largeFileOptimizations.ts | 8 +- .../quickaccess/gotoLineQuickAccess.ts | 8 +- .../quickaccess/gotoSymbolQuickAccess.ts | 12 +- .../codeEditor/browser/saveParticipants.ts | 12 +- .../browser/toggleColumnSelection.ts | 4 +- .../codeEditor/browser/toggleMinimap.ts | 4 +- .../browser/toggleMultiCursorModifier.ts | 4 +- .../browser/toggleRenderControlCharacter.ts | 4 +- .../browser/toggleRenderWhitespace.ts | 4 +- .../codeEditor/browser/toggleWordWrap.ts | 169 +- .../electron-browser/startDebugTextMate.ts | 3 +- .../codeEditor.contribution.ts | 1 + .../displayChangeRemeasureFonts.ts | 28 + .../contrib/comments/browser/commentNode.ts | 3 +- .../comments/browser/commentThreadWidget.ts | 28 +- .../contrib/comments/browser/commentsView.ts | 37 +- .../contrib/comments/browser/media/panel.css | 7 +- .../contrib/comments/common/commentModel.ts | 2 +- .../customEditor/browser/customEditors.ts | 6 +- .../common/contributedCustomEditors.ts | 8 +- .../browser/breakpointEditorContribution.ts | 48 +- .../contrib/debug/browser/breakpointsView.ts | 253 +- .../browser/callStackEditorContribution.ts | 26 +- .../contrib/debug/browser/callStackView.ts | 27 +- .../debug/browser/debug.contribution.ts | 41 +- .../debug/browser/debugANSIHandling.ts | 12 +- .../debug/browser/debugActionViewItems.ts | 44 +- .../contrib/debug/browser/debugActions.ts | 27 +- .../debug/browser/debugAdapterManager.ts | 268 + .../contrib/debug/browser/debugCommands.ts | 8 +- .../browser/debugConfigurationManager.ts | 375 +- .../debug/browser/debugEditorActions.ts | 160 +- .../debug/browser/debugEditorContribution.ts | 19 +- .../contrib/debug/browser/debugHover.ts | 23 +- .../contrib/debug/browser/debugIcons.ts | 71 + .../contrib/debug/browser/debugProgress.ts | 2 +- .../contrib/debug/browser/debugQuickAccess.ts | 27 +- .../contrib/debug/browser/debugService.ts | 42 +- .../contrib/debug/browser/debugSession.ts | 13 +- .../contrib/debug/browser/debugToolBar.ts | 33 +- .../contrib/debug/browser/debugViewlet.ts | 5 +- .../contrib/debug/browser/exceptionWidget.ts | 56 +- .../debug/browser/loadedScriptsView.ts | 6 +- .../debug/browser/media/debugHover.css | 11 +- .../debug/browser/media/debugViewlet.css | 7 +- .../debug/browser/media/exceptionWidget.css | 10 +- .../contrib/debug/browser/media/repl.css | 9 + .../contrib/debug/browser/rawDebugSession.ts | 6 +- .../workbench/contrib/debug/browser/repl.ts | 26 +- .../contrib/debug/browser/replFilter.ts | 7 +- .../contrib/debug/browser/replViewer.ts | 44 +- .../contrib/debug/browser/variablesView.ts | 9 +- .../debug/browser/watchExpressionsView.ts | 5 +- .../contrib/debug/browser/welcomeView.ts | 9 +- .../workbench/contrib/debug/common/debug.ts | 60 +- .../contrib/debug/common/debugModel.ts | 26 +- .../contrib/debug/common/debugProtocol.d.ts | 91 +- .../contrib/debug/common/debugStorage.ts | 14 +- .../contrib/debug/common/debugViewModel.ts | 14 +- .../contrib/debug/common/debugger.ts | 17 +- .../contrib/debug/common/replModel.ts | 39 +- .../contrib/debug/node/debugAdapter.ts | 8 +- .../workbench/contrib/debug/node/terminals.ts | 11 +- .../debug/test/browser/breakpoints.test.ts | 50 +- .../debug/test/browser/callStack.test.ts | 8 +- .../contrib/debug/test/browser/mockDebug.ts | 15 +- .../contrib/debug/test/browser/repl.test.ts | 41 +- .../debugANSIHandling.test.ts | 8 +- .../contrib/debug/test/node/debugger.test.ts | 7 +- .../browser/actions/showEmmetCommands.ts | 39 - .../emmet/browser/emmet.contribution.ts | 1 - .../experiments/common/experimentService.ts | 16 +- .../experimentalPrompts.test.ts | 2 +- .../abstractRuntimeExtensionsEditor.ts | 474 + .../browser/browserRuntimeExtensionsEditor.ts | 62 + .../dynamicWorkspaceRecommendations.ts | 4 +- .../extensions/browser/extensionEditor.ts | 148 +- ...ensionRecommendationNotificationService.ts | 39 +- .../extensionRecommendationsService.ts | 12 +- .../browser/extensions.contribution.ts | 262 +- .../browser/extensions.web.contribution.ts | 17 + .../extensions/browser/extensionsActions.ts | 1024 +- .../extensions/browser/extensionsIcons.ts | 33 + .../extensions/browser/extensionsList.ts | 20 +- .../extensions/browser/extensionsViewlet.ts | 254 +- .../extensions/browser/extensionsViews.ts | 387 +- .../extensions/browser/extensionsWidgets.ts | 46 +- .../browser/extensionsWorkbenchService.ts | 32 +- .../browser/fileBasedRecommendations.ts | 46 +- .../extensions/browser/media/extension.css | 9 + .../browser/media/extensionActions.css | 6 +- .../browser/media/extensionEditor.css | 10 +- .../browser/media/extensionsWidgets.css | 14 +- .../media/runtimeExtensionsEditor.css | 5 - .../browser/remoteExtensionsInstaller.ts | 19 +- .../browser/workspaceRecommendations.ts | 39 +- .../contrib/extensions/common/extensions.ts | 13 +- .../extensions/common/extensionsInput.ts | 3 +- .../runtimeExtensionsInput.ts | 0 .../debugExtensionHostAction.ts | 55 + .../extensionProfileService.ts | 8 +- .../extensions.contribution.ts | 43 +- .../extensionsAutoProfiler.ts | 6 +- .../extensionsSlowActions.ts | 0 .../reportExtensionIssueAction.ts | 78 + .../runtimeExtensionsEditor.ts | 601 +- .../electron-sandbox/extensionsActions.ts | 22 +- .../extensionRecommendationsService.test.ts | 56 +- .../extensionsActions.test.ts | 74 +- .../electron-browser/extensionsViews.test.ts | 34 +- .../extensionsWorkbenchService.test.ts | 43 +- .../browser/externalTerminal.contribution.ts | 3 +- .../externalTerminal/node/TerminalHelper.scpt | Bin 15244 -> 16284 bytes .../externalTerminal/node/iTermHelper.scpt | Bin 7380 -> 7498 bytes .../contrib/feedback/browser/feedback.ts | 4 +- .../feedback/browser/feedbackStatusbarItem.ts | 2 +- .../files/browser/editors/textFileEditor.ts | 17 +- .../editors/textFileSaveErrorHandler.ts | 15 +- .../{common => browser}/explorerService.ts | 201 +- .../contrib/files/browser/explorerViewlet.ts | 11 +- .../files/browser/fileActions.contribution.ts | 8 +- .../contrib/files/browser/fileActions.ts | 192 +- .../contrib/files/browser/fileCommands.ts | 39 +- .../files/browser/files.contribution.ts | 56 +- .../workbench/contrib/files/browser/files.ts | 49 +- .../files/browser/media/explorerviewlet.css | 1 - .../views/explorerDecorationsProvider.ts | 2 +- .../files/browser/views/explorerView.ts | 36 +- .../files/browser/views/explorerViewer.ts | 213 +- .../files/browser/views/openEditorsView.ts | 86 +- .../files/common/editors/fileEditorInput.ts | 63 +- .../workbench/contrib/files/common/files.ts | 52 +- .../fileActions.contribution.ts | 15 +- .../files/electron-sandbox/textFileEditor.ts | 2 +- .../test/browser/fileEditorInput.test.ts | 97 +- .../format/browser/formatActionsNone.ts | 4 +- .../electron-sandbox/issue.contribution.ts | 2 +- .../issue/electron-sandbox/issueActions.ts | 2 +- .../issue/electron-sandbox/issueService.ts | 10 +- .../browser/localizations.contribution.ts | 9 +- .../contrib/logs/common/logConstants.ts | 2 + .../contrib/logs/common/logs.contribution.ts | 20 +- .../contrib/logs/common/logsActions.ts | 2 +- .../markers/browser/markers.contribution.ts | 12 +- .../contrib/markers/browser/markers.ts | 45 +- .../markers/browser/markersFilterOptions.ts | 50 +- .../contrib/markers/browser/markersModel.ts | 13 +- .../markers/browser/markersTreeViewer.ts | 13 +- .../contrib/markers/browser/markersView.ts | 120 +- .../markers/browser/markersViewActions.ts | 8 +- .../contrib/notebook/browser/constants.ts | 2 +- .../notebook/browser/contrib/coreActions.ts | 107 +- .../browser/contrib/find/findController.ts | 13 +- .../notebook/browser/contrib/fold/folding.ts | 2 +- .../browser/contrib/fold/foldingModel.ts | 6 +- .../notebook/browser/contrib/scm/scm.ts | 163 - .../browser/contrib/status/editorStatus.ts | 20 +- .../notebook/browser/diff/cellComponents.ts | 57 +- .../browser/diff/notebookDiffActions.ts | 12 +- .../browser/diff/notebookTextDiffEditor.ts | 30 +- .../notebook/browser/media/notebook.css | 60 +- .../notebook/browser/notebook.contribution.ts | 15 +- .../notebook/browser/notebookBrowser.ts | 34 +- .../notebook/browser/notebookEditorInput.ts | 4 +- .../notebook/browser/notebookEditorWidget.ts | 503 +- .../browser/notebookEditorWidgetService.ts | 1 - .../contrib/notebook/browser/notebookIcons.ts | 32 + .../notebook/browser/notebookServiceImpl.ts | 177 +- .../notebook/browser/view/notebookCellList.ts | 31 +- .../view/output/transforms/richTransform.ts | 11 +- .../view/output/transforms/streamTransform.ts | 11 +- .../view/output/transforms/textHelper.ts | 117 + .../view/renderers/backLayerWebView.ts | 10 +- .../browser/view/renderers/cellActionView.ts | 27 +- .../view/renderers/{dnd.ts => cellDnd.ts} | 0 .../browser/view/renderers/cellOutput.ts | 564 + .../browser/view/renderers/cellRenderer.ts | 71 +- .../browser/view/renderers/cellWidgets.ts | 148 +- .../browser/view/renderers/codeCell.ts | 528 +- .../view/renderers/commonViewComponents.ts | 26 - .../browser/view/renderers/markdownCell.ts | 97 +- .../browser/view/renderers/sizeObserver.ts | 78 - .../browser/view/renderers/webviewPreloads.ts | 94 +- .../browser/viewModel/baseCellViewModel.ts | 42 +- .../browser/viewModel/codeCellViewModel.ts | 39 +- .../browser/viewModel/notebookViewModel.ts | 6 +- .../common/model/notebookCellTextModel.ts | 4 + .../common/model/notebookTextModel.ts | 17 +- .../contrib/notebook/common/notebookCommon.ts | 38 +- .../notebook/common/notebookEditorModel.ts | 65 +- .../notebook/common/notebookService.ts | 8 +- .../notebook/test/notebookViewModel.test.ts | 6 +- .../notebook/test/testNotebookEditor.ts | 3 + .../outline/browser/outline.contribution.ts | 6 +- .../contrib/outline/browser/outlinePane.ts | 9 +- .../output/browser/output.contribution.ts | 20 +- .../contrib/output/browser/outputServices.ts | 6 +- .../contrib/output/browser/outputView.ts | 20 +- .../output/common/outputChannelModel.ts | 2 +- .../common/outputChannelModelService.ts | 4 +- .../output/common/outputLinkComputer.ts | 2 +- .../outputChannelModelService.ts | 4 +- .../browser/performance.contribution.ts | 2 +- .../performance/browser/perfviewEditor.ts | 140 +- .../preferences/browser/keybindingWidgets.ts | 18 +- .../preferences/browser/keybindingsEditor.ts | 121 +- .../browser/keyboardLayoutPicker.ts | 19 +- .../preferences/browser/media/preferences.css | 22 - .../browser/media/settingsEditor2.css | 4 +- .../browser/preferences.contribution.ts | 39 +- .../preferences/browser/preferencesActions.ts | 2 +- .../preferences/browser/preferencesEditor.ts | 3 +- .../preferences/browser/preferencesIcons.ts | 28 + .../browser/preferencesRenderers.ts | 21 +- .../preferences/browser/preferencesWidgets.ts | 40 +- .../preferences/browser/settingsEditor2.ts | 43 +- .../preferences/browser/settingsLayout.ts | 15 +- .../preferences/browser/settingsTree.ts | 50 +- .../preferences/browser/settingsTreeModels.ts | 92 +- .../preferences/browser/settingsWidgets.ts | 14 +- .../contrib/preferences/browser/tocTree.ts | 9 +- .../contrib/preferences/common/preferences.ts | 30 +- .../test/browser/settingsTreeModels.test.ts | 84 +- .../browser/commandsQuickAccess.ts | 21 +- .../browser/relauncher.contribution.ts | 2 +- .../remote/browser/media/tunnelView.css | 26 +- .../contrib/remote/browser/remote.ts | 42 +- .../contrib/remote/browser/remoteExplorer.ts | 358 +- .../contrib/remote/browser/remoteIndicator.ts | 21 +- .../contrib/remote/browser/tunnelView.ts | 224 +- .../contrib/remote/browser/urlFinder.ts | 64 +- .../remote/common/remote.contribution.ts | 2 +- .../contrib/remote/common/tunnelFactory.ts | 19 +- .../contrib/sash/browser/sash.contribution.ts | 12 + .../contrib/scm/browser/dirtydiffDecorator.ts | 53 +- .../scm/browser/media/dirtydiffDecorator.css | 15 - .../contrib/scm/browser/scm.contribution.ts | 19 +- .../scm/browser/scmRepositoryRenderer.ts | 15 +- .../contrib/scm/browser/scmViewPane.ts | 189 +- .../contrib/scm/browser/scmViewService.ts | 103 +- src/vs/workbench/contrib/scm/common/scm.ts | 3 +- .../contrib/scm/common/scmService.ts | 4 +- .../contrib/search/browser/replaceService.ts | 6 +- .../search/browser/search.contribution.ts | 12 +- .../contrib/search/browser/searchActions.ts | 23 +- .../contrib/search/browser/searchIcons.ts | 34 +- .../contrib/search/browser/searchView.ts | 82 +- .../contrib/search/browser/searchWidget.ts | 25 +- .../search/common/searchHistoryService.ts | 4 +- .../contrib/search/common/searchModel.ts | 8 +- .../search/test/browser/queryBuilder.test.ts | 3 +- .../search/test/browser/searchViewlet.test.ts | 5 + .../search/test/common/searchModel.test.ts | 5 + .../search/test/common/searchResult.test.ts | 5 + .../electron-browser/queryBuilder.test.ts | 3 +- .../contrib/searchEditor/browser/constants.ts | 1 + .../browser/searchEditor.contribution.ts | 195 +- .../searchEditor/browser/searchEditor.ts | 4 +- .../browser/searchEditorActions.ts | 3 +- .../searchEditor/browser/searchEditorInput.ts | 8 +- .../contrib/snippets/browser/insertSnippet.ts | 122 +- .../snippets/browser/snippets.contribution.ts | 17 +- .../contrib/snippets/browser/snippetsFile.ts | 34 +- .../snippets/browser/snippetsService.ts | 154 +- .../test/browser/snippetsService.test.ts | 9 +- .../partsSplash.contribution.ts | 2 +- .../browser/languageSurveys.contribution.ts | 37 +- .../surveys/browser/nps.contribution.ts | 28 +- .../tags/browser/workspaceTagsService.ts | 2 +- .../contrib/tags/common/workspaceTags.ts | 2 +- .../tags/electron-browser/workspaceTags.ts | 10 +- .../electron-browser/workspaceTagsService.ts | 21 +- .../electron-browser/workspaceTags.test.ts | 34 +- .../tasks/browser/abstractTaskService.ts | 215 +- .../tasks/browser/runAutomaticTasks.ts | 8 +- .../contrib/tasks/browser/taskQuickPick.ts | 37 +- .../tasks/browser/terminalTaskSystem.ts | 54 +- .../contrib/tasks/common/problemCollectors.ts | 3 +- .../contrib/tasks/common/taskService.ts | 1 + .../tasks/electron-browser/taskService.ts | 154 +- .../tasks/test/common/configuration.test.ts | 3 +- .../browser/telemetry.contribution.ts | 16 +- .../browser/environmentVariableInfo.ts | 10 +- .../terminalValidatedLocalLinkProvider.ts | 5 + .../terminal/browser/media/scrollbar.css | 4 + .../terminal/browser/media/terminal.css | 4 - .../contrib/terminal/browser/media/xterm.css | 8 +- .../terminal/browser/terminal.contribution.ts | 26 +- .../contrib/terminal/browser/terminal.ts | 13 +- .../terminal/browser/terminalActions.ts | 46 +- .../terminal/browser/terminalConfigHelper.ts | 9 +- .../contrib/terminal/browser/terminalIcons.ts | 14 + .../terminal/browser/terminalInstance.ts | 57 +- .../terminal/browser/terminalQuickAccess.ts | 6 +- .../terminal/browser/terminalService.ts | 98 +- .../contrib/terminal/browser/terminalTab.ts | 20 - .../browser/terminalTypeAheadAddon.ts | 297 +- .../contrib/terminal/browser/terminalView.ts | 29 +- .../widgets/environmentVariableInfoWidget.ts | 3 +- .../terminal/common/environmentVariable.ts | 3 +- .../common/environmentVariableService.ts | 4 +- .../contrib/terminal/common/terminal.ts | 7 +- .../terminal/common/terminalConfiguration.ts | 18 +- .../terminal/common/terminalEnvironment.ts | 3 +- .../contrib/terminal/node/terminal.ts | 2 +- .../test/browser/terminalConfigHelper.test.ts | 35 +- .../test/browser/terminalTypeahead.test.ts | 65 +- .../testing/browser/testing.contribution.ts | 10 + .../contrib/testing/common/testCollection.ts | 229 + .../contrib/testing/common/testService.ts | 30 + .../contrib/testing/common/testServiceImpl.ts | 128 + .../themes/browser/themes.contribution.ts | 13 +- .../browser/themes.test.contribution.ts | 3 +- .../timeline/browser/timeline.contribution.ts | 10 +- .../contrib/timeline/browser/timelinePane.ts | 15 +- .../contrib/update/browser/update.ts | 86 +- .../contrib/url/browser/trustedDomains.ts | 10 +- .../trustedDomainsFileSystemProvider.ts | 12 +- .../url/browser/trustedDomainsValidator.ts | 14 +- .../contrib/url/browser/url.contribution.ts | 2 +- .../url/test/browser/trustedDomains.test.ts | 8 + .../userDataSync/browser/userDataSync.ts | 24 +- .../browser/userDataSyncMergesView.ts | 8 +- .../userDataSync/browser/userDataSyncViews.ts | 9 +- .../userDataSync.contribution.ts | 2 +- .../contrib/views/browser/treeView.ts | 1059 -- .../browser/dynamicWebviewEditorOverlay.ts | 32 +- .../contrib/webview/browser/pre/main.js | 20 +- .../contrib/webview/browser/webview.ts | 6 +- .../browser/webviewWindowDragMonitor.ts | 35 + .../electron-browser/webviewElement.ts | 76 +- .../webviewIgnoreMenuShortcutsManager.ts | 74 + .../electron-sandbox/iframeWebviewElement.ts | 22 +- .../windowIgnoreMenuShortcutsManager.ts | 46 + .../webviewPanel/browser/webviewEditor.ts | 34 +- .../webviewView/browser/webviewViewPane.ts | 14 +- .../common/viewsWelcomeContribution.ts | 2 + .../common/viewsWelcomeExtensionPoint.ts | 6 + .../browser/gettingStarted.contribution.ts | 52 + .../gettingStarted/browser/gettingStarted.css | 248 + .../gettingStarted/browser/gettingStarted.ts | 430 + .../browser/vs_code_editor_getting_started.ts | 38 + .../page/browser/vs_code_welcome_page.ts | 5 +- .../page/browser/welcomePage.contribution.ts | 15 +- .../welcome/page/browser/welcomePage.ts | 26 +- .../welcome/page/browser/welcomePageColors.ts | 12 + .../browser/telemetryOptOut.ts | 10 +- .../electron-sandbox/telemetryOptOut.ts | 4 +- .../browser/editor/editorWalkThrough.ts | 2 +- .../walkThrough/browser/walkThroughPart.css | 1 + .../electron-browser/desktop.main.ts | 45 +- .../actions/developerActions.ts | 5 +- .../electron-sandbox/desktop.contribution.ts | 15 +- .../electron-sandbox/desktop.main.ts | 8 +- .../parts/dialogs/dialog.contribution.ts | 100 + .../parts/dialogs/dialogHandler.ts} | 79 +- .../parts/titlebar/titlebarPart.ts | 18 +- .../sandbox.simpleservices.ts | 96 +- src/vs/workbench/electron-sandbox/window.ts | 42 +- .../browser/authenticationService.ts | 6 +- .../backup/browser/backupFileService.ts | 38 + .../backup/common/backupFileService.ts | 29 +- .../backupFileService.ts | 17 +- .../backupFileService.test.ts | 6 +- .../configuration/browser/configuration.ts | 3 +- .../browser/configurationService.ts | 59 +- .../common/configurationEditingService.ts | 2 +- .../test/common/configurationModels.test.ts | 3 +- .../configurationEditingService.test.ts | 50 +- .../configurationService.test.ts | 96 +- .../browser/configurationResolverService.ts | 16 +- .../common/variableResolver.ts | 87 +- .../configurationResolverService.test.ts | 15 + .../electron-sandbox/contextmenuService.ts | 2 +- .../decorations/browser/decorationsService.ts | 29 +- .../test/browser/decorationsService.test.ts | 10 +- .../browser/abstractFileDialogService.ts | 25 +- .../dialogs/browser/fileDialogService.ts | 8 +- .../dialogs/browser/simpleFileDialog.ts | 12 +- .../services/dialogs/common/dialogService.ts | 38 + .../electron-sandbox/fileDialogService.ts | 8 +- .../editor/browser/codeEditorService.ts | 24 +- .../services/editor/browser/editorService.ts | 32 +- .../editor/common/editorGroupsService.ts | 25 +- .../services/editor/common/editorOpenWith.ts | 3 +- .../test/browser/editorsObserver.test.ts | 6 +- .../environment/browser/environmentService.ts | 7 +- .../environment/common/environmentService.ts | 1 - .../electron-browser/environmentService.ts | 19 +- .../electron-sandbox/environmentService.ts | 5 +- .../experiment/common/experimentService.ts | 46 +- .../browser/extensionBisect.ts | 337 + .../browser/extensionEnablementService.ts | 29 +- .../browser/extensionUrlTrustService.ts | 18 + .../common/extensionManagement.ts | 5 +- .../extensionManagementServerService.ts | 5 +- .../common/extensionManagementService.ts | 104 +- .../common/webExtensionsScannerService.ts | 47 +- .../extensionManagementService.ts | 6 +- .../extensionUrlTrustService.ts | 20 + .../extensionEnablementService.test.ts | 51 +- .../extensionIgnoredRecommendationsService.ts | 11 +- .../common/extensionRecommendations.ts | 5 - .../common/workspaceExtensionsConfig.ts | 233 +- .../extensions/browser/extensionService.ts | 11 +- .../extensions/browser/extensionUrlHandler.ts | 63 +- .../extensions/common/extensionHostMain.ts | 2 - .../cachedExtensionScanner.ts | 20 +- .../services/extensions/node/proxyResolver.ts | 7 +- .../electron-browser/extensionService.test.ts | 88 + .../worker/extensionHostWorkerMain.ts | 10 +- .../httpWebWorkerExtensionHostIframe.html | 9 +- .../httpsWebWorkerExtensionHostIframe.html | 9 +- .../common/gettingStartedContent.ts | 170 + .../common/gettingStartedRegistry.ts | 150 + .../common/gettingStartedService.ts | 195 + .../common/media/ColorTheme.jpg | Bin 0 -> 101006 bytes .../common/media/Extensions.jpg | Bin 0 -> 103983 bytes .../gettingStarted/common/media/Languages.jpg | Bin 0 -> 101550 bytes .../common/media/OpenFolder.jpg | Bin 0 -> 103714 bytes .../services/history/browser/history.ts | 6 +- .../host/browser/browserHostService.ts | 5 +- .../workbench/services/host/browser/host.ts | 2 +- .../electron-sandbox/nativeHostService.ts | 4 +- .../services/hover/browser/hoverWidget.ts | 42 +- .../integrity/node/integrityService.ts | 4 +- .../issue/common}/issue.ts | 0 .../keybinding/browser/keybindingService.ts | 70 +- ...mapService.ts => keyboardLayoutService.ts} | 19 +- .../keybinding/common/keybindingEditing.ts | 38 +- .../services/keybinding/common/keymapInfo.ts | 185 +- .../common/macLinuxFallbackKeyboardMapper.ts | 2 +- .../common/macLinuxKeyboardMapper.ts | 47 +- .../common/windowsKeyboardMapper.ts | 50 +- .../electron-browser/nativeKeymapService.ts | 174 - .../electron-sandbox/nativeKeyboardLayout.ts | 144 + .../browserKeyboardMapper.test.ts | 2 +- .../keybindingEditing.test.ts | 16 +- .../keyboardMapperTestUtils.ts | 2 +- .../macLinuxKeyboardMapper.test.ts | 3 +- .../windowsKeyboardMapper.test.ts | 3 +- .../services/label/test/browser/label.test.ts | 3 +- .../electron-sandbox/lifecycleService.ts | 4 +- .../common/notificationService.ts | 13 +- .../services/output/node/outputAppender.ts | 5 +- .../preferences/browser/preferencesService.ts | 79 +- .../common/keybindingsEditorModel.ts | 23 +- .../preferences/common/preferences.ts | 42 +- .../preferences/common/preferencesModels.ts | 4 +- .../common/preferencesValidation.ts | 24 +- .../common/keybindingsEditorModel.test.ts | 49 +- .../test/common/preferencesValidation.test.ts | 16 + .../common/abstractRemoteAgentService.ts | 7 + .../common/remoteAgentEnvironmentChannel.ts | 4 + .../remote/common/remoteAgentService.ts | 2 + .../remote/common/remoteExplorerService.ts | 266 +- .../services/search/common/search.ts | 53 +- .../services/search/common/searchService.ts | 39 +- .../search/node/ripgrepTextSearchEngine.ts | 87 +- .../search/test/common/search.test.ts | 25 + .../test/node/ripgrepTextSearchEngine.test.ts | 50 +- .../browser/workbenchCommonProperties.ts | 4 +- .../electron-browser/telemetryService.ts | 2 +- .../electron-browser/commonProperties.test.ts | 4 +- .../browser/abstractTextMateService.ts | 4 +- .../textfile/browser/textFileService.ts | 26 +- .../textfile/common/textFileEditorModel.ts | 3 +- .../services/textfile/common/textfiles.ts | 10 - .../electron-browser/nativeTextFileService.ts | 4 +- .../test/common/textFileService.io.test.ts | 14 +- .../common/textResourcePropertiesService.ts | 4 +- .../themes/browser/fileIconThemeData.ts | 5 +- .../themes/browser/productIconThemeData.ts | 4 +- .../themes/browser/workbenchThemeService.ts | 181 +- .../services/themes/common/colorThemeData.ts | 5 +- .../themes/common/themeConfiguration.ts | 12 +- .../themes/common/themeExtensionPoints.ts | 26 +- .../services/timer/browser/timerService.ts | 43 +- .../common/untitledTextEditorModel.ts | 10 +- .../test/browser/untitledTextEditor.test.ts | 14 +- .../services/userData/browser/userDataInit.ts | 83 +- .../userData/common/fileUserDataProvider.ts | 54 +- .../fileUserDataProvider.test.ts | 163 +- .../browser/userDataSyncWorkbenchService.ts | 12 +- .../userDataSync/common/userDataSync.ts | 4 + .../storageKeysSyncRegistryService.ts | 20 - .../views/browser/viewDescriptorService.ts | 18 +- .../views/common/viewContainerModel.ts | 51 +- .../test/browser/viewContainerModel.test.ts | 8 +- .../workingCopyFileOperationParticipant.ts | 37 +- .../common/workingCopyFileService.ts | 44 +- .../abstractWorkspaceEditingService.ts | 3 +- .../workspaces/browser/workspacesService.ts | 11 +- .../workspaceEditingService.ts | 4 +- .../browser/api/extHostApiCommands.test.ts | 10 +- .../browser/api/extHostConfiguration.test.ts | 5 +- .../browser/api/extHostDecorations.test.ts | 9 +- .../test/browser/api/extHostTesting.test.ts | 310 + .../test/browser/api/extHostTreeViews.test.ts | 56 +- .../browser/api/extHostTypeConverter.test.ts | 13 +- .../test/browser/api/extHostTypes.test.ts | 6 + .../test/browser/api/extHostWebview.test.ts | 6 +- .../test/browser/api/extHostWorkspace.test.ts | 21 +- .../browser/api/mainThreadTreeViews.test.ts | 2 +- src/vs/workbench/test/browser/part.test.ts | 12 +- .../parts/editor/breadcrumbModel.test.ts | 3 +- .../test/browser/parts/editor/editor.test.ts | 2 +- .../parts/editor/editorDiffModel.test.ts | 2 +- .../browser/parts/editor/editorGroups.test.ts | 9 +- .../browser/parts/editor/editorInput.test.ts | 11 +- .../browser/parts/editor/editorPane.test.ts | 56 + .../test/browser/workbenchTestServices.ts | 23 +- src/vs/workbench/test/common/memento.test.ts | 71 +- .../test/common/workbenchTestServices.ts | 8 +- .../electron-browser/workbenchTestServices.ts | 3 +- src/vs/workbench/workbench.common.main.ts | 8 +- src/vs/workbench/workbench.desktop.main.ts | 8 +- src/vs/workbench/workbench.sandbox.main.ts | 13 +- src/vs/workbench/workbench.web.api.ts | 14 +- src/vs/workbench/workbench.web.main.ts | 24 +- test/automation/src/extensions.ts | 6 +- test/automation/src/keybindings.ts | 14 +- .../src/areas/extensions/extensions.test.ts | 2 +- .../smoke/src/areas/notebook/notebook.test.ts | 2 +- .../src/areas/preferences/preferences.test.ts | 2 +- .../src/areas/workbench/localization.test.ts | 2 +- yarn.lock | 702 +- 1351 files changed, 56356 insertions(+), 39096 deletions(-) create mode 100644 .vscode/notebooks/grooming-delta.github-issues create mode 100644 .vscode/notebooks/my-endgame.github-issues delete mode 100644 build/.nativeignore create mode 100644 build/azure-pipelines/common/retry.ts create mode 100755 build/azure-pipelines/linux/alpine/install-dependencies.sh create mode 100755 build/azure-pipelines/linux/alpine/publish.sh delete mode 100755 build/azure-pipelines/linux/multiarch/alpine/build.sh delete mode 100755 build/azure-pipelines/linux/multiarch/alpine/prebuild.sh delete mode 100755 build/azure-pipelines/linux/multiarch/alpine/publish.sh delete mode 100755 build/azure-pipelines/linux/multiarch/arm64/build.sh delete mode 100755 build/azure-pipelines/linux/multiarch/arm64/prebuild.sh delete mode 100755 build/azure-pipelines/linux/multiarch/arm64/publish.sh delete mode 100755 build/azure-pipelines/linux/multiarch/armhf/build.sh delete mode 100755 build/azure-pipelines/linux/multiarch/armhf/prebuild.sh delete mode 100755 build/azure-pipelines/linux/multiarch/armhf/publish.sh create mode 100644 build/azure-pipelines/linux/product-build-alpine.yml delete mode 100644 build/azure-pipelines/linux/product-build-linux-multiarch.yml delete mode 100644 build/azure-pipelines/win32/product-build-win32-arm64.yml create mode 100644 build/azure-pipelines/win32/retry.ps1 create mode 100755 extensions/configuration-editing/build/inline-allOf.ts create mode 100644 extensions/configuration-editing/build/tsconfig.json create mode 100644 extensions/configuration-editing/schemas/devContainer.schema.generated.json rename extensions/configuration-editing/schemas/{devContainer.schema.json => devContainer.schema.src.json} (97%) create mode 100644 extensions/emmet/src/test/evaluateMathExpression.test.ts delete mode 100644 extensions/emmet/src/typings/emmetio__math-expression.d.ts create mode 100644 extensions/npm/src/preferred-pm.ts create mode 100644 extensions/sql/build/update-grammar.js rename extensions/theme-tomorrow-night-blue/themes/{tomorrow-night-blue-theme.json => tomorrow-night-blue-color-theme.json} (96%) create mode 100644 src/vs/base/browser/hash.ts rename src/vs/base/test/{common => browser}/hash.test.ts (85%) create mode 100644 src/vs/base/test/common/network.test.ts create mode 100644 src/vs/code/electron-main/protocol.ts delete mode 100644 src/vs/code/test/electron-main/nativeHelpers.test.ts create mode 100644 src/vs/editor/common/controller/cursorAtomicMoveOperations.ts create mode 100644 src/vs/editor/contrib/colorPicker/colorContributions.ts rename src/vs/editor/contrib/{rename/onTypeRename.ts => linkedEditing/linkedEditing.ts} (81%) rename src/vs/editor/contrib/{rename/test/onTypeRename.test.ts => linkedEditing/test/linkedEditing.test..ts} (91%) create mode 100644 src/vs/editor/test/common/controller/cursorAtomicMoveOperations.test.ts rename src/vs/{editor/contrib/rename/media/onTypeRename.css => platform/display/common/displayMainService.ts} (68%) create mode 100644 src/vs/platform/display/electron-main/displayMainService.ts rename src/vs/{code => platform/environment}/test/node/argv.test.ts (99%) create mode 100644 src/vs/platform/environment/test/node/nativeModules.test.ts create mode 100644 src/vs/platform/extensionManagement/common/extensionUrlTrust.ts create mode 100644 src/vs/platform/extensionManagement/node/extensionUrlTrustService.ts rename src/vs/{workbench/services/keybinding => platform/keyboardLayout}/common/dispatchConfig.ts (100%) create mode 100644 src/vs/platform/keyboardLayout/common/keyboardLayout.ts create mode 100644 src/vs/platform/keyboardLayout/common/keyboardLayoutMainService.ts rename src/vs/{workbench/services/keybinding => platform/keyboardLayout}/common/keyboardMapper.ts (100%) create mode 100644 src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts create mode 100644 src/vs/platform/storage/test/common/storageService.test.ts delete mode 100644 src/vs/platform/theme/browser/checkbox.ts create mode 100644 src/vs/platform/userDataSync/common/extensionsStorageSync.ts delete mode 100644 src/vs/platform/userDataSync/common/storageKeys.ts rename src/vs/{code => platform/windows}/test/electron-main/windowsStateStorage.test.ts (100%) create mode 100644 src/vs/workbench/api/browser/mainThreadTesting.ts create mode 100644 src/vs/workbench/api/common/extHostTesting.ts delete mode 100644 src/vs/workbench/api/common/shared/editor.ts create mode 100644 src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts rename src/vs/workbench/{services/dialogs/browser/dialogService.ts => browser/parts/dialogs/dialogHandler.ts} (91%) rename src/vs/workbench/{contrib/views/browser => browser/parts/views}/media/views.css (98%) create mode 100644 src/vs/workbench/common/dialogs.ts create mode 100644 src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts create mode 100644 src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts create mode 100644 src/vs/workbench/contrib/debug/browser/debugIcons.ts delete mode 100644 src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts create mode 100644 src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts create mode 100644 src/vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor.ts create mode 100644 src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts create mode 100644 src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/runtimeExtensionsEditor.css (91%) rename src/vs/workbench/contrib/extensions/{electron-browser => common}/runtimeExtensionsInput.ts (100%) create mode 100644 src/vs/workbench/contrib/extensions/electron-browser/debugExtensionHostAction.ts rename src/vs/workbench/contrib/extensions/{electron-sandbox => electron-browser}/extensionsSlowActions.ts (100%) create mode 100644 src/vs/workbench/contrib/extensions/electron-browser/reportExtensionIssueAction.ts rename src/vs/workbench/contrib/files/{common => browser}/explorerService.ts (68%) delete mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/notebookIcons.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts rename src/vs/workbench/contrib/notebook/browser/view/renderers/{dnd.ts => cellDnd.ts} (100%) create mode 100644 src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts rename src/vs/workbench/{services => contrib}/output/common/outputChannelModel.ts (99%) rename src/vs/workbench/{services => contrib}/output/common/outputChannelModelService.ts (75%) rename src/vs/workbench/{services => contrib}/output/electron-browser/outputChannelModelService.ts (97%) create mode 100644 src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalIcons.ts create mode 100644 src/vs/workbench/contrib/testing/browser/testing.contribution.ts create mode 100644 src/vs/workbench/contrib/testing/common/testCollection.ts create mode 100644 src/vs/workbench/contrib/testing/common/testService.ts create mode 100644 src/vs/workbench/contrib/testing/common/testServiceImpl.ts delete mode 100644 src/vs/workbench/contrib/views/browser/treeView.ts create mode 100644 src/vs/workbench/contrib/webview/browser/webviewWindowDragMonitor.ts create mode 100644 src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts create mode 100644 src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts create mode 100644 src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts create mode 100644 src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css create mode 100644 src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts create mode 100644 src/vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started.ts create mode 100644 src/vs/workbench/contrib/welcome/page/browser/welcomePageColors.ts create mode 100644 src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts rename src/vs/workbench/{services/dialogs/electron-sandbox/dialogService.ts => electron-sandbox/parts/dialogs/dialogHandler.ts} (74%) create mode 100644 src/vs/workbench/services/backup/browser/backupFileService.ts rename src/vs/workbench/services/backup/{node => electron-browser}/backupFileService.ts (53%) create mode 100644 src/vs/workbench/services/dialogs/common/dialogService.ts create mode 100644 src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts create mode 100644 src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts create mode 100644 src/vs/workbench/services/extensionManagement/electron-sandbox/extensionUrlTrustService.ts create mode 100644 src/vs/workbench/services/extensions/test/electron-browser/extensionService.test.ts create mode 100644 src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts create mode 100644 src/vs/workbench/services/gettingStarted/common/gettingStartedRegistry.ts create mode 100644 src/vs/workbench/services/gettingStarted/common/gettingStartedService.ts create mode 100644 src/vs/workbench/services/gettingStarted/common/media/ColorTheme.jpg create mode 100644 src/vs/workbench/services/gettingStarted/common/media/Extensions.jpg create mode 100644 src/vs/workbench/services/gettingStarted/common/media/Languages.jpg create mode 100644 src/vs/workbench/services/gettingStarted/common/media/OpenFolder.jpg rename src/vs/workbench/{contrib/issue/electron-sandbox => services/issue/common}/issue.ts (100%) rename src/vs/workbench/services/keybinding/browser/{keymapService.ts => keyboardLayoutService.ts} (95%) delete mode 100644 src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts create mode 100644 src/vs/workbench/services/keybinding/electron-sandbox/nativeKeyboardLayout.ts delete mode 100644 src/vs/workbench/services/userDataSync/electron-browser/storageKeysSyncRegistryService.ts create mode 100644 src/vs/workbench/test/browser/api/extHostTesting.test.ts diff --git a/.eslintrc.json b/.eslintrc.json index 055bc22f8..fd72f0e58 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -983,6 +983,7 @@ "collapse", "create", "delete", + "discover", "dispose", "edit", "end", diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 4a04784b8..51e7f3660 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: Question - url: https://stackoverflow.com/questions/tagged/visual-studio-code + url: https://stackoverflow.com/questions/tagged/visual-studio-code about: Please ask and answer questions here. diff --git a/.github/calendar.yml b/.github/calendar.yml index f14957960..c1b5a2569 100644 --- a/.github/calendar.yml +++ b/.github/calendar.yml @@ -1,37 +1,37 @@ { - '2018-01-29 18:00, US/Pacific': 'endgame', - '2018-02-07 12:00, US/Pacific': 'release', # 1.20.0 - '2018-02-12 12:00, US/Pacific': 'development', - '2018-02-14 16:00, Europe/Zurich': 'release', # 1.20.1 - '2018-02-19 16:00, Europe/Zurich': 'development', - '2018-02-26 18:00, US/Pacific': 'endgame', - '2018-03-07 12:00, US/Pacific': 'release', # 1.21.0 - '2018-03-12 12:00, US/Pacific': 'development', - '2018-03-15 12:00, US/Pacific': 'release', # 1.21.1 - '2018-03-20 12:00, US/Pacific': 'development', - '2018-03-26 18:00, US/Pacific': 'endgame', - '2018-04-06 18:00, US/Pacific': 'release', # 1.22.1 - '2018-04-11 18:00, US/Pacific': 'development', - '2018-04-12 12:00, US/Pacific': 'release', # 1.22.2 - '2018-04-17 12:00, US/Pacific': 'development', - '2018-04-23 18:00, US/Pacific': 'endgame', - '2018-05-03 12:00, US/Pacific': 'release', # 1.23.0 - '2018-05-08 12:00, US/Pacific': 'development', - '2018-05-10 12:00, US/Pacific': 'release', # 1.23.1 - '2018-05-15 12:00, US/Pacific': 'development', - '2018-05-28 18:00, US/Pacific': 'endgame', + "2018-01-29 18:00, US/Pacific": "endgame", + "2018-02-07 12:00, US/Pacific": "release", # 1.20.0 + "2018-02-12 12:00, US/Pacific": "development", + "2018-02-14 16:00, Europe/Zurich": "release", # 1.20.1 + "2018-02-19 16:00, Europe/Zurich": "development", + "2018-02-26 18:00, US/Pacific": "endgame", + "2018-03-07 12:00, US/Pacific": "release", # 1.21.0 + "2018-03-12 12:00, US/Pacific": "development", + "2018-03-15 12:00, US/Pacific": "release", # 1.21.1 + "2018-03-20 12:00, US/Pacific": "development", + "2018-03-26 18:00, US/Pacific": "endgame", + "2018-04-06 18:00, US/Pacific": "release", # 1.22.1 + "2018-04-11 18:00, US/Pacific": "development", + "2018-04-12 12:00, US/Pacific": "release", # 1.22.2 + "2018-04-17 12:00, US/Pacific": "development", + "2018-04-23 18:00, US/Pacific": "endgame", + "2018-05-03 12:00, US/Pacific": "release", # 1.23.0 + "2018-05-08 12:00, US/Pacific": "development", + "2018-05-10 12:00, US/Pacific": "release", # 1.23.1 + "2018-05-15 12:00, US/Pacific": "development", + "2018-05-28 18:00, US/Pacific": "endgame", # 'release' not needed anymore, return to 'development' after releasing. - '2018-06-06 12:00, US/Pacific': 'development', # 1.24.0 released - '2018-06-25 18:00, US/Pacific': 'endgame', - '2018-07-05 12:00, US/Pacific': 'development', # 1.25.0 released - '2018-07-30 18:00, US/Pacific': 'endgame', - '2018-08-13 12:00, US/Pacific': 'development', # 1.26.0 released - '2018-08-27 18:00, US/Pacific': 'endgame', - '2018-09-05 12:00, US/Pacific': 'development', # 1.27.0 released - '2018-09-24 18:00, US/Pacific': 'endgame', - '2018-10-08 09:00, US/Pacific': 'development', # 1.28.0 released - '2018-10-29 18:00, US/Pacific': 'endgame', - '2018-11-12 11:00, US/Pacific': 'development', # 1.29.0 released - '2018-12-03 18:00, US/Pacific': 'endgame', - '2018-12-12 13:00, US/Pacific': 'development', # 1.30.0 released + "2018-06-06 12:00, US/Pacific": "development", # 1.24.0 released + "2018-06-25 18:00, US/Pacific": "endgame", + "2018-07-05 12:00, US/Pacific": "development", # 1.25.0 released + "2018-07-30 18:00, US/Pacific": "endgame", + "2018-08-13 12:00, US/Pacific": "development", # 1.26.0 released + "2018-08-27 18:00, US/Pacific": "endgame", + "2018-09-05 12:00, US/Pacific": "development", # 1.27.0 released + "2018-09-24 18:00, US/Pacific": "endgame", + "2018-10-08 09:00, US/Pacific": "development", # 1.28.0 released + "2018-10-29 18:00, US/Pacific": "endgame", + "2018-11-12 11:00, US/Pacific": "development", # 1.29.0 released + "2018-12-03 18:00, US/Pacific": "endgame", + "2018-12-12 13:00, US/Pacific": "development", # 1.30.0 released } diff --git a/.github/classifier.json b/.github/classifier.json index e4acc63c1..0ff0712c3 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -72,7 +72,7 @@ "file-watcher": {"assign": ["bpasero"]}, "font-rendering": {"assign": []}, "formatting": {"assign": []}, - "git": {"assign": ["joaomoreno"]}, + "git": {"assign": ["eamodio"]}, "gpu": {"assign": ["deepak1556"]}, "grammar": {"assign": ["mjbvz"]}, "grid-view": {"assign": ["joaomoreno"]}, @@ -81,9 +81,9 @@ "icon-brand": {"assign": []}, "icons-product": {"assign": ["misolori"]}, "install-update": {"assign": []}, - "integrated-terminal": {"assign": ["Tyriar"]}, - "integrated-terminal-conpty": {"assign": ["Tyriar"]}, - "integrated-terminal-links": {"assign": ["Tyriar"]}, + "integrated-terminal": {"assign": ["meganrogge"]}, + "integrated-terminal-conpty": {"assign": ["meganrogge"]}, + "integrated-terminal-links": {"assign": ["meganrogge"]}, "integration-test": {"assign": []}, "intellisense-config": {"assign": []}, "ipc": {"assign": ["joaomoreno"]}, @@ -119,7 +119,7 @@ "remote": {"assign": []}, "remote-explorer": {"assign": ["alexr00"]}, "rename": {"assign": ["jrieken"]}, - "scm": {"assign": ["joaomoreno"]}, + "scm": {"assign": ["eamodio"]}, "screencast-mode": {"assign": ["lszomoru"]}, "search": {"assign": ["roblourens"]}, "search-editor": {"assign": ["JacksonKearl"]}, @@ -159,7 +159,7 @@ "workbench-electron": {"assign": ["deepak1556"]}, "workbench-feedback": {"assign": ["bpasero"]}, "workbench-history": {"assign": ["bpasero"]}, - "workbench-hot-exit": {"assign": ["Tyriar"]}, + "workbench-hot-exit": {"assign": []}, "workbench-launch": {"assign": []}, "workbench-link": {"assign": []}, "workbench-multiroot": {"assign": ["bpasero"]}, diff --git a/.github/commands.json b/.github/commands.json index 7a1220f7d..980b2906b 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -202,6 +202,19 @@ "addLabel": "*caused-by-extension", "comment": "It looks like this is caused by the Python extension. Please file it with the repository [here](https://github.com/microsoft/vscode-python). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" }, + { + "type": "comment", + "name": "extJupyter", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the Jupyter extension. Please file it with the repository [here](https://github.com/microsoft/vscode-jupyter). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, { "type": "comment", "name": "extC", diff --git a/.github/commands.yml b/.github/commands.yml index 64fdf683b..5073658e5 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -1,12 +1,13 @@ { - perform: true, - commands: [ - { - type: 'comment', - name: 'findDuplicates', - allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], - action: 'comment', - comment: "Potential duplicates:\n${potentialDuplicates}" - } - ] + perform: true, + commands: + [ + { + type: "comment", + name: "findDuplicates", + allowUsers: ["cleidigh", "usernamehw", "gjsjohnmurray", "IllusionMH"], + action: "comment", + comment: "Potential duplicates:\n${potentialDuplicates}", + }, + ], } diff --git a/.github/endgame/insiders.yml b/.github/endgame/insiders.yml index f11bd611c..4996474f4 100644 --- a/.github/endgame/insiders.yml +++ b/.github/endgame/insiders.yml @@ -1,6 +1,6 @@ { - insidersLabel: 'insiders', - insidersColor: '006b75', - action: 'add', - perform: true -} \ No newline at end of file + insidersLabel: "insiders", + insidersColor: "006b75", + action: "add", + perform: true, +} diff --git a/.github/insiders.yml b/.github/insiders.yml index e9a49ce78..ab59e873b 100644 --- a/.github/insiders.yml +++ b/.github/insiders.yml @@ -1,6 +1,6 @@ { - insidersLabel: 'insiders', - insidersColor: '006b75', - action: 'remove', - perform: true -} \ No newline at end of file + insidersLabel: "insiders", + insidersColor: "006b75", + action: "remove", + perform: true, +} diff --git a/.github/similarity.yml b/.github/similarity.yml index cd51cd2da..cb00a4c9a 100644 --- a/.github/similarity.yml +++ b/.github/similarity.yml @@ -1,5 +1,5 @@ { - perform: true, - whenCreatedByTeam: false, - comment: "(Experimental duplicate detection)\nThanks for submitting this issue. Please also check if it is already covered by an existing one, like:\n${potentialDuplicates}" + perform: true, + whenCreatedByTeam: false, + comment: "(Experimental duplicate detection)\nThanks for submitting this issue. Please also check if it is already covered by an existing one, like:\n${potentialDuplicates}", } diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml index de8311d7a..6924d9650 100644 --- a/.github/workflows/author-verified.yml +++ b/.github/workflows/author-verified.yml @@ -16,8 +16,8 @@ jobs: if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') @@ -34,7 +34,7 @@ jobs: with: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} - requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by commenting `/verified` if things are now working as expected.\n\nIf things still don't seem right, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command pallette to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!" + requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by commenting `/verified` if things are now working as expected.\n\nIf things still don't seem right, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command palette to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!" pendingReleaseLabel: awaiting-insiders-release verifiedLabel: verified authorVerificationRequestedLabel: author-verification-requested diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d509cce2a..08775eab9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,22 +123,22 @@ jobs: CHILD_CONCURRENCY: "1" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v1 - # TODO: rename azure-pipelines/linux/xvfb.init to github-actions - - run: | - sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 - sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb - sudo chmod +x /etc/init.d/xvfb - sudo update-rc.d xvfb defaults - sudo service xvfb start - name: Setup Build Environment - - uses: actions/setup-node@v1 - with: - node-version: 10 - - run: yarn --frozen-lockfile - name: Install Dependencies - - run: yarn monaco-compile-check - name: Run Monaco Editor Checks - - run: yarn gulp editor-esm-bundle - name: Editor Distro & ESM Bundle + - uses: actions/checkout@v1 + # TODO: rename azure-pipelines/linux/xvfb.init to github-actions + - run: | + sudo apt-get update + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 + sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + sudo chmod +x /etc/init.d/xvfb + sudo update-rc.d xvfb defaults + sudo service xvfb start + name: Setup Build Environment + - uses: actions/setup-node@v1 + with: + node-version: 10 + - run: yarn --frozen-lockfile + name: Install Dependencies + - run: yarn monaco-compile-check + name: Run Monaco Editor Checks + - run: yarn gulp editor-esm-bundle + name: Editor Distro & ESM Bundle diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 017708844..88761f8aa 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,48 +2,47 @@ name: "Code Scanning" on: schedule: - - cron: '0 0 * * 2' + - cron: "0 0 * * 2" jobs: CodeQL-Build: - # CodeQL runs on ubuntu-latest and windows-latest runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: javascript + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: javascript - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language - #- run: | - # make bootstrap - # make release + #- run: | + # make bootstrap + # make release - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index cac277575..c2b8f95e0 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -11,9 +11,9 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Commands diff --git a/.github/workflows/deep-classifier-monitor.yml b/.github/workflows/deep-classifier-monitor.yml index ef7fedaec..2734f6b78 100644 --- a/.github/workflows/deep-classifier-monitor.yml +++ b/.github/workflows/deep-classifier-monitor.yml @@ -10,8 +10,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index de49bf3bc..1aaf05a87 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -12,8 +12,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml index acf3c50da..42b5f1b63 100644 --- a/.github/workflows/deep-classifier-scraper.yml +++ b/.github/workflows/deep-classifier-scraper.yml @@ -10,8 +10,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/devcontainer-cache.yml b/.github/workflows/devcontainer-cache.yml index 82290a475..a250b56cd 100644 --- a/.github/workflows/devcontainer-cache.yml +++ b/.github/workflows/devcontainer-cache.yml @@ -4,12 +4,12 @@ on: push: # Currently doing this for master, but could be done for PRs as well branches: - - 'master' + - "master" # Only updates to these files result in changes to installed packages, so skip otherwise paths: - - '**/package-lock.json' - - '**/yarn.lock' + - "**/package-lock.json" + - "**/yarn.lock" jobs: devcontainer: @@ -38,4 +38,3 @@ jobs: if [ "$GIT_BRANCH" == "" ]; then GIT_BRANCH=master; fi .devcontainer/cache/build-cache-image.sh "${{ secrets.CONTAINER_IMAGE_REGISTRY }}/public/vscode/devcontainers/repos/microsoft/vscode" "${GIT_BRANCH}" - diff --git a/.github/workflows/english-please.yml b/.github/workflows/english-please.yml index 0fdae28b0..5253fb73d 100644 --- a/.github/workflows/english-please.yml +++ b/.github/workflows/english-please.yml @@ -12,8 +12,8 @@ jobs: if: contains(github.event.issue.labels.*.name, '*english-please') uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions if: contains(github.event.issue.labels.*.name, '*english-please') diff --git a/.github/workflows/feature-request.yml b/.github/workflows/feature-request.yml index 005e8818b..ec3bbb5e1 100644 --- a/.github/workflows/feature-request.yml +++ b/.github/workflows/feature-request.yml @@ -16,9 +16,9 @@ jobs: if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') run: npm install --production --prefix ./actions diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml index f4fe00799..b27d79fda 100644 --- a/.github/workflows/latest-release-monitor.yml +++ b/.github/workflows/latest-release-monitor.yml @@ -12,9 +12,9 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions run: npm install --production --prefix ./actions - name: Install Storage Module diff --git a/.github/workflows/locker.yml b/.github/workflows/locker.yml index 4d5e9ea1b..dca0f5be6 100644 --- a/.github/workflows/locker.yml +++ b/.github/workflows/locker.yml @@ -12,9 +12,9 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Locker @@ -24,3 +24,5 @@ jobs: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} daysSinceUpdate: 3 ignoredLabel: "*out-of-scope" + ignoreLabelUntil: "author-verification-requested" + labelUntil: "verified" diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml index 8ef8347f6..75b7af423 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -12,9 +12,9 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Needs More Info Closer @@ -24,7 +24,7 @@ jobs: token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} label: needs more info closeDays: 7 - additionalTeam: "cleidigh|usernamehw|gjsjohnmurray|IllusionMH" + additionalTeam: "cleidigh|usernamehw|gjsjohnmurray|IllusionMH" closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" pingDays: 80 pingComment: "Hey @${assignee}, this issue might need further attention.\n\n@${author}, you can help us out by closing this issue if the problem no longer exists, or adding more information." diff --git a/.github/workflows/on-label.yml b/.github/workflows/on-label.yml index 60d2cfe22..67b8ec94c 100644 --- a/.github/workflows/on-label.yml +++ b/.github/workflows/on-label.yml @@ -10,8 +10,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions @@ -28,7 +28,7 @@ jobs: uses: ./actions/author-verified with: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} - requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by confirming things are working as expected in the latest Insiders release. If things look good, please leave a comment with the text `/verified` to let us know. If not, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command pallete to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!" + requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by confirming things are working as expected in the latest Insiders release. If things look good, please leave a comment with the text `/verified` to let us know. If not, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command palette to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!" pendingReleaseLabel: awaiting-insiders-release verifiedLabel: verified authorVerificationRequestedLabel: author-verification-requested diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index ba392bc4c..ebfa1cb34 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -10,8 +10,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/release-pipeline-labeler.yml b/.github/workflows/release-pipeline-labeler.yml index 0b38142f2..e5ea1a26b 100644 --- a/.github/workflows/release-pipeline-labeler.yml +++ b/.github/workflows/release-pipeline-labeler.yml @@ -12,8 +12,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Checkout Repo if: github.event_name != 'issues' diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index bd2444b60..aee0796fa 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -9,24 +9,28 @@ jobs: richnav: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2 - - uses: actions/cache@v2 - id: caching-stage - name: Cache VS Code dependencies - with: - path: node_modules - key: ${{ runner.os }}-dependencies-${{ hashfiles('yarn.lock') }} - restore-keys: ${{ runner.os }}-dependencies- + - uses: actions/cache@v2 + id: caching-stage + name: Cache VS Code dependencies + with: + path: node_modules + key: ${{ runner.os }}-dependencies-${{ hashfiles('yarn.lock') }} + restore-keys: ${{ runner.os }}-dependencies- - - name: Install dependencies - if: steps.caching-stage.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile - env: - CHILD_CONCURRENCY: 1 + - uses: actions/setup-node@v1 + with: + node-version: 10 - - uses: microsoft/RichCodeNavIndexer@v0.1 - with: - languages: typescript - repo-token: ${{ secrets.GITHUB_TOKEN }} - continue-on-error: true + - name: Install dependencies + if: steps.caching-stage.outputs.cache-hit != 'true' + run: yarn --frozen-lockfile + env: + CHILD_CONCURRENCY: 1 + + - uses: microsoft/RichCodeNavIndexer@v0.1 + with: + languages: typescript + repo-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml index 3174a463b..6c2752107 100644 --- a/.github/workflows/test-plan-item-validator.yml +++ b/.github/workflows/test-plan-item-validator.yml @@ -12,9 +12,9 @@ jobs: if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') run: npm install --production --prefix ./actions diff --git a/.vscode/launch.json b/.vscode/launch.json index 6153d7a99..a83c164f3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -41,10 +41,7 @@ "port": 5876, "outFiles": [ "${workspaceFolder}/out/**/*.js" - ], - "presentation": { - "hidden": true, - } + ] }, { "type": "node", @@ -200,7 +197,8 @@ "request": "attach", "name": "Attach to VS Code", "browserAttachLocation": "workspace", - "port": 9222 + "port": 9222, + "perScriptSourcemaps": "yes" }, { "type": "pwa-chrome", @@ -377,7 +375,7 @@ } }, { - "type": "node", + "type": "pwa-node", "request": "launch", "name": "Run Unit Tests", "program": "${workspaceFolder}/test/unit/electron/index.js", @@ -396,6 +394,41 @@ "outFiles": [ "${workspaceFolder}/out/**/*.js" ], + "cascadeTerminateToConfigurations": [ + "Attach to VS Code" + ], + "env": { + "MOCHA_COLORS": "true" + }, + "presentation": { + "hidden": true + } + }, + { + "type": "pwa-node", + "request": "launch", + "name": "Run Unit Tests For Current File", + "program": "${workspaceFolder}/test/unit/electron/index.js", + "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron", + "windows": { + "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.exe" + }, + "linux": { + "runtimeExecutable": "${workspaceFolder}/.build/electron/code-oss" + }, + "cascadeTerminateToConfigurations": [ + "Attach to VS Code" + ], + "outputCapture": "std", + "args": [ + "--remote-debugging-port=9222", + "--run", + "${relativeFile}" + ], + "cwd": "${workspaceFolder}", + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], "env": { "MOCHA_COLORS": "true" }, @@ -482,6 +515,17 @@ "group": "1_vscode", "order": 2 } + }, + { + "name": "Debug Unit Tests (Current File)", + "configurations": [ + "Attach to VS Code", + "Run Unit Tests For Current File" + ], + "presentation": { + "group": "1_vscode", + "order": 2 + } } ] } diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 2eb4b6432..8ff55e2c6 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -8,7 +8,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"September 2020\"", + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"November 2020\"", "editable": true }, { diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 0d6e45334..e749a11c4 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -2,43 +2,49 @@ { "kind": 1, "language": "markdown", - "value": "# Endgame", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "## Macros", + "value": "#### Macros", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github\n\n$MILESTONE=milestone:\"October 2020\"\n\n$MINE=assignee:@me", + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server\n\n$MILESTONE=milestone:\"November 2020\"", + "editable": false + }, + { + "kind": 1, + "language": "markdown", + "value": "# Preparation", "editable": true }, { "kind": 1, "language": "markdown", - "value": "## Endgame Champion", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### Test Plan Items", + "value": "## Open Pull Requests on the Milestone", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE label:testplan-item is:open", + "value": "$REPOS $MILESTONE is:pr is:open", "editable": true }, { "kind": 1, "language": "markdown", - "value": "### Feature Requests Missing Labels", + "value": "## Open Issues on the Milestone", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE is:issue is:open -label:iteration-plan -label:endgame-plan -label:testplan-item", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Feature Requests Missing Labels", "editable": true }, { @@ -50,133 +56,55 @@ { "kind": 1, "language": "markdown", - "value": "### Open Issues on the Milestone", + "value": "# Testing", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Test Plan Items", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -label:testplan-item -label:iteration-plan -label:endgame-plan is:open", + "value": "$REPOS $MILESTONE is:issue is:open label:testplan-item", "editable": true }, { "kind": 1, "language": "markdown", - "value": "## Testing", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### My Assigned Test Plan Items", + "value": "## Verification Needed", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE label:testplan-item is:open", + "value": "$REPOS $MILESTONE is:issue is:closed label:feature-request label:verification-needed -label:verified", "editable": true }, { "kind": 1, "language": "markdown", - "value": "### My Created Test Plan Items", + "value": "# Verification", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE author:@me label:testplan-item", + "value": "$REPOS $MILESTONE is:issue is:closed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", "editable": true }, { "kind": 1, "language": "markdown", - "value": "## Verification", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### Issues to Verify", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "#### Issues filed by me", + "value": "# Candidates", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE author:@me sort:updated-asc is:closed is:issue label:bug -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "#### Issues filed by others", - "editable": true - }, - { - "kind": 2, - "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE -author:@me sort:updated-asc is:closed is:issue label:bug -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### Verification Needed", - "editable": true - }, - { - "kind": 2, - "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate ", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### My Issues Needing Verification", - "editable": true - }, - { - "kind": 2, - "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE sort:updated-desc is:closed is:issue -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found\n", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### My Open Issues", - "editable": true - }, - { - "kind": 2, - "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:open is:issue", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "## Documentation", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### Needing Release Notes", - "editable": true - }, - { - "kind": 2, - "language": "github-issues", - "value": "repo:microsoft/vscode $MILESTONE $MINE is:closed label:feature-request -label:on-release-notes", + "value": "$REPOS $MILESTONE label:candidate", "editable": true } ] \ No newline at end of file diff --git a/.vscode/notebooks/grooming-delta.github-issues b/.vscode/notebooks/grooming-delta.github-issues new file mode 100644 index 000000000..8bc9d38b4 --- /dev/null +++ b/.vscode/notebooks/grooming-delta.github-issues @@ -0,0 +1,767 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "## Config", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$since=2020-10-01", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode\n\nQuery exceeds the maximum result. Run the query manually: `is:issue is:open closed:>2020-10-01`", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "//repo:microsoft/vscode is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "//repo:microsoft/vscode is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-remote-release", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-remote-release is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-remote-release is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-editor", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-editor is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-editor is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-docs", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-docs is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-docs is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-js-debug", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-js-debug is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-js-debug is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# language-server-protocol", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/language-server-protocol is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/language-server-protocol is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-eslint", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-eslint is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-eslint is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-css-languageservice", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-css-languageservice is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-css-languageservice is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-test", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-test is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-test is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-pull-request-github" + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-pull-request-github is:issue closed:>$since" + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-test is:issue created:>$since" + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-chrome-debug (deprecated)", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-chrome-debug is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-chrome-debug is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-chrome-debug-core", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-chrome-debug-core is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-chrome-debug-core is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-debugadapter-node", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-debugadapter-node is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-debugadapter-node is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-emmet-helper", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-emmet-helper is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-emmet-helper is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-extension-vscode\n\nDeprecated", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-extension-vscode is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-extension-vscode is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-extension-samples", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-extension-samples is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-extension-samples is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-filewatcher-windows", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-filewatcher-windows is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-filewatcher-windows is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-generator-code", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-generator-code is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-generator-code is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-html-languageservice", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-html-languageservice is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-html-languageservice is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-jshint", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-jshint is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-jshint is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-json-languageservice", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-json-languageservice is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-json-languageservice is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-languageserver-node", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-languageserver-node is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-languageserver-node is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-loader", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-loader is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-loader is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-mono-debug", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-mono-debug is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-mono-debug is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-node-debug", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-node-debug is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-node-debug is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-node-debug2", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-node-debug2 is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-node-debug2 is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-recipes", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-recipes is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-recipes is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-textmate", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-textmate is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-textmate is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-themes", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-themes is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-themes is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-vsce", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-vsce is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-vsce is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-website", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-website is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-website is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-windows-process-tree", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-windows-process-tree is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-windows-process-tree is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# debug-adapter-protocol", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/debug-adapter-protocol is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/debug-adapter-protocol is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# inno-updater", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/inno-updater is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/inno-updater is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# language-server-protocol-inspector", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/language-server-protocol-inspector is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/language-server-protocol-inspector is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-languages", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-languages is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-languages is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-typescript", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-typescript is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-typescript is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-css", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-css is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-css is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-json", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-json is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-json is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-html", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-html is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-html is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-editor-webpack-plugin", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-editor-webpack-plugin is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-editor-webpack-plugin is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# node-jsonc-parser", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/node-jsonc-parser is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/node-jsonc-parser is:issue created:>$since", + "editable": true + } +] \ No newline at end of file diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues index 687ea5b57..f44b2c71e 100644 --- a/.vscode/notebooks/grooming.github-issues +++ b/.vscode/notebooks/grooming.github-issues @@ -20,7 +20,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode is:open is:issue assignee:@me -label:L10N -label:VIM -label:api -label:api-finalization -label:api-proposal -label:authentication -label:breadcrumbs -label:callhierarchy -label:code-lens -label:color-palette -label:comments -label:config -label:context-keys -label:css-less-scss -label:custom-editors -label:debug -label:debug-console -label:dialogs -label:diff-editor -label:dropdown -label:editor -label:editor-RTL -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-scrollbar -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:error-list -label:explorer-custom -label:extension-host -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-guess-encoding -label:file-io -label:file-watcher -label:font-rendering -label:formatting -label:git -label:github -label:gpu -label:grammar -label:grid-view -label:html -label:i18n -label:icon-brand -label:icons-product -label:install-update -label:integrated-terminal -label:integrated-terminal-conpty -label:integrated-terminal-links -label:integrated-terminal-rendering -label:integrated-terminal-winpty -label:intellisense-config -label:ipc -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:notebook -label:outline -label:output -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-explorer -label:rename -label:samples -label:sandbox -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview -label:suggest -label:sync-error-handling -label:tasks -label:telemetry -label:themes -label:timeline -label:timeline-git -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree -label:typescript -label:undo-redo -label:uri -label:ux -label:variable-resolving -label:vscode-build -label:vscode-website -label:web -label:webview -label:workbench-actions -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editors -label:workbench-electron -label:workbench-feedback -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:zoom", + "value": "repo:microsoft/vscode is:open is:issue assignee:@me -label:L10N -label:VIM -label:api -label:api-finalization -label:api-proposal -label:authentication -label:breadcrumbs -label:callhierarchy -label:code-lens -label:color-palette -label:comments -label:config -label:context-keys -label:css-less-scss -label:custom-editors -label:debug -label:debug-console -label:dialogs -label:diff-editor -label:dropdown -label:editor -label:editor-RTL -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-scrollbar -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:error-list -label:explorer-custom -label:extension-host -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-guess-encoding -label:file-io -label:file-watcher -label:font-rendering -label:formatting -label:git -label:github -label:gpu -label:grammar -label:grid-view -label:html -label:i18n -label:icon-brand -label:icons-product -label:install-update -label:integrated-terminal -label:integrated-terminal-conpty -label:integrated-terminal-links -label:integrated-terminal-rendering -label:integrated-terminal-winpty -label:intellisense-config -label:ipc -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:notebook -label:outline -label:output -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-explorer -label:rename -label:sandbox -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview -label:suggest -label:sync-error-handling -label:tasks -label:telemetry -label:themes -label:timeline -label:timeline-git -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree -label:typescript -label:undo-redo -label:uri -label:ux -label:variable-resolving -label:vscode-build -label:vscode-website -label:web -label:webview -label:workbench-actions -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editors -label:workbench-electron -label:workbench-feedback -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:zoom", "editable": true } ] \ No newline at end of file diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues new file mode 100644 index 000000000..54ec5328e --- /dev/null +++ b/.vscode/notebooks/my-endgame.github-issues @@ -0,0 +1,218 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "#### Macros", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server\n\n$MILESTONE=milestone:\"November 2020\"\n\n$MINE=assignee:@me", + "editable": false + }, + { + "kind": 1, + "language": "markdown", + "value": "# Preparation", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Open Pull Requests on the Milestone", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:pr is:open", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Open Issues on the Milestone", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open -label:iteration-plan -label:endgame-plan -label:testplan-item", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Feature Requests Missing Labels", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Test Plan Items", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE is:issue is:open author:@me label:testplan-item", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Verification Needed", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:closed label:feature-request label:verification-needed", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# Testing", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Test Plan Items", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open label:testplan-item", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Verification Needed", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed -assignee:@me -label:verified label:feature-request label:verification-needed", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# Fixing", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Open Issues", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open -label:endgame-plan", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Open Bugs", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open label:bug", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# Verification", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## My Issues (verification-steps-needed)", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open label:bug label:verification-steps-needed", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## My Issues (verification-found)", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open label:bug label:verification-found", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Issues filed by me", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed author:@me sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Issues filed from outside team", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:bamurtaugh -author:bpasero -author:btholt -author:chrisdias -author:chrmarti -author:Chuxel -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:eamodio -author:egamma -author:fiveisprime -author:gregvanl -author:isidorn -author:ItalyPaleAle -author:JacksonKearl -author:joaomoreno -author:jrieken -author:kieferrm -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:ornellaalt -author:orta -author:rebornix -author:RMacfarlane -author:roblourens -author:rzhao271 -author:sana-ajani -author:sandy081 -author:sbatten -author:stevencl -author:Tyriar -author:weinand", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Issues filed by others", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed -author:@me sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# Release Notes", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode $MILESTONE is:issue is:closed label:feature-request -label:on-release-notes", + "editable": true + } +] \ No newline at end of file diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index a5de65e0b..55fc585e3 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -8,7 +8,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks\n\n// current milestone name\n$milestone=milestone:\"September 2020\"", + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog\n\n// current milestone name\n$milestone=milestone:\"November 2020\"", "editable": true }, { diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues index 67db0cb97..582859397 100644 --- a/.vscode/notebooks/verification.github-issues +++ b/.vscode/notebooks/verification.github-issues @@ -14,7 +14,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks \n$milestone=milestone:\"October 2020\"", + "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks \n$milestone=milestone:\"November 2020\"", "editable": true }, { diff --git a/.yarnrc b/.yarnrc index d97527dab..264a5e3a3 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://electronjs.org/headers" -target "9.3.3" +target "9.3.5" runtime "electron" diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 074844d81..0d0db3aca 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -28,44 +28,45 @@ This project incorporates components from the projects listed below. The origina 21. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) 22. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) 23. jeff-hykin/cpp-textmate-grammar version 1.12.11 (https://github.com/jeff-hykin/cpp-textmate-grammar) -24. jeff-hykin/cpp-textmate-grammar version 1.14.15 (https://github.com/jeff-hykin/cpp-textmate-grammar) +24. jeff-hykin/cpp-textmate-grammar version 1.15.3 (https://github.com/jeff-hykin/cpp-textmate-grammar) 25. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) 26. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) 27. language-docker (https://github.com/moby/moby) 28. language-less version 0.34.2 (https://github.com/atom/language-less) 29. language-php version 0.44.5 (https://github.com/atom/language-php) -30. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust) -31. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) -32. marked version 0.6.2 (https://github.com/markedjs/marked) -33. mdn-data version 1.1.12 (https://github.com/mdn/data) -34. microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/microsoft/TypeScript-TmLanguage) -35. microsoft/vscode-JSON.tmLanguage (https://github.com/microsoft/vscode-JSON.tmLanguage) +30. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) +31. marked version 1.1.0 (https://github.com/markedjs/marked) +32. mdn-data version 1.1.12 (https://github.com/mdn/data) +33. microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/microsoft/TypeScript-TmLanguage) +34. microsoft/vscode-JSON.tmLanguage (https://github.com/microsoft/vscode-JSON.tmLanguage) +35. microsoft/vscode-markdown-tm-grammar (https://github.com/microsoft/vscode-markdown-tm-grammar) 36. microsoft/vscode-mssql version 1.9.0 (https://github.com/microsoft/vscode-mssql) 37. mmims/language-batchfile version 0.7.5 (https://github.com/mmims/language-batchfile) 38. octref/language-css version 0.42.11 (https://github.com/octref/language-css) 39. PowerShell/EditorSyntax version 1.0.0 (https://github.com/PowerShell/EditorSyntax) -40. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) -41. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) -42. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) -43. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) -44. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) -45. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) -46. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) -47. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) -48. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) -49. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) -50. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) -51. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) -52. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) -53. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) -54. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) -55. TypeScript-TmLanguage version 0.1.8 (https://github.com/microsoft/TypeScript-TmLanguage) -56. TypeScript-TmLanguage version 1.0.0 (https://github.com/microsoft/TypeScript-TmLanguage) -57. Unicode version 12.0.0 (https://home.unicode.org/) -58. vscode-codicons version 0.0.1 (https://github.com/microsoft/vscode-codicons) -59. vscode-logfile-highlighter version 2.8.0 (https://github.com/emilast/vscode-logfile-highlighter) -60. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) -61. Web Background Synchronization (https://github.com/WICG/background-sync) +40. rust-syntax version 0.2.13 (https://github.com/dustypomerleau/rust-syntax) +41. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) +42. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) +43. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) +44. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) +45. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) +46. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) +47. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) +48. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) +49. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) +50. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) +51. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) +52. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) +53. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) +54. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) +55. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) +56. TypeScript-TmLanguage version 0.1.8 (https://github.com/microsoft/TypeScript-TmLanguage) +57. TypeScript-TmLanguage version 1.0.0 (https://github.com/microsoft/TypeScript-TmLanguage) +58. Unicode version 12.0.0 (https://home.unicode.org/) +59. vscode-codicons version 0.0.1 (https://github.com/microsoft/vscode-codicons) +60. vscode-logfile-highlighter version 2.8.0 (https://github.com/emilast/vscode-logfile-highlighter) +61. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +62. Web Background Synchronization (https://github.com/WICG/background-sync) %% atom/language-clojure NOTICES AND INFORMATION BEGIN HERE @@ -1101,31 +1102,6 @@ suitability for any purpose. ========================================= END OF language-php NOTICES AND INFORMATION -%% language-rust NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright © `2013` `Andreas Neuhaus` `http://zargony.com/` - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -========================================= -END OF language-rust NOTICES AND INFORMATION - %% MagicStack/MagicPython NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License @@ -1164,6 +1140,7 @@ all code is your original work. `` ## Marked +Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/) Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/) Permission is hereby granted, free of charge, to any person obtaining a copy @@ -1632,6 +1609,32 @@ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFT ========================================= END OF microsoft/vscode-JSON.tmLanguage NOTICES AND INFORMATION +%% microsoft/vscode-markdown-tm-grammar NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Microsoft 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF microsoft/vscode-markdown-tm-grammar NOTICES AND INFORMATION + %% microsoft/vscode-mssql NOTICES AND INFORMATION BEGIN HERE ========================================= ------------------------------------------ START OF LICENSE ----------------------------------------- @@ -1738,6 +1741,32 @@ SOFTWARE. ========================================= END OF PowerShell/EditorSyntax NOTICES AND INFORMATION +%% rust-syntax NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2020 Dustin Pomerleau + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF rust-syntax NOTICES AND INFORMATION + %% seti-ui NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) 2014 Jesse Weed diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b2f9a13c2..cce8119d4 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,3 +16,8 @@ jobs: vmImage: macOS-latest steps: - template: build/azure-pipelines/darwin/continuous-build-darwin.yml + +trigger: + branches: + exclude: + - electron-11.x.y diff --git a/build/.cachesalt b/build/.cachesalt index 3f2ee542a..a7a4d5082 100644 --- a/build/.cachesalt +++ b/build/.cachesalt @@ -1 +1 @@ -2020-10-05T20:24:23.714Z +2020-11-30T16:21:34.566Z diff --git a/build/.moduleignore b/build/.moduleignore index 489d4709f..d1f9194ba 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -1,5 +1,117 @@ +# cleanup rules for node modules, .gitignore style -# additional cleanup rules for node modules, .gitignore style +# native node modules + +nan/** +*/node_modules/nan/** + +fsevents/binding.gyp +fsevents/fsevents.cc +fsevents/build/** +fsevents/src/** +fsevents/test/** +!fsevents/**/*.node + +vscode-sqlite3/binding.gyp +vscode-sqlite3/benchmark/** +vscode-sqlite3/cloudformation/** +vscode-sqlite3/deps/** +vscode-sqlite3/test/** +vscode-sqlite3/build/** +vscode-sqlite3/src/** +!vscode-sqlite3/build/Release/*.node + +windows-mutex/binding.gyp +windows-mutex/build/** +windows-mutex/src/** +!windows-mutex/**/*.node + +native-keymap/binding.gyp +native-keymap/build/** +native-keymap/src/** +native-keymap/deps/** +!native-keymap/build/Release/*.node + +native-is-elevated/binding.gyp +native-is-elevated/build/** +native-is-elevated/src/** +native-is-elevated/deps/** +!native-is-elevated/build/Release/*.node + +native-watchdog/binding.gyp +native-watchdog/build/** +native-watchdog/src/** +!native-watchdog/build/Release/*.node + +spdlog/binding.gyp +spdlog/build/** +spdlog/deps/** +spdlog/src/** +spdlog/test/** +spdlog/*.yml +!spdlog/build/Release/*.node + +jschardet/dist/** + +windows-foreground-love/binding.gyp +windows-foreground-love/build/** +windows-foreground-love/src/** +!windows-foreground-love/**/*.node + +windows-process-tree/binding.gyp +windows-process-tree/build/** +windows-process-tree/src/** +!windows-process-tree/**/*.node + +keytar/binding.gyp +keytar/build/** +keytar/src/** +keytar/script/** +keytar/node_modules/** +!keytar/**/*.node + +node-pty/binding.gyp +node-pty/build/** +node-pty/src/** +node-pty/tools/** +node-pty/deps/** +node-pty/scripts/** +!node-pty/build/Release/*.exe +!node-pty/build/Release/*.dll +!node-pty/build/Release/*.node + +vscode-nsfw/binding.gyp +vscode-nsfw/build/** +vscode-nsfw/src/** +vscode-nsfw/openpa/** +vscode-nsfw/includes/** +!vscode-nsfw/build/Release/*.node +!vscode-nsfw/**/*.a + +vsda/build/** +vsda/ci/** +vsda/src/** +vsda/.gitignore +vsda/binding.gyp +vsda/README.md +vsda/targets +!vsda/build/Release/vsda.node + +vscode-encrypt/build/** +vscode-encrypt/src/** +vscode-encrypt/vendor/** +vscode-encrypt/.gitignore +vscode-encrypt/binding.gyp +vscode-encrypt/README.md +!vscode-encrypt/build/Release/vscode-encrypt-native.node + +vscode-windows-ca-certs/**/* +!vscode-windows-ca-certs/package.json +!vscode-windows-ca-certs/**/*.node + +node-addon-api/**/* + +# other node modules **/docs/** **/example/** diff --git a/build/.nativeignore b/build/.nativeignore deleted file mode 100644 index a27530745..000000000 --- a/build/.nativeignore +++ /dev/null @@ -1,102 +0,0 @@ -# cleanup rules for native node modules, .gitignore style - -nan/** -*/node_modules/nan/** - -fsevents/binding.gyp -fsevents/fsevents.cc -fsevents/build/** -fsevents/src/** -fsevents/test/** -!fsevents/**/*.node - -vscode-sqlite3/binding.gyp -vscode-sqlite3/benchmark/** -vscode-sqlite3/cloudformation/** -vscode-sqlite3/deps/** -vscode-sqlite3/test/** -vscode-sqlite3/build/** -vscode-sqlite3/src/** -!vscode-sqlite3/build/Release/*.node - -windows-mutex/binding.gyp -windows-mutex/build/** -windows-mutex/src/** -!windows-mutex/**/*.node - -native-keymap/binding.gyp -native-keymap/build/** -native-keymap/src/** -native-keymap/deps/** -!native-keymap/build/Release/*.node - -native-is-elevated/binding.gyp -native-is-elevated/build/** -native-is-elevated/src/** -native-is-elevated/deps/** -!native-is-elevated/build/Release/*.node - -native-watchdog/binding.gyp -native-watchdog/build/** -native-watchdog/src/** -!native-watchdog/build/Release/*.node - -spdlog/binding.gyp -spdlog/build/** -spdlog/deps/** -spdlog/src/** -spdlog/test/** -spdlog/*.yml -!spdlog/build/Release/*.node - -jschardet/dist/** - -windows-foreground-love/binding.gyp -windows-foreground-love/build/** -windows-foreground-love/src/** -!windows-foreground-love/**/*.node - -windows-process-tree/binding.gyp -windows-process-tree/build/** -windows-process-tree/src/** -!windows-process-tree/**/*.node - -keytar/binding.gyp -keytar/build/** -keytar/src/** -keytar/script/** -keytar/node_modules/** -!keytar/**/*.node - -node-pty/binding.gyp -node-pty/build/** -node-pty/src/** -node-pty/tools/** -node-pty/deps/** -node-pty/scripts/** -!node-pty/build/Release/*.exe -!node-pty/build/Release/*.dll -!node-pty/build/Release/*.node - -vscode-nsfw/binding.gyp -vscode-nsfw/build/** -vscode-nsfw/src/** -vscode-nsfw/openpa/** -vscode-nsfw/includes/** -!vscode-nsfw/build/Release/*.node -!vscode-nsfw/**/*.a - -vsda/build/** -vsda/ci/** -vsda/src/** -vsda/.gitignore -vsda/binding.gyp -vsda/README.md -vsda/targets -!vsda/build/Release/vsda.node - -vscode-windows-ca-certs/**/* -!vscode-windows-ca-certs/package.json -!vscode-windows-ca-certs/**/*.node - -node-addon-api/**/* diff --git a/build/azure-pipelines/common/createAsset.ts b/build/azure-pipelines/common/createAsset.ts index d7e62629c..daf60d710 100644 --- a/build/azure-pipelines/common/createAsset.ts +++ b/build/azure-pipelines/common/createAsset.ts @@ -11,6 +11,7 @@ import * as crypto from 'crypto'; import * as azure from 'azure-storage'; import * as mime from 'mime'; import { CosmosClient } from '@azure/cosmos'; +import { retry } from './retry'; interface Asset { platform: string; @@ -121,7 +122,7 @@ async function main(): Promise { const client = new CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT']!, key: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); const scripts = client.database('builds').container(quality).scripts; - await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); + await retry(() => scripts.storedProcedure('createAsset').execute('', [commit, asset, true])); } main().then(() => { diff --git a/build/azure-pipelines/common/createBuild.ts b/build/azure-pipelines/common/createBuild.ts index c8fd66b79..e314d7c09 100644 --- a/build/azure-pipelines/common/createBuild.ts +++ b/build/azure-pipelines/common/createBuild.ts @@ -6,6 +6,7 @@ 'use strict'; import { CosmosClient } from '@azure/cosmos'; +import { retry } from './retry'; if (process.argv.length !== 3) { console.error('Usage: node createBuild.js VERSION'); @@ -48,7 +49,7 @@ async function main(): Promise { const client = new CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT']!, key: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); const scripts = client.database('builds').container(quality).scripts; - await scripts.storedProcedure('createBuild').execute('', [{ ...build, _partitionKey: '' }]); + await retry(() => scripts.storedProcedure('createBuild').execute('', [{ ...build, _partitionKey: '' }])); } main().then(() => { diff --git a/build/azure-pipelines/common/releaseBuild.ts b/build/azure-pipelines/common/releaseBuild.ts index ac49e7b00..d42b3f1a0 100644 --- a/build/azure-pipelines/common/releaseBuild.ts +++ b/build/azure-pipelines/common/releaseBuild.ts @@ -6,6 +6,7 @@ 'use strict'; import { CosmosClient } from '@azure/cosmos'; +import { retry } from './retry'; function getEnv(name: string): string { const result = process.env[name]; @@ -58,7 +59,7 @@ async function main(): Promise { console.log(`Releasing build ${commit}...`); const scripts = client.database('builds').container(quality).scripts; - await scripts.storedProcedure('releaseBuild').execute('', [commit]); + await retry(() => scripts.storedProcedure('releaseBuild').execute('', [commit])); } main().then(() => { diff --git a/build/azure-pipelines/common/retry.ts b/build/azure-pipelines/common/retry.ts new file mode 100644 index 000000000..173767659 --- /dev/null +++ b/build/azure-pipelines/common/retry.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export async function retry(fn: () => Promise): Promise { + for (let run = 1; run <= 10; run++) { + try { + return await fn(); + } catch (err) { + if (!/ECONNRESET/.test(err.message)) { + throw err; + } + + const millis = (Math.random() * 200) + (50 * Math.pow(1.5, run)); + console.log(`Failed with ECONNRESET, retrying in ${millis}ms...`); + + // maximum delay is 10th retry: ~3 seconds + await new Promise(c => setTimeout(c, millis)); + } + } + + throw new Error('Retried too many times'); +} diff --git a/build/azure-pipelines/common/sync-mooncake.ts b/build/azure-pipelines/common/sync-mooncake.ts index 76f12185e..4ffe7a8f1 100644 --- a/build/azure-pipelines/common/sync-mooncake.ts +++ b/build/azure-pipelines/common/sync-mooncake.ts @@ -9,6 +9,7 @@ import * as url from 'url'; import * as azure from 'azure-storage'; import * as mime from 'mime'; import { CosmosClient } from '@azure/cosmos'; +import { retry } from './retry'; function log(...args: any[]) { console.log(...[`[${new Date().toISOString()}]`, ...args]); @@ -99,8 +100,8 @@ async function sync(commit: string, quality: string): Promise { log(` Updating build in DB...`); const mooncakeUrl = `${process.env['MOONCAKE_CDN_URL']}${blobPath}`; - await container.scripts.storedProcedure('setAssetMooncakeUrl') - .execute('', [commit, asset.platform, asset.type, mooncakeUrl]); + await retry(() => container.scripts.storedProcedure('setAssetMooncakeUrl') + .execute('', [commit, asset.platform, asset.type, mooncakeUrl])); log(` Done ✔️`); } catch (err) { diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index 954e5bca6..55efe5f32 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -1,79 +1,79 @@ steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" -- script: | - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install Dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install Dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- script: | - yarn electron x64 - displayName: Download Electron + - script: | + yarn electron x64 + displayName: Download Electron -- script: | - yarn monaco-compile-check - displayName: Run Monaco Editor Checks + - script: | + yarn monaco-compile-check + displayName: Run Monaco Editor Checks -- script: | - yarn valid-layers-check - displayName: Run Valid Layers Checks + - script: | + yarn valid-layers-check + displayName: Run Valid Layers Checks -- script: | - yarn compile - displayName: Compile Sources + - script: | + yarn compile + displayName: Compile Sources -- script: | - yarn download-builtin-extensions - displayName: Download Built-in Extensions + - script: | + yarn download-builtin-extensions + displayName: Download Built-in Extensions -- script: | - ./scripts/test.sh --tfs "Unit Tests" - displayName: Run Unit Tests (Electron) + - script: | + ./scripts/test.sh --tfs "Unit Tests" + displayName: Run Unit Tests (Electron) -- script: | - yarn test-browser --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" - displayName: Run Unit Tests (Browser) + - script: | + yarn test-browser --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" + displayName: Run Unit Tests (Browser) -- script: | - ./scripts/test-integration.sh --tfs "Integration Tests" - displayName: Run Integration Tests (Electron) + - script: | + ./scripts/test-integration.sh --tfs "Integration Tests" + displayName: Run Integration Tests (Electron) -- task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-macos - targetPath: .build/crashes - displayName: 'Publish Crash Reports' - continueOnError: true - condition: failed() + - task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-macos + targetPath: .build/crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 0500f84ac..6f7dec16a 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -1,270 +1,337 @@ steps: -- script: | - mkdir -p .build - echo -n $BUILD_SOURCEVERSION > .build/commit - echo -n $VSCODE_QUALITY > .build/quality - displayName: Prepare cache flag + - script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + echo -n $VSCODE_QUALITY > .build/quality + echo -n $ENABLE_TERRAPIN > .build/terrapin + displayName: Prepare compilation cache flags -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" -- script: | - set -e - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e + - script: | + set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling -- script: | - set -e - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - displayName: Merge distro + - script: | + set -e + sudo xcode-select -s /Applications/Xcode_12.2.app + displayName: Switch to Xcode 12 + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' + - script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro -- script: | - set -e - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + echo -n $(VSCODE_ARCH) > .build/arch + displayName: Prepare yarn cache flags -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" -- script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + - script: | + set -e + npm install -g node-gyp@7.1.0 + node-gyp --version + displayName: Update node-gyp + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-darwin-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-darwin-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-web-darwin-min-ci - displayName: Build + - script: | + set -e + export npm_config_arch=$(VSCODE_ARCH) + export npm_config_node_gyp=$(which node-gyp) + export SDKROOT=/Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk + export CHILD_CONCURRENCY="1" -- script: | - set -e - ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn test-browser --build --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ - ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + export npm_config_arch=$(VSCODE_ARCH) + export npm_config_node_gyp=$(which node-gyp) + export SDKROOT=/Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk + ls /Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ - ./resources/server/test/test-web-integration.sh --browser webkit - displayName: Run integration tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + export npm_config_arch=$(VSCODE_ARCH) + export npm_config_node_gyp=$(which node-gyp) + export npm_config_build_from_source=true + export SDKROOT=/Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk + ls /Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ + yarn electron-rebuild + cd ./node_modules/keytar + node-gyp rebuild + displayName: Rebuild native modules for ARM64 + condition: eq(variables['VSCODE_ARCH'], 'arm64') -- script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ - ./resources/server/test/test-remote-integration.sh - displayName: Run remote integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality -- script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - yarn smoketest --build "$APP_ROOT/$APP_NAME" - continueOnError: true - displayName: Run smoke tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci + displayName: Build -- script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ - yarn smoketest --web --headless - continueOnError: true - displayName: Run smoke tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-darwin-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-darwin-min-ci + displayName: Build reh + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) -- task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-macos - targetPath: .build/crashes - displayName: 'Publish Crash Reports' - continueOnError: true - condition: failed() + - script: | + set -e + yarn electron $(VSCODE_ARCH) + displayName: Download Electron + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - script: | + set -e + security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + security default-keychain -s $(agent.tempdirectory)/buildagent.keychain + security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 + security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain + VSCODE_ARCH="$(VSCODE_ARCH)" DEBUG=electron-osx-sign* node build/darwin/sign.js + displayName: Set Hardened Entitlements -- script: | - set -e - security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain - security default-keychain -s $(agent.tempdirectory)/buildagent.keychain - security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain - echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 - security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain - DEBUG=electron-osx-sign* node build/darwin/sign.js - displayName: Set Hardened Entitlements + - script: | + set -e + ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - pushd $(agent.builddirectory)/VSCode-darwin && zip -r -X -y $(agent.builddirectory)/VSCode-darwin.zip * && popd - displayName: Archive build + - script: | + set -e + yarn test-browser --build --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '$(agent.builddirectory)' - Pattern: 'VSCode-darwin.zip' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-401337-Apple", - "operationSetCode": "MacAppDeveloperSign", - "parameters": [ - { - "parameterName": "Hardening", - "parameterValue": "--options=runtime" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 60 - displayName: Codesign + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ + ./scripts/test-integration.sh --build --tfs "Integration Tests" + displayName: Run integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - zip -d $(agent.builddirectory)/VSCode-darwin.zip "*.pkg" - displayName: Clean Archive + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ + ./resources/server/test/test-web-integration.sh --browser webkit + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - BUNDLE_IDENTIFIER=$(node -p "require(\"$APP_ROOT/$APP_NAME/Contents/Resources/app/product.json\").darwinBundleIdentifier") - echo "##vso[task.setvariable variable=BundleIdentifier]$BUNDLE_IDENTIFIER" - displayName: Export bundle identifier + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ + ./resources/server/test/test-remote-integration.sh + displayName: Run remote integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '$(agent.builddirectory)' - Pattern: 'VSCode-darwin.zip' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-401337-Apple", - "operationSetCode": "MacAppNotarize", - "parameters": [ - { - "parameterName": "BundleId", - "parameterValue": "$(BundleIdentifier)" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 60 - displayName: Notarization + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + yarn smoketest --build "$APP_ROOT/$APP_NAME" + continueOnError: true + displayName: Run smoke tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - "$APP_ROOT/$APP_NAME/Contents/Resources/app/bin/code" --export-default-configuration=.build - displayName: Verify start after signing (export configuration) + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ + yarn smoketest --web --headless + continueOnError: true + displayName: Run smoke tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - VSCODE_ARCH="$(VSCODE_ARCH)" \ - ./build/azure-pipelines/darwin/publish.sh - displayName: Publish + - task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-macos-$(VSCODE_ARCH) + targetPath: .build/crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() -- script: | - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ - yarn gulp upload-vscode-configuration - displayName: Upload configuration (for Bing settings search) - continueOnError: true + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true + - script: | + set -e + pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * && popd + displayName: Archive build + + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: "ESRP CodeSign" + FolderPath: "$(agent.builddirectory)" + Pattern: "VSCode-darwin-$(VSCODE_ARCH).zip" + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-401337-Apple", + "operationSetCode": "MacAppDeveloperSign", + "parameters": [ + { + "parameterName": "Hardening", + "parameterValue": "--options=runtime" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 60 + displayName: Codesign + + - script: | + zip -d $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip "*.pkg" + displayName: Clean Archive + + - script: | + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + BUNDLE_IDENTIFIER=$(node -p "require(\"$APP_ROOT/$APP_NAME/Contents/Resources/app/product.json\").darwinBundleIdentifier") + echo "##vso[task.setvariable variable=BundleIdentifier]$BUNDLE_IDENTIFIER" + displayName: Export bundle identifier + + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: "ESRP CodeSign" + FolderPath: "$(agent.builddirectory)" + Pattern: "VSCode-darwin-$(VSCODE_ARCH).zip" + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-401337-Apple", + "operationSetCode": "MacAppNotarize", + "parameters": [ + { + "parameterName": "BundleId", + "parameterValue": "$(BundleIdentifier)" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 60 + displayName: Notarization + + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + "$APP_ROOT/$APP_NAME/Contents/Resources/app/bin/code" --export-default-configuration=.build + displayName: Verify start after signing (export configuration) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_ARCH="$(VSCODE_ARCH)" \ + ./build/azure-pipelines/darwin/publish.sh + displayName: Publish + + - script: | + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + VSCODE_ARCH="$(VSCODE_ARCH)" \ + yarn gulp upload-vscode-configuration + displayName: Upload configuration (for Bing settings search) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + continueOnError: true + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + continueOnError: true diff --git a/build/azure-pipelines/darwin/publish.sh b/build/azure-pipelines/darwin/publish.sh index 51ac2e635..c9f5b4bab 100755 --- a/build/azure-pipelines/darwin/publish.sh +++ b/build/azure-pipelines/darwin/publish.sh @@ -1,12 +1,18 @@ #!/usr/bin/env bash set -e +# Publish DEB +case $VSCODE_ARCH in + x64) ASSET_ID="darwin" ;; + arm64) ASSET_ID="darwin-arm64" ;; +esac + # publish the build node build/azure-pipelines/common/createAsset.js \ - darwin \ + "$ASSET_ID" \ archive \ - "VSCode-darwin-$VSCODE_QUALITY.zip" \ - ../VSCode-darwin.zip + "VSCode-$ASSET_ID.zip" \ + ../VSCode-darwin-$VSCODE_ARCH.zip if [ "$VSCODE_ARCH" == "x64" ]; then # package Remote Extension Host diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index f9bdf7fef..99bad6b72 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -1,42 +1,42 @@ trigger: branches: - include: ['master', 'release/*'] + include: ["master", "release/*"] pr: branches: - include: ['master', 'release/*'] + include: ["master", "release/*"] steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e + - script: | + set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" - git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" - git fetch distro + git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" + git fetch distro - # Push master branch into oss/master - git push distro origin/master:refs/heads/oss/master + # Push master branch into oss/master + git push distro origin/master:refs/heads/oss/master - # Push every release branch into oss/release - git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro + # Push every release branch into oss/release + git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro - git merge $(node -p "require('./package.json').distro") + git merge $(node -p "require('./package.json').distro") - displayName: Sync & Merge Distro + displayName: Sync & Merge Distro diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index cf1ced09d..4a7acf129 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -1,40 +1,35 @@ pool: - vmImage: 'Ubuntu-16.04' + vmImage: "Ubuntu-16.04" -trigger: - branches: - include: ['master'] -pr: - branches: - include: ['master'] +trigger: none steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e + - script: | + set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" - git checkout origin/electron-11.x.y - git merge origin/master + git checkout origin/electron-11.x.y + git merge origin/master - # Push master branch into exploration branch - git push origin HEAD:electron-11.x.y + # Push master branch into exploration branch + git push origin HEAD:electron-11.x.y - displayName: Sync & Merge Exploration + displayName: Sync & Merge Exploration diff --git a/build/azure-pipelines/linux/alpine/install-dependencies.sh b/build/azure-pipelines/linux/alpine/install-dependencies.sh new file mode 100755 index 000000000..1d2a23254 --- /dev/null +++ b/build/azure-pipelines/linux/alpine/install-dependencies.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -e + +echo "Installing remote dependencies" +(cd remote && rm -rf node_modules && yarn) \ No newline at end of file diff --git a/build/azure-pipelines/linux/alpine/publish.sh b/build/azure-pipelines/linux/alpine/publish.sh new file mode 100755 index 000000000..4bf874c63 --- /dev/null +++ b/build/azure-pipelines/linux/alpine/publish.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -e +REPO="$(pwd)" +ROOT="$REPO/.." + +PLATFORM_LINUX="linux-alpine" + +# Publish Remote Extension Host +LEGACY_SERVER_BUILD_NAME="vscode-reh-$PLATFORM_LINUX" +SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX" +SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX.tar.gz" +SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" + +rm -rf $ROOT/vscode-server-*.tar.* +(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) + +node build/azure-pipelines/common/createAsset.js "server-$PLATFORM_LINUX" archive-unsigned "$SERVER_TARBALL_FILENAME" "$SERVER_TARBALL_PATH" + +# Publish Remote Extension Host (Web) +LEGACY_SERVER_BUILD_NAME="vscode-reh-web-$PLATFORM_LINUX" +SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX-web" +SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX-web.tar.gz" +SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" + +rm -rf $ROOT/vscode-server-*.tar.* +(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) + +node build/azure-pipelines/common/createAsset.js "server-$PLATFORM_LINUX-web" archive-unsigned "$SERVER_TARBALL_FILENAME" "$SERVER_TARBALL_PATH" diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index 58e14ab0d..e777f821d 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -1,92 +1,92 @@ steps: -- script: | - set -e - sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 - sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb - sudo chmod +x /etc/init.d/xvfb - sudo update-rc.d xvfb defaults - sudo service xvfb start + - script: | + set -e + sudo apt-get update + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 + sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + sudo chmod +x /etc/init.d/xvfb + sudo update-rc.d xvfb defaults + sudo service xvfb start -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" -- script: | - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install Dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install Dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- script: | - yarn electron x64 - displayName: Download Electron + - script: | + yarn electron x64 + displayName: Download Electron -- script: | - yarn gulp hygiene - displayName: Run Hygiene Checks + - script: | + yarn gulp hygiene + displayName: Run Hygiene Checks -- script: | - yarn monaco-compile-check - displayName: Run Monaco Editor Checks + - script: | + yarn monaco-compile-check + displayName: Run Monaco Editor Checks -- script: | - yarn valid-layers-check - displayName: Run Valid Layers Checks + - script: | + yarn valid-layers-check + displayName: Run Valid Layers Checks -- script: | - yarn compile - displayName: Compile Sources + - script: | + yarn compile + displayName: Compile Sources -- script: | - yarn download-builtin-extensions - displayName: Download Built-in Extensions + - script: | + yarn download-builtin-extensions + displayName: Download Built-in Extensions -- script: | - DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" - displayName: Run Unit Tests (Electron) + - script: | + DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" + displayName: Run Unit Tests (Electron) -- script: | - DISPLAY=:10 yarn test-browser --browser chromium --tfs "Browser Unit Tests" - displayName: Run Unit Tests (Browser) + - script: | + DISPLAY=:10 yarn test-browser --browser chromium --tfs "Browser Unit Tests" + displayName: Run Unit Tests (Browser) -- script: | - DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" - displayName: Run Integration Tests (Electron) + - script: | + DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" + displayName: Run Integration Tests (Electron) -- task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-linux - targetPath: .build/crashes - displayName: 'Publish Crash Reports' - continueOnError: true - condition: failed() + - task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-linux + targetPath: .build/crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() diff --git a/build/azure-pipelines/linux/multiarch/alpine/build.sh b/build/azure-pipelines/linux/multiarch/alpine/build.sh deleted file mode 100755 index 4f01bc9a7..000000000 --- a/build/azure-pipelines/linux/multiarch/alpine/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/alpine/prebuild.sh b/build/azure-pipelines/linux/multiarch/alpine/prebuild.sh deleted file mode 100755 index 4f01bc9a7..000000000 --- a/build/azure-pipelines/linux/multiarch/alpine/prebuild.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/alpine/publish.sh b/build/azure-pipelines/linux/multiarch/alpine/publish.sh deleted file mode 100755 index 4f01bc9a7..000000000 --- a/build/azure-pipelines/linux/multiarch/alpine/publish.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/arm64/build.sh b/build/azure-pipelines/linux/multiarch/arm64/build.sh deleted file mode 100755 index 4f01bc9a7..000000000 --- a/build/azure-pipelines/linux/multiarch/arm64/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/arm64/prebuild.sh b/build/azure-pipelines/linux/multiarch/arm64/prebuild.sh deleted file mode 100755 index 4f01bc9a7..000000000 --- a/build/azure-pipelines/linux/multiarch/arm64/prebuild.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/arm64/publish.sh b/build/azure-pipelines/linux/multiarch/arm64/publish.sh deleted file mode 100755 index 4f01bc9a7..000000000 --- a/build/azure-pipelines/linux/multiarch/arm64/publish.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/armhf/build.sh b/build/azure-pipelines/linux/multiarch/armhf/build.sh deleted file mode 100755 index 4f01bc9a7..000000000 --- a/build/azure-pipelines/linux/multiarch/armhf/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/armhf/prebuild.sh b/build/azure-pipelines/linux/multiarch/armhf/prebuild.sh deleted file mode 100755 index 4f01bc9a7..000000000 --- a/build/azure-pipelines/linux/multiarch/armhf/prebuild.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/armhf/publish.sh b/build/azure-pipelines/linux/multiarch/armhf/publish.sh deleted file mode 100755 index 4f01bc9a7..000000000 --- a/build/azure-pipelines/linux/multiarch/armhf/publish.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/product-build-alpine.yml b/build/azure-pipelines/linux/product-build-alpine.yml new file mode 100644 index 000000000..9a54bc2bc --- /dev/null +++ b/build/azure-pipelines/linux/product-build-alpine.yml @@ -0,0 +1,135 @@ +steps: + - script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + echo -n $VSCODE_QUALITY > .build/quality + echo -n $ENABLE_TERRAPIN > .build/terrapin + displayName: Prepare compilation cache flags + + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" + + - script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" + + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" + + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + + - task: Docker@1 + displayName: "Pull image" + inputs: + azureSubscriptionEndpoint: "vscode-builds-subscription" + azureContainerRegistry: vscodehub.azurecr.io + command: "Run an image" + imageName: "vscode-linux-build-agent:alpine" + containerCommand: uname + + - script: | + set -e + + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF + + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling + + - script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) + + - script: | + echo -n "alpine" > .build/arch + displayName: Prepare yarn cache flags + + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + + - script: | + set -e + export CHILD_CONCURRENCY="1" + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality + + - script: | + set -e + docker run -e VSCODE_QUALITY -e CHILD_CONCURRENCY=1 -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:alpine /root/vscode/build/azure-pipelines/linux/alpine/install-dependencies.sh + displayName: Prebuild + + - script: | + set -e + yarn gulp vscode-reh-linux-alpine-min-ci + yarn gulp vscode-reh-web-linux-alpine-min-ci + displayName: Build + + - script: | + set -e + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + ./build/azure-pipelines/linux/alpine/publish.sh + displayName: Publish + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + continueOnError: true diff --git a/build/azure-pipelines/linux/product-build-linux-multiarch.yml b/build/azure-pipelines/linux/product-build-linux-multiarch.yml deleted file mode 100644 index 258f87ea3..000000000 --- a/build/azure-pipelines/linux/product-build-linux-multiarch.yml +++ /dev/null @@ -1,115 +0,0 @@ -steps: -- script: | - mkdir -p .build - echo -n $BUILD_SOURCEVERSION > .build/commit - echo -n $VSCODE_QUALITY > .build/quality - displayName: Prepare cache flag - -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' - -- script: | - set -e - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) - -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" - -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode - -- task: Docker@1 - displayName: 'Pull image' - inputs: - azureSubscriptionEndpoint: 'vscode-builds-subscription' - azureContainerRegistry: vscodehub.azurecr.io - command: 'Run an image' - imageName: 'vscode-linux-build-agent:$(VSCODE_ARCH)' - containerCommand: uname - -- script: | - set -e - - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF - - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling - -- script: | - set -e - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - displayName: Merge distro - -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - -- script: | - set -e - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) - -- script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality - -- script: | - set -e - CHILD_CONCURRENCY=1 ./build/azure-pipelines/linux/multiarch/$(VSCODE_ARCH)/prebuild.sh - displayName: Prebuild - -- script: | - set -e - ./build/azure-pipelines/linux/multiarch/$(VSCODE_ARCH)/build.sh - displayName: Build - -- script: | - set -e - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - ./build/azure-pipelines/linux/multiarch/$(VSCODE_ARCH)/publish.sh - displayName: Publish - -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 031a49164..1f7884a1c 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -1,217 +1,233 @@ steps: -- script: | - mkdir -p .build - echo -n $BUILD_SOURCEVERSION > .build/commit - echo -n $VSCODE_QUALITY > .build/quality - displayName: Prepare cache flag + - script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + echo -n $VSCODE_QUALITY > .build/quality + echo -n $ENABLE_TERRAPIN > .build/terrapin + displayName: Prepare compilation cache flags -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" -- script: | - set -e - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling -- script: | - set -e - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - displayName: Merge distro + - script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro -- script: | - echo -n $VSCODE_ARCH > .build/arch - displayName: Prepare arch cache flag + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' + - script: | + echo -n $(VSCODE_ARCH) > .build/arch + displayName: Prepare yarn cache flags -- script: | - set -e - CHILD_CONCURRENCY=1 npm_config_arch=$(NPM_ARCH) yarn --frozen-lockfile - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + set -e + export npm_config_arch=$(NPM_ARCH) + export CHILD_CONCURRENCY="1" + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci - displayName: Build + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality -- script: | - set -e - service xvfb start - displayName: Start xvfb - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci + displayName: Build -- script: | - set -e - DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + service xvfb start + displayName: Start xvfb + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - DISPLAY=:10 yarn test-browser --build --browser chromium --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - DISPLAY=:10 ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + DISPLAY=:10 yarn test-browser --build --browser chromium --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ - DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium - displayName: Run integration tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + INTEGRATION_TEST_APP_NAME="$APP_NAME" \ + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + DISPLAY=:10 ./scripts/test-integration.sh --build --tfs "Integration Tests" + displayName: Run integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - DISPLAY=:10 ./resources/server/test/test-remote-integration.sh - displayName: Run remote integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ + DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- task: PublishPipelineArtifact@0 - inputs: - artifactName: 'crash-dump-linux-$(VSCODE_ARCH)' - targetPath: .build/crashes - displayName: 'Publish Crash Reports' - continueOnError: true - condition: failed() + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + INTEGRATION_TEST_APP_NAME="$APP_NAME" \ + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + DISPLAY=:10 ./resources/server/test/test-remote-integration.sh + displayName: Run remote integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - task: PublishPipelineArtifact@0 + inputs: + artifactName: "crash-dump-linux-$(VSCODE_ARCH)" + targetPath: .build/crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() -- script: | - set -e - yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" - yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" - displayName: Build deb, rpm packages + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" - displayName: Prepare snap package - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" + displayName: Build deb, rpm packages -# needed for code signing -- task: UseDotNet@2 - displayName: 'Install .NET Core SDK 2.x' - inputs: - version: 2.x + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + displayName: Prepare snap package -- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '.build/linux/rpm' - Pattern: '*.rpm' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-450779-Pgp", - "operationSetCode": "LinuxSign", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 120 - displayName: Codesign rpm + # needed for code signing + - task: UseDotNet@2 + displayName: "Install .NET Core SDK 2.x" + inputs: + version: 2.x -- script: | - set -e - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - VSCODE_ARCH="$(VSCODE_ARCH)" \ - ./build/azure-pipelines/linux/publish.sh - displayName: Publish + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: "ESRP CodeSign" + FolderPath: ".build/linux/rpm" + Pattern: "*.rpm" + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-450779-Pgp", + "operationSetCode": "LinuxSign", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 120 + displayName: Codesign rpm -- task: PublishPipelineArtifact@0 - displayName: 'Publish Pipeline Artifact' - inputs: - artifactName: 'snap-$(VSCODE_ARCH)' - targetPath: .build/linux/snap-tarball - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + - script: | + set -e + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + VSCODE_ARCH="$(VSCODE_ARCH)" \ + ./build/azure-pipelines/linux/publish.sh + displayName: Publish -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true + - task: PublishPipelineArtifact@0 + displayName: "Publish Pipeline Artifact" + inputs: + artifactName: "snap-$(VSCODE_ARCH)" + targetPath: .build/linux/snap-tarball + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + continueOnError: true diff --git a/build/azure-pipelines/linux/publish.sh b/build/azure-pipelines/linux/publish.sh index 72fe2ad7b..e0f1ade30 100755 --- a/build/azure-pipelines/linux/publish.sh +++ b/build/azure-pipelines/linux/publish.sh @@ -52,11 +52,9 @@ RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" node build/azure-pipelines/common/createAsset.js "$PLATFORM_RPM" package "$RPM_FILENAME" "$RPM_PATH" -if [ "$VSCODE_ARCH" == "x64" ]; then - # Publish Snap - # Pack snap tarball artifact, in order to preserve file perms - mkdir -p $REPO/.build/linux/snap-tarball - SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$VSCODE_ARCH.tar.gz" - rm -rf $SNAP_TARBALL_PATH - (cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) -fi +# Publish Snap +# Pack snap tarball artifact, in order to preserve file perms +mkdir -p $REPO/.build/linux/snap-tarball +SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$VSCODE_ARCH.tar.gz" +rm -rf $SNAP_TARBALL_PATH +(cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 39c39e86c..f08c7b3c3 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -1,52 +1,56 @@ steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- task: DownloadPipelineArtifact@0 - displayName: 'Download Pipeline Artifact' - inputs: - artifactName: snap-x64 - targetPath: .build/linux/snap-tarball + - task: DownloadPipelineArtifact@0 + displayName: "Download Pipeline Artifact" + inputs: + artifactName: snap-$(VSCODE_ARCH) + targetPath: .build/linux/snap-tarball -- script: | - set -e + - script: | + set -e - # Get snapcraft version - snapcraft --version + # Get snapcraft version + snapcraft --version - # Make sure we get latest packages - sudo apt-get update - sudo apt-get upgrade -y + # Make sure we get latest packages + sudo apt-get update + sudo apt-get upgrade -y - # Define variables - REPO="$(pwd)" - SNAP_ROOT="$REPO/.build/linux/snap/x64" + # Define variables + REPO="$(pwd)" + SNAP_ROOT="$REPO/.build/linux/snap/$(VSCODE_ARCH)" - # Install build dependencies - (cd build && yarn) + # Install build dependencies + (cd build && yarn) - # Unpack snap tarball artifact, in order to preserve file perms - SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-x64.tar.gz" - (cd .build/linux && tar -xzf $SNAP_TARBALL_PATH) + # Unpack snap tarball artifact, in order to preserve file perms + SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$(VSCODE_ARCH).tar.gz" + (cd .build/linux && tar -xzf $SNAP_TARBALL_PATH) - # Create snap package - BUILD_VERSION="$(date +%s)" - SNAP_FILENAME="code-$VSCODE_QUALITY-$BUILD_VERSION.snap" - SNAP_PATH="$SNAP_ROOT/$SNAP_FILENAME" - (cd $SNAP_ROOT/code-* && sudo --preserve-env snapcraft snap --output "$SNAP_PATH") + # Create snap package + BUILD_VERSION="$(date +%s)" + SNAP_FILENAME="code-$VSCODE_QUALITY-$(VSCODE_ARCH)-$BUILD_VERSION.snap" + SNAP_PATH="$SNAP_ROOT/$SNAP_FILENAME" + case $(VSCODE_ARCH) in + x64) SNAPCRAFT_TARGET_ARGS="" ;; + *) SNAPCRAFT_TARGET_ARGS="--target-arch $(VSCODE_ARCH)" ;; + esac + (cd $SNAP_ROOT/code-* && sudo --preserve-env snapcraft snap $SNAPCRAFT_TARGET_ARGS --output "$SNAP_PATH") - # Publish snap package - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - node build/azure-pipelines/common/createAsset.js "linux-snap-x64" package "$SNAP_FILENAME" "$SNAP_PATH" + # Publish snap package + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + node build/azure-pipelines/common/createAsset.js "linux-snap-$(VSCODE_ARCH)" package "$SNAP_FILENAME" "$SNAP_PATH" diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 83966344f..ebe2fe6ab 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -2,160 +2,201 @@ trigger: none pr: none schedules: -- cron: "0 5 * * Mon-Fri" - displayName: Mon-Fri at 7:00 - branches: - include: - - master + - cron: "0 5 * * Mon-Fri" + displayName: Mon-Fri at 7:00 + branches: + include: + - master resources: containers: - - container: vscode-x64 - image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 - endpoint: VSCodeHub - - container: vscode-arm64 - image: vscodehub.azurecr.io/vscode-linux-build-agent:stretch-arm64 - endpoint: VSCodeHub - - container: vscode-armhf - image: vscodehub.azurecr.io/vscode-linux-build-agent:stretch-armhf - endpoint: VSCodeHub - - container: snapcraft - image: snapcore/snapcraft:stable + - container: vscode-x64 + image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 + endpoint: VSCodeHub + - container: vscode-arm64 + image: vscodehub.azurecr.io/vscode-linux-build-agent:stretch-arm64 + endpoint: VSCodeHub + - container: vscode-armhf + image: vscodehub.azurecr.io/vscode-linux-build-agent:stretch-armhf + endpoint: VSCodeHub + - container: snapcraft + image: snapcore/snapcraft:stable stages: -- stage: Compile - jobs: - - job: Compile - pool: - vmImage: 'Ubuntu-16.04' - container: vscode-x64 - steps: - - template: product-compile.yml + - stage: Compile + jobs: + - job: Compile + pool: + vmImage: "Ubuntu-16.04" + container: vscode-x64 + variables: + VSCODE_ARCH: x64 + steps: + - template: product-compile.yml -- stage: Windows - dependsOn: - - Compile - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - pool: - vmImage: VS2017-Win2016 - jobs: - - job: Windows - condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true')) - variables: - VSCODE_ARCH: x64 - steps: - - template: win32/product-build-win32.yml - - - job: Windows32 - condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) - variables: - VSCODE_ARCH: ia32 - steps: - - template: win32/product-build-win32.yml - - - job: WindowsARM64 - condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) - variables: - VSCODE_ARCH: arm64 - steps: - - template: win32/product-build-win32-arm64.yml - -- stage: Linux - dependsOn: - - Compile - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - pool: - vmImage: 'Ubuntu-16.04' - jobs: - - job: Linux - condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) - container: vscode-x64 - variables: - VSCODE_ARCH: x64 - NPM_ARCH: x64 - steps: - - template: linux/product-build-linux.yml - - - job: LinuxSnap + - stage: Windows dependsOn: - - Linux - condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) - container: snapcraft - variables: - VSCODE_ARCH: x64 - steps: - - template: linux/snap-build-linux.yml + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: VS2017-Win2016 + jobs: + - job: Windows + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: win32/product-build-win32.yml - - job: LinuxArmhf - condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) - container: vscode-armhf - variables: - VSCODE_ARCH: armhf - NPM_ARCH: armv7l - steps: - - template: linux/product-build-linux.yml + - job: Windows32 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: ia32 + steps: + - template: win32/product-build-win32.yml - - job: LinuxArm64 - condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) - container: vscode-arm64 - variables: - VSCODE_ARCH: arm64 - NPM_ARCH: arm64 - steps: - - template: linux/product-build-linux.yml + - job: WindowsARM64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: arm64 + steps: + - template: win32/product-build-win32.yml - - job: LinuxAlpine - condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true')) - variables: - VSCODE_ARCH: alpine - steps: - - template: linux/product-build-linux-multiarch.yml + - stage: Linux + dependsOn: + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: "Ubuntu-16.04" + jobs: + - job: Linux + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) + container: vscode-x64 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + steps: + - template: linux/product-build-linux.yml - - job: LinuxWeb - condition: and(succeeded(), eq(variables['VSCODE_BUILD_WEB'], 'true')) - variables: - VSCODE_ARCH: x64 - steps: - - template: web/product-build-web.yml + - job: LinuxSnap + dependsOn: + - Linux + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) + container: snapcraft + variables: + VSCODE_ARCH: x64 + steps: + - template: linux/snap-build-linux.yml -- stage: macOS - dependsOn: - - Compile - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - pool: - vmImage: macOS-latest - jobs: - - job: macOS - condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS'], 'true')) - variables: - VSCODE_ARCH: x64 - steps: - - template: darwin/product-build-darwin.yml + - job: LinuxArmhf + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) + container: vscode-armhf + variables: + VSCODE_ARCH: armhf + NPM_ARCH: armv7l + steps: + - template: linux/product-build-linux.yml -- stage: Mooncake - dependsOn: - - Windows - - Linux - - macOS - condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - pool: - vmImage: 'Ubuntu-16.04' - jobs: - - job: SyncMooncake - displayName: Sync Mooncake - steps: - - template: sync-mooncake.yml + - job: LinuxSnapArmhf + dependsOn: + - LinuxArmhf + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) + container: snapcraft + variables: + VSCODE_ARCH: armhf + steps: + - template: linux/snap-build-linux.yml -- stage: Publish - dependsOn: - - Windows - - Linux - - macOS - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) - pool: - vmImage: 'Ubuntu-16.04' - jobs: - - job: BuildService - displayName: Build Service - steps: - - template: release.yml + - job: LinuxArm64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) + container: vscode-arm64 + variables: + VSCODE_ARCH: arm64 + NPM_ARCH: arm64 + steps: + - template: linux/product-build-linux.yml + + - job: LinuxSnapArm64 + dependsOn: + - LinuxArm64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) + container: snapcraft + variables: + VSCODE_ARCH: arm64 + steps: + - template: linux/snap-build-linux.yml + + - job: LinuxAlpine + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true')) + steps: + - template: linux/product-build-alpine.yml + + - job: LinuxWeb + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WEB'], 'true')) + variables: + VSCODE_ARCH: x64 + steps: + - template: web/product-build-web.yml + + - stage: macOS + dependsOn: + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: macOS-latest + jobs: + - job: macOS + condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: darwin/product-build-darwin.yml + + - stage: macOSARM64 + dependsOn: + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: macOS-latest + jobs: + - job: macOSARM64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS_ARM64'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: arm64 + steps: + - template: darwin/product-build-darwin.yml + + - stage: Mooncake + dependsOn: + - Windows + - Linux + - macOS + - macOSARM64 + condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: "Ubuntu-16.04" + jobs: + - job: SyncMooncake + displayName: Sync Mooncake + steps: + - template: sync-mooncake.yml + + - stage: Publish + dependsOn: + - Windows + - Linux + - macOS + - macOSARM64 + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) + pool: + vmImage: "Ubuntu-16.04" + jobs: + - job: BuildService + displayName: Build Service + steps: + - template: release.yml diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 2eff40b53..eaf0a65e8 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -1,146 +1,161 @@ steps: -- script: | - mkdir -p .build - echo -n $BUILD_SOURCEVERSION > .build/commit - echo -n $VSCODE_QUALITY > .build/quality - displayName: Prepare cache flag + - script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + echo -n $VSCODE_QUALITY > .build/quality + echo -n $ENABLE_TERRAPIN > .build/terrapin + displayName: Prepare compilation cache flag -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' - dryRun: true + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" + dryRun: true -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set -e - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - displayName: Merge distro - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - echo -n $VSCODE_ARCH > .build/arch - displayName: Prepare arch cache flag + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + echo -n $(VSCODE_ARCH) > .build/arch + displayName: Prepare yarn cache flags -- script: | - set -e - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) + - script: | + set -e + export CHILD_CONCURRENCY="1" + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) -# Mixin must run before optimize, because the CSS loader will -# inline small SVGs -- script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn gulp hygiene - yarn monaco-compile-check - yarn valid-layers-check - displayName: Run hygiene, monaco compile & valid layers checks - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + # Mixin must run before optimize, because the CSS loader will + # inline small SVGs + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set - - ./build/azure-pipelines/common/extract-telemetry.sh - displayName: Extract Telemetry - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + yarn gulp hygiene + yarn monaco-compile-check + yarn valid-layers-check + displayName: Run hygiene, monaco compile & valid layers checks + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - AZURE_WEBVIEW_STORAGE_ACCESS_KEY="$(vscode-webview-storage-key)" \ - ./build/azure-pipelines/common/publish-webview.sh - displayName: Publish Webview - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set - + ./build/azure-pipelines/common/extract-telemetry.sh + displayName: Extract Telemetry + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set -e - yarn gulp compile-build - yarn gulp compile-extensions-build - yarn gulp minify-vscode - yarn gulp minify-vscode-reh - yarn gulp minify-vscode-reh-web - displayName: Compile - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + AZURE_WEBVIEW_STORAGE_ACCESS_KEY="$(vscode-webview-storage-key)" \ + ./build/azure-pipelines/common/publish-webview.sh + displayName: Publish Webview + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set -e - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ - node build/azure-pipelines/upload-sourcemaps - displayName: Upload sourcemaps - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + yarn gulp compile-build + yarn gulp compile-extensions-build + yarn gulp minify-vscode + yarn gulp minify-vscode-reh + yarn gulp minify-vscode-reh-web + displayName: Compile + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set -e - VERSION=`node -p "require(\"./package.json\").version"` - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - node build/azure-pipelines/common/createBuild.js $VERSION - displayName: Create build - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + node build/azure-pipelines/upload-sourcemaps + displayName: Upload sourcemaps + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + VERSION=`node -p "require(\"./package.json\").version"` + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + node build/azure-pipelines/common/createBuild.js $VERSION + displayName: Create build + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) diff --git a/build/azure-pipelines/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml index 10b6aa4e1..5f1cf6c28 100644 --- a/build/azure-pipelines/publish-types/publish-types.yml +++ b/build/azure-pipelines/publish-types/publish-types.yml @@ -2,82 +2,82 @@ trigger: branches: - include: ['refs/tags/*'] + include: ["refs/tags/*"] pr: none steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- bash: | - TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) - CHANNEL="G1C14HJ2F" + - bash: | + TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + CHANNEL="G1C14HJ2F" - if [ "$TAG_VERSION" == "1.999.0" ]; then - MESSAGE=". Someone pushed 1.999.0 tag. Please delete it ASAP from remote and local." + if [ "$TAG_VERSION" == "1.999.0" ]; then + MESSAGE=". Someone pushed 1.999.0 tag. Please delete it ASAP from remote and local." + + curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ + -H 'Content-type: application/json; charset=utf-8' \ + --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \ + https://slack.com/api/chat.postMessage + + exit 1 + fi + displayName: Check 1.999.0 tag + + - bash: | + # Install build dependencies + (cd build && yarn) + node build/azure-pipelines/publish-types/check-version.js + displayName: Check version + + - bash: | + git config --global user.email "vscode@microsoft.com" + git config --global user.name "VSCode" + + git clone https://$(GITHUB_TOKEN)@github.com/DefinitelyTyped/DefinitelyTyped.git --depth=1 + node build/azure-pipelines/publish-types/update-types.js + + TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + + cd DefinitelyTyped + + git diff --color | cat + git add -A + git status + git checkout -b "vscode-types-$TAG_VERSION" + git commit -m "VS Code $TAG_VERSION Extension API" + git push origin "vscode-types-$TAG_VERSION" + + displayName: Push update to DefinitelyTyped + + - bash: | + TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + CHANNEL="G1C14HJ2F" + + MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:" + LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details." + MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ -H 'Content-type: application/json; charset=utf-8' \ --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \ https://slack.com/api/chat.postMessage - exit 1 - fi - displayName: Check 1.999.0 tag + curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ + -H 'Content-type: application/json; charset=utf-8' \ + --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$LINK"'"}' \ + https://slack.com/api/chat.postMessage -- bash: | - # Install build dependencies - (cd build && yarn) - node build/azure-pipelines/publish-types/check-version.js - displayName: Check version + curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ + -H 'Content-type: application/json; charset=utf-8' \ + --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE2"'"}' \ + https://slack.com/api/chat.postMessage -- bash: | - git config --global user.email "vscode@microsoft.com" - git config --global user.name "VSCode" - - git clone https://$(GITHUB_TOKEN)@github.com/DefinitelyTyped/DefinitelyTyped.git --depth=1 - node build/azure-pipelines/publish-types/update-types.js - - TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) - - cd DefinitelyTyped - - git diff --color | cat - git add -A - git status - git checkout -b "vscode-types-$TAG_VERSION" - git commit -m "VS Code $TAG_VERSION Extension API" - git push origin "vscode-types-$TAG_VERSION" - - displayName: Push update to DefinitelyTyped - -- bash: | - TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) - CHANNEL="G1C14HJ2F" - - MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:" - LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details." - MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." - - curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ - -H 'Content-type: application/json; charset=utf-8' \ - --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \ - https://slack.com/api/chat.postMessage - - curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ - -H 'Content-type: application/json; charset=utf-8' \ - --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$LINK"'"}' \ - https://slack.com/api/chat.postMessage - - curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ - -H 'Content-type: application/json; charset=utf-8' \ - --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE2"'"}' \ - https://slack.com/api/chat.postMessage - - displayName: Send message on Slack + displayName: Send message on Slack diff --git a/build/azure-pipelines/release.yml b/build/azure-pipelines/release.yml index f5365b80c..edd293c04 100644 --- a/build/azure-pipelines/release.yml +++ b/build/azure-pipelines/release.yml @@ -1,22 +1,22 @@ steps: -- task: NodeTool@0 - inputs: - versionSpec: "10.x" + - task: NodeTool@0 + inputs: + versionSpec: "10.x" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e + - script: | + set -e - (cd build ; yarn) + (cd build ; yarn) - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - node build/azure-pipelines/common/releaseBuild.js + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + node build/azure-pipelines/common/releaseBuild.js diff --git a/build/azure-pipelines/sync-mooncake.yml b/build/azure-pipelines/sync-mooncake.yml index 49dfc9ced..109709418 100644 --- a/build/azure-pipelines/sync-mooncake.yml +++ b/build/azure-pipelines/sync-mooncake.yml @@ -1,24 +1,24 @@ steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e + - script: | + set -e - (cd build ; yarn) + (cd build ; yarn) - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(vscode-mooncake-storage-key)" \ - node build/azure-pipelines/common/sync-mooncake.js "$VSCODE_QUALITY" + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + MOONCAKE_STORAGE_ACCESS_KEY="$(vscode-mooncake-storage-key)" \ + node build/azure-pipelines/common/sync-mooncake.js "$VSCODE_QUALITY" diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index aded67174..33282adbe 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -1,113 +1,132 @@ steps: -- script: | - mkdir -p .build - echo -n $BUILD_SOURCEVERSION > .build/commit - echo -n $VSCODE_QUALITY > .build/quality - displayName: Prepare cache flag + - script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + echo -n $VSCODE_QUALITY > .build/quality + echo -n $ENABLE_TERRAPIN > .build/terrapin + displayName: Prepare compilation cache flag -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" -- script: | - set -e - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling -- script: | - set -e - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - displayName: Merge distro + - script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro -# - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 -# inputs: -# keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' -# targetfolder: '**/node_modules, !**/node_modules/**/node_modules' -# vstsFeed: 'npm-vscode' + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) -- script: | - set -e - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install dependencies - # condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + echo -n "web" > .build/arch + displayName: Prepare yarn cache flag -# - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 -# inputs: -# keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' -# targetfolder: '**/node_modules, !**/node_modules/**/node_modules' -# vstsFeed: 'npm-vscode' -# condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" -# - script: | -# set -e -# yarn postinstall -# displayName: Run postinstall scripts -# condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - script: | + set -e + export CHILD_CONCURRENCY="1" + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-web-min-ci - displayName: Build + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- script: | - set -e - AZURE_STORAGE_ACCOUNT="$(web-storage-account)" \ - AZURE_STORAGE_ACCESS_KEY="$(web-storage-key)" \ - node build/azure-pipelines/upload-cdn.js - displayName: Upload to CDN + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality - # upload only the workbench.web.api.js source maps because - # we just compiled these bits in the previous step and the - # general task to upload source maps has already been run -- script: | - set -e - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ - node build/azure-pipelines/upload-sourcemaps out-vscode-web-min out-vscode-web-min/vs/workbench/workbench.web.api.js.map - displayName: Upload sourcemaps (Web) + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-web-min-ci + displayName: Build -- script: | - set -e - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - ./build/azure-pipelines/web/publish.sh - displayName: Publish + - script: | + set -e + AZURE_STORAGE_ACCOUNT="$(web-storage-account)" \ + AZURE_STORAGE_ACCESS_KEY="$(web-storage-key)" \ + node build/azure-pipelines/upload-cdn.js + displayName: Upload to CDN + + # upload only the workbench.web.api.js source maps because + # we just compiled these bits in the previous step and the + # general task to upload source maps has already been run + - script: | + set -e + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + node build/azure-pipelines/upload-sourcemaps out-vscode-web-min out-vscode-web-min/vs/workbench/workbench.web.api.js.map + displayName: Upload sourcemaps (Web) + + - script: | + set -e + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + ./build/azure-pipelines/web/publish.sh + displayName: Publish diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index 6107eacc6..87b820cb0 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -1,87 +1,87 @@ steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: UsePythonVersion@0 - inputs: - versionSpec: '2.x' - addToPath: true + - task: UsePythonVersion@0 + inputs: + versionSpec: "2.x" + addToPath: true -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" -- powershell: | - yarn --frozen-lockfile - env: - CHILD_CONCURRENCY: "1" - displayName: Install Dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - powershell: | + yarn --frozen-lockfile + env: + CHILD_CONCURRENCY: "1" + displayName: Install Dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn postinstall } - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn postinstall } + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- powershell: | - yarn electron - displayName: Download Electron + - powershell: | + yarn electron + displayName: Download Electron -- powershell: | - yarn monaco-compile-check - displayName: Run Monaco Editor Checks + - powershell: | + yarn monaco-compile-check + displayName: Run Monaco Editor Checks -- script: | - yarn valid-layers-check - displayName: Run Valid Layers Checks + - script: | + yarn valid-layers-check + displayName: Run Valid Layers Checks -- powershell: | - yarn compile - displayName: Compile Sources + - powershell: | + yarn compile + displayName: Compile Sources -- powershell: | - yarn download-builtin-extensions - displayName: Download Built-in Extensions + - powershell: | + yarn download-builtin-extensions + displayName: Download Built-in Extensions -- powershell: | - .\scripts\test.bat --tfs "Unit Tests" - displayName: Run Unit Tests (Electron) + - powershell: | + .\scripts\test.bat --tfs "Unit Tests" + displayName: Run Unit Tests (Electron) -- powershell: | - yarn test-browser --browser chromium --browser firefox --tfs "Browser Unit Tests" - displayName: Run Unit Tests (Browser) + - powershell: | + yarn test-browser --browser chromium --browser firefox --tfs "Browser Unit Tests" + displayName: Run Unit Tests (Browser) -- powershell: | - .\scripts\test-integration.bat --tfs "Integration Tests" - displayName: Run Integration Tests (Electron) + - powershell: | + .\scripts\test-integration.bat --tfs "Integration Tests" + displayName: Run Integration Tests (Electron) -- task: PublishPipelineArtifact@0 - displayName: 'Publish Crash Reports' - inputs: - artifactName: crash-dump-windows - targetPath: .build\crashes - continueOnError: true - condition: failed() + - task: PublishPipelineArtifact@0 + displayName: "Publish Crash Reports" + inputs: + artifactName: crash-dump-windows + targetPath: .build\crashes + continueOnError: true + condition: failed() -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() diff --git a/build/azure-pipelines/win32/product-build-win32-arm64.yml b/build/azure-pipelines/win32/product-build-win32-arm64.yml deleted file mode 100644 index 2e53167e6..000000000 --- a/build/azure-pipelines/win32/product-build-win32-arm64.yml +++ /dev/null @@ -1,192 +0,0 @@ -steps: -- powershell: | - mkdir .build -ea 0 - "$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit - "$env:VSCODE_QUALITY" | Out-File -Encoding ascii -NoNewLine .build\quality - displayName: Prepare cache flag - -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' - -- powershell: | - $ErrorActionPreference = "Stop" - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) - -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" - -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - -- task: UsePythonVersion@0 - inputs: - versionSpec: '2.x' - addToPath: true - -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII - - exec { git config user.email "vscode@microsoft.com" } - exec { git config user.name "VSCode" } - - mkdir .build -ea 0 - "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch - displayName: Prepare tooling - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } - exec { git fetch distro } - exec { git merge $(node -p "require('./package.json').distro") } - displayName: Merge distro - -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:npm_config_arch="$(VSCODE_ARCH)" - $env:CHILD_CONCURRENCY="1" - exec { yarn --frozen-lockfile } - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn postinstall } - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build/azure-pipelines/mixin } - displayName: Mix in quality - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min-ci" } - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-code-helper" } - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" } - displayName: Build - -- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)' - Pattern: '*.dll,*.exe,*.node' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "VS Code" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "https://code.visualstudio.com/" - }, - { - "parameterName": "Append", - "parameterValue": "/as" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [ - { - "parameterName": "VerifyAll", - "parameterValue": "/all" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 120 - -- task: NuGetCommand@2 - displayName: Install ESRPClient.exe - inputs: - restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config' - feedsToUse: config - nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config' - externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b - restoreDirectory: packages - -- task: ESRPImportCertTask@1 - displayName: Import ESRP Request Signing Certificate - inputs: - ESRP: 'ESRP CodeSign' - -- task: PowerShell@2 - inputs: - targetType: filePath - filePath: .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 - arguments: "$(ESRP-SSL-AADAuth)" - displayName: Import ESRP Auth Certificate - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" - $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - .\build\azure-pipelines\win32\publish.ps1 - displayName: Publish - -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 43bd2479a..1992ac1af 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -1,254 +1,272 @@ steps: -- powershell: | - mkdir .build -ea 0 - "$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit - "$env:VSCODE_QUALITY" | Out-File -Encoding ascii -NoNewLine .build\quality - displayName: Prepare cache flag + - powershell: | + mkdir .build -ea 0 + "$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit + "$env:VSCODE_QUALITY" | Out-File -Encoding ascii -NoNewLine .build\quality + "$env:ENABLE_TERRAPIN" | Out-File -Encoding ascii -NoNewLine .build\terrapin + displayName: Prepare compilation cache flags -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" -- powershell: | - $ErrorActionPreference = "Stop" - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - powershell: | + $ErrorActionPreference = "Stop" + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: UsePythonVersion@0 - inputs: - versionSpec: '2.x' - addToPath: true + - task: UsePythonVersion@0 + inputs: + versionSpec: "2.x" + addToPath: true -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII - exec { git config user.email "vscode@microsoft.com" } - exec { git config user.name "VSCode" } + exec { git config user.email "vscode@microsoft.com" } + exec { git config user.name "VSCode" } + displayName: Prepare tooling - mkdir .build -ea 0 - "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch - displayName: Prepare tooling + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } + exec { git fetch distro } + exec { git merge $(node -p "require('./package.json').distro") } + displayName: Merge distro -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } - exec { git fetch distro } - exec { git merge $(node -p "require('./package.json').distro") } - displayName: Merge distro + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' + - powershell: | + "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch + displayName: Prepare yarn cache flags -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:npm_config_arch="$(VSCODE_ARCH)" - $env:CHILD_CONCURRENCY="1" - exec { yarn --frozen-lockfile } - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + . build/azure-pipelines/win32/retry.ps1 + $ErrorActionPreference = "Stop" + $env:npm_config_arch="$(VSCODE_ARCH)" + $env:CHILD_CONCURRENCY="1" + retry { exec { yarn --frozen-lockfile } } + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn postinstall } - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build/azure-pipelines/mixin } - displayName: Mix in quality + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn postinstall } + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min-ci" } - exec { yarn gulp "vscode-reh-win32-$env:VSCODE_ARCH-min-ci" } - exec { yarn gulp "vscode-reh-web-win32-$env:VSCODE_ARCH-min-ci" } - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-code-helper" } - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" } - displayName: Build + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build/azure-pipelines/mixin } + displayName: Mix in quality -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn electron $(VSCODE_ARCH) } - exec { .\scripts\test.bat --build --tfs "Unit Tests" } - displayName: Run unit tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-min-ci" } + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-code-helper" } + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" } + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)" + displayName: Build -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn test-browser --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } - displayName: Run unit tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + exec { yarn gulp "vscode-reh-win32-$(VSCODE_ARCH)-min-ci" } + exec { yarn gulp "vscode-reh-web-win32-$(VSCODE_ARCH)-min-ci" } + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(agent.builddirectory)/vscode-reh-win32-$(VSCODE_ARCH)" + displayName: Build Server + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) -- powershell: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } - displayName: Run integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn electron $(VSCODE_ARCH) } + exec { .\scripts\test.bat --build --tfs "Unit Tests" } + displayName: Run unit tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser firefox } - displayName: Run integration tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn test-browser --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } + displayName: Run unit tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-remote-integration.bat } - displayName: Run remote integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - powershell: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } + displayName: Run integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) -- task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-windows-$(VSCODE_ARCH) - targetPath: .build\crashes - displayName: 'Publish Crash Reports' - continueOnError: true - condition: failed() + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser firefox } + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-remote-integration.bat } + displayName: Run remote integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) -- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH),$(agent.builddirectory)/vscode-reh-win32-$(VSCODE_ARCH)' - Pattern: '*.dll,*.exe,*.node' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "VS Code" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "https://code.visualstudio.com/" - }, - { - "parameterName": "Append", - "parameterValue": "/as" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [ - { - "parameterName": "VerifyAll", - "parameterValue": "/all" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 120 + - task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-windows-$(VSCODE_ARCH) + targetPath: .build\crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() -- task: NuGetCommand@2 - displayName: Install ESRPClient.exe - inputs: - restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config' - feedsToUse: config - nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config' - externalFeedCredentials: 'ESRP Nuget' - restoreDirectory: packages + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: and(succeededOrFailed(), ne(variables['VSCODE_ARCH'], 'arm64')) -- task: ESRPImportCertTask@1 - displayName: Import ESRP Request Signing Certificate - inputs: - ESRP: 'ESRP CodeSign' + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: "ESRP CodeSign" + FolderPath: "$(CodeSigningFolderPath)" + Pattern: "*.dll,*.exe,*.node" + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolSign", + "parameters": [ + { + "parameterName": "OpusName", + "parameterValue": "VS Code" + }, + { + "parameterName": "OpusInfo", + "parameterValue": "https://code.visualstudio.com/" + }, + { + "parameterName": "Append", + "parameterValue": "/as" + }, + { + "parameterName": "FileDigest", + "parameterValue": "/fd \"SHA256\"" + }, + { + "parameterName": "PageHash", + "parameterValue": "/NPH" + }, + { + "parameterName": "TimeStamp", + "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + }, + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolVerify", + "parameters": [ + { + "parameterName": "VerifyAll", + "parameterValue": "/all" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 120 -- task: PowerShell@2 - inputs: - targetType: filePath - filePath: .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 - arguments: "$(ESRP-SSL-AADAuth)" - displayName: Import ESRP Auth Certificate + - task: NuGetCommand@2 + displayName: Install ESRPClient.exe + inputs: + restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config' + feedsToUse: config + nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config' + externalFeedCredentials: "ESRP Nuget" + restoreDirectory: packages -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" - $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - .\build\azure-pipelines\win32\publish.ps1 - displayName: Publish + - task: ESRPImportCertTask@1 + displayName: Import ESRP Request Signing Certificate + inputs: + ESRP: "ESRP CodeSign" -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true + - task: PowerShell@2 + inputs: + targetType: filePath + filePath: .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 + arguments: "$(ESRP-SSL-AADAuth)" + displayName: Import ESRP Auth Certificate + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" + $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + .\build\azure-pipelines\win32\publish.ps1 + displayName: Publish + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + continueOnError: true diff --git a/build/azure-pipelines/win32/retry.ps1 b/build/azure-pipelines/win32/retry.ps1 new file mode 100644 index 000000000..002a5e274 --- /dev/null +++ b/build/azure-pipelines/win32/retry.ps1 @@ -0,0 +1,19 @@ +function Retry +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd + ) + $retry = 0 + + while ($retry++ -lt 3) { + try { + & $cmd + return + } catch { + # noop + } + } + + throw "Max retries reached" +} diff --git a/build/darwin/sign.ts b/build/darwin/sign.ts index ee5d2eeb1..538dc97ad 100644 --- a/build/darwin/sign.ts +++ b/build/darwin/sign.ts @@ -13,6 +13,7 @@ import * as product from '../../product.json'; async function main(): Promise { const buildDir = process.env['AGENT_BUILDDIRECTORY']; const tempDir = process.env['AGENT_TEMPDIRECTORY']; + const arch = process.env['VSCODE_ARCH']; if (!buildDir) { throw new Error('$AGENT_BUILDDIRECTORY not set'); @@ -23,7 +24,7 @@ async function main(): Promise { } const baseDir = path.dirname(__dirname); - const appRoot = path.join(buildDir, 'VSCode-darwin'); + const appRoot = path.join(buildDir, `VSCode-darwin-${arch}`); const appName = product.nameLong + '.app'; const appFrameworkPath = path.join(appRoot, appName, 'Contents', 'Frameworks'); const helperAppBaseName = product.nameShort; diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index aa0282bff..118fe6e81 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -434,7 +434,7 @@ function createTscCompileTask(watch) { // stdio: [null, 'pipe', 'inherit'] }); let errors = []; - let reporter = createReporter(); + let reporter = createReporter('monaco'); let report; // eslint-disable-next-line no-control-regex let magic = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; // https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 6ae72e4cf..c204e84fd 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -64,7 +64,7 @@ const tasks = compilations.map(function (tsconfigFile) { } function createPipeline(build, emitError) { - const reporter = createReporter(); + const reporter = createReporter('extensions'); overrideOptions.inlineSources = Boolean(build); overrideOptions.base = path.dirname(absolutePath); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 3c8bbe5b8..42a9c2b27 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -58,7 +58,7 @@ const vscodeResources = [ 'out-build/bootstrap-node.js', 'out-build/bootstrap-window.js', 'out-build/paths.js', - 'out-build/vs/**/*.{svg,png,html}', + 'out-build/vs/**/*.{svg,png,html,jpg}', '!out-build/vs/code/browser/**/*.html', '!out-build/vs/editor/standalone/**/*.svg', 'out-build/vs/base/common/performance.js', @@ -201,13 +201,12 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const telemetry = gulp.src('.build/telemetry/**', { base: '.build/telemetry', dot: true }); - const jsFilter = util.filter(data => !data.isDirectory() &&/\.js$/.test(data.path)); + const jsFilter = util.filter(data => !data.isDirectory() && /\.js$/.test(data.path)); const root = path.resolve(path.join(__dirname, '..')); const dependenciesSrc = _.flatten(productionDependencies.map(d => path.relative(root, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`])); const deps = gulp.src(dependenciesSrc, { base: '.', dot: true }) .pipe(filter(['**', '!**/package-lock.json', '!**/yarn.lock', '!**/*.js.map'])) - .pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore'))) .pipe(util.cleanNodeModules(path.join(__dirname, '.moduleignore'))) .pipe(jsFilter) .pipe(util.rewriteSourceMappingURL(sourceMappingURLBase)) @@ -326,7 +325,8 @@ const BUILD_TARGETS = [ { platform: 'win32', arch: 'ia32' }, { platform: 'win32', arch: 'x64' }, { platform: 'win32', arch: 'arm64' }, - { platform: 'darwin', arch: null, opts: { stats: true } }, + { platform: 'darwin', arch: 'x64', opts: { stats: true } }, + { platform: 'darwin', arch: 'arm64', opts: { stats: true } }, { platform: 'linux', arch: 'ia32' }, { platform: 'linux', arch: 'x64' }, { platform: 'linux', arch: 'armhf' }, @@ -338,7 +338,7 @@ BUILD_TARGETS.forEach(buildTarget => { const arch = buildTarget.arch; const opts = buildTarget.opts; - ['', 'min'].forEach(minified => { + const [vscode, vscodeMin] = ['', 'min'].map(minified => { const sourceFolderName = `out-vscode${dashed(minified)}`; const destinationFolderName = `VSCode${dashed(platform)}${dashed(arch)}`; @@ -355,7 +355,14 @@ BUILD_TARGETS.forEach(buildTarget => { vscodeTaskCI )); gulp.task(vscodeTask); + + return vscodeTask; }); + + if (process.platform === platform && process.arch === arch) { + gulp.task(task.define('vscode', task.series(vscode))); + gulp.task(task.define('vscode-min', task.series(vscodeMin))); + } }); // Transifex Localizations @@ -456,8 +463,10 @@ const generateVSCodeConfigurationTask = task.define('generate-vscode-configurati const userDataDir = path.join(os.tmpdir(), 'tmpuserdata'); const extensionsDir = path.join(os.tmpdir(), 'tmpextdir'); + const arch = process.env['VSCODE_ARCH']; + const appRoot = path.join(buildDir, `VSCode-darwin-${arch}`); const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app'; - const appPath = path.join(buildDir, `VSCode-darwin/${appName}/Contents/Resources/app/bin/code`); + const appPath = path.join(appRoot, appName, 'Contents', 'Resources', 'app', 'bin', 'code'); const codeProc = cp.exec( `${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`, (err, stdout, stderr) => { diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 1d8a09e4f..b6732b7fa 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -98,6 +98,7 @@ function prepareDebPackage(arch) { .pipe(replace('@@ARCHITECTURE@@', debArch)) .pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@')) .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) + .pipe(replace('@@REPOSITORY_NAME@@', arch === 'x64' ? 'vscode' : 'code')) .pipe(rename('DEBIAN/postinst')); const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, workspaceMime, icon, bash_completion, zsh_completion, code); @@ -237,6 +238,8 @@ function prepareSnapPackage(arch) { const snapcraft = gulp.src('resources/linux/snap/snapcraft.yaml', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@VERSION@@', commit.substr(0, 8))) + // Possible run-on values https://snapcraft.io/docs/architectures + .pipe(replace('@@ARCHITECTURE@@', arch === 'x64' ? 'amd64' : arch)) .pipe(rename('snap/snapcraft.yaml')); const electronLaunch = gulp.src('resources/linux/snap/electron-launch', { base: '.' }) diff --git a/build/hygiene.js b/build/hygiene.js index b95b4ee98..ee6598912 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -83,7 +83,7 @@ const indentationFilter = [ '!src/vs/*/**/*.d.ts', '!src/typings/**/*.d.ts', '!extensions/**/*.d.ts', - '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}', + '!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}', '!build/{lib,download,darwin}/**/*.js', '!build/**/*.sh', '!build/azure-pipelines/**/*.js', @@ -119,6 +119,7 @@ const copyrightFilter = [ '!resources/win32/bin/code.js', '!resources/web/code-web.js', '!resources/completions/**', + '!extensions/configuration-editing/build/inline-allOf.ts', '!extensions/markdown-language-features/media/highlight.css', '!extensions/html-language-features/server/src/modes/typescript/*', '!extensions/*/server/bin/*', diff --git a/build/lib/eslint/code-no-unexternalized-strings.ts b/build/lib/eslint/code-no-unexternalized-strings.ts index 29db884cd..8d9e142bb 100644 --- a/build/lib/eslint/code-no-unexternalized-strings.ts +++ b/build/lib/eslint/code-no-unexternalized-strings.ts @@ -47,7 +47,7 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { // extract key so that it can be checked later let key: string | undefined; if (isStringLiteral(keyNode)) { - doubleQuotedStringLiterals.delete(keyNode); //todo@joh reconsider + doubleQuotedStringLiterals.delete(keyNode); key = keyNode.value; } else if (keyNode.type === AST_NODE_TYPES.ObjectExpression) { @@ -55,7 +55,7 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { if (property.type === AST_NODE_TYPES.Property && !property.computed) { if (property.key.type === AST_NODE_TYPES.Identifier && property.key.name === 'key') { if (isStringLiteral(property.value)) { - doubleQuotedStringLiterals.delete(property.value); //todo@joh reconsider + doubleQuotedStringLiterals.delete(property.value); key = property.value.value; break; } @@ -123,4 +123,3 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { }; } }; - diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 47f0155d8..9ec898986 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -62,6 +62,10 @@ "name": "vs/workbench/contrib/debug", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/dialogs", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/emmet", "project": "vscode-workbench" @@ -365,6 +369,14 @@ { "name": "vs/workbench/services/authentication", "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/extensionRecommendations", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/gettingStarted", + "project": "vscode-workbench" } ] } diff --git a/build/lib/reporter.ts b/build/lib/reporter.ts index ec9088175..f875fce90 100644 --- a/build/lib/reporter.ts +++ b/build/lib/reporter.ts @@ -12,72 +12,89 @@ import * as ansiColors from 'ansi-colors'; import * as fs from 'fs'; import * as path from 'path'; -const allErrors: string[][] = []; -let startTime: number | null = null; -let count = 0; +class ErrorLog { + constructor(public id: string) { + } + allErrors: string[][] = []; + startTime: number | null = null; + count = 0; -function onStart(): void { - if (count++ > 0) { - return; + onStart(): void { + if (this.count++ > 0) { + return; + } + + this.startTime = new Date().getTime(); + fancyLog(`Starting ${ansiColors.green('compilation')}${this.id ? ansiColors.blue(` ${this.id}`) : ''}...`); } - startTime = new Date().getTime(); - fancyLog(`Starting ${ansiColors.green('compilation')}...`); -} + onEnd(): void { + if (--this.count > 0) { + return; + } -function onEnd(): void { - if (--count > 0) { - return; + this.log(); + } + + log(): void { + const errors = _.flatten(this.allErrors); + const seen = new Set(); + + errors.map(err => { + if (!seen.has(err)) { + seen.add(err); + fancyLog(`${ansiColors.red('Error')}: ${err}`); + } + }); + + fancyLog(`Finished ${ansiColors.green('compilation')}${this.id ? ansiColors.blue(` ${this.id}`) : ''} with ${errors.length} errors after ${ansiColors.magenta((new Date().getTime() - this.startTime!) + ' ms')}`); + + const regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/s; + const messages = errors + .map(err => regex.exec(err)) + .filter(match => !!match) + .map(x => x as string[]) + .map(([, path, line, column, message]) => ({ path, line: parseInt(line), column: parseInt(column), message })); + + try { + const logFileName = 'log' + (this.id ? `_${this.id}` : ''); + fs.writeFileSync(path.join(buildLogFolder, logFileName), JSON.stringify(messages)); + } catch (err) { + //noop + } } - log(); } -const buildLogPath = path.join(path.dirname(path.dirname(__dirname)), '.build', 'log'); +const errorLogsById = new Map(); +function getErrorLog(id: string = '') { + let errorLog = errorLogsById.get(id); + if (!errorLog) { + errorLog = new ErrorLog(id); + errorLogsById.set(id, errorLog); + } + return errorLog; +} + +const buildLogFolder = path.join(path.dirname(path.dirname(__dirname)), '.build'); try { - fs.mkdirSync(path.dirname(buildLogPath)); + fs.mkdirSync(buildLogFolder); } catch (err) { // ignore } -function log(): void { - const errors = _.flatten(allErrors); - const seen = new Set(); - - errors.map(err => { - if (!seen.has(err)) { - seen.add(err); - fancyLog(`${ansiColors.red('Error')}: ${err}`); - } - }); - - const regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/; - const messages = errors - .map(err => regex.exec(err)) - .filter(match => !!match) - .map(x => x as string[]) - .map(([, path, line, column, message]) => ({ path, line: parseInt(line), column: parseInt(column), message })); - - try { - - fs.writeFileSync(buildLogPath, JSON.stringify(messages)); - } catch (err) { - //noop - } - - fancyLog(`Finished ${ansiColors.green('compilation')} with ${errors.length} errors after ${ansiColors.magenta((new Date().getTime() - startTime!) + ' ms')}`); -} - export interface IReporter { (err: string): void; hasErrors(): boolean; end(emitError: boolean): NodeJS.ReadWriteStream; } -export function createReporter(): IReporter { +export function createReporter(id?: string): IReporter { + const errorLog = getErrorLog(id); + const errors: string[] = []; - allErrors.push(errors); + errorLog.allErrors.push(errors); const result = (err: string) => errors.push(err); @@ -85,14 +102,14 @@ export function createReporter(): IReporter { result.end = (emitError: boolean): NodeJS.ReadWriteStream => { errors.length = 0; - onStart(); + errorLog.onStart(); return es.through(undefined, function () { - onEnd(); + errorLog.onEnd(); if (emitError && errors.length > 0) { if (!(errors as any).__logged__) { - log(); + errorLog.log(); } (errors as any).__logged__ = true; diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index cb88d37ad..94dbb3b9e 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -7,8 +7,8 @@ let err = false; const majorNodeVersion = parseInt(/^(\d+)\./.exec(process.versions.node)[1]); -if (majorNodeVersion < 10 || majorNodeVersion >= 13) { - console.error('\033[1;31m*** Please use node >=10 and <=12.\033[0;0m'); +if (majorNodeVersion < 10 || majorNodeVersion >= 16) { + console.error('\033[1;31m*** Please use node >=10 and <=16.\033[0;0m'); err = true; } diff --git a/build/package.json b/build/package.json index d7e68e7ce..20fc702f9 100644 --- a/build/package.json +++ b/build/package.json @@ -48,7 +48,7 @@ "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "typescript": "^4.1.0-dev.20201018", + "typescript": "^4.2.0-dev.20201119", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" @@ -60,6 +60,6 @@ "npmCheckJs": "tsc --noEmit" }, "dependencies": { - "@azure/cosmos": "^3.4.0" + "@azure/cosmos": "^3.9.3" } } diff --git a/build/win32/code.iss b/build/win32/code.iss index 7f4b36e71..50dcf76b8 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -108,6 +108,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ascx\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ASCX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.asp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -115,6 +116,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.asp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ASP}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.aspx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -122,6 +124,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.aspx\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ASPX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -129,6 +132,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_login\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -136,6 +140,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_login\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash Login}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_logout\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -143,6 +148,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_logout\OpenWith Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash Logout}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_profile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -150,6 +156,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_profile\OpenWit Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash Profile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bashrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -157,6 +164,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bashrc\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bib\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -164,6 +172,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bib\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,BibTeX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bowerrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -171,6 +180,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bowerrc\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bower RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\bower.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c++\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -185,6 +195,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\c.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -192,6 +203,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cc\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -199,6 +211,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -206,6 +219,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Clojure}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -213,6 +227,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljs\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ClojureScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -220,6 +235,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljx\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CLJX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clojure\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -227,6 +243,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clojure\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Clojure}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.code-workspace\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -234,6 +251,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.code-workspace\OpenW Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Code Workspace}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.coffee\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -241,6 +259,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.coffee\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CoffeeScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.config\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -248,6 +267,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.config\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Configuration}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.containerfile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -262,6 +282,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cpp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -269,6 +290,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cs\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C#}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\csharp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cshtml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -276,6 +298,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cshtml\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CSHTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csproj\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -283,6 +306,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csproj\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C# Project}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.css\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -290,6 +314,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.css\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CSS}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\css.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -297,6 +322,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C# Script}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\csharp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ctp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -304,6 +330,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ctp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CakePHP Template}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cxx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -311,6 +338,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cxx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dockerfile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -318,6 +346,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dockerfile\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Dockerfile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dot\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -325,6 +354,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dot\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Dot}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dtd\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -332,6 +362,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dtd\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Document Type Definition}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.editorconfig\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -339,6 +370,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.editorconfig\OpenWit Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Editor Config}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.edn\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -346,6 +378,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.edn\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Extensible Data Notation}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyaml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -353,6 +386,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyaml\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Hiera Eyaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -360,6 +394,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyml\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Hiera Eyaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -367,6 +402,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fs\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F#}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsi\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -374,6 +410,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsi\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F# Signature}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsscript\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -381,6 +418,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsscript\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F# Script}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -388,6 +426,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F# Script}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gemspec\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -395,6 +434,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gemspec\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Gemspec}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\ruby.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gitattributes\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -403,6 +443,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitat Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gitconfig\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -411,6 +452,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitco Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gitignore\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -419,6 +461,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitig Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.go\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -426,6 +469,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.go\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Go}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\go.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -433,6 +477,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\c.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.handlebars\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -440,6 +485,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.handlebars\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Handlebars}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hbs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -447,6 +493,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hbs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Handlebars}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h++\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -461,6 +508,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hh\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hpp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -468,6 +516,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hpp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.htm\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -475,6 +524,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.htm\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,HTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.html\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -482,6 +532,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.html\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,HTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hxx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -489,6 +540,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hxx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ini\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -496,6 +548,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ini\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,INI}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jade\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -503,6 +556,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jade\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Jade}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\jade.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jav\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -510,6 +564,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jav\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Java}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\java.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.java\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -517,6 +572,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.java\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Java}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\java.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.js\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -524,6 +580,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.js\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -531,6 +588,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\react.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jscsrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -538,6 +596,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jscsrc\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JSCS RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshintrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -545,6 +604,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshintrc\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JSHint RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshtm\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -552,6 +612,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshtm\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript HTML Template}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.json\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -559,6 +620,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.json\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JSON}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\json.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -566,6 +628,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Java Server Pages}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.less\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -573,6 +636,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.less\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,LESS}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\less.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.lua\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -580,6 +644,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.lua\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Lua}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.m\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -587,6 +652,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.m\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Objective C}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.makefile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -594,6 +660,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.makefile\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Makefile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.markdown\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -601,6 +668,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.markdown\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.md\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -608,6 +676,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.md\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdoc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -615,6 +684,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdoc\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,MDoc}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdown\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -622,6 +692,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdown\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtext\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -629,6 +700,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtext\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtxt\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -636,6 +708,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtxt\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdwn\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -643,6 +716,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdwn\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkd\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -650,6 +724,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkd\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkdn\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -657,6 +732,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkdn\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -664,6 +740,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ml\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,OCaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mli\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -671,6 +748,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mli\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,OCaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mjs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -678,6 +756,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mjs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.npmignore\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -686,6 +765,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmig Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.php\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -693,6 +773,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.php\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PHP}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\php.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.phtml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -700,6 +781,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.phtml\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PHP HTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -707,6 +789,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl6\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -714,6 +797,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl6\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl 6}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -721,6 +805,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl Module}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm6\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -728,6 +813,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm6\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl 6 Module}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pod\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -735,6 +821,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pod\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl POD}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -742,6 +829,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pp\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.profile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -749,6 +837,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.profile\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Profile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.properties\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -756,6 +845,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.properties\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Properties}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ps1\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -763,6 +853,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ps1\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PowerShell}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\powershell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psd1\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -770,6 +861,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psd1\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PowerShell Module Manifest}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\powershell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psgi\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -777,6 +869,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psgi\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl CGI}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psm1\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -784,6 +877,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psm1\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PowerShell Module}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\powershell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.py\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -791,6 +885,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.py\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Python}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\python.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.r\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -798,6 +893,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.r\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,R}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rb\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -805,6 +901,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rb\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Ruby}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\ruby.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rhistory\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -812,6 +909,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rhistory\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,R History}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rprofile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -819,6 +917,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rprofile\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,R Profile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -826,6 +925,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rs\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Rust}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rt\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -833,6 +933,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rt\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Rich Text}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.scss\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -840,6 +941,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.scss\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Sass}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\sass.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sh\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -847,6 +949,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sh\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SH}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.shtml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -854,6 +957,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.shtml\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SHTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -861,6 +965,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sql\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SQL}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\sql.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svg\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -868,6 +973,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svg\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SVG}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svgz\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -875,6 +981,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svgz\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SVGZ}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.t\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -882,6 +989,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.t\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tex\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -889,6 +997,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tex\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,LaTeX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ts\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -896,6 +1005,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ts\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,TypeScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\typescript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tsx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -903,6 +1013,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tsx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,TypeScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\react.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.txt\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -910,6 +1021,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.txt\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Text}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vb\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -917,6 +1029,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vb\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Visual Basic}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vue\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -924,6 +1037,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vue\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,VUE}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\vue.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxi\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -931,6 +1045,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxi\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,WiX Include}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxl\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -938,6 +1053,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxl\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,WiX Localization}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -945,6 +1061,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,WiX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xaml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -952,6 +1069,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xaml\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,XAML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -959,6 +1077,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xml\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,XML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yaml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -966,6 +1085,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yaml\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Yaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -973,6 +1093,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yml\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Yaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.zsh\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -980,14 +1101,17 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.zsh\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ZSH}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,{#NameLong}}"; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico" +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\shell\open"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe"; ValueType: none; ValueName: ""; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico" +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\*\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "{cm:OpenWithCodeContextMenu,{#ShellNameShort}}"; Tasks: addcontextmenufiles; Flags: uninsdeletekey diff --git a/build/yarn.lock b/build/yarn.lock index afb6ff0b7..17c5c201d 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2,21 +2,22 @@ # yarn lockfile v1 -"@azure/cosmos@^3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@azure/cosmos/-/cosmos-3.4.0.tgz#96f36a4522be23e1389d0516ea4d77e5fc153221" - integrity sha512-4ym+ezk7qBe4s7/tb6IJ5kmXE4xgEbAPbraT3382oeCRlYpGrblIZIDoWbthMCJfLyLBDX5T05Fhm18QeY1R/w== +"@azure/cosmos@^3.9.3": + version "3.9.3" + resolved "https://registry.yarnpkg.com/@azure/cosmos/-/cosmos-3.9.3.tgz#7e95ff92e5c3e9da7e8316bc50c9cc928be6c1d6" + integrity sha512-1mh8a6LAIykz24tJvQpafXiABUfq+HSAZBFJVZXea0Rd0qG8Ia9z8AK9FtPbC1nPvDC2RID2mRIjJvYbxRM/BA== dependencies: "@types/debug" "^4.1.4" debug "^4.1.1" fast-json-stable-stringify "^2.0.0" + jsbi "^3.1.3" node-abort-controller "^1.0.4" node-fetch "^2.6.0" - os-name "^3.1.0" priorityqueuejs "^1.0.0" semaphore "^1.0.5" - tslib "^1.9.3" - uuid "^3.3.2" + tslib "^2.0.0" + universal-user-agent "^6.0.0" + uuid "^8.3.0" "@dsherret/to-absolute-glob@^2.0.2": version "2.0.2" @@ -926,17 +927,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -1004,13 +994,20 @@ debug@2.X, debug@^2.6.8: dependencies: ms "2.0.0" -debug@4, debug@^4.1.1: +debug@4: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: ms "^2.1.1" +debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1241,19 +1238,6 @@ event-stream@3.3.4: stream-combiner "~0.0.4" through "~2.3.1" -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" @@ -1324,9 +1308,9 @@ fast-glob@^3.0.3: micromatch "^4.0.2" fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fastq@^1.6.0: version "1.6.0" @@ -1441,13 +1425,6 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -1922,11 +1899,6 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -1978,11 +1950,6 @@ isbinaryfile@^3.0.2: dependencies: buffer-alloc "^1.2.0" -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" @@ -1993,6 +1960,11 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +jsbi@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.1.4.tgz#9654dd02207a66a4911b4e4bb74265bc2cbc9dd0" + integrity sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg== + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -2216,11 +2188,6 @@ lodash@^4.15.0, lodash@^4.17.10: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== -macos-release@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" - integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA== - make-error-cause@^1.1.1: version "1.2.2" resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" @@ -2338,7 +2305,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -2366,20 +2333,15 @@ mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - node-abort-controller@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-1.0.4.tgz#4095e41d58b2fae169d2f9892904d603e11c7a39" - integrity sha512-7cNtLKTAg0LrW3ViS2C7UfIzbL3rZd8L0++5MidbKqQVJ8yrH6+1VRSHl33P0ZjBTbOJd37d9EYekvHyKkB0QQ== + version "1.1.0" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-1.1.0.tgz#8a734a631b022af29963be7245c1483cbb9e070d" + integrity sha512-dEYmUqjtbivotqjraOe8UvhT/poFfog1BQRNsZm/MSEDDESk2cQ1tvD8kGyuN07TM/zoW+n42odL8zTeJupYdQ== node-fetch@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" @@ -2395,13 +2357,6 @@ now-and-later@^2.0.0: dependencies: once "^1.3.2" -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - nth-check@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -2476,14 +2431,6 @@ os-homedir@^1.0.0: resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-name@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801" - integrity sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg== - dependencies: - macos-release "^2.2.0" - windows-release "^3.1.0" - os-tmpdir@^1.0.0, os-tmpdir@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -2497,11 +2444,6 @@ osenv@^0.1.3: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -2555,11 +2497,6 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -2662,14 +2599,6 @@ pump@^2.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - pumpify@^1.3.5: version "1.5.1" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" @@ -2971,11 +2900,6 @@ semver@^5.1.0, semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== -semver@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -2986,23 +2910,6 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -3175,11 +3082,6 @@ strip-bom@2.X: dependencies: is-utf8 "^0.2.0" -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -3307,10 +3209,10 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslib@^1.9.3: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== tsutils@^3.17.1: version "3.17.1" @@ -3349,10 +3251,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^4.1.0-dev.20201018: - version "4.1.0-dev.20201018" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20201018.tgz#1a4b8e3f9b640218a44299773371354d75bcfa34" - integrity sha512-cOFYP1I+IrMWa6ZfefxcacZha1pQMxrq8DGMBLkvrl8k3CqIdD8APq9LXaMj/PWrB8IPgDprY6jHwqiHg0/oGA== +typescript@^4.2.0-dev.20201119: + version "4.2.0-dev.20201119" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.0-dev.20201119.tgz#d4a43511cd9931adac05e1a47b6425f6b0e76cc3" + integrity sha512-HIgv+D/0VpVYRTbcVVf9oac/0GtLKMqaufTcPgohNaFWlCOh4lq8syefANgENXTG5Q4VEC6xwDGzHW6EJAVr3A== typical@^4.0.0: version "4.0.0" @@ -3395,6 +3297,11 @@ unique-stream@^2.0.2: json-stable-stringify-without-jsonify "^1.0.1" through2-filter "^3.0.0" +universal-user-agent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -3433,9 +3340,14 @@ uuid@^3.1.0: integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA== uuid@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" - integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.0: + version "8.3.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" + integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== validator@~3.35.0: version "3.35.0" @@ -3589,20 +3501,6 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -windows-release@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f" - integrity sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA== - dependencies: - execa "^1.0.0" - wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" diff --git a/cglicenses.json b/cglicenses.json index 56f92bef6..a0131e4f9 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -389,5 +389,35 @@ "", "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ] + }, + { + // Reason: The license at https://github.com/jquery/esprima/blob/master/LICENSE.BSD + // cannot be found by the OSS tool automatically. + "name": "esprima", + "fullLicenseText": [ + "BSD 2-Clause \"Simplified\" License", + "Copyright JS Foundation and other contributors, https://js.foundation/", + "", + "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:", + " * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.", + " * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.", + "", + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ] + }, + { + // Reason: The license at https://github.com/LinusU/load-yaml-file/blob/master/readme.md + // cannot be found by the OSS tool automatically. + "name": "load-yaml-file", + "fullLicenseText": [ + "MIT License", + "Copyright (C) 2012-2018 by various contributors (see AUTHORS)", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] } -] \ No newline at end of file +] diff --git a/cgmanifest.json b/cgmanifest.json index bd95a0d80..656e3f821 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "0b5f24002b4f18adee112ed39fe269aa51f6705c" + "commitHash": "415c1f9e9b35d9599b1a8ad1200476afa47a3323" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "9.3.3" + "version": "9.3.5" }, { "component": { diff --git a/extensions/configuration-editing/.vscodeignore b/extensions/configuration-editing/.vscodeignore index de8e6dc59..c5234c346 100644 --- a/extensions/configuration-editing/.vscodeignore +++ b/extensions/configuration-editing/.vscodeignore @@ -5,3 +5,5 @@ out/** extension.webpack.config.js extension-browser.webpack.config.js yarn.lock +build/** +schemas/*.schema.src.json diff --git a/extensions/configuration-editing/build/inline-allOf.ts b/extensions/configuration-editing/build/inline-allOf.ts new file mode 100755 index 000000000..7da64fddd --- /dev/null +++ b/extensions/configuration-editing/build/inline-allOf.ts @@ -0,0 +1,103 @@ +#!/usr/bin/env ts-node + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Inlines "allOf"s to allow for "additionalProperties": false. (https://github.com/microsoft/vscode-remote-release/issues/2967) +// Run this manually after updating devContainer.schema.src.json. + +import * as fs from 'fs'; + +function transform(schema: any) { + + const definitions = Object.keys(schema.definitions) + .reduce((d, k) => { + d[`#/definitions/${k}`] = (schema.definitions as any)[k]; + return d; + }, {} as any); + + function copy(from: any) { + const type = Array.isArray(from) ? 'array' : typeof from; + switch (type) { + case 'object': { + const to: any = {}; + for (const key in from) { + switch (key) { + case 'definitions': + break; + case 'oneOf': + const list = copy(from[key]) + .reduce((a: any[], o: any) => { + if (o.oneOf) { + a.push(...o.oneOf); + } else { + a.push(o); + } + return a; + }, [] as any[]); + if (list.length === 1) { + Object.assign(to, list[0]); + } else { + to.oneOf = list; + } + break; + case 'allOf': + const all = copy(from[key]); + const leaves = all.map((one: any) => (one.oneOf ? one.oneOf : [one])); + function cross(res: any, leaves: any[][]): any[] { + if (leaves.length) { + const rest = leaves.slice(1); + return ([] as any[]).concat(...leaves[0].map(leaf => { + const intermediate = { ...res, ...leaf }; + if ('properties' in res && 'properties' in leaf) { + intermediate.properties = { + ...res.properties, + ...leaf.properties, + }; + } + return cross(intermediate, rest); + })); + } + return [res]; + } + const list2 = cross({}, leaves); + if (list2.length === 1) { + Object.assign(to, list2[0]); + } else { + to.oneOf = list2; + } + break; + case '$ref': + const ref = from[key]; + const definition = definitions[ref]; + if (definition) { + Object.assign(to, copy(definition)); + } else { + to[key] = ref; + } + break; + default: + to[key] = copy(from[key]); + break; + } + } + if (to.type === 'object' && !('additionalProperties' in to)) { + to.additionalProperties = false; + } + return to; + } + case 'array': { + return from.map(copy); + } + default: + return from; + } + } + + return copy(schema); +} + +const devContainer = JSON.parse(fs.readFileSync('../schemas/devContainer.schema.src.json', 'utf8')); +fs.writeFileSync('../schemas/devContainer.schema.generated.json', JSON.stringify(transform(devContainer), undefined, ' ')); diff --git a/extensions/configuration-editing/build/tsconfig.json b/extensions/configuration-editing/build/tsconfig.json new file mode 100644 index 000000000..0ac2f0ded --- /dev/null +++ b/extensions/configuration-editing/build/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../shared.tsconfig.json", + "compilerOptions": { + "resolveJsonModule": true, + "outDir": "./out" + } +} diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index 3afe9fa43..423400413 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -111,11 +111,11 @@ }, { "fileMatch": "/.devcontainer/devcontainer.json", - "url": "./schemas/devContainer.schema.json" + "url": "./schemas/devContainer.schema.generated.json" }, { "fileMatch": "/.devcontainer.json", - "url": "./schemas/devContainer.schema.json" + "url": "./schemas/devContainer.schema.generated.json" }, { "fileMatch": "%APP_SETTINGS_HOME%/globalStorage/ms-vscode-remote.remote-containers/nameConfigs/*.json", diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index b408c46f4..b3b865333 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -31,7 +31,7 @@ "null" ] }, - "description": "Remote environment variables." + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." }, "remoteUser": { "type": "string", diff --git a/extensions/configuration-editing/schemas/devContainer.schema.generated.json b/extensions/configuration-editing/schemas/devContainer.schema.generated.json new file mode 100644 index 000000000..8cdf40090 --- /dev/null +++ b/extensions/configuration-editing/schemas/devContainer.schema.generated.json @@ -0,0 +1,832 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Defines a dev container", + "allowComments": true, + "allowTrailingCommas": true, + "oneOf": [ + { + "type": "object", + "properties": { + "build": { + "type": "object", + "description": "Docker build-related options.", + "properties": { + "dockerfile": { + "type": "string", + "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." + }, + "context": { + "type": "string", + "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." + }, + "target": { + "type": "string", + "description": "Target stage in a multi-stage build." + }, + "args": { + "type": "object", + "additionalProperties": { + "type": [ + "string" + ] + }, + "description": "Build arguments." + } + }, + "required": [ + "dockerfile" + ], + "additionalProperties": false + }, + "appPort": { + "type": [ + "integer", + "string", + "array" + ], + "description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".", + "items": { + "type": [ + "integer", + "string" + ] + } + }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "updateRemoteUserUID": { + "type": "boolean", + "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "type": "string" + } + }, + "runArgs": { + "type": "array", + "description": "The arguments required when starting in the container.", + "items": { + "type": "string" + } + }, + "shutdownAction": { + "type": "string", + "enum": [ + "none", + "stopContainer" + ], + "description": "Action to take when the VS Code window is closed. The default is to stop the container." + }, + "overrideCommand": { + "type": "boolean", + "description": "Whether to overwrite the command specified in the image. The default is true." + }, + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container." + }, + "workspaceMount": { + "type": "string", + "description": "The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project." + }, + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration." + } + }, + "required": [ + "build" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "dockerFile": { + "type": "string", + "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." + }, + "context": { + "type": "string", + "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." + }, + "build": { + "description": "Docker build-related options.", + "type": "object", + "properties": { + "target": { + "type": "string", + "description": "Target stage in a multi-stage build." + }, + "args": { + "type": "object", + "additionalProperties": { + "type": [ + "string" + ] + }, + "description": "Build arguments." + } + }, + "additionalProperties": false + }, + "appPort": { + "type": [ + "integer", + "string", + "array" + ], + "description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".", + "items": { + "type": [ + "integer", + "string" + ] + } + }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "updateRemoteUserUID": { + "type": "boolean", + "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "type": "string" + } + }, + "runArgs": { + "type": "array", + "description": "The arguments required when starting in the container.", + "items": { + "type": "string" + } + }, + "shutdownAction": { + "type": "string", + "enum": [ + "none", + "stopContainer" + ], + "description": "Action to take when the VS Code window is closed. The default is to stop the container." + }, + "overrideCommand": { + "type": "boolean", + "description": "Whether to overwrite the command specified in the image. The default is true." + }, + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container." + }, + "workspaceMount": { + "type": "string", + "description": "The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project." + }, + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration." + } + }, + "required": [ + "dockerFile" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "image": { + "type": "string", + "description": "The docker image that will be used to create the container." + }, + "appPort": { + "type": [ + "integer", + "string", + "array" + ], + "description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".", + "items": { + "type": [ + "integer", + "string" + ] + } + }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "updateRemoteUserUID": { + "type": "boolean", + "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "type": "string" + } + }, + "runArgs": { + "type": "array", + "description": "The arguments required when starting in the container.", + "items": { + "type": "string" + } + }, + "shutdownAction": { + "type": "string", + "enum": [ + "none", + "stopContainer" + ], + "description": "Action to take when the VS Code window is closed. The default is to stop the container." + }, + "overrideCommand": { + "type": "boolean", + "description": "Whether to overwrite the command specified in the image. The default is true." + }, + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container." + }, + "workspaceMount": { + "type": "string", + "description": "The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project." + }, + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration." + } + }, + "required": [ + "image" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "dockerComposeFile": { + "type": [ + "string", + "array" + ], + "description": "The name of the docker-compose file(s) used to start the services.", + "items": { + "type": "string" + } + }, + "service": { + "type": "string", + "description": "The service you want to work on." + }, + "runServices": { + "type": "array", + "description": "An array of services that should be started and stopped.", + "items": { + "type": "string" + } + }, + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container. This is typically the target path of a volume mount in the docker-compose.yml." + }, + "shutdownAction": { + "type": "string", + "enum": [ + "none", + "stopCompose" + ], + "description": "Action to take when the VS Code window is closed. The default is to stop the containers." + }, + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration." + } + }, + "required": [ + "dockerComposeFile", + "service", + "workspaceFolder" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration." + } + }, + "additionalProperties": false + } + ] +} \ No newline at end of file diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.src.json similarity index 97% rename from extensions/configuration-editing/schemas/devContainer.schema.json rename to extensions/configuration-editing/schemas/devContainer.schema.src.json index 4719d53e6..ba140ba94 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.src.json @@ -3,7 +3,6 @@ "description": "Defines a dev container", "allowComments": true, "allowTrailingCommas": true, - "type": "object", "definitions": { "devContainerCommon": { "type": "object", @@ -42,7 +41,7 @@ "null" ] }, - "description": "Remote environment variables." + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." }, "remoteUser": { "type": "string", @@ -104,6 +103,7 @@ }, "codespaces": { "type": "object", + "additionalProperties": true, "description": "Codespaces-specific configuration." } } diff --git a/extensions/cpp/build/update-grammars.js b/extensions/cpp/build/update-grammars.js index e6293eb2e..28b4ca923 100644 --- a/extensions/cpp/build/update-grammars.js +++ b/extensions/cpp/build/update-grammars.js @@ -6,9 +6,9 @@ var updateGrammar = require('../../../build/npm/update-grammar'); -updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/c.tmLanguage.json', './syntaxes/c.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); -updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/cpp.tmLanguage.json', './syntaxes/cpp.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); -updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/cpp.embedded.macro.tmLanguage.json', './syntaxes/cpp.embedded.macro.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', 'syntaxes/c.tmLanguage.json', './syntaxes/c.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', 'syntaxes/cpp.tmLanguage.json', './syntaxes/cpp.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', 'syntaxes/cpp.embedded.macro.tmLanguage.json', './syntaxes/cpp.embedded.macro.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); // `source.c.platform` which is still included by other grammars updateGrammar.update('textmate/c.tmbundle', 'Syntaxes/Platform.tmLanguage', './syntaxes/platform.tmLanguage.json'); diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index 070312a63..05938de60 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/cpp-textmate-grammar", "repositoryUrl": "https://github.com/jeff-hykin/cpp-textmate-grammar", - "commitHash": "666808cab3907fc91ed4d3901060ee6b045cca58" + "commitHash": "ad9f1541fd376740c30a7257614c9cb5ed25005d" } }, "license": "MIT", - "version": "1.14.15", + "version": "1.15.3", "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/cpp/syntaxes/c.tmLanguage.json b/extensions/cpp/syntaxes/c.tmLanguage.json index eef07eeb5..952730f5a 100644 --- a/extensions/cpp/syntaxes/c.tmLanguage.json +++ b/extensions/cpp/syntaxes/c.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master//syntaxes/c.tmLanguage.json", + "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master/syntaxes/c.tmLanguage.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/72b309aabb63bf14a3cdf0280149121db005d616", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/afa42b3f5529833714ae354fbc638ece8f253074", "name": "C", "scopeName": "source.c", "patterns": [ @@ -364,7 +364,7 @@ { "name": "meta.function.c", "begin": "(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:\\n|$)", + "captures": { "1": { - "name": "meta.encoding.c" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "2": { - "name": "punctuation.definition.string.begin.assembly.c" - } - }, - "end": "(\")", - "endCaptures": { - "1": { - "name": "punctuation.definition.string.end.assembly.c" - } - }, - "patterns": [ - { - "include": "source.asm" + "name": "comment.block.c punctuation.definition.comment.begin.c" }, - { - "include": "source.x86" + "3": { + "name": "comment.block.c" }, - { - "include": "source.x86_64" - }, - { - "include": "source.arm" - }, - { - "include": "#backslash_escapes" - }, - { - "include": "#string_escaped_char" - }, - { - "match": "(?=not)possible" + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.c punctuation.definition.comment.end.c" + }, + { + "match": "\\*", + "name": "comment.block.c" + } + ] } - ] - }, - { - "begin": "(\\()", - "beginCaptures": { - "1": { - "name": "punctuation.section.parens.begin.bracket.round.assembly.inner.c" - } - }, - "end": "(\\))", - "endCaptures": { - "1": { - "name": "punctuation.section.parens.end.bracket.round.assembly.inner.c" - } - }, - "patterns": [ - { - "include": "#evaluation_context" - } - ] - }, - { - "match": ":", - "name": "punctuation.separator.delimiter.colon.assembly.c" + } }, { "include": "#comments_context" }, { "include": "#comments" + }, + { + "begin": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\()", + "beginCaptures": { + "1": { + "name": "punctuation.section.parens.begin.bracket.round.assembly.c" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.c punctuation.definition.comment.begin.c" + }, + "4": { + "name": "comment.block.c" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.c punctuation.definition.comment.end.c" + }, + { + "match": "\\*", + "name": "comment.block.c" + } + ] + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.parens.end.bracket.round.assembly.c" + } + }, + "patterns": [ + { + "name": "string.quoted.double.c", + "contentName": "meta.embedded.assembly.c", + "begin": "(R?)(\")", + "beginCaptures": { + "1": { + "name": "meta.encoding.c" + }, + "2": { + "name": "punctuation.definition.string.begin.assembly.c" + } + }, + "end": "(\")", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.assembly.c" + } + }, + "patterns": [ + { + "include": "source.asm" + }, + { + "include": "source.x86" + }, + { + "include": "source.x86_64" + }, + { + "include": "source.arm" + }, + { + "include": "#backslash_escapes" + }, + { + "include": "#string_escaped_char" + } + ] + }, + { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.section.parens.begin.bracket.round.assembly.inner.c" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.parens.end.bracket.round.assembly.inner.c" + } + }, + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + { + "match": "\\[((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))([a-zA-Z_]\\w*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\]", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.c punctuation.definition.comment.begin.c" + }, + "3": { + "name": "comment.block.c" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.c punctuation.definition.comment.end.c" + }, + { + "match": "\\*", + "name": "comment.block.c" + } + ] + }, + "5": { + "name": "variable.other.asm.label.c" + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.c punctuation.definition.comment.begin.c" + }, + "8": { + "name": "comment.block.c" + }, + "9": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.c punctuation.definition.comment.end.c" + }, + { + "match": "\\*", + "name": "comment.block.c" + } + ] + } + } + }, + { + "match": ":", + "name": "punctuation.separator.delimiter.colon.assembly.c" + }, + { + "include": "#comments_context" + }, + { + "include": "#comments" + } + ] } ] } @@ -3194,4 +3342,4 @@ "name": "punctuation.vararg-ellipses.c" } } -} +} \ No newline at end of file diff --git a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json index 4c0e9a582..151cad6fe 100644 --- a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json +++ b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master//syntaxes/cpp.embedded.macro.tmLanguage.json", + "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master/syntaxes/cpp.embedded.macro.tmLanguage.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/666808cab3907fc91ed4d3901060ee6b045cca58", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/ad9f1541fd376740c30a7257614c9cb5ed25005d", "name": "C++", "scopeName": "source.cpp.embedded.macro", "patterns": [ @@ -27,13 +27,13 @@ "include": "#using_namespace" }, { - "include": "#type_alias" + "include": "source.cpp#type_alias" }, { - "include": "#using_name" + "include": "source.cpp#using_name" }, { - "include": "#namespace_alias" + "include": "source.cpp#namespace_alias" }, { "include": "#namespace_block" @@ -51,10 +51,10 @@ "include": "#typedef_union" }, { - "include": "#typedef_keyword" + "include": "source.cpp#misc_keywords" }, { - "include": "#standard_declares" + "include": "source.cpp#standard_declares" }, { "include": "#class_block" @@ -69,13 +69,13 @@ "include": "#enum_block" }, { - "include": "#template_isolated_definition" + "include": "source.cpp#template_isolated_definition" }, { "include": "#template_definition" }, { - "include": "#access_control_keywords" + "include": "source.cpp#access_control_keywords" }, { "include": "#block" @@ -94,56 +94,20 @@ } ], "repository": { - "access_control_keywords": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?:protected|private|public))\\s*(:))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "storage.type.modifier.access.control.$6.cpp" - }, - "7": { - "name": "punctuation.separator.colon.access.control.cpp" - } - } - }, "alignas_attribute": { - "name": "support.other.attribute.cpp", - "begin": "(alignas\\()", + "begin": "alignas\\(", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\n)|$)", + "captures": { "1": { - "name": "meta.encoding.cpp" + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] }, "2": { - "name": "punctuation.definition.string.begin.assembly.cpp" - } - }, - "end": "(\")|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\(", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]", + "captures": { + "1": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "5": { + "name": "variable.other.asm.label.cpp" + }, + "6": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "8": { + "name": "comment.block.cpp" + }, + "9": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": ":", + "name": "punctuation.separator.delimiter.colon.assembly.cpp" + }, + { + "include": "#comments_context" + }, + { + "include": "#comments" + } + ] } ] }, - "assignment_operator": { - "match": "\\=", - "name": "keyword.operator.assignment.cpp" - }, "attributes_context": { "patterns": [ { @@ -402,24 +489,20 @@ } ] }, - "backslash_escapes": { - "match": "(?x)\\\\ (\n\\\\\t\t\t |\n[abefnprtv'\"?] |\n[0-3]\\d{,2}\t |\n[4-7]\\d?\t\t|\nx[a-fA-F0-9]{,2} |\nu[a-fA-F0-9]{,4} |\nU[a-fA-F0-9]{,8} )", - "name": "constant.character.escape.cpp" - }, "block": { - "name": "meta.block.cpp", - "begin": "({)", + "begin": "{", + "end": "}|(?=\\s*#\\s*(?:elif|else|endif)\\b)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "(?:\\s)*+(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.class.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -768,134 +804,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "source.cpp#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", - "captures": { - "1": { - "name": "storage.type.class.declare.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.class.cpp" - }, - "7": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "8": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "10": { - "name": "comment.block.cpp" - }, - "11": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "name": "variable.other.object.declare.cpp" - }, - "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "comma": { - "match": ",", - "name": "punctuation.separator.delimiter.comma.cpp" - }, - "comma_in_template_argument": { - "match": ",", - "name": "punctuation.separator.delimiter.comma.template.argument.cpp" - }, "comments": { "patterns": [ { - "name": "comment.line.double-slash.documentation.cpp", - "begin": "(?:^)(?>\\s*)(\\/\\/[!\\/]+)", + "begin": "^(?:(?:\\s)+)?+(\\/\\/[!\\/]+)", + "end": "(?<=\\n)(?|%|\"|\\.|=|::|\\||\\-\\-|\\-\\-\\-)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1186,7 +939,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@]b)\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@]b)(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1197,7 +950,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1216,22 +969,30 @@ "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@]param)\\s+(\\b\\w+\\b)", + "match": "((?<=[\\s*!\\/])[\\\\@]param)(?:\\s*\\[((?:,?(?:(?:\\s)+)?(?:in|out)(?:(?:\\s)+)?)+)\\])?(?:\\s)+(\\b\\w+\\b)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" }, "2": { + "patterns": [ + { + "match": "in|out", + "name": "keyword.other.parameter.direction.$0.cpp" + } + ] + }, + "3": { "name": "variable.parameter.cpp" } } }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|throws|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|uml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|startuml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { @@ -1253,7 +1014,7 @@ "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1264,7 +1025,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@]b)\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@]b)(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1275,7 +1036,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1294,22 +1055,30 @@ "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@]param)\\s+(\\b\\w+\\b)", + "match": "((?<=[\\s*!\\/])[\\\\@]param)(?:\\s*\\[((?:,?(?:(?:\\s)+)?(?:in|out)(?:(?:\\s)+)?)+)\\])?(?:\\s)+(\\b\\w+\\b)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" }, "2": { + "patterns": [ + { + "match": "in|out", + "name": "keyword.other.parameter.direction.$0.cpp" + } + ] + }, + "3": { "name": "variable.parameter.cpp" } } }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|throws|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|uml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|startuml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { @@ -1325,26 +1094,26 @@ "name": "comment.block.documentation.cpp" }, { - "name": "comment.block.documentation.cpp", - "begin": "((?>\\s*)\\/\\*[!*]+(?:(?:\\n|$)|(?=\\s)))", + "begin": "(?:(?:\\s)+)?+\\/\\*[!*]+(?:(?:(?:\\n)|$)|(?=\\s))", + "end": "[!*]*\\*\\/|(?=(?|%|\"|\\.|=|::|\\||\\-\\-|\\-\\-\\-)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1355,7 +1124,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@]b)\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@]b)(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1366,7 +1135,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1385,22 +1154,30 @@ "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@]param)\\s+(\\b\\w+\\b)", + "match": "((?<=[\\s*!\\/])[\\\\@]param)(?:\\s*\\[((?:,?(?:(?:\\s)+)?(?:in|out)(?:(?:\\s)+)?)+)\\])?(?:\\s)+(\\b\\w+\\b)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" }, "2": { + "patterns": [ + { + "match": "in|out", + "name": "keyword.other.parameter.direction.$0.cpp" + } + ] + }, + "3": { "name": "variable.parameter.cpp" } } }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|throws|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|uml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|startuml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { @@ -1410,7 +1187,7 @@ ] }, { - "include": "#emacs_file_banner" + "include": "source.cpp#emacs_file_banner" }, { "include": "#block_comment" @@ -1419,31 +1196,31 @@ "include": "#line_comment" }, { - "include": "#invalid_comment_end" + "include": "source.cpp#invalid_comment_end" } ] }, "constructor_inline": { - "name": "meta.function.definition.special.constructor.cpp", - "begin": "(^((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:constexpr)|(?:explicit)|(?:mutable)|(?:virtual)|(?:inline)|(?:friend))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" - } - } - } - ] - }, - { - "include": "#functional_specifiers_pre_parameters" - }, - { - "begin": "(:)", - "beginCaptures": { + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" + } + } + }, + { + "include": "source.cpp#functional_specifiers_pre_parameters" + }, + { + "begin": ":", + "end": "(?=\\{)|(?=(?(?:(?>[^<>]*)\\g<3>?)+)>)\\s*)?(\\()", + "begin": "((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" - } - } - } - ] - }, - { - "include": "#functional_specifiers_pre_parameters" - }, - { - "begin": "(:)", - "beginCaptures": { + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" + } + } + }, + { + "include": "source.cpp#functional_specifiers_pre_parameters" + }, + { + "begin": ":", + "end": "(?=\\{)|(?=(?(?:(?>[^<>]*)\\g<3>?)+)>)\\s*)?(\\()", + "begin": "((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "end": "\\}|(?=(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(~(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:constexpr)|(?:explicit)|(?:mutable)|(?:virtual)|(?:inline)|(?:friend))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(~(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { - "contentName": "meta.function.definition.parameters.special.member.destructor.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))~\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { - "contentName": "meta.function.definition.parameters.special.member.destructor.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:error|warning)))\\b\\s*", + "begin": "(^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:error|warning)))\\b(?:(?:\\s)+)?", + "end": "(?[#;\\/=*C~]+)(?![#;\\/=*C~]))\\s*.+\\s*\\8\\s*(?:\\n|$)))|(^\\s*((\\/\\*)\\s*?((?>[#;\\/=*C~]+)(?![#;\\/=*C~]))\\s*.+\\s*\\8\\s*\\*\\/)))", - "captures": { - "1": { - "name": "meta.toc-list.banner.double-slash.cpp" - }, - "2": { - "name": "comment.line.double-slash.cpp" - }, - "3": { - "name": "punctuation.definition.comment.cpp" - }, - "4": { - "name": "meta.banner.character.cpp" - }, - "5": { - "name": "meta.toc-list.banner.block.cpp" - }, - "6": { - "name": "comment.line.banner.cpp" - }, - "7": { - "name": "punctuation.definition.comment.cpp" - }, - "8": { - "name": "meta.banner.character.cpp" - } - } - }, - "empty_square_brackets": { - "name": "storage.modifier.array.bracket.square.cpp", - "match": "(?-mix:(?-mix:(?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?(::))?\\s*((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", - "captures": { - "1": { - "name": "storage.type.enum.declare.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.enum.cpp" - }, - "7": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "8": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "10": { - "name": "comment.block.cpp" - }, - "11": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "name": "variable.other.object.declare.cpp" - }, - "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "enumerator_list": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(extern)(?=\\s*\\\"))", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(extern)(?=\\s*\\\")", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)|(?=(?))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\())", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))", + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", "captures": { "1": { "name": "storage.modifier.$1.cpp" @@ -4002,7 +3521,7 @@ "2": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4029,12 +3548,12 @@ ] }, "12": { - "name": "storage.modifier.$1.cpp" + "name": "storage.modifier.$12.cpp" }, "13": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4060,32 +3579,50 @@ "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4264,45 +3782,95 @@ } ] }, - "45": { + "36": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "46": { + "37": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "47": { + "38": { "name": "comment.block.cpp" }, + "39": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "40": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "41": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "42": { + "name": "comment.block.cpp" + }, + "43": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "44": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "45": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "46": { + "name": "comment.block.cpp" + }, + "47": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "48": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "49": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "50": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "51": { + "50": { "name": "comment.block.cpp" }, - "52": { + "51": { "patterns": [ { "match": "\\*\\/", @@ -4314,10 +3882,13 @@ } ] }, + "52": { + "name": "storage.type.modifier.calling-convention.cpp" + }, "53": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4342,35 +3913,28 @@ "57": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#scope_resolution_function_definition_inner_generated" } ] }, "58": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" }, "59": { - "name": "comment.block.cpp" - }, - "60": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#template_call_range" } ] }, + "60": {}, "61": { - "name": "storage.type.modifier.calling-convention.cpp" + "name": "entity.name.function.definition.cpp" }, "62": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4391,83 +3955,39 @@ "name": "comment.block.cpp" } ] - }, - "66": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_inner_generated" - } - ] - }, - "67": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" - }, - "69": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "71": { - "name": "entity.name.function.definition.cpp" - }, - "72": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "73": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "74": { - "name": "comment.block.cpp" - }, - "75": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "captures": { + "1": { + "name": "punctuation.definition.function.return-type.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "meta.qualified_type.cpp", + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + { + "match": "(?|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4739,111 +4459,110 @@ } ] }, - "29": { + "20": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "variable.other.definition.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -5091,111 +4810,110 @@ } ] }, - "29": { + "20": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "variable.parameter.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)", - "captures": { - "1": { - "name": "keyword.control.goto.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.label.call.cpp" - } - } - }, - "include": { - "match": "(?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((#)\\s*((?:include|include_next))\\b)\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "keyword.control.directive.$7.cpp" - }, - "6": { - "name": "punctuation.definition.directive.cpp" - }, - "8": { - "name": "string.quoted.other.lt-gt.include.cpp" - }, - "9": { - "name": "punctuation.definition.string.begin.cpp" - }, - "10": { - "name": "punctuation.definition.string.end.cpp" - }, - "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "15": { - "name": "string.quoted.double.include.cpp" - }, - "16": { - "name": "punctuation.definition.string.begin.cpp" - }, - "17": { - "name": "punctuation.definition.string.end.cpp" - }, - "18": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "19": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "20": { - "name": "comment.block.cpp" - }, - "21": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "22": { - "name": "entity.name.other.preprocessor.macro.include.cpp" - }, - "23": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "24": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "25": { - "name": "comment.block.cpp" - }, - "26": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "27": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "28": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "29": { - "name": "comment.block.cpp" - }, - "30": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "31": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "32": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "33": { - "name": "comment.block.cpp" - }, - "34": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "meta.preprocessor.include.cpp" - }, "inheritance_context": { "patterns": [ { @@ -5527,7 +5024,7 @@ "name": "punctuation.separator.delimiter.comma.inheritance.cpp" }, { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "5": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] }, "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "7": { "patterns": [ { - "include": "#inline_comment" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "13": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "15": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "17": { - "name": "entity.name.scope-resolution.cpp" - }, - "18": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "20": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "25": { - "name": "entity.name.type.cpp" - }, - "26": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } + "12": {} } } ] }, - "inline_comment": { - "match": "(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/))", - "captures": { - "1": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "2": { - "name": "comment.block.cpp" - }, - "3": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "invalid_comment_end": { - "match": "\\*\\/", - "name": "invalid.illegal.unexpected.punctuation.definition.comment.end.cpp" - }, - "label": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "entity.name.label.cpp" - }, - "6": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "7": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "8": { - "name": "comment.block.cpp" - }, - "9": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "10": { - "name": "punctuation.separator.label.cpp" - } - } - }, "lambdas": { - "begin": "((?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))\\s*(\\[(?!\\[| *+\"| *+\\d))((?>(?:[^\\[\\]]|((?(?:(?>[^\\[\\]]*)\\g<4>?)+)\\]))*))(\\](?!\\[)))", + "begin": "(?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))(?:(?:\\s)+)?(\\[(?!\\[| *+\"| *+\\d))((?:[^\\[\\]]|((??)++\\]))*+)(\\](?!((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))[\\[\\];]))", + "end": "(?<=[;}])|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", + "match": "((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", "captures": { "1": { "name": "variable.parameter.capture.cpp" @@ -5815,7 +5251,7 @@ "2": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -5850,26 +5286,52 @@ } ] }, - "5": { + "3": {}, + "4": { "name": "punctuation.definition.capture.end.lambda.cpp" + }, + "5": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "6": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "7": { + "name": "comment.block.cpp" + }, + "8": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] } }, - "end": "(?<=})|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*line\\b)", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?line\\b", + "end": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*define\\b)\\s*((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?define\\b)(?:(?:\\s)+)?((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!uint_least64_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|double[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|float[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|short[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|char[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|void[^(?-mix:\\w)]|long[^(?-mix:\\w)]|auto[^(?-mix:\\w)]|int[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "variable.language.this.cpp" - }, - "6": { - "name": "variable.other.object.access.cpp" - }, - "7": { - "name": "punctuation.separator.dot-access.cpp" - }, - "8": { - "name": "punctuation.separator.pointer-access.cpp" - }, - "9": { - "patterns": [ - { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "variable.language.this.cpp" - }, - "6": { - "name": "variable.other.object.property.cpp" - }, - "7": { - "name": "punctuation.separator.dot-access.cpp" - }, - "8": { - "name": "punctuation.separator.pointer-access.cpp" - } - } - }, - { - "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "variable.language.this.cpp" - }, - "6": { - "name": "variable.other.object.access.cpp" - }, - "7": { - "name": "punctuation.separator.dot-access.cpp" - }, - "8": { - "name": "punctuation.separator.pointer-access.cpp" - } - } - }, - { - "include": "#member_access" - }, - { - "include": "#method_access" - } - ] - }, - "10": { - "name": "variable.other.property.cpp" - } - } - }, - "memory_operators": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:(delete)\\s*(\\[\\])|(delete))|(new))(?!\\w))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "keyword.operator.wordlike.cpp" - }, - "6": { - "name": "keyword.operator.delete.array.cpp" - }, - "7": { - "name": "keyword.operator.delete.array.bracket.cpp" - }, - "8": { - "name": "keyword.operator.delete.cpp" - }, - "9": { - "name": "keyword.operator.new.cpp" - } - } - }, "method_access": { - "begin": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\()", + "begin": "(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?(\\()", + "end": "\\)|(?=(?|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))(?:(?:\\s)+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -6350,12 +5615,12 @@ } }, { - "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -6392,7 +5657,7 @@ } }, { - "include": "#member_access" + "include": "source.cpp#member_access" }, { "include": "#method_access" @@ -6406,9 +5671,8 @@ "name": "punctuation.section.arguments.begin.bracket.round.function.member.cpp" } }, - "end": "(\\))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((import))\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))\\s*(;?)", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "keyword.control.directive.import.cpp" - }, - "7": { - "name": "string.quoted.other.lt-gt.include.cpp" - }, - "8": { - "name": "punctuation.definition.string.begin.cpp" - }, - "9": { - "name": "punctuation.definition.string.end.cpp" - }, - "10": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "11": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "12": { - "name": "comment.block.cpp" - }, - "13": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "14": { - "name": "string.quoted.double.include.cpp" - }, - "15": { - "name": "punctuation.definition.string.begin.cpp" - }, - "16": { - "name": "punctuation.definition.string.end.cpp" - }, - "17": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "18": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "19": { - "name": "comment.block.cpp" - }, - "20": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "21": { - "name": "entity.name.other.preprocessor.macro.include.cpp" - }, - "22": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "23": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "24": { - "name": "comment.block.cpp" - }, - "25": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "26": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "27": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "28": { - "name": "comment.block.cpp" - }, - "29": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { - "name": "punctuation.terminator.statement.cpp" - } - }, - "name": "meta.preprocessor.import.cpp" - }, "ms_attributes": { - "name": "support.other.attribute.cpp", - "begin": "(__declspec\\()", + "begin": "__declspec\\(", + "end": "\\)|(?=(?(?:(?>[^<>]*)\\g<9>?)+)>)\\s*)?::)*\\s*+)\\s*((?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?>[^<>]*)\\g<5>?)+)>)\\s*)?::)*\\s*+)\\s*((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(operator)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(?:(?:((?:delete\\[\\]|delete|new\\[\\]|<=>|<<=|new|>>=|\\->\\*|\\/=|%=|&=|>=|\\|=|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|<<|>>|\\-\\-|<=|\\^=|==|!=|&&|\\|\\||\\+=|\\-=|\\*=|,|\\+|\\-|!|~|\\*|&|\\*|\\/|%|\\+|\\-|<|>|&|\\^|\\||=))|((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:\\[\\])?)))|(\"\")((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\<|\\())", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -7366,20 +6103,20 @@ } ] }, - "30": { + "20": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "31": { + "21": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "32": { + "22": { "name": "comment.block.cpp" }, - "33": { + "23": { "patterns": [ { "match": "\\*\\/", @@ -7391,135 +6128,135 @@ } ] }, - "34": { + "24": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "35": { + "25": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "36": { + "26": { "name": "comment.block.cpp" }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "30": { + "name": "comment.block.cpp" + }, + "31": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "32": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "33": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "34": { + "name": "comment.block.cpp" + }, + "35": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "36": { + "name": "storage.type.modifier.calling-convention.cpp" + }, "37": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "38": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "39": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "40": { + "39": { "name": "comment.block.cpp" }, + "40": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "41": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "42": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "43": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "44": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "45": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "46": { - "name": "storage.type.modifier.calling-convention.cpp" - }, - "47": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "48": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "49": { - "name": "comment.block.cpp" - }, - "50": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "51": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "52": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "53": { - "name": "comment.block.cpp" - }, - "54": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "55": { "patterns": [ { "match": "::", @@ -7534,31 +6271,31 @@ } ] }, - "57": { - "name": "meta.template.call.cpp", + "46": { "patterns": [ { "include": "#template_call_range" } ] }, - "59": { + "47": {}, + "48": { "name": "keyword.other.operator.overload.cpp" }, - "60": { + "49": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "61": { + "50": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "62": { + "51": { "name": "comment.block.cpp" }, - "63": { + "52": { "patterns": [ { "match": "\\*\\/", @@ -7570,7 +6307,7 @@ } ] }, - "64": { + "53": { "patterns": [ { "match": "::", @@ -7585,33 +6322,33 @@ } ] }, - "66": { - "name": "meta.template.call.cpp", + "54": { "patterns": [ { "include": "#template_call_range" } ] }, - "68": { + "55": {}, + "56": { "name": "entity.name.operator.cpp" }, - "69": { + "57": { "name": "entity.name.operator.type.cpp" }, - "70": { + "58": { "patterns": [ { "match": "\\*", "name": "entity.name.operator.type.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -7642,20 +6379,20 @@ } ] }, - "71": { + "59": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "72": { + "60": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "73": { + "61": { "name": "comment.block.cpp" }, - "74": { + "62": { "patterns": [ { "match": "\\*\\/", @@ -7667,104 +6404,104 @@ } ] }, - "75": { + "63": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "76": { + "64": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "77": { + "65": { "name": "comment.block.cpp" }, + "66": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "67": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "68": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "69": { + "name": "comment.block.cpp" + }, + "70": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "71": { + "name": "entity.name.operator.type.array.cpp" + }, + "72": { + "name": "entity.name.operator.custom-literal.cpp" + }, + "73": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "74": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "75": { + "name": "comment.block.cpp" + }, + "76": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "77": { + "name": "entity.name.operator.custom-literal.cpp" + }, "78": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "79": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "80": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "81": { - "name": "comment.block.cpp" - }, - "82": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "83": { - "name": "entity.name.operator.type.array.cpp" - }, - "84": { - "name": "entity.name.operator.custom-literal.cpp" - }, - "85": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "86": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "87": { - "name": "comment.block.cpp" - }, - "88": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "89": { - "name": "entity.name.operator.custom-literal.cpp" - }, - "90": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "91": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "92": { - "name": "comment.block.cpp" - }, - "93": { "patterns": [ { "match": "\\*\\/", @@ -7777,17 +6514,19 @@ ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\w)", + "end": "(?:(?=\\))|(,))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", + "match": "((?:((?:(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -7998,7 +6994,7 @@ "3": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8023,7 +7019,7 @@ "7": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8046,159 +7042,34 @@ ] }, "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "15": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "16": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "17": { - "name": "comment.block.cpp" - }, - "18": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "19": { "name": "storage.type.primitive.cpp storage.type.built-in.primitive.cpp" }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { + "12": { "name": "storage.type.cpp storage.type.built-in.cpp" }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { + "13": { "name": "support.type.posix-reserved.pthread.cpp support.type.built-in.posix-reserved.pthread.cpp" }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { + "14": { "name": "support.type.posix-reserved.cpp support.type.built-in.posix-reserved.cpp" }, - "35": { + "15": { "name": "entity.name.type.parameter.cpp" }, - "36": { + "16": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "37": { + "17": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "38": { + "18": { "name": "comment.block.cpp" }, - "39": { + "19": { "patterns": [ { "match": "\\*\\/", @@ -8216,15 +7087,16 @@ "include": "#storage_types" }, { - "include": "#scope_resolution_parameter_inner_generated" + "include": "source.cpp#scope_resolution_parameter_inner_generated" }, { - "match": "(?:struct|class|union|enum)", + "match": "(?:(?:struct)|(?:class)|(?:union)|(?:enum))", "name": "storage.type.$0.cpp" }, { "begin": "(?<==)", "end": "(?:(?=\\))|(,))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\)|,|\\[|=|\\n)", + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\)|,|\\[|=|\\n)", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8273,7 +7146,7 @@ "6": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8301,19 +7174,19 @@ "include": "#attributes_context" }, { - "name": "meta.bracket.square.array.cpp", - "begin": "(\\[)", + "begin": "\\[", + "end": "\\]|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", + "match": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*))", "captures": { "0": { "patterns": [ @@ -8337,12 +7210,12 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8376,7 +7249,7 @@ "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8401,7 +7274,7 @@ "5": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8427,528 +7300,14 @@ } ] }, - "parameter_class": { - "match": "(class)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", - "captures": { - "1": { - "name": "storage.type.class.parameter.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.class.parameter.cpp" - }, - "7": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "11": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "parameter_enum": { - "match": "(enum)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", - "captures": { - "1": { - "name": "storage.type.enum.parameter.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.enum.parameter.cpp" - }, - "7": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "11": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, "parameter_or_maybe_value": { - "name": "meta.parameter.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\w)", + "end": "(?:(?=\\))|(,))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", + "match": "((?:((?:(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -9015,7 +7374,7 @@ "3": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9040,7 +7399,7 @@ "7": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9063,159 +7422,34 @@ ] }, "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "15": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "16": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "17": { - "name": "comment.block.cpp" - }, - "18": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "19": { "name": "storage.type.primitive.cpp storage.type.built-in.primitive.cpp" }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { + "12": { "name": "storage.type.cpp storage.type.built-in.cpp" }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { + "13": { "name": "support.type.posix-reserved.pthread.cpp support.type.built-in.posix-reserved.pthread.cpp" }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { + "14": { "name": "support.type.posix-reserved.cpp support.type.built-in.posix-reserved.cpp" }, - "35": { + "15": { "name": "entity.name.type.parameter.cpp" }, - "36": { + "16": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "37": { + "17": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "38": { + "18": { "name": "comment.block.cpp" }, - "39": { + "19": { "patterns": [ { "match": "\\*\\/", @@ -9236,15 +7470,16 @@ "include": "#function_call" }, { - "include": "#scope_resolution_parameter_inner_generated" + "include": "source.cpp#scope_resolution_parameter_inner_generated" }, { - "match": "(?:struct|class|union|enum)", + "match": "(?:(?:struct)|(?:class)|(?:union)|(?:enum))", "name": "storage.type.$0.cpp" }, { "begin": "(?<==)", "end": "(?:(?=\\))|(,))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=(?:\\)|,|\\[|=|\\/\\/|(?:\\n|$)))", + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=(?:\\)|,|\\[|=|\\/\\/|(?:(?:\\n)|$)))", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9290,7 +7525,7 @@ "6": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9318,19 +7553,19 @@ "include": "#attributes_context" }, { - "name": "meta.bracket.square.array.cpp", - "begin": "(\\[)", + "begin": "\\[", + "end": "\\]|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", + "match": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*))", "captures": { "0": { "patterns": [ @@ -9354,12 +7589,12 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9393,7 +7628,7 @@ "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9418,7 +7653,7 @@ "5": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9447,537 +7682,23 @@ } ] }, - "parameter_struct": { - "match": "(struct)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", - "captures": { - "1": { - "name": "storage.type.struct.parameter.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.struct.parameter.cpp" - }, - "7": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "11": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "parameter_union": { - "match": "(union)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", - "captures": { - "1": { - "name": "storage.type.union.parameter.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.union.parameter.cpp" - }, - "7": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "11": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, "parentheses": { - "name": "meta.parens.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\b)", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma\\b", + "end": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\s+mark)\\s+(.*)", - "captures": { - "1": { - "name": "keyword.control.directive.pragma.pragma-mark.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "punctuation.definition.directive.cpp" - }, - "7": { - "name": "entity.name.tag.pragma-mark.cpp" - } - }, - "name": "meta.preprocessor.pragma.cpp" - }, - "predefined_macros": { - "patterns": [ - { - "match": "\\b(__cplusplus|__DATE__|__FILE__|__LINE__|__STDC__|__STDC_HOSTED__|__STDC_NO_COMPLEX__|__STDC_VERSION__|__STDCPP_THREADS__|__TIME__|NDEBUG|__OBJC__|__ASSEMBLER__|__ATOM__|__AVX__|__AVX2__|_CHAR_UNSIGNED|__CLR_VER|_CONTROL_FLOW_GUARD|__COUNTER__|__cplusplus_cli|__cplusplus_winrt|_CPPRTTI|_CPPUNWIND|_DEBUG|_DLL|__FUNCDNAME__|__FUNCSIG__|__FUNCTION__|_INTEGRAL_MAX_BITS|__INTELLISENSE__|_ISO_VOLATILE|_KERNEL_MODE|_M_AMD64|_M_ARM|_M_ARM_ARMV7VE|_M_ARM_FP|_M_ARM64|_M_CEE|_M_CEE_PURE|_M_CEE_SAFE|_M_FP_EXCEPT|_M_FP_FAST|_M_FP_PRECISE|_M_FP_STRICT|_M_IX86|_M_IX86_FP|_M_X64|_MANAGED|_MSC_BUILD|_MSC_EXTENSIONS|_MSC_FULL_VER|_MSC_VER|_MSVC_LANG|__MSVC_RUNTIME_CHECKS|_MT|_NATIVE_WCHAR_T_DEFINED|_OPENMP|_PREFAST|__TIMESTAMP__|_VC_NO_DEFAULTLIB|_WCHAR_T_DEFINED|_WIN32|_WIN64|_WINRT_DLL|_ATL_VER|_MFC_VER|__GFORTRAN__|__GNUC__|__GNUC_MINOR__|__GNUC_PATCHLEVEL__|__GNUG__|__STRICT_ANSI__|__BASE_FILE__|__INCLUDE_LEVEL__|__ELF__|__VERSION__|__OPTIMIZE__|__OPTIMIZE_SIZE__|__NO_INLINE__|__GNUC_STDC_INLINE__|__CHAR_UNSIGNED__|__WCHAR_UNSIGNED__|__REGISTER_PREFIX__|__REGISTER_PREFIX__|__SIZE_TYPE__|__PTRDIFF_TYPE__|__WCHAR_TYPE__|__WINT_TYPE__|__INTMAX_TYPE__|__UINTMAX_TYPE__|__SIG_ATOMIC_TYPE__|__INT8_TYPE__|__INT16_TYPE__|__INT32_TYPE__|__INT64_TYPE__|__UINT8_TYPE__|__UINT16_TYPE__|__UINT32_TYPE__|__UINT64_TYPE__|__INT_LEAST8_TYPE__|__INT_LEAST16_TYPE__|__INT_LEAST32_TYPE__|__INT_LEAST64_TYPE__|__UINT_LEAST8_TYPE__|__UINT_LEAST16_TYPE__|__UINT_LEAST32_TYPE__|__UINT_LEAST64_TYPE__|__INT_FAST8_TYPE__|__INT_FAST16_TYPE__|__INT_FAST32_TYPE__|__INT_FAST64_TYPE__|__UINT_FAST8_TYPE__|__UINT_FAST16_TYPE__|__UINT_FAST32_TYPE__|__UINT_FAST64_TYPE__|__INTPTR_TYPE__|__UINTPTR_TYPE__|__CHAR_BIT__|__SCHAR_MAX__|__WCHAR_MAX__|__SHRT_MAX__|__INT_MAX__|__LONG_MAX__|__LONG_LONG_MAX__|__WINT_MAX__|__SIZE_MAX__|__PTRDIFF_MAX__|__INTMAX_MAX__|__UINTMAX_MAX__|__SIG_ATOMIC_MAX__|__INT8_MAX__|__INT16_MAX__|__INT32_MAX__|__INT64_MAX__|__UINT8_MAX__|__UINT16_MAX__|__UINT32_MAX__|__UINT64_MAX__|__INT_LEAST8_MAX__|__INT_LEAST16_MAX__|__INT_LEAST32_MAX__|__INT_LEAST64_MAX__|__UINT_LEAST8_MAX__|__UINT_LEAST16_MAX__|__UINT_LEAST32_MAX__|__UINT_LEAST64_MAX__|__INT_FAST8_MAX__|__INT_FAST16_MAX__|__INT_FAST32_MAX__|__INT_FAST64_MAX__|__UINT_FAST8_MAX__|__UINT_FAST16_MAX__|__UINT_FAST32_MAX__|__UINT_FAST64_MAX__|__INTPTR_MAX__|__UINTPTR_MAX__|__WCHAR_MIN__|__WINT_MIN__|__SIG_ATOMIC_MIN__|__SCHAR_WIDTH__|__SHRT_WIDTH__|__INT_WIDTH__|__LONG_WIDTH__|__LONG_LONG_WIDTH__|__PTRDIFF_WIDTH__|__SIG_ATOMIC_WIDTH__|__SIZE_WIDTH__|__WCHAR_WIDTH__|__WINT_WIDTH__|__INT_LEAST8_WIDTH__|__INT_LEAST16_WIDTH__|__INT_LEAST32_WIDTH__|__INT_LEAST64_WIDTH__|__INT_FAST8_WIDTH__|__INT_FAST16_WIDTH__|__INT_FAST32_WIDTH__|__INT_FAST64_WIDTH__|__INTPTR_WIDTH__|__INTMAX_WIDTH__|__SIZEOF_INT__|__SIZEOF_LONG__|__SIZEOF_LONG_LONG__|__SIZEOF_SHORT__|__SIZEOF_POINTER__|__SIZEOF_FLOAT__|__SIZEOF_DOUBLE__|__SIZEOF_LONG_DOUBLE__|__SIZEOF_SIZE_T__|__SIZEOF_WCHAR_T__|__SIZEOF_WINT_T__|__SIZEOF_PTRDIFF_T__|__BYTE_ORDER__|__ORDER_LITTLE_ENDIAN__|__ORDER_BIG_ENDIAN__|__ORDER_PDP_ENDIAN__|__FLOAT_WORD_ORDER__|__DEPRECATED|__EXCEPTIONS|__GXX_RTTI|__USING_SJLJ_EXCEPTIONS__|__GXX_EXPERIMENTAL_CXX0X__|__GXX_WEAK__|__NEXT_RUNTIME__|__LP64__|_LP64|__SSP__|__SSP_ALL__|__SSP_STRONG__|__SSP_EXPLICIT__|__SANITIZE_ADDRESS__|__SANITIZE_THREAD__|__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1|__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2|__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4|__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8|__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16|__HAVE_SPECULATION_SAFE_VALUE|__GCC_HAVE_DWARF2_CFI_ASM|__FP_FAST_FMA|__FP_FAST_FMAF|__FP_FAST_FMAL|__FP_FAST_FMAF16|__FP_FAST_FMAF32|__FP_FAST_FMAF64|__FP_FAST_FMAF128|__FP_FAST_FMAF32X|__FP_FAST_FMAF64X|__FP_FAST_FMAF128X|__GCC_IEC_559|__GCC_IEC_559_COMPLEX|__NO_MATH_ERRNO__|__has_builtin|__has_feature|__has_extension|__has_cpp_attribute|__has_c_attribute|__has_attribute|__has_declspec_attribute|__is_identifier|__has_include|__has_include_next|__has_warning|__BASE_FILE__|__FILE_NAME__|__clang__|__clang_major__|__clang_minor__|__clang_patchlevel__|__clang_version__|__fp16|_Float16)\\b", - "captures": { - "1": { - "name": "entity.name.other.preprocessor.macro.predefined.$1.cpp" - } - } - }, - { - "match": "\\b__([A-Z_]+)__\\b", - "name": "entity.name.other.preprocessor.macro.predefined.probably.$1.cpp" + "include": "source.cpp#line_continuation_character" } ] }, @@ -10142,30 +7775,31 @@ "include": "#comments" }, { - "include": "#language_constants" + "include": "source.cpp#language_constants" }, { - "include": "#string_context_c" + "include": "#string_context" }, { - "include": "#preprocessor_number_literal" + "include": "source.cpp#d9bc4796b0b_preprocessor_number_literal" }, { "include": "#operators" }, { - "include": "#predefined_macros" + "include": "source.cpp#predefined_macros" }, { - "include": "#macro_name" + "include": "source.cpp#macro_name" }, { - "include": "#line_continuation_character" + "include": "source.cpp#line_continuation_character" } ] }, "preprocessor_conditional_defined": { "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:(?:ifndef|ifdef)|if)))", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:(?:ifndef|ifdef)|if))", + "end": "^(?!\\s*+#\\s*(?:else|endif))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(?![\\w<:.])", - "captures": { - "0": { - "name": "meta.qualified_type.cpp", - "patterns": [ - { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_function_call": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_function_call_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_function_call_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_function_call_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.function.call.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" - } - } - }, - "scope_resolution_function_definition": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_function_definition_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.function.definition.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" - } - } - }, - "scope_resolution_function_definition_operator_overload": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_operator_overload_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_function_definition_operator_overload_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_operator_overload_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.function.definition.operator-overload.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" - } - } - }, - "scope_resolution_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - } - } - }, - "scope_resolution_namespace_alias": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_namespace_alias_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_namespace_alias_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_namespace_alias_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.namespace.alias.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" - } - } - }, - "scope_resolution_namespace_block": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_namespace_block_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_namespace_block_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_namespace_block_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.namespace.block.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" - } - } - }, - "scope_resolution_namespace_using": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_namespace_using_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_namespace_using_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_namespace_using_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.namespace.using.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" - } - } - }, - "scope_resolution_parameter": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_parameter_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_parameter_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_parameter_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.parameter.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" - } - } - }, - "scope_resolution_template_call": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_template_call_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_template_call_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_template_call_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.template.call.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" - } - } - }, - "scope_resolution_template_definition": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_template_definition_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_template_definition_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_template_definition_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.template.definition.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" - } - } - }, - "semicolon": { - "match": ";", - "name": "punctuation.terminator.statement.cpp" - }, - "simple_type": { - "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?", - "captures": { - "1": { - "name": "meta.qualified_type.cpp", - "patterns": [ - { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "single_line_macro": { - "match": "^((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))#define.*(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "(\\bsizeof\\.\\.\\.)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.struct.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -12252,134 +8531,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "source.cpp#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", - "captures": { - "1": { - "name": "storage.type.struct.declare.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.struct.cpp" - }, - "7": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "8": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "10": { - "name": "comment.block.cpp" - }, - "11": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "name": "variable.other.object.declare.cpp" - }, - "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, "switch_conditional_parentheses": { - "name": "meta.conditional.switch.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?>[^<>]*)\\g<1>?)+)>)\\s*", - "captures": { - "0": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, "template_call_range": { - "name": "meta.template.call.cpp", - "begin": "(<)", + "begin": "<", + "end": ">|(?=(?)|(?=(?|(?=(?)|(?=(?|(?=(?)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*(?:(,)|(?=>|$))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "storage.type.template.argument.$5.cpp" - }, - "6": { - "patterns": [ - { - "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", - "name": "storage.type.template.argument.$0.cpp" - } - ] - }, - "7": { - "name": "entity.name.type.template.cpp" - }, - "8": { - "name": "storage.type.template.cpp" - }, - "9": { - "name": "punctuation.vararg-ellipses.template.definition.cpp" - }, - "10": { - "name": "entity.name.type.template.cpp" - }, - "11": { - "name": "punctuation.separator.delimiter.comma.template.argument.cpp" - } - } - }, "template_definition_context": { "patterns": [ { - "include": "#scope_resolution_template_definition_inner_generated" + "include": "source.cpp#scope_resolution_template_definition_inner_generated" }, { - "include": "#template_definition_argument" + "include": "source.cpp#template_definition_argument" }, { - "include": "#template_argument_defaulted" + "include": "source.cpp#template_argument_defaulted" }, { - "include": "#template_call_innards" + "include": "source.cpp#template_call_innards" }, { "include": "#evaluation_context" } ] }, - "template_isolated_definition": { - "match": "(?\\s*$)", - "captures": { - "1": { - "name": "storage.type.template.cpp" - }, - "2": { - "name": "punctuation.section.angle-brackets.start.template.definition.cpp" - }, - "3": { - "name": "meta.template.definition.cpp", - "patterns": [ - { - "include": "#template_definition_context" - } - ] - }, - "4": { - "name": "punctuation.section.angle-brackets.end.template.definition.cpp" - } - } - }, "ternary_operator": { - "applyEndPatternLast": true, - "begin": "(\\?)", + "begin": "\\?", + "end": ":|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))\\s*(\\=)\\s*((?:typename)?)\\s*((?:(?:(?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)(?:(?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))|(.*(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:(\\[)(\\w*)(\\])\\s*)?\\s*(?:(;)|\\n)", - "captures": { - "1": { - "name": "keyword.other.using.directive.cpp" - }, - "2": { - "name": "meta.qualified_type.cpp", - "patterns": [ - { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "61": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "62": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "63": { - "name": "comment.block.cpp" - }, - "64": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "65": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "66": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "67": { - "name": "comment.block.cpp" - }, - "68": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "69": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "70": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "71": { - "name": "comment.block.cpp" - }, - "72": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "73": { - "name": "punctuation.definition.begin.bracket.square.cpp" - }, - "74": { - "patterns": [ - { - "include": "#evaluation_context" - } - ] - }, - "75": { - "name": "punctuation.definition.end.bracket.square.cpp" - }, - "76": { - "name": "punctuation.terminator.statement.cpp" - } - }, - "name": "meta.declaration.type.alias.cpp" - }, - "type_casting_operators": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.class.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -13759,134 +9200,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "source.cpp#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -13998,7 +9345,7 @@ "2": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14023,7 +9370,7 @@ "6": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14048,7 +9395,7 @@ "10": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14085,47 +9432,67 @@ ] }, "typedef_function_pointer": { - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14304,111 +9652,110 @@ } ] }, - "29": { + "20": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "entity.name.type.alias.cpp entity.name.type.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.struct.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -14587,134 +10005,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "source.cpp#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14826,7 +10150,7 @@ "2": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14851,7 +10175,7 @@ "6": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14876,7 +10200,7 @@ "10": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14913,101 +10237,205 @@ ] }, "typedef_union": { - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.union.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -15019,134 +10447,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "source.cpp#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15258,7 +10592,7 @@ "2": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15283,7 +10617,7 @@ "6": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15308,7 +10642,7 @@ "10": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15345,8 +10679,8 @@ ] }, "typeid_operator": { - "contentName": "meta.arguments.operator.typeid.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(?![\\w<:.]))", - "captures": { + "union_block": { + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } }, { - "include": "#attributes_context" + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.union.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } }, { - "include": "#function_type" - }, - { - "include": "#storage_types" - }, - { - "include": "#number_literal" - }, - { - "include": "#string_context" - }, - { - "include": "#comma" - }, - { - "include": "#scope_resolution_inner_generated" - }, - { - "include": "#template_call_range" + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", - "name": "entity.name.type.cpp" - } - ] - }, - "11": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" } ] }, "12": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15523,7 +10930,7 @@ "16": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15545,367 +10952,10 @@ } ] }, - "21": { - "patterns": [ - { - "include": "#scope_resolution_inner_generated" - } - ] - }, - "22": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "24": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "26": { - "name": "entity.name.scope-resolution.cpp" - }, - "27": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "29": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { - "name": "entity.name.type.cpp" - }, - "35": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "undef": { - "match": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*undef\\b)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", - "beginCaptures": { - "1": { - "name": "meta.head.union.cpp" - }, - "3": { - "name": "storage.type.$3.cpp" - }, - "4": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "5": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "6": { - "name": "comment.block.cpp" - }, - "7": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "11": { - "name": "comment.block.cpp" - }, - "12": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "16": { - "name": "comment.block.cpp" - }, - "17": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "18": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "19": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", - "captures": { - "1": { - "name": "storage.type.union.declare.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.union.cpp" - }, - "7": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "8": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "10": { - "name": "comment.block.cpp" - }, - "11": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "name": "variable.other.object.declare.cpp" - }, - "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "using_name": { - "match": "(using)\\s+(?!namespace\\b)", - "captures": { - "1": { - "name": "keyword.other.using.directive.cpp" - } - } - }, "using_namespace": { - "name": "meta.using-namespace.cpp", - "begin": "(?(?:(?>[^<>]*)\\g<7>?)+)>)\\s*)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?:protected|private|public))\\s*(:))", + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?:(?:protected)|(?:private)|(?:public)))(?:(?:\\s)+)?(:))", "captures": { "1": { "patterns": [ @@ -105,45 +105,55 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "storage.type.modifier.access.control.$6.cpp" + "3": { + "name": "storage.type.modifier.access.control.$4.cpp" }, - "7": { + "4": {}, + "5": { "name": "punctuation.separator.colon.access.control.cpp" } } }, "alignas_attribute": { - "name": "support.other.attribute.cpp", - "begin": "(alignas\\()", + "begin": "alignas\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.begin.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.end.cpp" } }, + "name": "support.other.attribute.cpp", "patterns": [ { "include": "#attributes_context" @@ -151,6 +161,8 @@ { "begin": "\\(", "end": "\\)", + "beginCaptures": {}, + "endCaptures": {}, "patterns": [ { "include": "#attributes_context" @@ -161,7 +173,7 @@ ] }, { - "match": "(using)\\s+((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.alignas.cpp" @@ -228,12 +240,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.alignas.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.alignas.cpp" } }, + "contentName": "meta.arguments.operator.alignas", "patterns": [ { "include": "#evaluation_context" @@ -241,8 +253,8 @@ ] }, "alignof_operator": { - "contentName": "meta.arguments.operator.alignof.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.alignof.cpp" @@ -276,12 +288,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.alignof.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.alignof.cpp" } }, + "contentName": "meta.arguments.operator.alignof", "patterns": [ { "include": "#evaluation_context" @@ -289,96 +301,221 @@ ] }, "assembly": { - "name": "meta.asm.cpp", - "begin": "(\\b(?:__asm__|asm)\\b)\\s*((?:volatile)?)\\s*(\\()", + "begin": "(\\b(?:__asm__|asm)\\b)(?:(?:\\s)+)?((?:volatile)?)", + "end": "(?!\\G)", "beginCaptures": { "1": { "name": "storage.type.asm.cpp" }, "2": { "name": "storage.modifier.cpp" - }, - "3": { - "name": "punctuation.section.parens.begin.bracket.round.assembly.cpp" - } - }, - "end": "(\\))", - "endCaptures": { - "1": { - "name": "punctuation.section.parens.end.bracket.round.assembly.cpp" } }, + "endCaptures": {}, + "name": "meta.asm.cpp", "patterns": [ { - "name": "string.quoted.double.cpp", - "contentName": "meta.embedded.assembly.cpp", - "begin": "(R?)(\")", - "beginCaptures": { + "match": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\n)|$)", + "captures": { "1": { - "name": "meta.encoding.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "2": { - "name": "punctuation.definition.string.begin.assembly.cpp" - } - }, - "end": "(\")", - "endCaptures": { - "1": { - "name": "punctuation.definition.string.end.assembly.cpp" - } - }, - "patterns": [ - { - "include": "source.asm" + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - { - "include": "source.x86" + "3": { + "name": "comment.block.cpp" }, - { - "include": "source.x86_64" - }, - { - "include": "source.arm" - }, - { - "include": "#backslash_escapes" - }, - { - "include": "#string_escaped_char" - }, - { - "match": "(?=not)possible" + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] } - ] - }, - { - "begin": "(\\()", - "beginCaptures": { - "1": { - "name": "punctuation.section.parens.begin.bracket.round.assembly.inner.cpp" - } - }, - "end": "(\\))", - "endCaptures": { - "1": { - "name": "punctuation.section.parens.end.bracket.round.assembly.inner.cpp" - } - }, - "patterns": [ - { - "include": "#evaluation_context" - } - ] - }, - { - "match": ":", - "name": "punctuation.separator.delimiter.colon.assembly.cpp" + } }, { "include": "#comments_context" }, { "include": "#comments" + }, + { + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\(", + "end": "\\)", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.assembly.cpp" + }, + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.assembly.cpp" + } + }, + "patterns": [ + { + "begin": "(R?)(\")", + "end": "\"", + "beginCaptures": { + "1": { + "name": "meta.encoding.cpp" + }, + "2": { + "name": "punctuation.definition.string.begin.assembly.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.assembly.cpp" + } + }, + "name": "string.quoted.double.cpp", + "contentName": "meta.embedded.assembly", + "patterns": [ + { + "include": "source.asm" + }, + { + "include": "source.x86" + }, + { + "include": "source.x86_64" + }, + { + "include": "source.arm" + }, + { + "include": "#backslash_escapes" + }, + { + "include": "#string_escaped_char" + } + ] + }, + { + "begin": "\\(", + "end": "\\)", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.assembly.inner.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.assembly.inner.cpp" + } + }, + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + { + "match": "\\[((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "5": { + "name": "variable.other.asm.label.cpp" + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "8": { + "name": "comment.block.cpp" + }, + "9": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": ":", + "name": "punctuation.separator.delimiter.colon.assembly.cpp" + }, + { + "include": "#comments_context" + }, + { + "include": "#comments" + } + ] } ] }, @@ -403,23 +540,23 @@ ] }, "backslash_escapes": { - "match": "(?x)\\\\ (\n\\\\\t\t\t |\n[abefnprtv'\"?] |\n[0-3]\\d{,2}\t |\n[4-7]\\d?\t\t|\nx[a-fA-F0-9]{,2} |\nu[a-fA-F0-9]{,4} |\nU[a-fA-F0-9]{,8} )", - "name": "constant.character.escape.cpp" + "match": "(?x)\\\\ (\n\\\\\t\t\t |\n[abefnprtv'\"?] |\n[0-3][0-7]{,2}\t |\n[4-7]\\d?\t\t|\nx[a-fA-F0-9]{,2} |\nu[a-fA-F0-9]{,4} |\nU[a-fA-F0-9]{,8} )", + "name": "constant.character.escape" }, "block": { - "name": "meta.block.cpp", - "begin": "({)", + "begin": "{", + "end": "}|(?=\\s*#\\s*(?:elif|else|endif)\\b)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.cpp" } }, - "end": "(}|(?=\\s*#\\s*(?:elif|else|endif)\\b))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.cpp" } }, + "name": "meta.block.cpp", "patterns": [ { "include": "#function_body_context" @@ -427,192 +564,42 @@ ] }, "block_comment": { - "name": "comment.block.cpp", "begin": "\\s*+(\\/\\*)", + "end": "\\*\\/", "beginCaptures": { "1": { "name": "punctuation.definition.comment.begin.cpp" } }, - "end": "(\\*\\/)", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.comment.end.cpp" } - } + }, + "name": "comment.block.cpp" }, "builtin_storage_type_initilizer": { - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "(?:\\s)*+(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.class.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.class.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -768,134 +858,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -904,16 +895,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.class.cpp", "patterns": [ { - "name": "meta.head.class.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.class.cpp" } }, + "name": "meta.head.class.cpp", "patterns": [ { "include": "#ever_present_context" @@ -927,14 +920,15 @@ ] }, { - "name": "meta.body.class.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.class.cpp" } }, + "name": "meta.body.class.cpp", "patterns": [ { "include": "#function_pointer" @@ -954,9 +948,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.class.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -966,7 +962,7 @@ ] }, "class_declare": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", "captures": { "1": { "name": "storage.type.class.declare.cpp" @@ -979,34 +975,43 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.class.cpp" }, - "7": { + "5": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -1042,6 +1047,40 @@ } ] }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "8": { "patterns": [ { @@ -1050,98 +1089,100 @@ ] }, "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "10": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "11": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { "name": "variable.other.object.declare.cpp" }, - "21": { + "13": { "patterns": [ { "include": "#inline_comment" } ] }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { + "14": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } @@ -1158,14 +1199,15 @@ "comments": { "patterns": [ { - "name": "comment.line.double-slash.documentation.cpp", - "begin": "(?:^)(?>\\s*)(\\/\\/[!\\/]+)", + "begin": "^(?:(?:\\s)+)?+(\\/\\/[!\\/]+)", + "end": "(?<=\\n)(?\\s*)\\/\\*[!*]+(?:(?:\\n|$)|(?=\\s)))", + "begin": "(?:(?:\\s)+)?+\\/\\*[!*]+(?:(?:(?:\\n)|$)|(?=\\s))", + "end": "[!*]*\\*\\/", "beginCaptures": { - "1": { + "0": { "name": "punctuation.definition.comment.begin.documentation.cpp" } }, - "end": "([!*]*\\*\\/)", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.comment.end.documentation.cpp" } }, + "name": "comment.block.documentation.cpp", "patterns": [ { "match": "(?<=[\\s*!\\/])[\\\\@](?:callergraph|callgraph|else|endif|f\\$|f\\[|f\\]|hidecallergraph|hidecallgraph|hiderefby|hiderefs|hideinitializer|htmlinclude|n|nosubgrouping|private|privatesection|protected|protectedsection|public|publicsection|pure|showinitializer|showrefby|showrefs|tableofcontents|\\$|\\#|<|>|%|\"|\\.|=|::|\\||\\-\\-|\\-\\-\\-)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1355,7 +1413,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@]b)\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@]b)(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1366,7 +1424,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1385,22 +1443,30 @@ "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@]param)\\s+(\\b\\w+\\b)", + "match": "((?<=[\\s*!\\/])[\\\\@]param)(?:\\s*\\[((?:,?(?:(?:\\s)+)?(?:in|out)(?:(?:\\s)+)?)+)\\])?(?:\\s)+(\\b\\w+\\b)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" }, "2": { + "patterns": [ + { + "match": "in|out", + "name": "keyword.other.parameter.direction.$0.cpp" + } + ] + }, + "3": { "name": "variable.parameter.cpp" } } }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|throws|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|uml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|startuml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { @@ -1424,26 +1490,26 @@ ] }, "constructor_inline": { - "name": "meta.function.definition.special.constructor.cpp", - "begin": "(^((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:constexpr)|(?:explicit)|(?:mutable)|(?:virtual)|(?:inline)|(?:friend))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.special.constructor.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -1455,52 +1521,52 @@ } ] }, + "5": { + "patterns": [ + { + "include": "#functional_specifiers_pre_parameters" + } + ] + }, "6": { "patterns": [ { - "include": "#functional_specifiers_pre_parameters" + "include": "#inline_comment" } ] }, "7": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "8": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "9": { + "8": { "name": "comment.block.cpp" }, + "9": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "10": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { "name": "comment.block.cpp" }, - "14": { + "13": { "patterns": [ { "match": "\\*\\/", @@ -1512,23 +1578,23 @@ } ] }, - "15": { + "14": { "name": "storage.type.modifier.calling-convention.cpp" }, - "16": { + "15": { "patterns": [ { "include": "#inline_comment" } ] }, - "17": { + "16": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "18": { + "17": { "name": "comment.block.cpp" }, - "19": { + "18": { "patterns": [ { "match": "\\*\\/", @@ -1540,83 +1606,82 @@ } ] }, - "20": { + "19": { "name": "entity.name.function.constructor.cpp entity.name.function.definition.special.constructor.cpp" } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.special.constructor.cpp", "patterns": [ { - "name": "meta.head.function.definition.special.constructor.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.special.constructor.cpp" } }, + "name": "meta.head.function.definition.special.constructor.cpp", "patterns": [ { "include": "#ever_present_context" }, { - "patterns": [ - { - "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { "include": "#functional_specifiers_pre_parameters" }, { - "begin": "(:)", + "begin": ":", + "end": "(?=\\{)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.separator.initializers.cpp" } }, - "end": "(?=\\{)", + "endCaptures": {}, "patterns": [ { - "contentName": "meta.parameter.initialization.cpp", - "begin": "((?(?:(?>[^<>]*)\\g<3>?)+)>)\\s*)?(\\()", + "begin": "((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "entity.name.function.call.initializer.cpp" @@ -1629,16 +1694,17 @@ } ] }, + "3": {}, "4": { "name": "punctuation.section.arguments.begin.bracket.round.function.call.initializer.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.function.call.initializer.cpp" } }, + "contentName": "meta.parameter.initialization", "patterns": [ { "include": "#evaluation_context" @@ -1646,8 +1712,8 @@ ] }, { - "contentName": "meta.parameter.initialization.cpp", "begin": "((?|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.special.constructor.cpp" } }, + "name": "meta.body.function.definition.special.constructor.cpp", "patterns": [ { "include": "#function_body_context" @@ -1720,9 +1796,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.special.constructor.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -1732,26 +1810,26 @@ ] }, "constructor_root": { - "name": "meta.function.definition.special.constructor.cpp", - "begin": "(\\s*+((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.special.constructor.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -1763,23 +1841,23 @@ } ] }, - "6": { + "5": { "name": "storage.type.modifier.calling-convention.cpp" }, - "7": { + "6": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { + "7": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "9": { + "8": { "name": "comment.block.cpp" }, - "10": { + "9": { "patterns": [ { "match": "\\*\\/", @@ -1791,7 +1869,7 @@ } ] }, - "11": { + "10": { "patterns": [ { "match": "::", @@ -1806,15 +1884,15 @@ } ] }, - "13": { - "name": "meta.template.call.cpp", + "11": { "patterns": [ { "include": "#template_call_range" } ] }, - "15": { + "12": {}, + "13": { "patterns": [ { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?=:)", @@ -1830,45 +1908,46 @@ } ] }, - "17": { + "14": {}, + "15": { "patterns": [ { "include": "#inline_comment" } ] }, + "16": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "17": { + "name": "comment.block.cpp" + }, "18": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "19": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "20": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { "name": "comment.block.cpp" }, - "24": { + "22": { "patterns": [ { "match": "\\*\\/", @@ -1880,20 +1959,20 @@ } ] }, - "25": { + "23": { "patterns": [ { "include": "#inline_comment" } ] }, - "26": { + "24": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "27": { + "25": { "name": "comment.block.cpp" }, - "28": { + "26": { "patterns": [ { "match": "\\*\\/", @@ -1906,79 +1985,78 @@ ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.special.constructor.cpp", "patterns": [ { - "name": "meta.head.function.definition.special.constructor.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.special.constructor.cpp" } }, + "name": "meta.head.function.definition.special.constructor.cpp", "patterns": [ { "include": "#ever_present_context" }, { - "patterns": [ - { - "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { "include": "#functional_specifiers_pre_parameters" }, { - "begin": "(:)", + "begin": ":", + "end": "(?=\\{)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.separator.initializers.cpp" } }, - "end": "(?=\\{)", + "endCaptures": {}, "patterns": [ { - "contentName": "meta.parameter.initialization.cpp", - "begin": "((?(?:(?>[^<>]*)\\g<3>?)+)>)\\s*)?(\\()", + "begin": "((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "entity.name.function.call.initializer.cpp" @@ -1991,16 +2069,17 @@ } ] }, + "3": {}, "4": { "name": "punctuation.section.arguments.begin.bracket.round.function.call.initializer.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.function.call.initializer.cpp" } }, + "contentName": "meta.parameter.initialization", "patterns": [ { "include": "#evaluation_context" @@ -2008,8 +2087,8 @@ ] }, { - "contentName": "meta.parameter.initialization.cpp", "begin": "((?|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.special.constructor.cpp" } }, + "name": "meta.body.function.definition.special.constructor.cpp", "patterns": [ { "include": "#function_body_context" @@ -2082,9 +2171,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.special.constructor.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -2094,7 +2185,7 @@ ] }, "control_flow_keywords": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "keyword.control.$5.cpp" + "3": { + "name": "keyword.control.$3.cpp" } } }, "cpp_attributes": { - "name": "support.other.attribute.cpp", - "begin": "(\\[\\[)", + "begin": "\\[\\[", + "end": "\\]\\]", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.begin.cpp" } }, - "end": "(\\]\\])", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.end.cpp" } }, + "name": "support.other.attribute.cpp", "patterns": [ { "include": "#attributes_context" @@ -2147,6 +2247,8 @@ { "begin": "\\(", "end": "\\)", + "beginCaptures": {}, + "endCaptures": {}, "patterns": [ { "include": "#attributes_context" @@ -2157,7 +2259,7 @@ ] }, { - "match": "(using)\\s+((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "end": "\\}", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -2289,109 +2409,90 @@ } ] }, + "11": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((import))(?:(?:\\s)+)?(?:(?:(?:((<)[^>]*(>?)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=\\/\\/)))|((\\\")[^\\\"]*((?:\\\")?)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=\\/\\/))))|(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=(?:\\/\\/|;)))))|((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=(?:\\/\\/|;))))(?:(?:\\s)+)?(;?)", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "3": { + "name": "keyword.control.directive.import.cpp" + }, + "5": { + "name": "string.quoted.other.lt-gt.include.cpp" + }, + "6": { + "name": "punctuation.definition.string.begin.cpp" + }, + "7": { + "name": "punctuation.definition.string.end.cpp" + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "name": "string.quoted.double.include.cpp" + }, + "11": { + "name": "punctuation.definition.string.begin.cpp" + }, + "12": { + "name": "punctuation.definition.string.end.cpp" + }, + "13": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "14": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "15": { + "name": "entity.name.other.preprocessor.macro.include.cpp" + }, + "16": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "17": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "18": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "19": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "20": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "21": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "22": { + "name": "punctuation.terminator.statement.cpp" + } + }, + "name": "meta.preprocessor.import.cpp" + }, + "d9bc4796b0b_preprocessor_number_literal": { + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.other.decltype.cpp storage.type.decltype.cpp" @@ -2437,12 +3030,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.decltype.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.decltype.cpp" } }, + "contentName": "meta.arguments.decltype", "patterns": [ { "include": "#evaluation_context" @@ -2450,8 +3043,8 @@ ] }, "decltype_specifier": { - "contentName": "meta.arguments.decltype.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.other.decltype.cpp storage.type.decltype.cpp" @@ -2485,12 +3078,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.decltype.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.decltype.cpp" } }, + "contentName": "meta.arguments.decltype", "patterns": [ { "include": "#evaluation_context" @@ -2498,8 +3091,8 @@ ] }, "default_statement": { - "name": "meta.conditional.case.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(~(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:constexpr)|(?:explicit)|(?:mutable)|(?:virtual)|(?:inline)|(?:friend))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(~(?|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.special.member.destructor.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "6": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "7": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "8": { "name": "comment.block.cpp" }, - "9": { + "8": { "patterns": [ { "match": "\\*\\/", @@ -2602,23 +3195,23 @@ } ] }, - "10": { + "9": { "name": "storage.type.modifier.calling-convention.cpp" }, - "11": { + "10": { "patterns": [ { "include": "#inline_comment" } ] }, - "12": { + "11": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "13": { + "12": { "name": "comment.block.cpp" }, - "14": { + "13": { "patterns": [ { "match": "\\*\\/", @@ -2630,27 +3223,27 @@ } ] }, - "15": { + "14": { "patterns": [ { "include": "#functional_specifiers_pre_parameters" } ] }, - "16": { + "15": { "patterns": [ { "include": "#inline_comment" } ] }, - "17": { + "16": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "18": { + "17": { "name": "comment.block.cpp" }, - "19": { + "18": { "patterns": [ { "match": "\\*\\/", @@ -2662,81 +3255,88 @@ } ] }, - "20": { + "19": { "name": "entity.name.function.destructor.cpp entity.name.function.definition.special.member.destructor.cpp" } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.special.member.destructor.cpp", "patterns": [ { - "name": "meta.head.function.definition.special.member.destructor.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.special.member.destructor.cpp" } }, + "name": "meta.head.function.definition.special.member.destructor.cpp", "patterns": [ { "include": "#ever_present_context" }, { - "patterns": [ - { - "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { - "contentName": "meta.function.definition.parameters.special.member.destructor.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.begin.bracket.round.special.member.destructor.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.end.bracket.round.special.member.destructor.cpp" } + }, + "contentName": "meta.function.definition.parameters.special.member.destructor", + "patterns": [] + }, + { + "match": "((?:(?:final)|(?:override)))+", + "captures": { + "1": { + "name": "keyword.operator.wordlike.cpp keyword.operator.$1.cpp" + } } }, { @@ -2745,14 +3345,15 @@ ] }, { - "name": "meta.body.function.definition.special.member.destructor.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.special.member.destructor.cpp" } }, + "name": "meta.body.function.definition.special.member.destructor.cpp", "patterns": [ { "include": "#function_body_context" @@ -2760,9 +3361,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.special.member.destructor.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -2772,26 +3375,26 @@ ] }, "destructor_root": { - "name": "meta.function.definition.special.member.destructor.cpp", - "begin": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))~\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.special.member.destructor.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -2803,23 +3406,23 @@ } ] }, - "6": { + "5": { "name": "storage.type.modifier.calling-convention.cpp" }, - "7": { + "6": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { + "7": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "9": { + "8": { "name": "comment.block.cpp" }, - "10": { + "9": { "patterns": [ { "match": "\\*\\/", @@ -2831,7 +3434,7 @@ } ] }, - "11": { + "10": { "patterns": [ { "match": "::", @@ -2846,15 +3449,15 @@ } ] }, - "13": { - "name": "meta.template.call.cpp", + "11": { "patterns": [ { "include": "#template_call_range" } ] }, - "15": { + "12": {}, + "13": { "patterns": [ { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?=:)", @@ -2870,45 +3473,46 @@ } ] }, - "17": { + "14": {}, + "15": { "patterns": [ { "include": "#inline_comment" } ] }, + "16": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "17": { + "name": "comment.block.cpp" + }, "18": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "19": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "20": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { "name": "comment.block.cpp" }, - "24": { + "22": { "patterns": [ { "match": "\\*\\/", @@ -2920,20 +3524,20 @@ } ] }, - "25": { + "23": { "patterns": [ { "include": "#inline_comment" } ] }, - "26": { + "24": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "27": { + "25": { "name": "comment.block.cpp" }, - "28": { + "26": { "patterns": [ { "match": "\\*\\/", @@ -2946,77 +3550,84 @@ ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.special.member.destructor.cpp", "patterns": [ { - "name": "meta.head.function.definition.special.member.destructor.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.special.member.destructor.cpp" } }, + "name": "meta.head.function.definition.special.member.destructor.cpp", "patterns": [ { "include": "#ever_present_context" }, { - "patterns": [ - { - "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { - "contentName": "meta.function.definition.parameters.special.member.destructor.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.begin.bracket.round.special.member.destructor.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.end.bracket.round.special.member.destructor.cpp" } + }, + "contentName": "meta.function.definition.parameters.special.member.destructor", + "patterns": [] + }, + { + "match": "((?:(?:final)|(?:override)))+", + "captures": { + "1": { + "name": "keyword.operator.wordlike.cpp keyword.operator.$1.cpp" + } } }, { @@ -3025,14 +3636,15 @@ ] }, { - "name": "meta.body.function.definition.special.member.destructor.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.special.member.destructor.cpp" } }, + "name": "meta.body.function.definition.special.member.destructor.cpp", "patterns": [ { "include": "#function_body_context" @@ -3040,9 +3652,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.special.member.destructor.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -3052,8 +3666,8 @@ ] }, "diagnostic": { - "name": "meta.preprocessor.diagnostic.$reference(directive).cpp", - "begin": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:error|warning)))\\b\\s*", + "begin": "(^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:error|warning)))\\b(?:(?:\\s)+)?", + "end": "(?[#;\\/=*C~]+)(?![#;\\/=*C~]))\\s*.+\\s*\\8\\s*(?:\\n|$)))|(^\\s*((\\/\\*)\\s*?((?>[#;\\/=*C~]+)(?![#;\\/=*C~]))\\s*.+\\s*\\8\\s*\\*\\/)))", + "match": "(?:(^(?:(?:\\s)+)?((\\/\\/)(?:(?:\\s)+)?((?:[#;\\/=*C~]+)++(?![#;\\/=*C~]))(?:(?:\\s)+)?.+(?:(?:\\s)+)?\\4(?:(?:\\s)+)?(?:\\n|$)))|(^(?:(?:\\s)+)?((\\/\\*)(?:(?:\\s)+)?((?:[#;\\/=*C~]+)++(?![#;\\/=*C~]))(?:(?:\\s)+)?.+(?:(?:\\s)+)?\\8(?:(?:\\s)+)?\\*\\/)))", "captures": { "1": { "name": "meta.toc-list.banner.double-slash.cpp" @@ -3174,23 +3792,23 @@ } }, "empty_square_brackets": { - "name": "storage.modifier.array.bracket.square.cpp", - "match": "(?-mix:(?-mix:(?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?(::))?\\s*((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.enum.cpp" }, - "2": { + "1": { "name": "storage.type.enum.cpp" }, - "3": { - "name": "storage.type.enum.enum-key.$3.cpp" + "2": { + "name": "storage.type.enum.enum-key.$2.cpp" }, - "4": { + "3": { "patterns": [ { "include": "#attributes_context" @@ -3200,22 +3818,33 @@ } ] }, - "5": { + "4": { "name": "entity.name.type.enum.cpp" }, - "6": { + "5": { "name": "punctuation.separator.colon.type-specifier.cpp" }, - "8": { + "6": { "patterns": [ { "include": "#scope_resolution_inner_generated" } ] }, - "9": { + "7": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, + "8": { + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "9": {}, + "10": { + "name": "entity.name.scope-resolution.cpp" + }, "11": { "name": "meta.template.call.cpp", "patterns": [ @@ -3224,25 +3853,14 @@ } ] }, + "12": {}, "13": { - "name": "entity.name.scope-resolution.cpp" - }, - "14": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "16": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, - "17": { - "name": "storage.type.integral.$17.cpp" + "14": { + "name": "storage.type.integral.$14.cpp" } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -3251,16 +3869,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.enum.cpp", "patterns": [ { - "name": "meta.head.enum.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.enum.cpp" } }, + "name": "meta.head.enum.cpp", "patterns": [ { "include": "$self" @@ -3268,14 +3888,15 @@ ] }, { - "name": "meta.body.enum.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.enum.cpp" } }, + "name": "meta.body.enum.cpp", "patterns": [ { "include": "#ever_present_context" @@ -3295,9 +3916,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.enum.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -3307,7 +3930,7 @@ ] }, "enum_declare": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", "captures": { "1": { "name": "storage.type.enum.declare.cpp" @@ -3320,34 +3943,43 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.enum.cpp" }, - "7": { + "5": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -3383,6 +4015,40 @@ } ] }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "8": { "patterns": [ { @@ -3391,105 +4057,107 @@ ] }, "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "10": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "11": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { "name": "variable.other.object.declare.cpp" }, - "21": { + "13": { "patterns": [ { "include": "#inline_comment" } ] }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { + "14": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "enumerator_list": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "keyword.control.exception.$5.cpp" + "3": { + "name": "keyword.control.exception.$3.cpp" } } }, "extern_block": { - "name": "meta.block.extern.cpp", - "begin": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(extern)(?=\\s*\\\"))", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(extern)(?=\\s*\\\")", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.extern.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -3726,11 +4394,10 @@ } ] }, - "6": { + "5": { "name": "storage.type.extern.cpp" } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -3739,16 +4406,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.extern.cpp", "patterns": [ { - "name": "meta.head.extern.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.extern.cpp" } }, + "name": "meta.head.extern.cpp", "patterns": [ { "include": "$self" @@ -3756,14 +4425,15 @@ ] }, { - "name": "meta.body.extern.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.extern.cpp" } }, + "name": "meta.body.extern.cpp", "patterns": [ { "include": "$self" @@ -3771,9 +4441,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.extern.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -3812,7 +4484,7 @@ "include": "#typedef_union" }, { - "include": "#typedef_keyword" + "include": "#misc_keywords" }, { "include": "#standard_declares" @@ -3859,7 +4531,8 @@ ] }, "function_call": { - "begin": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)", "beginCaptures": { "1": { "patterns": [ @@ -3871,31 +4544,31 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.function.call.cpp" }, - "7": { + "6": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { + "7": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "9": { + "8": { "name": "comment.block.cpp" }, - "10": { + "9": { "patterns": [ { "match": "\\*\\/", @@ -3907,7 +4580,7 @@ } ] }, - "11": { + "10": { "name": "meta.template.call.cpp", "patterns": [ { @@ -3915,13 +4588,13 @@ } ] }, - "13": { + "11": {}, + "12": { "name": "punctuation.section.arguments.begin.bracket.round.function.call.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.function.call.cpp" } }, @@ -3932,26 +4605,26 @@ ] }, "function_definition": { - "name": "meta.function.definition.cpp", - "begin": "((?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\())", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -3963,38 +4636,48 @@ } ] }, - "6": { + "5": { "name": "storage.type.template.cpp" }, - "7": { + "6": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { + "7": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "9": { + "8": { "name": "comment.block.cpp" }, + "9": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "10": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + "include": "#attributes_context" }, { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#number_literal" } ] }, "11": { "patterns": [ { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))", + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", "captures": { "1": { "name": "storage.modifier.$1.cpp" @@ -4029,7 +4712,7 @@ ] }, "12": { - "name": "storage.modifier.$1.cpp" + "name": "storage.modifier.$12.cpp" }, "13": { "patterns": [ @@ -4060,15 +4743,16 @@ "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -4153,52 +4854,43 @@ } ] }, + "27": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -4264,45 +4946,95 @@ } ] }, - "45": { + "36": { "patterns": [ { "include": "#inline_comment" } ] }, - "46": { + "37": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "47": { + "38": { "name": "comment.block.cpp" }, + "39": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "40": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "41": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "42": { + "name": "comment.block.cpp" + }, + "43": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "44": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "45": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "46": { + "name": "comment.block.cpp" + }, + "47": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "48": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "49": { "patterns": [ { "include": "#inline_comment" } ] }, - "50": { + "49": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "51": { + "50": { "name": "comment.block.cpp" }, - "52": { + "51": { "patterns": [ { "match": "\\*\\/", @@ -4314,6 +5046,9 @@ } ] }, + "52": { + "name": "storage.type.modifier.calling-convention.cpp" + }, "53": { "patterns": [ { @@ -4342,30 +5077,23 @@ "57": { "patterns": [ { - "include": "#inline_comment" + "include": "#scope_resolution_function_definition_inner_generated" } ] }, "58": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" }, "59": { - "name": "comment.block.cpp" - }, - "60": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#template_call_range" } ] }, + "60": {}, "61": { - "name": "storage.type.modifier.calling-convention.cpp" + "name": "entity.name.function.definition.cpp" }, "62": { "patterns": [ @@ -4391,83 +5119,39 @@ "name": "comment.block.cpp" } ] - }, - "66": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_inner_generated" - } - ] - }, - "67": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" - }, - "69": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "71": { - "name": "entity.name.function.definition.cpp" - }, - "72": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "73": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "74": { - "name": "comment.block.cpp" - }, - "75": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.cpp", "patterns": [ { - "name": "meta.head.function.definition.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.cpp" } }, + "name": "meta.head.function.definition.cpp", "patterns": [ { "include": "#ever_present_context" }, { - "contentName": "meta.function.definition.parameters.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.begin.bracket.round.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.end.bracket.round.cpp" } }, + "contentName": "meta.function.definition.parameters", "patterns": [ { "include": "#ever_present_context" @@ -4483,20 +5167,218 @@ } ] }, + { + "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "captures": { + "1": { + "name": "punctuation.definition.function.return-type.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "meta.qualified_type.cpp", + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + { + "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.type.cpp" + } + ] + }, + "7": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "10": { + "name": "comment.block.cpp" + }, + "11": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "14": { + "name": "comment.block.cpp" + }, + "15": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "16": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.cpp" } }, + "name": "meta.body.function.definition.cpp", "patterns": [ { "include": "#function_body_context" @@ -4504,9 +5386,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -4529,21 +5413,23 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -4628,52 +5531,43 @@ } ] }, + "11": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -4739,111 +5623,110 @@ } ] }, - "29": { + "20": { "patterns": [ { "include": "#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "variable.other.definition.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()", "endCaptures": { "1": { "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" @@ -4881,21 +5764,23 @@ ] }, "function_pointer_parameter": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -4980,52 +5882,43 @@ } ] }, + "11": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -5091,111 +5974,110 @@ } ] }, - "29": { + "20": { "patterns": [ { "include": "#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "variable.parameter.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()", "endCaptures": { "1": { "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" @@ -5233,23 +6115,23 @@ ] }, "functional_specifiers_pre_parameters": { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)", + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)", "captures": { "1": { "name": "keyword.control.goto.cpp" @@ -5312,30 +6196,42 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.label.call.cpp" } } }, + "identifier": { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*" + }, "include": { - "match": "(?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((#)\\s*((?:include|include_next))\\b)\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))", + "match": "^((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((#)(?:(?:\\s)+)?((?:include|include_next))\\b)(?:(?:\\s)+)?(?:(?:(?:((<)[^>]*(>?)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=\\/\\/)))|((\\\")[^\\\"]*((?:\\\")?)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=\\/\\/))))|(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=(?:\\/\\/|;)))))|((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=(?:\\/\\/|;))))", "captures": { "1": { "patterns": [ @@ -5345,172 +6241,226 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "keyword.control.directive.$7.cpp" + "3": { + "name": "keyword.control.directive.$5.cpp" }, - "6": { + "4": { "name": "punctuation.definition.directive.cpp" }, - "8": { + "6": { "name": "string.quoted.other.lt-gt.include.cpp" }, - "9": { + "7": { "name": "punctuation.definition.string.begin.cpp" }, - "10": { + "8": { "name": "punctuation.definition.string.end.cpp" }, - "11": { + "9": { "patterns": [ { "include": "#inline_comment" } ] }, + "10": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "11": { + "name": "string.quoted.double.include.cpp" + }, "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "punctuation.definition.string.begin.cpp" }, "13": { - "name": "comment.block.cpp" + "name": "punctuation.definition.string.end.cpp" }, "14": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "15": { - "name": "string.quoted.double.include.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "16": { - "name": "punctuation.definition.string.begin.cpp" + "name": "entity.name.other.preprocessor.macro.include.cpp" }, "17": { - "name": "punctuation.definition.string.end.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "18": { "patterns": [ { - "include": "#inline_comment" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "19": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "20": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "21": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "22": { - "name": "entity.name.other.preprocessor.macro.include.cpp" - }, - "23": { "patterns": [ { - "include": "#inline_comment" - } - ] - }, - "24": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "25": { - "name": "comment.block.cpp" - }, - "26": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "27": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "28": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "29": { - "name": "comment.block.cpp" - }, - "30": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "31": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "32": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "33": { - "name": "comment.block.cpp" - }, - "34": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } @@ -5527,7 +6477,7 @@ "name": "punctuation.separator.delimiter.comma.inheritance.cpp" }, { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -5592,122 +6560,147 @@ ] }, "4": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "5": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "7": { "patterns": [ { - "include": "#inline_comment" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "25": { - "name": "entity.name.type.cpp" - }, - "26": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } + "12": {} } } ] }, + "inline_builtin_storage_type": { + "match": "(?:\\s)*+(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/))", + "match": "(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/))", "captures": { "1": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" @@ -5734,7 +6727,7 @@ "name": "invalid.illegal.unexpected.punctuation.definition.comment.end.cpp" }, "label": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)", + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:)", "captures": { "1": { "patterns": [ @@ -5744,70 +6737,89 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { + "3": { "name": "entity.name.label.cpp" }, - "6": { + "4": { "patterns": [ { "include": "#inline_comment" } ] }, - "7": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "8": { - "name": "comment.block.cpp" - }, - "9": { + "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "10": { + "6": { "name": "punctuation.separator.label.cpp" } } }, "lambdas": { - "begin": "((?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))\\s*(\\[(?!\\[| *+\"| *+\\d))((?>(?:[^\\[\\]]|((?(?:(?>[^\\[\\]]*)\\g<4>?)+)\\]))*))(\\](?!\\[)))", + "begin": "(?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))(?:(?:\\s)+)?(\\[(?!\\[| *+\"| *+\\d))((?:[^\\[\\]]|((??)++\\]))*+)(\\](?!((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))[\\[\\];]))", + "end": "(?<=[;}])", "beginCaptures": { - "2": { + "1": { "name": "punctuation.definition.capture.begin.lambda.cpp" }, - "3": { + "2": { "name": "meta.lambda.capture.cpp", "patterns": [ { "include": "#the_this_keyword" }, { - "match": "((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", + "match": "((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", "captures": { "1": { "name": "variable.parameter.capture.cpp" @@ -5850,26 +6862,52 @@ } ] }, - "5": { + "3": {}, + "4": { "name": "punctuation.definition.capture.end.lambda.cpp" + }, + "5": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "6": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "7": { + "name": "comment.block.cpp" + }, + "8": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] } }, - "end": "(?<=})", + "endCaptures": {}, "patterns": [ { - "name": "meta.function.definition.parameters.lambda.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.definition.parameters.begin.lambda.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.parameters.end.lambda.cpp" } }, + "name": "meta.function.definition.parameters.lambda.cpp", "patterns": [ { "include": "#function_parameter_context" @@ -5877,7 +6915,7 @@ ] }, { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*line\\b)", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?line\\b", + "end": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*define\\b)\\s*((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?define\\b)(?:(?:\\s)+)?((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!uint_least64_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|double[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|float[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|short[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|char[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|void[^(?-mix:\\w)]|long[^(?-mix:\\w)]|auto[^(?-mix:\\w)]|int[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", + "match": "(?:((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(\\b(?!uint_least32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_least16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_least64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_least32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_least64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_fast32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_fast64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_least8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_fast16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_least16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_fast16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_least8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_fast8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_fast64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_fast32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_fast8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|suseconds_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|useconds_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|in_addr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uintmax_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uintmax_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uintmax_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|in_port_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uintptr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|blksize_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|u_quad_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|intmax_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|intmax_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|unsigned[^Pattern.new(\n match: \\/\\w\\/,\n)]|blkcnt_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|intptr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|swblk_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|wchar_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|u_short[^Pattern.new(\n match: \\/\\w\\/,\n)]|qaddr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|caddr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|daddr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|fixpt_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|nlink_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|segsz_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|clock_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|ssize_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|mode_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|quad_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|ushort[^Pattern.new(\n match: \\/\\w\\/,\n)]|u_long[^Pattern.new(\n match: \\/\\w\\/,\n)]|u_char[^Pattern.new(\n match: \\/\\w\\/,\n)]|double[^Pattern.new(\n match: \\/\\w\\/,\n)]|signed[^Pattern.new(\n match: \\/\\w\\/,\n)]|time_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|size_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|key_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|div_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|ino_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uid_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|gid_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|off_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|pid_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|float[^Pattern.new(\n match: \\/\\w\\/,\n)]|dev_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|u_int[^Pattern.new(\n match: \\/\\w\\/,\n)]|short[^Pattern.new(\n match: \\/\\w\\/,\n)]|bool[^Pattern.new(\n match: \\/\\w\\/,\n)]|id_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint[^Pattern.new(\n match: \\/\\w\\/,\n)]|long[^Pattern.new(\n match: \\/\\w\\/,\n)]|char[^Pattern.new(\n match: \\/\\w\\/,\n)]|void[^Pattern.new(\n match: \\/\\w\\/,\n)]|auto[^Pattern.new(\n match: \\/\\w\\/,\n)]|id_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int[^Pattern.new(\n match: \\/\\w\\/,\n)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", "captures": { "1": { "patterns": [ @@ -6092,39 +7132,48 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { + "3": { "name": "variable.language.this.cpp" }, - "6": { + "4": { "name": "variable.other.object.access.cpp" }, - "7": { + "5": { "name": "punctuation.separator.dot-access.cpp" }, - "8": { + "6": { "name": "punctuation.separator.pointer-access.cpp" }, - "9": { + "7": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))(?:(?:\\s)+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6166,7 +7215,7 @@ } }, { - "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6215,13 +7264,13 @@ } ] }, - "10": { + "8": { "name": "variable.other.property.cpp" } } }, "memory_operators": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:(delete)\\s*(\\[\\])|(delete))|(new))(?!\\w))", + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(delete)(?:(?:\\s)+)?(\\[\\])|(delete))|(new))(?!\\w))", "captures": { "1": { "patterns": [ @@ -6231,42 +7280,52 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { + "3": { "name": "keyword.operator.wordlike.cpp" }, - "6": { + "4": { "name": "keyword.operator.delete.array.cpp" }, - "7": { + "5": { "name": "keyword.operator.delete.array.bracket.cpp" }, - "8": { + "6": { "name": "keyword.operator.delete.cpp" }, - "9": { + "7": { "name": "keyword.operator.new.cpp" } } }, "method_access": { - "begin": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\()", + "begin": "(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?(\\()", + "end": "\\)", "beginCaptures": { "1": { "patterns": [ @@ -6308,7 +7367,7 @@ "9": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))(?:(?:\\s)+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6350,7 +7409,7 @@ } }, { - "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6406,9 +7465,8 @@ "name": "punctuation.section.arguments.begin.bracket.round.function.member.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.function.member.cpp" } }, @@ -6418,12 +7476,8 @@ } ] }, - "misc_storage_modifiers": { - "match": "\\b(?:export|mutable|typename|thread_local|register|restrict|static|volatile|inline)\\b", - "name": "storage.modifier.$0.cpp" - }, - "module_import": { - "match": "(?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((import))\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))\\s*(;?)", + "misc_keywords": { + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "keyword.control.directive.import.cpp" - }, - "7": { - "name": "string.quoted.other.lt-gt.include.cpp" - }, - "8": { - "name": "punctuation.definition.string.begin.cpp" - }, - "9": { - "name": "punctuation.definition.string.end.cpp" - }, - "10": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "11": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "12": { - "name": "comment.block.cpp" - }, - "13": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "14": { - "name": "string.quoted.double.include.cpp" - }, - "15": { - "name": "punctuation.definition.string.begin.cpp" - }, - "16": { - "name": "punctuation.definition.string.end.cpp" - }, - "17": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "18": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "19": { - "name": "comment.block.cpp" - }, - "20": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "21": { - "name": "entity.name.other.preprocessor.macro.include.cpp" - }, - "22": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "23": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "24": { - "name": "comment.block.cpp" - }, - "25": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "26": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "27": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "28": { - "name": "comment.block.cpp" - }, - "29": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { - "name": "punctuation.terminator.statement.cpp" + "name": "keyword.other.$3.cpp" } - }, - "name": "meta.preprocessor.import.cpp" + } }, "ms_attributes": { - "name": "support.other.attribute.cpp", - "begin": "(__declspec\\()", + "begin": "__declspec\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.begin.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.end.cpp" } }, + "name": "support.other.attribute.cpp", "patterns": [ { "include": "#attributes_context" @@ -6626,6 +7539,8 @@ { "begin": "\\(", "end": "\\)", + "beginCaptures": {}, + "endCaptures": {}, "patterns": [ { "include": "#attributes_context" @@ -6636,7 +7551,7 @@ ] }, { - "match": "(using)\\s+((?(?:(?>[^<>]*)\\g<9>?)+)>)\\s*)?::)*\\s*+)\\s*((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<8>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.namespace.cpp" }, - "2": { + "1": { "name": "keyword.other.namespace.definition.cpp storage.type.namespace.definition.cpp" } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.block.namespace.cpp", "patterns": [ { - "name": "meta.head.namespace.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.namespace.cpp" } }, + "name": "meta.head.namespace.cpp", "patterns": [ { "include": "#ever_present_context" @@ -6739,7 +7655,7 @@ "include": "#attributes_context" }, { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<5>?)+)>)\\s*)?::)*\\s*+)\\s*((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.namespace.cpp" } }, + "name": "meta.body.namespace.cpp", "patterns": [ { "include": "$self" @@ -6788,9 +7705,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.namespace.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -6800,8 +7719,8 @@ ] }, "noexcept_operator": { - "contentName": "meta.arguments.operator.noexcept.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.noexcept.cpp" @@ -6835,51 +7754,18 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.noexcept.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.noexcept.cpp" } }, + "contentName": "meta.arguments.operator.noexcept", "patterns": [ { "include": "#evaluation_context" } ] }, - "non_primitive_types": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(operator)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(?:(?:((?:delete\\[\\]|delete|new\\[\\]|<=>|<<=|new|>>=|\\->\\*|\\/=|%=|&=|>=|\\|=|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|<<|>>|\\-\\-|<=|\\^=|==|!=|&&|\\|\\||\\+=|\\-=|\\*=|,|\\+|\\-|!|~|\\*|&|\\*|\\/|%|\\+|\\-|<|>|&|\\^|\\||=))|((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:\\[\\])?)))|(\"\")((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\<|\\())", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.special.operator-overload.cpp" }, - "2": { + "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -7195,7 +8101,7 @@ } ] }, - "3": { + "2": { "patterns": [ { "include": "#attributes_context" @@ -7205,102 +8111,93 @@ } ] }, - "4": { + "3": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "4": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "5": { "name": "comment.block.cpp" }, + "6": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "9": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "10": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "11": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" }, { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -7366,20 +8253,20 @@ } ] }, - "30": { + "20": { "patterns": [ { "include": "#inline_comment" } ] }, - "31": { + "21": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "32": { + "22": { "name": "comment.block.cpp" }, - "33": { + "23": { "patterns": [ { "match": "\\*\\/", @@ -7391,135 +8278,135 @@ } ] }, - "34": { + "24": { "patterns": [ { "include": "#inline_comment" } ] }, - "35": { + "25": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "36": { + "26": { "name": "comment.block.cpp" }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "30": { + "name": "comment.block.cpp" + }, + "31": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "32": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "33": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "34": { + "name": "comment.block.cpp" + }, + "35": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "36": { + "name": "storage.type.modifier.calling-convention.cpp" + }, "37": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "38": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "39": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "40": { + "39": { "name": "comment.block.cpp" }, + "40": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "41": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "42": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "43": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "44": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "45": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "46": { - "name": "storage.type.modifier.calling-convention.cpp" - }, - "47": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "48": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "49": { - "name": "comment.block.cpp" - }, - "50": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "51": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "52": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "53": { - "name": "comment.block.cpp" - }, - "54": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "55": { "patterns": [ { "match": "::", @@ -7534,31 +8421,31 @@ } ] }, - "57": { - "name": "meta.template.call.cpp", + "46": { "patterns": [ { "include": "#template_call_range" } ] }, - "59": { + "47": {}, + "48": { "name": "keyword.other.operator.overload.cpp" }, - "60": { + "49": { "patterns": [ { "include": "#inline_comment" } ] }, - "61": { + "50": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "62": { + "51": { "name": "comment.block.cpp" }, - "63": { + "52": { "patterns": [ { "match": "\\*\\/", @@ -7570,7 +8457,7 @@ } ] }, - "64": { + "53": { "patterns": [ { "match": "::", @@ -7585,28 +8472,28 @@ } ] }, - "66": { - "name": "meta.template.call.cpp", + "54": { "patterns": [ { "include": "#template_call_range" } ] }, - "68": { + "55": {}, + "56": { "name": "entity.name.operator.cpp" }, - "69": { + "57": { "name": "entity.name.operator.type.cpp" }, - "70": { + "58": { "patterns": [ { "match": "\\*", "name": "entity.name.operator.type.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -7642,20 +8529,20 @@ } ] }, - "71": { + "59": { "patterns": [ { "include": "#inline_comment" } ] }, - "72": { + "60": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "73": { + "61": { "name": "comment.block.cpp" }, - "74": { + "62": { "patterns": [ { "match": "\\*\\/", @@ -7667,104 +8554,104 @@ } ] }, - "75": { + "63": { "patterns": [ { "include": "#inline_comment" } ] }, - "76": { + "64": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "77": { + "65": { "name": "comment.block.cpp" }, + "66": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "67": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "68": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "69": { + "name": "comment.block.cpp" + }, + "70": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "71": { + "name": "entity.name.operator.type.array.cpp" + }, + "72": { + "name": "entity.name.operator.custom-literal.cpp" + }, + "73": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "74": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "75": { + "name": "comment.block.cpp" + }, + "76": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "77": { + "name": "entity.name.operator.custom-literal.cpp" + }, "78": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "79": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "80": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "81": { - "name": "comment.block.cpp" - }, - "82": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "83": { - "name": "entity.name.operator.type.array.cpp" - }, - "84": { - "name": "entity.name.operator.custom-literal.cpp" - }, - "85": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "86": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "87": { - "name": "comment.block.cpp" - }, - "88": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "89": { - "name": "entity.name.operator.custom-literal.cpp" - }, - "90": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "91": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "92": { - "name": "comment.block.cpp" - }, - "93": { "patterns": [ { "match": "\\*\\/", @@ -7777,17 +8664,19 @@ ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.special.operator-overload.cpp", "patterns": [ { - "name": "meta.head.function.definition.special.operator-overload.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.special.operator-overload.cpp" } }, + "name": "meta.head.function.definition.special.operator-overload.cpp", "patterns": [ { "include": "#ever_present_context" @@ -7796,19 +8685,19 @@ "include": "#template_call_range" }, { - "contentName": "meta.function.definition.parameters.special.operator-overload.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.begin.bracket.round.special.operator-overload.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.end.bracket.round.special.operator-overload.cpp" } }, + "contentName": "meta.function.definition.parameters.special.operator-overload", "patterns": [ { "include": "#function_parameter_context" @@ -7827,14 +8716,15 @@ ] }, { - "name": "meta.body.function.definition.special.operator-overload.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.special.operator-overload.cpp" } }, + "name": "meta.body.function.definition.special.operator-overload.cpp", "patterns": [ { "include": "#function_body_context" @@ -7842,9 +8732,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.special.operator-overload.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -7856,22 +8748,292 @@ "operators": { "patterns": [ { - "include": "#sizeof_operator" + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.sizeof.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.sizeof.cpp" + } + }, + "contentName": "meta.arguments.operator.sizeof", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { - "include": "#alignof_operator" + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.alignof.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.alignof.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.alignof.cpp" + } + }, + "contentName": "meta.arguments.operator.alignof", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { - "include": "#alignas_operator" + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.alignas.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.alignas.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.alignas.cpp" + } + }, + "contentName": "meta.arguments.operator.alignas", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { - "include": "#typeid_operator" + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.typeid.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.typeid.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.typeid.cpp" + } + }, + "contentName": "meta.arguments.operator.typeid", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { - "include": "#noexcept_operator" + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.noexcept.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.noexcept.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.noexcept.cpp" + } + }, + "contentName": "meta.arguments.operator.noexcept", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { - "include": "#sizeof_variadic_operator" + "begin": "(\\bsizeof\\.\\.\\.)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.variadic.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.sizeof.variadic.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.sizeof.variadic.cpp" + } + }, + "contentName": "meta.arguments.operator.sizeof.variadic", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { "match": "--", @@ -7920,22 +9082,1326 @@ "over_qualified_types": { "patterns": [ { - "include": "#parameter_struct" + "match": "(struct)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", + "captures": { + "1": { + "name": "storage.type.struct.parameter.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.struct.parameter.cpp" + }, + "5": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "6": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "7": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "14": { + "name": "variable.other.object.declare.cpp" + }, + "15": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "16": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "17": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "18": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "19": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "20": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#parameter_enum" + "match": "(enum)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", + "captures": { + "1": { + "name": "storage.type.enum.parameter.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.enum.parameter.cpp" + }, + "5": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "6": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "7": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "14": { + "name": "variable.other.object.declare.cpp" + }, + "15": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "16": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "17": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "18": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "19": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "20": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#parameter_union" + "match": "(union)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", + "captures": { + "1": { + "name": "storage.type.union.parameter.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.union.parameter.cpp" + }, + "5": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "6": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "7": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "14": { + "name": "variable.other.object.declare.cpp" + }, + "15": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "16": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "17": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "18": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "19": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "20": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#parameter_class" + "match": "(class)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", + "captures": { + "1": { + "name": "storage.type.class.parameter.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.class.parameter.cpp" + }, + "5": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "6": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "7": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "14": { + "name": "variable.other.object.declare.cpp" + }, + "15": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "16": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "17": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "18": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "19": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "20": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } } ] }, "parameter": { - "name": "meta.parameter.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\w)", + "end": "(?:(?=\\))|(,))", "beginCaptures": { "1": { "patterns": [ @@ -7963,12 +10429,12 @@ ] } }, - "end": "(?:(?=\\))|(,))", "endCaptures": { "1": { "name": "punctuation.separator.delimiter.comma.cpp" } }, + "name": "meta.parameter.cpp", "patterns": [ { "include": "#ever_present_context" @@ -7983,7 +10449,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:volatile|register|restrict|static|extern|const))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", + "match": "((?:((?:(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -8046,159 +10512,34 @@ ] }, "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "15": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "16": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "17": { - "name": "comment.block.cpp" - }, - "18": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "19": { "name": "storage.type.primitive.cpp storage.type.built-in.primitive.cpp" }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { + "12": { "name": "storage.type.cpp storage.type.built-in.cpp" }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { + "13": { "name": "support.type.posix-reserved.pthread.cpp support.type.built-in.posix-reserved.pthread.cpp" }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { + "14": { "name": "support.type.posix-reserved.cpp support.type.built-in.posix-reserved.cpp" }, - "35": { + "15": { "name": "entity.name.type.parameter.cpp" }, - "36": { + "16": { "patterns": [ { "include": "#inline_comment" } ] }, - "37": { + "17": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "38": { + "18": { "name": "comment.block.cpp" }, - "39": { + "19": { "patterns": [ { "match": "\\*\\/", @@ -8219,12 +10560,13 @@ "include": "#scope_resolution_parameter_inner_generated" }, { - "match": "(?:struct|class|union|enum)", + "match": "(?:(?:struct)|(?:class)|(?:union)|(?:enum))", "name": "storage.type.$0.cpp" }, { "begin": "(?<==)", "end": "(?:(?=\\))|(,))", + "beginCaptures": {}, "endCaptures": { "1": { "name": "punctuation.separator.delimiter.comma.cpp" @@ -8237,10 +10579,11 @@ ] }, { - "include": "#assignment_operator" + "match": "\\=", + "name": "keyword.operator.assignment.cpp" }, { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\)|,|\\[|=|\\n)", + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\)|,|\\[|=|\\n)", "captures": { "1": { "patterns": [ @@ -8301,19 +10644,19 @@ "include": "#attributes_context" }, { - "name": "meta.bracket.square.array.cpp", - "begin": "(\\[)", + "begin": "\\[", + "end": "\\]", "beginCaptures": { - "1": { + "0": { "name": "punctuation.definition.begin.bracket.square.array.type.cpp" } }, - "end": "(\\])", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.end.bracket.square.array.type.cpp" } }, + "name": "meta.bracket.square.array.cpp", "patterns": [ { "include": "#evaluation_context" @@ -8328,7 +10671,7 @@ "include": "#template_call_range" }, { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", + "match": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*))", "captures": { "0": { "patterns": [ @@ -8337,7 +10680,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8428,7 +10771,7 @@ ] }, "parameter_class": { - "match": "(class)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", + "match": "(class)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.class.parameter.cpp" @@ -8441,59 +10784,77 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.class.parameter.cpp" }, - "7": { + "5": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { + "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "11": { + "7": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8529,6 +10890,74 @@ } ] }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "12": { "patterns": [ { @@ -8537,155 +10966,141 @@ ] }, "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "14": { - "name": "comment.block.cpp" + "name": "variable.other.object.declare.cpp" }, "15": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "16": { "patterns": [ { - "include": "#inline_comment" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "18": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "19": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "20": { "patterns": [ { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "parameter_enum": { - "match": "(enum)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", + "match": "(enum)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.enum.parameter.cpp" @@ -8698,59 +11113,77 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.enum.parameter.cpp" }, - "7": { + "5": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { + "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "11": { + "7": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8786,6 +11219,74 @@ } ] }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "12": { "patterns": [ { @@ -8794,156 +11295,142 @@ ] }, "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "14": { - "name": "comment.block.cpp" + "name": "variable.other.object.declare.cpp" }, "15": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "16": { "patterns": [ { - "include": "#inline_comment" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "18": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "19": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "20": { "patterns": [ { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "parameter_or_maybe_value": { - "name": "meta.parameter.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\w)", + "end": "(?:(?=\\))|(,))", "beginCaptures": { "1": { "patterns": [ @@ -8971,12 +11458,12 @@ ] } }, - "end": "(?:(?=\\))|(,))", "endCaptures": { "1": { "name": "punctuation.separator.delimiter.comma.cpp" } }, + "name": "meta.parameter.cpp", "patterns": [ { "include": "#ever_present_context" @@ -9000,7 +11487,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:volatile|register|restrict|static|extern|const))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", + "match": "((?:((?:(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -9063,159 +11550,34 @@ ] }, "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "15": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "16": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "17": { - "name": "comment.block.cpp" - }, - "18": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "19": { "name": "storage.type.primitive.cpp storage.type.built-in.primitive.cpp" }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { + "12": { "name": "storage.type.cpp storage.type.built-in.cpp" }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { + "13": { "name": "support.type.posix-reserved.pthread.cpp support.type.built-in.posix-reserved.pthread.cpp" }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { + "14": { "name": "support.type.posix-reserved.cpp support.type.built-in.posix-reserved.cpp" }, - "35": { + "15": { "name": "entity.name.type.parameter.cpp" }, - "36": { + "16": { "patterns": [ { "include": "#inline_comment" } ] }, - "37": { + "17": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "38": { + "18": { "name": "comment.block.cpp" }, - "39": { + "19": { "patterns": [ { "match": "\\*\\/", @@ -9239,12 +11601,13 @@ "include": "#scope_resolution_parameter_inner_generated" }, { - "match": "(?:struct|class|union|enum)", + "match": "(?:(?:struct)|(?:class)|(?:union)|(?:enum))", "name": "storage.type.$0.cpp" }, { "begin": "(?<==)", "end": "(?:(?=\\))|(,))", + "beginCaptures": {}, "endCaptures": { "1": { "name": "punctuation.separator.delimiter.comma.cpp" @@ -9257,7 +11620,7 @@ ] }, { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=(?:\\)|,|\\[|=|\\/\\/|(?:\\n|$)))", + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=(?:\\)|,|\\[|=|\\/\\/|(?:(?:\\n)|$)))", "captures": { "1": { "patterns": [ @@ -9318,19 +11681,19 @@ "include": "#attributes_context" }, { - "name": "meta.bracket.square.array.cpp", - "begin": "(\\[)", + "begin": "\\[", + "end": "\\]", "beginCaptures": { - "1": { + "0": { "name": "punctuation.definition.begin.bracket.square.array.type.cpp" } }, - "end": "(\\])", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.end.bracket.square.array.type.cpp" } }, + "name": "meta.bracket.square.array.cpp", "patterns": [ { "include": "#evaluation_context" @@ -9345,7 +11708,7 @@ "include": "#template_call_range" }, { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", + "match": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*))", "captures": { "0": { "patterns": [ @@ -9354,7 +11717,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9448,7 +11811,7 @@ ] }, "parameter_struct": { - "match": "(struct)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", + "match": "(struct)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.struct.parameter.cpp" @@ -9461,59 +11824,77 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.struct.parameter.cpp" }, - "7": { + "5": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { + "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "11": { + "7": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9549,6 +11930,74 @@ } ] }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "12": { "patterns": [ { @@ -9557,155 +12006,141 @@ ] }, "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "14": { - "name": "comment.block.cpp" + "name": "variable.other.object.declare.cpp" }, "15": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "16": { "patterns": [ { - "include": "#inline_comment" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "18": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "19": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "20": { "patterns": [ { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "parameter_union": { - "match": "(union)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", + "match": "(union)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.union.parameter.cpp" @@ -9718,59 +12153,77 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.union.parameter.cpp" }, - "7": { + "5": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { + "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "11": { + "7": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9806,6 +12259,74 @@ } ] }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "12": { "patterns": [ { @@ -9814,167 +12335,153 @@ ] }, "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "14": { - "name": "comment.block.cpp" + "name": "variable.other.object.declare.cpp" }, "15": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "16": { "patterns": [ { - "include": "#inline_comment" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "18": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "19": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "20": { "patterns": [ { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "parentheses": { - "name": "meta.parens.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.parens.begin.bracket.round.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parens.end.bracket.round.cpp" } }, + "name": "meta.parens.cpp", "patterns": [ { "include": "#over_qualified_types" @@ -9988,60 +12495,27 @@ } ] }, - "posix_reserved_types": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\b)", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma\\b", + "end": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\s+mark)\\s+(.*)", + "match": "(^((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma(?:\\s)+mark)(?:\\s)+(.*)", "captures": { "1": { "name": "keyword.control.directive.pragma.pragma-mark.cpp" @@ -10091,27 +12566,36 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "punctuation.definition.directive.cpp" }, - "7": { + "5": { "name": "entity.name.tag.pragma-mark.cpp" } }, @@ -10145,10 +12629,10 @@ "include": "#language_constants" }, { - "include": "#string_context_c" + "include": "#string_context" }, { - "include": "#preprocessor_number_literal" + "include": "#d9bc4796b0b_preprocessor_number_literal" }, { "include": "#operators" @@ -10166,6 +12650,7 @@ }, "preprocessor_conditional_defined": { "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:(?:ifndef|ifdef)|if)))", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:(?:ifndef|ifdef)|if))", + "end": "^(?!\\s*+#\\s*(?:else|endif))", "beginCaptures": { - "1": { - "name": "keyword.control.directive.conditional.$7.cpp" + "0": { + "name": "keyword.control.directive.conditional.$6.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -10232,16 +12717,19 @@ } ] }, - "6": { + "5": { "name": "punctuation.definition.directive.cpp" - } + }, + "6": {} }, - "end": "(?:^)(?!\\s*+#\\s*(?:else|endif))", + "endCaptures": {}, "patterns": [ { - "name": "meta.preprocessor.conditional.cpp", "begin": "\\G(?<=ifndef|ifdef|if)", "end": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { + "3": { "name": "punctuation.definition.directive.cpp" } }, - "name": "keyword.control.directive.$6.cpp" + "name": "keyword.control.directive.$4.cpp" }, - "preprocessor_number_literal": { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(?![\\w<:.])", + "match": "\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)?(?![\\w<:.])", "captures": { "0": { - "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -10664,120 +12896,127 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "4": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "6": { "patterns": [ { - "include": "#inline_comment" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } - } + }, + "name": "meta.qualified_type.cpp" }, "qualifiers_and_specifiers_post_parameters": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "3": { - "name": "comment.block.cpp" + "name": "storage.modifier.specifier.functional.post-parameters.$3.cpp" }, "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "5": { - "name": "storage.modifier.specifier.functional.post-parameters.$5.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] } } }, "scope_resolution": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10822,8 +13104,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -10833,7 +13114,7 @@ } }, "scope_resolution_function_call": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10845,8 +13126,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -10856,7 +13136,7 @@ } }, "scope_resolution_function_call_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -10868,18 +13148,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.function.call.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -10887,13 +13167,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" } } }, "scope_resolution_function_definition": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10905,8 +13186,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -10916,7 +13196,7 @@ } }, "scope_resolution_function_definition_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -10928,18 +13208,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.function.definition.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -10947,13 +13227,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" } } }, "scope_resolution_function_definition_operator_overload": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10965,8 +13246,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -10976,7 +13256,7 @@ } }, "scope_resolution_function_definition_operator_overload_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -10988,18 +13268,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.function.definition.operator-overload.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11007,13 +13287,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" } } }, "scope_resolution_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11025,18 +13306,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11044,13 +13325,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" } } }, "scope_resolution_namespace_alias": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11062,8 +13344,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11073,7 +13354,7 @@ } }, "scope_resolution_namespace_alias_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11085,18 +13366,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.namespace.alias.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11104,13 +13385,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" } } }, "scope_resolution_namespace_block": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11122,8 +13404,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11133,7 +13414,7 @@ } }, "scope_resolution_namespace_block_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11145,18 +13426,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.namespace.block.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11164,13 +13445,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" } } }, "scope_resolution_namespace_using": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11182,8 +13464,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11193,7 +13474,7 @@ } }, "scope_resolution_namespace_using_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11205,18 +13486,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.namespace.using.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11224,13 +13505,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" } } }, "scope_resolution_parameter": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11242,8 +13524,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11253,7 +13534,7 @@ } }, "scope_resolution_parameter_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11265,18 +13546,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.parameter.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11284,13 +13565,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" } } }, "scope_resolution_template_call": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11302,8 +13584,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11313,7 +13594,7 @@ } }, "scope_resolution_template_call_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11325,18 +13606,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.template.call.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11344,13 +13625,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" } } }, "scope_resolution_template_definition": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11362,8 +13644,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11373,7 +13654,7 @@ } }, "scope_resolution_template_definition_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11385,18 +13666,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.template.definition.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11404,7 +13685,8 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" } } @@ -11414,21 +13696,22 @@ "name": "punctuation.terminator.statement.cpp" }, "simple_type": { - "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?", + "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?", "captures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -11471,124 +13771,132 @@ ] }, "4": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "5": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "7": { "patterns": [ { - "include": "#inline_comment" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "25": { - "name": "entity.name.type.cpp" - }, - "26": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "28": { + "12": {}, + "13": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -11624,60 +13932,78 @@ } ] }, - "29": { + "14": { "patterns": [ { "include": "#inline_comment" } ] }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { + "15": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "33": { + "16": { "patterns": [ { "include": "#inline_comment" } ] }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { + "17": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "single_line_macro": { - "match": "^((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))#define.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))#define.*(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "sizeof_operator": { - "contentName": "meta.arguments.operator.sizeof.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.cpp" @@ -11752,12 +14087,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.sizeof.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.sizeof.cpp" } }, + "contentName": "meta.arguments.operator.sizeof", "patterns": [ { "include": "#evaluation_context" @@ -11765,8 +14100,8 @@ ] }, "sizeof_variadic_operator": { - "contentName": "meta.arguments.operator.sizeof.variadic.cpp", - "begin": "(\\bsizeof\\.\\.\\.)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "(\\bsizeof\\.\\.\\.)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.variadic.cpp" @@ -11800,12 +14135,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.sizeof.variadic.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.sizeof.variadic.cpp" } }, + "contentName": "meta.arguments.operator.sizeof.variadic", "patterns": [ { "include": "#evaluation_context" @@ -11813,20 +14148,20 @@ ] }, "square_brackets": { - "name": "meta.bracket.square.access.cpp", + "name": "meta.bracket.square.access", "begin": "([a-zA-Z_][a-zA-Z_0-9]*|(?<=[\\]\\)]))?(\\[)(?!\\])", "beginCaptures": { "1": { - "name": "variable.other.object.cpp" + "name": "variable.other.object" }, "2": { - "name": "punctuation.definition.begin.bracket.square.cpp" + "name": "punctuation.definition.begin.bracket.square" } }, "end": "\\]", "endCaptures": { "0": { - "name": "punctuation.definition.end.bracket.square.cpp" + "name": "punctuation.definition.end.bracket.square" } }, "patterns": [ @@ -11838,21 +14173,918 @@ "standard_declares": { "patterns": [ { - "include": "#struct_declare" + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", + "captures": { + "1": { + "name": "storage.type.struct.declare.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.struct.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "name": "variable.other.object.declare.cpp" + }, + "13": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "14": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#union_declare" + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", + "captures": { + "1": { + "name": "storage.type.union.declare.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.union.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "name": "variable.other.object.declare.cpp" + }, + "13": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "14": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#enum_declare" + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", + "captures": { + "1": { + "name": "storage.type.enum.declare.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.enum.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "name": "variable.other.object.declare.cpp" + }, + "13": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "14": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#class_declare" + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", + "captures": { + "1": { + "name": "storage.type.class.declare.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.class.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "name": "variable.other.object.declare.cpp" + }, + "13": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "14": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } } ] }, "static_assert": { - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "patterns": [ @@ -11911,22 +15143,22 @@ "name": "punctuation.section.arguments.begin.bracket.round.static_assert.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.static_assert.cpp" } }, "patterns": [ { - "name": "meta.static_assert.message.cpp", - "begin": "(,)\\s*(?=(?:L|u8|u|U\\s*\\\")?)", + "begin": "(,)(?:(?:\\s)+)?(?=(?:L|u8|u|U(?:(?:\\s)+)?\\\")?)", + "end": "(?=\\))", "beginCaptures": { "1": { "name": "punctuation.separator.delimiter.comma.cpp" } }, - "end": "(?=\\))", + "endCaptures": {}, + "name": "meta.static_assert.message.cpp", "patterns": [ { "include": "#string_context" @@ -11938,8 +15170,47 @@ } ] }, + "std_space": { + "match": "(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))", + "captures": { + "0": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "1": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } + }, "storage_specifiers": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "storage.modifier.specifier.$5.cpp" + "3": { + "name": "storage.modifier.specifier.$3.cpp" } } }, @@ -11977,16 +15257,7 @@ "include": "#storage_specifiers" }, { - "include": "#primitive_types" - }, - { - "include": "#non_primitive_types" - }, - { - "include": "#pthread_types" - }, - { - "include": "#posix_reserved_types" + "include": "#inline_builtin_storage_type" }, { "include": "#decltype" @@ -11999,22 +15270,22 @@ "string_context": { "patterns": [ { - "name": "string.quoted.double.cpp", - "begin": "(((?:u|u8|U|L)?)\")", + "begin": "((?:u|u8|U|L)?)\"", + "end": "\"", "beginCaptures": { - "1": { + "0": { "name": "punctuation.definition.string.begin.cpp" }, - "2": { + "1": { "name": "meta.encoding.cpp" } }, - "end": "(\")", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.string.end.cpp" } }, + "name": "string.quoted.double.cpp", "patterns": [ { "match": "(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8})", @@ -12029,8 +15300,15 @@ "name": "constant.character.escape.cpp" }, { - "match": "\\\\x[0-9a-fA-F]{2,2}", - "name": "constant.character.escape.cpp" + "match": "(?:(\\\\x0*[0-9a-fA-F]{2}(?![0-9a-fA-F]))|((?:\\\\x[0-9a-fA-F]*|\\\\x)))", + "captures": { + "1": { + "name": "constant.character.escape.cpp" + }, + "2": { + "name": "invalid.illegal.unknown-escape.cpp" + } + } }, { "include": "#string_escapes_context_c" @@ -12038,23 +15316,34 @@ ] }, { - "name": "string.quoted.single.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.struct.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.struct.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -12252,134 +15664,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -12388,16 +15701,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.struct.cpp", "patterns": [ { - "name": "meta.head.struct.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.struct.cpp" } }, + "name": "meta.head.struct.cpp", "patterns": [ { "include": "#ever_present_context" @@ -12411,14 +15726,15 @@ ] }, { - "name": "meta.body.struct.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.struct.cpp" } }, + "name": "meta.body.struct.cpp", "patterns": [ { "include": "#function_pointer" @@ -12438,9 +15754,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.struct.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -12450,7 +15768,7 @@ ] }, "struct_declare": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", "captures": { "1": { "name": "storage.type.struct.declare.cpp" @@ -12463,34 +15781,43 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.struct.cpp" }, - "7": { + "5": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -12526,6 +15853,40 @@ } ] }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "8": { "patterns": [ { @@ -12534,106 +15895,108 @@ ] }, "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "10": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "11": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { "name": "variable.other.object.declare.cpp" }, - "21": { + "13": { "patterns": [ { "include": "#inline_comment" } ] }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { + "14": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "switch_conditional_parentheses": { - "name": "meta.conditional.switch.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "patterns": [ @@ -12664,12 +16027,12 @@ "name": "punctuation.section.parens.begin.bracket.round.conditional.switch.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parens.end.bracket.round.conditional.switch.cpp" } }, + "name": "meta.conditional.switch.cpp", "patterns": [ { "include": "#evaluation_context" @@ -12680,26 +16043,26 @@ ] }, "switch_statement": { - "name": "meta.block.switch.cpp", - "begin": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.switch.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -12711,21 +16074,23 @@ } ] }, - "6": { + "5": { "name": "keyword.control.switch.cpp" } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.block.switch.cpp", "patterns": [ { - "name": "meta.head.switch.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.switch.cpp" } }, + "name": "meta.head.switch.cpp", "patterns": [ { "include": "#switch_conditional_parentheses" @@ -12736,14 +16101,15 @@ ] }, { - "name": "meta.body.switch.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.switch.cpp" } }, + "name": "meta.body.switch.cpp", "patterns": [ { "include": "#default_statement" @@ -12760,9 +16126,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.switch.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -12772,7 +16140,7 @@ ] }, "template_argument_defaulted": { - "match": "(?<=<|,)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*([=])", + "match": "(?<=<|,)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s)+)*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?([=])", "captures": { "1": { "name": "storage.type.template.cpp" @@ -12820,32 +16188,32 @@ ] }, "template_call_innards": { - "match": "((?(?:(?>[^<>]*)\\g<1>?)+)>)\\s*", + "match": "((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<1>?)+>)(?:\\s)*+", "captures": { "0": { - "name": "meta.template.call.cpp", "patterns": [ { "include": "#template_call_range" } ] } - } + }, + "name": "meta.template.call.cpp" }, "template_call_range": { - "name": "meta.template.call.cpp", - "begin": "(<)", + "begin": "<", + "end": ">", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.angle-brackets.begin.template.call.cpp" } }, - "end": "(>)", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.angle-brackets.end.template.call.cpp" } }, + "name": "meta.template.call.cpp", "patterns": [ { "include": "#template_call_context" @@ -12853,8 +16221,8 @@ ] }, "template_definition": { - "name": "meta.template.definition.cpp", - "begin": "(?", "beginCaptures": { "1": { "name": "storage.type.template.cpp" @@ -12863,23 +16231,23 @@ "name": "punctuation.section.angle-brackets.start.template.definition.cpp" } }, - "end": "(>)", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.angle-brackets.end.template.definition.cpp" } }, + "name": "meta.template.definition.cpp", "patterns": [ { - "begin": "((?<=\\w)\\s*<)", + "begin": "(?<=\\w)(?:(?:\\s)+)?<", + "end": ">", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.angle-brackets.begin.template.call.cpp" } }, - "end": "(>)", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.angle-brackets.begin.template.call.cpp" } }, @@ -12895,7 +16263,7 @@ ] }, "template_definition_argument": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*(?:(,)|(?=>|$))", + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s)+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?(\\.\\.\\.)(?:(?:\\s)+)?((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))(?:(?:\\s)+)?(?:(,)|(?=>|$))", "captures": { "1": { "patterns": [ @@ -12905,27 +16273,36 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "storage.type.template.argument.$5.cpp" + "3": { + "name": "storage.type.template.argument.$3.cpp" }, - "6": { + "4": { "patterns": [ { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -12933,19 +16310,19 @@ } ] }, - "7": { + "5": { "name": "entity.name.type.template.cpp" }, - "8": { + "6": { "name": "storage.type.template.cpp" }, - "9": { + "7": { "name": "punctuation.vararg-ellipses.template.definition.cpp" }, - "10": { + "8": { "name": "entity.name.type.template.cpp" }, - "11": { + "9": { "name": "punctuation.separator.delimiter.comma.template.argument.cpp" } } @@ -12970,7 +16347,7 @@ ] }, "template_isolated_definition": { - "match": "(?\\s*$)", + "match": "(?(?:(?:\\s)+)?$)", "captures": { "1": { "name": "storage.type.template.cpp" @@ -12992,16 +16369,15 @@ } }, "ternary_operator": { - "applyEndPatternLast": true, - "begin": "(\\?)", + "begin": "\\?", + "end": ":", "beginCaptures": { - "1": { + "0": { "name": "keyword.operator.ternary.cpp" } }, - "end": "(:)", "endCaptures": { - "1": { + "0": { "name": "keyword.operator.ternary.cpp" } }, @@ -13060,9 +16436,6 @@ { "include": "#storage_types" }, - { - "include": "#misc_storage_modifiers" - }, { "include": "#lambdas" }, @@ -13081,19 +16454,17 @@ { "include": "#square_brackets" }, - { - "include": "#empty_square_brackets" - }, { "include": "#semicolon" }, { "include": "#comma" } - ] + ], + "applyEndPatternLast": 1 }, "the_this_keyword": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { + "3": { "name": "variable.language.this.cpp" } } }, "type_alias": { - "match": "(using)\\s*(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))\\s*(\\=)\\s*((?:typename)?)\\s*((?:(?:(?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)(?:(?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))|(.*(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:(\\[)(\\w*)(\\])\\s*)?\\s*(?:(;)|\\n)", + "match": "(using)(?:(?:\\s)+)?(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))(?:(?:\\s)+)?(\\=)(?:(?:\\s)+)?((?:typename)?)(?:(?:\\s)+)?((?:(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))|(.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)?(?:(?:\\s)+)?(?:(;)|\\n)", "captures": { "1": { "name": "keyword.other.using.directive.cpp" @@ -13135,15 +16515,16 @@ "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -13186,79 +16584,260 @@ ] }, "5": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "6": { - "name": "comment.block.cpp" - }, - "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "8": { + "6": { "patterns": [ { "include": "#inline_comment" } ] }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, - "10": { - "name": "comment.block.cpp" + "8": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "14": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + "name": "keyword.operator.assignment.cpp" + }, + "15": { + "name": "keyword.other.typename.cpp" }, "16": { - "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#storage_specifiers" + } + ] + }, + "17": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "18": { - "name": "entity.name.scope-resolution.cpp" - }, - "19": { - "name": "meta.template.call.cpp", + "name": "meta.qualified_type.cpp", "patterns": [ { - "include": "#template_call_range" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + { + "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.type.cpp" + } + ] + }, + "19": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "20": { + "patterns": [ + { + "include": "#inline_comment" } ] }, "21": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "22": { "patterns": [ @@ -13268,213 +16847,89 @@ ] }, "23": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "24": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "30": { - "name": "keyword.other.typename.cpp" - }, - "31": { - "patterns": [ - { - "include": "#storage_specifiers" - } - ] - }, - "32": { - "name": "meta.qualified_type.cpp", - "patterns": [ - { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -13525,102 +16980,129 @@ } ] }, - "61": { + "32": { "patterns": [ { "include": "#inline_comment" } ] }, - "62": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "63": { - "name": "comment.block.cpp" - }, - "64": { + "33": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "65": { + "34": { "patterns": [ { "include": "#inline_comment" } ] }, - "66": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "67": { - "name": "comment.block.cpp" - }, - "68": { + "35": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "69": { + "36": { "patterns": [ { "include": "#inline_comment" } ] }, - "70": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "71": { - "name": "comment.block.cpp" - }, - "72": { + "37": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "73": { + "38": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "74": { + "39": { "patterns": [ { "include": "#evaluation_context" } ] }, - "75": { + "40": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "76": { + "41": { "name": "punctuation.terminator.statement.cpp" } }, "name": "meta.declaration.type.alias.cpp" }, "type_casting_operators": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "keyword.operator.wordlike.cpp keyword.operator.cast.$5.cpp" + "3": { + "name": "keyword.operator.wordlike.cpp keyword.operator.cast.$3.cpp" } } }, "typedef_class": { - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.class.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.class.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -13759,134 +17354,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -13895,16 +17391,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.class.cpp", "patterns": [ { - "name": "meta.head.class.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.class.cpp" } }, + "name": "meta.head.class.cpp", "patterns": [ { "include": "#ever_present_context" @@ -13918,14 +17416,15 @@ ] }, { - "name": "meta.body.class.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.class.cpp" } }, + "name": "meta.body.class.cpp", "patterns": [ { "include": "#function_pointer" @@ -13945,12 +17444,14 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.class.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { - "match": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14085,30 +17586,33 @@ ] }, "typedef_function_pointer": { - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -14193,52 +17714,43 @@ } ] }, + "11": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14304,111 +17806,110 @@ } ] }, - "29": { + "20": { "patterns": [ { "include": "#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "entity.name.type.alias.cpp entity.name.type.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()", "endCaptures": { "1": { "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" @@ -14447,135 +17948,206 @@ } ] }, - "typedef_keyword": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.struct.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.struct.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -14587,134 +18159,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -14723,16 +18196,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.struct.cpp", "patterns": [ { - "name": "meta.head.struct.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.struct.cpp" } }, + "name": "meta.head.struct.cpp", "patterns": [ { "include": "#ever_present_context" @@ -14746,14 +18221,15 @@ ] }, { - "name": "meta.body.struct.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.struct.cpp" } }, + "name": "meta.body.struct.cpp", "patterns": [ { "include": "#function_pointer" @@ -14773,12 +18249,14 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.struct.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { - "match": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14913,101 +18391,205 @@ ] }, "typedef_union": { - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.union.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.union.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -15019,134 +18601,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -15155,16 +18638,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.union.cpp", "patterns": [ { - "name": "meta.head.union.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.union.cpp" } }, + "name": "meta.head.union.cpp", "patterns": [ { "include": "#ever_present_context" @@ -15178,14 +18663,15 @@ ] }, { - "name": "meta.body.union.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.union.cpp" } }, + "name": "meta.body.union.cpp", "patterns": [ { "include": "#function_pointer" @@ -15205,12 +18691,14 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.union.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { - "match": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -15345,8 +18833,8 @@ ] }, "typeid_operator": { - "contentName": "meta.arguments.operator.typeid.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.typeid.cpp" @@ -15380,12 +18868,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.typeid.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.typeid.cpp" } }, + "contentName": "meta.arguments.operator.typeid", "patterns": [ { "include": "#evaluation_context" @@ -15393,7 +18881,7 @@ ] }, "typename": { - "match": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(?![\\w<:.]))", + "match": "(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "storage.modifier.cpp" @@ -15406,61 +18894,80 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "patterns": [ { "include": "#inline_comment" } ] }, - "7": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "8": { - "name": "comment.block.cpp" - }, - "9": { + "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "10": { + "6": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -15485,7 +19009,7 @@ } ] }, - "11": { + "7": { "patterns": [ { "include": "#attributes_context" @@ -15495,128 +19019,136 @@ } ] }, - "12": { + "8": { "patterns": [ { "include": "#inline_comment" } ] }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "21": { - "patterns": [ - { - "include": "#scope_resolution_inner_generated" - } - ] - }, - "22": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "24": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "26": { - "name": "entity.name.scope-resolution.cpp" - }, - "27": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "29": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { - "name": "entity.name.type.cpp" - }, - "35": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } + "17": {} } }, "undef": { - "match": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*undef\\b)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?undef\\b)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "punctuation.definition.directive.cpp" }, - "7": { + "5": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { + "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "11": { + "7": { "name": "entity.name.function.preprocessor.cpp" } }, "name": "meta.preprocessor.undef.cpp" }, "union_block": { - "name": "meta.block.union.cpp", - "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.union.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.union.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -15778,134 +19431,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -15914,16 +19468,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.union.cpp", "patterns": [ { - "name": "meta.head.union.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.union.cpp" } }, + "name": "meta.head.union.cpp", "patterns": [ { "include": "#ever_present_context" @@ -15937,14 +19493,15 @@ ] }, { - "name": "meta.body.union.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.union.cpp" } }, + "name": "meta.body.union.cpp", "patterns": [ { "include": "#function_pointer" @@ -15964,9 +19521,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.union.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -15976,7 +19535,7 @@ ] }, "union_declare": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", "captures": { "1": { "name": "storage.type.union.declare.cpp" @@ -15989,34 +19548,43 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.union.cpp" }, - "7": { + "5": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -16052,6 +19620,40 @@ } ] }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "8": { "patterns": [ { @@ -16060,105 +19662,107 @@ ] }, "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "10": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "11": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { "name": "variable.other.object.declare.cpp" }, - "21": { + "13": { "patterns": [ { "include": "#inline_comment" } ] }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { + "14": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "using_name": { - "match": "(using)\\s+(?!namespace\\b)", + "match": "(using)(?:\\s)+(?!namespace\\b)", "captures": { "1": { "name": "keyword.other.using.directive.cpp" @@ -16166,8 +19770,8 @@ } }, "using_namespace": { - "name": "meta.using-namespace.cpp", - "begin": "(?(?:(?>[^<>]*)\\g<7>?)+)>)\\s*)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ",", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pugi", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "xml_node", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": ",", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "less", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": "<", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "basic_string", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": "<", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "char", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ",", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "allocator", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": "<", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pair", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": "<", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "const", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp storage.modifier.specifier.const.cpp", - "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "basic_string", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": "<", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "char", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ",", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pugi", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "xml_node", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "type dnode", + "c": "std::tuple_element<0, std::pair, pugi::xml_node, std::less >, std::allocator, pugi::xml_node> > > > >::type dnode", "t": "source.cpp", "r": { "dark_plus": "default: #D4D4D4", @@ -869,29 +11,7 @@ } }, { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "_Rb_tree_iterator", + "c": "std::_Rb_tree_iterator, pugi::xml_node, std::less >, std::allocator, pugi::xml_node> > > > > > dnode_it = dnodes_.find(uid.position)", "t": "source.cpp", "r": { "dark_plus": "default: #D4D4D4", @@ -900,1028 +20,5 @@ "light_vs": "default: #000000", "hc_black": "default: #FFFFFF" } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pair", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "const", - "t": "source.cpp storage.modifier.specifier.const.cpp", - "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "long", - "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pair", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "pugi", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "xml_node", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "map", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "basic_string", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "char", - "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pugi", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "xml_node", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "less", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "basic_string", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "char", - "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "allocator", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pair", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "const", - "t": "source.cpp storage.modifier.specifier.const.cpp", - "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "basic_string", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "char", - "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pugi", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "xml_node", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " dnode_it ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "=", - "t": "source.cpp keyword.operator.assignment.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "dnodes_", - "t": "source.cpp variable.other.object.access.cpp", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" - } - }, - { - "c": ".", - "t": "source.cpp punctuation.separator.dot-access.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "find", - "t": "source.cpp entity.name.function.member.cpp", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "(", - "t": "source.cpp punctuation.section.arguments.begin.bracket.round.function.member.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "uid", - "t": "source.cpp variable.other.object.access.cpp", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" - } - }, - { - "c": ".", - "t": "source.cpp punctuation.separator.dot-access.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "position", - "t": "source.cpp variable.other.property.cpp", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" - } - }, - { - "c": ")", - "t": "source.cpp punctuation.section.arguments.end.bracket.round.function.member.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } } ] \ No newline at end of file diff --git a/extensions/cpp/test/colorize-results/test_cc.json b/extensions/cpp/test/colorize-results/test_cc.json index 324b231bb..cb2fb192e 100644 --- a/extensions/cpp/test/colorize-results/test_cc.json +++ b/extensions/cpp/test/colorize-results/test_cc.json @@ -122,7 +122,7 @@ }, { "c": "%d", - "t": "source.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp string.quoted.double.cpp constant.other.placeholder", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -430,7 +430,7 @@ }, { "c": "%d", - "t": "source.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp string.quoted.double.cpp constant.other.placeholder", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -474,7 +474,7 @@ }, { "c": "user_candidate", - "t": "source.cpp meta.bracket.square.access.cpp variable.other.object.cpp", + "t": "source.cpp meta.bracket.square.access variable.other.object", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -485,7 +485,7 @@ }, { "c": "[", - "t": "source.cpp meta.bracket.square.access.cpp punctuation.definition.begin.bracket.square.cpp", + "t": "source.cpp meta.bracket.square.access punctuation.definition.begin.bracket.square", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -496,7 +496,7 @@ }, { "c": "i", - "t": "source.cpp meta.bracket.square.access.cpp", + "t": "source.cpp meta.bracket.square.access", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,7 +507,7 @@ }, { "c": "]", - "t": "source.cpp meta.bracket.square.access.cpp punctuation.definition.end.bracket.square.cpp", + "t": "source.cpp meta.bracket.square.access punctuation.definition.end.bracket.square", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -727,7 +727,7 @@ }, { "c": "O", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp entity.name.type.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp entity.name.type.parameter.cpp", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -738,7 +738,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -749,7 +749,7 @@ }, { "c": "obj", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp variable.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp variable.parameter.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1464,7 +1464,7 @@ }, { "c": "%s", - "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.other.placeholder", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1486,7 +1486,7 @@ }, { "c": "%s", - "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.other.placeholder", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1805,7 +1805,7 @@ }, { "c": "movw $0x38, %ax; ltr %ax", - "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly.cpp", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly", "r": { "dark_plus": "meta.embedded.assembly: #CE9178", "light_plus": "meta.embedded.assembly: #A31515", @@ -1979,4 +1979,4 @@ "hc_black": "default: #FFFFFF" } } -] +] \ No newline at end of file diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json index 10f6041bb..7752bbfa6 100644 --- a/extensions/cpp/test/colorize-results/test_cpp.json +++ b/extensions/cpp/test/colorize-results/test_cpp.json @@ -287,7 +287,7 @@ }, { "c": "Rectangle", - "t": "source.cpp meta.block.class.cpp meta.head.class.cpp entity.name.type.class.cpp", + "t": "source.cpp meta.block.class.cpp entity.name.type.class.cpp", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -298,7 +298,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.head.class.cpp", + "t": "source.cpp meta.block.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -485,7 +485,7 @@ }, { "c": "int", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -496,7 +496,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp punctuation.separator.delimiter.comma.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp punctuation.separator.delimiter.comma.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,7 +507,7 @@ }, { "c": "int", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -793,7 +793,7 @@ }, { "c": "int", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -804,7 +804,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -815,7 +815,7 @@ }, { "c": "x", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp variable.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp variable.parameter.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -826,7 +826,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp punctuation.separator.delimiter.comma.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp punctuation.separator.delimiter.comma.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -837,7 +837,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +848,7 @@ }, { "c": "int", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -859,7 +859,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -870,7 +870,7 @@ }, { "c": "y", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp variable.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp variable.parameter.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1123,7 +1123,7 @@ }, { "c": "long", - "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1134,7 +1134,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload.cpp meta.parameter.cpp", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload meta.parameter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1145,7 +1145,7 @@ }, { "c": "double", - "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1519,7 +1519,7 @@ }, { "c": "movl %a %b", - "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly.cpp", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly", "r": { "dark_plus": "meta.embedded.assembly: #CE9178", "light_plus": "meta.embedded.assembly: #A31515", diff --git a/extensions/css-language-features/.vscode/launch.json b/extensions/css-language-features/.vscode/launch.json index 4f7166e6d..8fccefff4 100644 --- a/extensions/css-language-features/.vscode/launch.json +++ b/extensions/css-language-features/.vscode/launch.json @@ -1,14 +1,5 @@ { "version": "0.2.0", - "compounds": [ - { - "name": "Debug Extension and Language Server", - "configurations": [ - "Launch Extension", - "Attach Language Server" - ] - } - ], "configurations": [ { "name": "Launch Extension", @@ -41,19 +32,6 @@ ] }, { - "name": "Attach Language Server", - "type": "node", - "request": "attach", - "protocol": "inspector", - "port": 6044, - "sourceMaps": true, - "outFiles": [ - "${workspaceFolder}/server/out/**/*.js" - ], - "smartStep": true, - "restart": true - }, - { "name": "Server Unit Tests", "type": "node", "request": "launch", @@ -74,4 +52,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/extensions/css-language-features/client/src/node/cssClientMain.ts b/extensions/css-language-features/client/src/node/cssClientMain.ts index cc675b494..b88838d89 100644 --- a/extensions/css-language-features/client/src/node/cssClientMain.ts +++ b/extensions/css-language-features/client/src/node/cssClientMain.ts @@ -11,14 +11,13 @@ import { TextDecoder } from 'util'; // this method is called when vs code is activated export function activate(context: ExtensionContext) { - const clientMain = extensions.getExtension('vscode.css-language-features')?.packageJSON?.main || ''; const serverMain = `./server/${clientMain.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/cssServerMain`; const serverModule = context.asAbsolutePath(serverMain); // The debug options for the server - const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; + const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (7000 + Math.round(Math.random() * 999))] }; // If the extension is launch in debug mode the debug server options are use // Otherwise the run options are used diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index f249a512a..5a2cd56cf 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -10,7 +10,7 @@ "main": "./out/node/cssServerMain", "browser": "./dist/browser/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^4.3.5", + "vscode-css-languageservice": "^4.4.0", "vscode-languageserver": "7.0.0-next.3", "vscode-uri": "^2.1.2" }, diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts index 0b30955bd..e62a8ecc2 100644 --- a/extensions/css-language-features/server/src/cssServer.ts +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -90,7 +90,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, - completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-'] } : undefined, + completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-', ':'] } : undefined, hoverProvider: true, documentSymbolProvider: true, referencesProvider: true, diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 2d2ffcf8e..af0eddbb9 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -696,10 +696,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -vscode-css-languageservice@^4.3.5: - version "4.3.5" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.5.tgz#92f8817057dee7c381df2289aad539c7b553548a" - integrity sha512-g9Pjxt9T32jhY0nTOo7WRFm0As27IfdaAxcFa8c7Rml1ZqBn3XXbkExjzxY7sBWYm7I1Tp4dK6UHXHoUQHGwig== +vscode-css-languageservice@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.4.0.tgz#a7c5edf3057e707601ca18fa3728784a298513b4" + integrity sha512-jWi+297PJUUWTHwlcrZz0zIuEXuHOBJIQMapXmEzbosWGv/gMnNSAMV4hTKnl5wzxvZKZzV6j+WFdrSlKQ5qnw== dependencies: vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" diff --git a/extensions/debug-auto-launch/src/extension.ts b/extensions/debug-auto-launch/src/extension.ts index 5ae907a9d..b47641f2d 100644 --- a/extensions/debug-auto-launch/src/extension.ts +++ b/extensions/debug-auto-launch/src/extension.ts @@ -9,9 +9,13 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -const TEXT_ALWAYS = localize('status.text.auto.attach.always', 'Auto Attach: Always'); -const TEXT_SMART = localize('status.text.auto.attach.smart', 'Auto Attach: Smart'); -const TEXT_WITH_FLAG = localize('status.text.auto.attach.withFlag', 'Auto Attach: With Flag'); +const TEXT_STATUSBAR_LABEL = { + [State.Disabled]: localize('status.text.auto.attach.disabled', 'Auto Attach: Disabled'), + [State.Always]: localize('status.text.auto.attach.always', 'Auto Attach: Always'), + [State.Smart]: localize('status.text.auto.attach.smart', 'Auto Attach: Smart'), + [State.OnlyWithFlag]: localize('status.text.auto.attach.withFlag', 'Auto Attach: With Flag'), +}; + const TEXT_STATE_LABEL = { [State.Disabled]: localize('debug.javascript.autoAttach.disabled.label', 'Disabled'), [State.Always]: localize('debug.javascript.autoAttach.always.label', 'Always'), @@ -41,6 +45,9 @@ const TEXT_STATE_DESCRIPTION = { }; const TEXT_TOGGLE_WORKSPACE = localize('scope.workspace', 'Toggle auto attach in this workspace'); const TEXT_TOGGLE_GLOBAL = localize('scope.global', 'Toggle auto attach on this machine'); +const TEXT_TEMP_DISABLE = localize('tempDisable.disable', 'Temporarily disable auto attach in this session'); +const TEXT_TEMP_ENABLE = localize('tempDisable.enable', 'Re-enable auto attach'); +const TEXT_TEMP_DISABLE_LABEL = localize('tempDisable.suffix', 'Auto Attach: Disabled'); const TOGGLE_COMMAND = 'extension.node-debug.toggleAutoAttach'; const STORAGE_IPC = 'jsDebugIpcState'; @@ -65,12 +72,13 @@ const enum State { let currentState: Promise<{ context: vscode.ExtensionContext; state: State | null }>; let statusItem: vscode.StatusBarItem | undefined; // and there is no status bar item let server: Promise | undefined; // auto attach server +let isTemporarilyDisabled = false; // whether the auto attach server is disabled temporarily, reset whenever the state changes export function activate(context: vscode.ExtensionContext): void { currentState = Promise.resolve({ context, state: null }); context.subscriptions.push( - vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttachSetting), + vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttachSetting.bind(null, context)), ); context.subscriptions.push( @@ -108,24 +116,36 @@ function getDefaultScope(info: ReturnType { +async function toggleAutoAttachSetting(context: vscode.ExtensionContext, scope?: vscode.ConfigurationTarget): Promise { const section = vscode.workspace.getConfiguration(SETTING_SECTION); scope = scope || getDefaultScope(section.inspect(SETTING_STATE)); const isGlobalScope = scope === vscode.ConfigurationTarget.Global; - const quickPick = vscode.window.createQuickPick(); + const quickPick = vscode.window.createQuickPick(); const current = readCurrentState(); - quickPick.items = [State.Always, State.Smart, State.OnlyWithFlag, State.Disabled].map(state => ({ + const items: PickItem[] = [State.Always, State.Smart, State.OnlyWithFlag, State.Disabled].map(state => ({ state, label: TEXT_STATE_LABEL[state], description: TEXT_STATE_DESCRIPTION[state], alwaysShow: true, })); - quickPick.activeItems = quickPick.items.filter(i => i.state === current); + if (current !== State.Disabled) { + items.unshift({ + setTempDisabled: !isTemporarilyDisabled, + label: isTemporarilyDisabled ? TEXT_TEMP_ENABLE : TEXT_TEMP_DISABLE, + alwaysShow: true, + }); + } + + quickPick.items = items; + quickPick.activeItems = isTemporarilyDisabled + ? [items[0]] + : quickPick.items.filter(i => 'state' in i && i.state === current); quickPick.title = isGlobalScope ? TEXT_TOGGLE_GLOBAL : TEXT_TOGGLE_WORKSPACE; quickPick.buttons = [ { @@ -136,7 +156,7 @@ async function toggleAutoAttachSetting(scope?: vscode.ConfigurationTarget): Prom quickPick.show(); - const result = await new Promise(resolve => { + let result = await new Promise(resolve => { quickPick.onDidAccept(() => resolve(quickPick.selectedItems[0])); quickPick.onDidHide(() => resolve(undefined)); quickPick.onDidTriggerButton(() => { @@ -155,11 +175,26 @@ async function toggleAutoAttachSetting(scope?: vscode.ConfigurationTarget): Prom } if ('scope' in result) { - return await toggleAutoAttachSetting(result.scope); + return await toggleAutoAttachSetting(context, result.scope); } if ('state' in result) { - section.update(SETTING_STATE, result.state, scope); + if (result.state !== current) { + section.update(SETTING_STATE, result.state, scope); + } else if (isTemporarilyDisabled) { + result = { setTempDisabled: false }; + } + } + + if ('setTempDisabled' in result) { + updateStatusBar(context, current, true); + isTemporarilyDisabled = result.setTempDisabled; + if (result.setTempDisabled) { + await destroyAttachServer(); + } else { + await createAttachServer(context); // unsets temp disabled var internally + } + updateStatusBar(context, current, false); } } @@ -168,26 +203,6 @@ function readCurrentState(): State { return section.get(SETTING_STATE) ?? State.Disabled; } -/** - * Makes sure the status bar exists and is visible. - */ -function ensureStatusBarExists(context: vscode.ExtensionContext) { - if (!statusItem) { - statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); - statusItem.command = TOGGLE_COMMAND; - statusItem.tooltip = localize( - 'status.tooltip.auto.attach', - 'Automatically attach to node.js processes in debug mode', - ); - statusItem.show(); - context.subscriptions.push(statusItem); - } else { - statusItem.show(); - } - - return statusItem; -} - async function clearJsDebugAttachState(context: vscode.ExtensionContext) { await context.workspaceState.update(STORAGE_IPC, undefined); await vscode.commands.executeCommand('extension.js-debug.clearAutoAttachVariables'); @@ -275,28 +290,46 @@ interface CachedIpcState { const transitions: { [S in State]: (context: vscode.ExtensionContext) => Promise } = { async [State.Disabled](context) { await clearJsDebugAttachState(context); - statusItem?.hide(); }, async [State.OnlyWithFlag](context) { await createAttachServer(context); - const statusItem = ensureStatusBarExists(context); - statusItem.text = TEXT_WITH_FLAG; }, async [State.Smart](context) { await createAttachServer(context); - const statusItem = ensureStatusBarExists(context); - statusItem.text = TEXT_SMART; }, async [State.Always](context) { await createAttachServer(context); - const statusItem = ensureStatusBarExists(context); - statusItem.text = TEXT_ALWAYS; }, }; +/** + * Ensures the status bar text reflects the current state. + */ +function updateStatusBar(context: vscode.ExtensionContext, state: State, busy = false) { + if (state === State.Disabled && !busy) { + statusItem?.hide(); + return; + } + + if (!statusItem) { + statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); + statusItem.command = TOGGLE_COMMAND; + statusItem.tooltip = localize( + 'status.tooltip.auto.attach', + 'Automatically attach to node.js processes in debug mode', + ); + context.subscriptions.push(statusItem); + } + + let text = busy ? '$(loading) ' : ''; + text += isTemporarilyDisabled ? TEXT_TEMP_DISABLE_LABEL : TEXT_STATUSBAR_LABEL[state]; + statusItem.text = text; + statusItem.show(); +} + /** * Updates the auto attach feature based on the user or workspace setting */ @@ -306,7 +339,13 @@ function updateAutoAttach(newState: State) { return { context, state: oldState }; } + if (oldState !== null) { + updateStatusBar(context, oldState, true); + } + await transitions[newState](context); + isTemporarilyDisabled = false; + updateStatusBar(context, newState, false); return { context, state: newState }; }); } diff --git a/extensions/debug-server-ready/package.json b/extensions/debug-server-ready/package.json index abb9a683e..111699064 100644 --- a/extensions/debug-server-ready/package.json +++ b/extensions/debug-server-ready/package.json @@ -39,8 +39,7 @@ "openExternally" ], "enumDescriptions": [ - "%debug.server.ready.action.openExternally.description%", - "%debug.server.ready.action.debugWithChrome.description%" + "%debug.server.ready.action.openExternally.description%" ], "markdownDescription": "%debug.server.ready.action.description%", "default": "openExternally" @@ -71,7 +70,6 @@ "debugWithChrome" ], "enumDescriptions": [ - "%debug.server.ready.action.openExternally.description%", "%debug.server.ready.action.debugWithChrome.description%" ], "markdownDescription": "%debug.server.ready.action.description%", @@ -93,6 +91,39 @@ "default": "${workspaceFolder}" } } + }, + { + "type": "object", + "additionalProperties": false, + "markdownDescription": "%debug.server.ready.serverReadyAction.description%", + "default": { + "action": "startDebugging", + "name": "" + }, + "required": ["name"], + "properties": { + "action": { + "type": "string", + "enum": [ + "startDebugging" + ], + "enumDescriptions": [ + "%debug.server.ready.action.startDebugging.description%" + ], + "markdownDescription": "%debug.server.ready.action.description%", + "default": "startDebugging" + }, + "pattern": { + "type": "string", + "markdownDescription": "%debug.server.ready.pattern.description%", + "default": "listening on port ([0-9]+)" + }, + "name": { + "type": "string", + "markdownDescription": "%debug.server.ready.debugConfigName.description%", + "default": "Launch Browser" + } + } } ] } diff --git a/extensions/debug-server-ready/package.nls.json b/extensions/debug-server-ready/package.nls.json index e9d2705cf..c5212d7ee 100644 --- a/extensions/debug-server-ready/package.nls.json +++ b/extensions/debug-server-ready/package.nls.json @@ -6,7 +6,9 @@ "debug.server.ready.action.description": "What to do with the URI when the server is ready.", "debug.server.ready.action.openExternally.description": "Open URI externally with the default application.", "debug.server.ready.action.debugWithChrome.description": "Start debugging with the 'Debugger for Chrome'.", + "debug.server.ready.action.startDebugging.description": "Run another launch configuration.", "debug.server.ready.pattern.description": "Server is ready if this pattern appears on the debug console. The first capture group must include a URI or a port number.", "debug.server.ready.uriFormat.description": "A format string used when constructing the URI from a port number. The first '%s' is substituted with the port number.", - "debug.server.ready.webRoot.description": "Value passed to the debug configuration for the 'Debugger for Chrome'." + "debug.server.ready.webRoot.description": "Value passed to the debug configuration for the 'Debugger for Chrome'.", + "debug.server.ready.debugConfigName.description": "Name of the launch configuration to run." } diff --git a/extensions/debug-server-ready/src/extension.ts b/extensions/debug-server-ready/src/extension.ts index 9f8737f30..19fc67322 100644 --- a/extensions/debug-server-ready/src/extension.ts +++ b/extensions/debug-server-ready/src/extension.ts @@ -16,9 +16,10 @@ const WEB_ROOT = '${workspaceFolder}'; interface ServerReadyAction { pattern: string; - action?: 'openExternally' | 'debugWithChrome'; + action?: 'openExternally' | 'debugWithChrome' | 'startDebugging'; uriFormat?: string; webRoot?: string; + name?: string; } class ServerReadyDetector extends vscode.Disposable { @@ -155,6 +156,10 @@ class ServerReadyDetector extends vscode.Disposable { }); break; + case 'startDebugging': + vscode.debug.startDebugging(session.workspaceFolder, args.name || 'unspecified'); + break; + default: // not supported break; diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index fbb17884a..12178fa8b 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -342,6 +342,11 @@ "command": "editor.emmet.action.reflectCSSValue", "title": "%command.reflectCSSValue%", "category": "Emmet" + }, + { + "command": "workbench.action.showEmmetCommands", + "title": "%command.showEmmetCommands%", + "category": "" } ], "menus": { @@ -433,7 +438,7 @@ "dependencies": { "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", "@emmetio/html-matcher": "^0.3.3", - "@emmetio/math-expression": "^0.1.1", + "@emmetio/math-expression": "^1.0.4", "image-size": "^0.5.2", "vscode-emmet-helper": "~2.0.0", "vscode-html-languageservice": "^3.0.3" diff --git a/extensions/emmet/package.nls.json b/extensions/emmet/package.nls.json index 3f3bf912f..2a1add893 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -23,6 +23,7 @@ "command.decrementNumberByOneTenth": "Decrement by 0.1", "command.incrementNumberByTen": "Increment by 10", "command.decrementNumberByTen": "Decrement by 10", + "command.showEmmetCommands": "Show Emmet Commands", "emmetSyntaxProfiles": "Define profile for specified syntax or use your own profile with specific rules.", "emmetExclude": "An array of languages where Emmet abbreviations should not be expanded.", "emmetExtensionsPath": "Path to a folder containing Emmet profiles and snippets.", diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 16dc7b61d..a37741b8e 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -138,6 +138,7 @@ function doWrapping(individualLines: boolean, args: any) { newText = newText.replace(/\$\{[\d]*(:[^}]*)?\}/g, (match) => { // Replacing Placeholders return match.replace(/^\$\{[\d]*:/, '').replace('}', ''); }); + newText = newText.replace(/\\\$/g, '$'); // Remove backslashes before $ builder.replace(oldPreviewRange, newText); const expandedTextLines = newText.split('\n'); diff --git a/extensions/emmet/src/emmetCommon.ts b/extensions/emmet/src/emmetCommon.ts index b508b2a9f..e768b03af 100644 --- a/extensions/emmet/src/emmetCommon.ts +++ b/extensions/emmet/src/emmetCommon.ts @@ -124,6 +124,10 @@ export function activateEmmetExtension(context: vscode.ExtensionContext) { return reflectCssValue(); })); + context.subscriptions.push(vscode.commands.registerCommand('workbench.action.showEmmetCommands', () => { + vscode.commands.executeCommand('workbench.action.quickOpen', '>Emmet: '); + })); + updateEmmetExtensionsPath(); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { diff --git a/extensions/emmet/src/evaluateMathExpression.ts b/extensions/emmet/src/evaluateMathExpression.ts index 8a7de6ca4..588d4dce9 100644 --- a/extensions/emmet/src/evaluateMathExpression.ts +++ b/extensions/emmet/src/evaluateMathExpression.ts @@ -6,24 +6,42 @@ /* Based on @sergeche's work in his emmet plugin */ import * as vscode from 'vscode'; -import evaluate from '@emmetio/math-expression'; +import evaluate, { extract } from '@emmetio/math-expression'; import { DocumentStreamReader } from './bufferStream'; -export function evaluateMathExpression() { +export function evaluateMathExpression(): Thenable { if (!vscode.window.activeTextEditor) { vscode.window.showInformationMessage('No editor is active'); - return; + return Promise.resolve(false); } const editor = vscode.window.activeTextEditor; const stream = new DocumentStreamReader(editor.document); - editor.edit(editBuilder => { + return editor.edit(editBuilder => { editor.selections.forEach(selection => { - const pos = selection.isReversed ? selection.anchor : selection.active; - stream.pos = pos; + // startpos always comes before endpos + const startpos = selection.isReversed ? selection.active : selection.anchor; + const endpos = selection.isReversed ? selection.anchor : selection.active; + const selectionText = stream.substring(startpos, endpos); try { - const result = String(evaluate(stream, true)); - editBuilder.replace(new vscode.Range(stream.pos, pos), result); + if (selectionText) { + // respect selections + const result = String(evaluate(selectionText)); + editBuilder.replace(new vscode.Range(startpos, endpos), result); + } else { + // no selection made, extract expression from line + const lineToSelectionEnd = stream.substring(new vscode.Position(selection.end.line, 0), endpos); + const extractedIndices = extract(lineToSelectionEnd); + if (!extractedIndices) { + throw new Error('Invalid extracted indices'); + } + const result = String(evaluate(lineToSelectionEnd.substr(extractedIndices[0], extractedIndices[1]))); + const rangeToReplace = new vscode.Range( + new vscode.Position(selection.end.line, extractedIndices[0]), + new vscode.Position(selection.end.line, extractedIndices[1]) + ); + editBuilder.replace(rangeToReplace, result); + } } catch (err) { vscode.window.showErrorMessage('Could not evaluate expression'); // Ignore error since most likely it’s because of non-math expression @@ -31,5 +49,4 @@ export function evaluateMathExpression() { } }); }); - } diff --git a/extensions/emmet/src/test/evaluateMathExpression.test.ts b/extensions/emmet/src/test/evaluateMathExpression.test.ts new file mode 100644 index 000000000..553d04171 --- /dev/null +++ b/extensions/emmet/src/test/evaluateMathExpression.test.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'mocha'; +import * as assert from 'assert'; +import { Position, Selection } from 'vscode'; +import { withRandomFileEditor, closeAllEditors } from './testUtils'; +import { evaluateMathExpression } from '../evaluateMathExpression'; + +suite('Tests for Evaluate Math Expression', () => { + teardown(closeAllEditors); + + function testEvaluateMathExpression(fileContents: string, selection: [number, number] | number, expectedFileContents: string): Thenable { + return withRandomFileEditor(fileContents, 'html', async (editor, _doc) => { + const selectionToUse = typeof selection === 'number' ? + new Selection(new Position(0, selection), new Position(0, selection)) : + new Selection(new Position(0, selection[0]), new Position(0, selection[1])); + editor.selection = selectionToUse; + + await evaluateMathExpression(); + + assert.strictEqual(editor.document.getText(), expectedFileContents); + return Promise.resolve(); + }); + } + + test('Selected sanity check', () => { + return testEvaluateMathExpression('1 + 2', [0, 5], '3'); + }); + + test('Selected with surrounding text', () => { + return testEvaluateMathExpression('test1 + 2test', [4, 9], 'test3test'); + }); + + test('Selected with number not part of selection', () => { + return testEvaluateMathExpression('test3 1+2', [6, 9], 'test3 3'); + }); + + test('Non-selected sanity check', () => { + return testEvaluateMathExpression('1 + 2', 5, '3'); + }); + + test('Non-selected midway', () => { + return testEvaluateMathExpression('1 + 2', 1, '1 + 2'); + }); + + test('Non-selected with surrounding text', () => { + return testEvaluateMathExpression('test1 + 3test', 9, 'test4test'); + }); +}); diff --git a/extensions/emmet/src/typings/emmetio__math-expression.d.ts b/extensions/emmet/src/typings/emmetio__math-expression.d.ts deleted file mode 100644 index 4c3d862b0..000000000 --- a/extensions/emmet/src/typings/emmetio__math-expression.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -declare module '@emmetio/math-expression' { - import { BufferStream } from 'EmmetNode'; - - function index(stream: BufferStream, backward: boolean): number; - - export default index; -} - diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index bb19fbcd0..d956865f4 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -31,13 +31,12 @@ "@emmetio/stream-reader" "^2.0.0" "@emmetio/stream-reader-utils" "^0.1.0" -"@emmetio/math-expression@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@emmetio/math-expression/-/math-expression-0.1.1.tgz#1ff2c7f05800f64c57ca89038ee18bce9f5776dc" - integrity sha1-H/LH8FgA9kxXyokDjuGLzp9Xdtw= +"@emmetio/math-expression@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@emmetio/math-expression/-/math-expression-1.0.4.tgz#cb657ed944f82b3728f863bf5ece1b1ff3ae7497" + integrity sha512-1m7y8/VeXCAfgFoPGTerbqCIadApcIINujd3TaM/LRLPPKiod8aT1PPmh542spnsUSsSnZJjbuF7xiO4WFA42g== dependencies: - "@emmetio/stream-reader" "^2.0.1" - "@emmetio/stream-reader-utils" "^0.1.0" + "@emmetio/scanner" "^1.0.0" "@emmetio/scanner@^1.0.0": version "1.0.0" @@ -49,15 +48,15 @@ resolved "https://registry.yarnpkg.com/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz#244cb02c77ec2e74f78a9bd318218abc9c500a61" integrity sha1-JEywLHfsLnT3ipvTGCGKvJxQCmE= -"@emmetio/stream-reader@^2.0.0", "@emmetio/stream-reader@^2.0.1", "@emmetio/stream-reader@^2.2.0": +"@emmetio/stream-reader@^2.0.0", "@emmetio/stream-reader@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz#46cffea119a0a003312a21c2d9b5628cb5fcd442" integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI= "@types/node@^12.11.7": - version "12.19.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.2.tgz#9565ed5c72ba96038fc3add643edd5e7820598e7" - integrity sha512-SRH6QM0IMOBBFmDiJ75vlhcbUEYEquvSuhsVW9ijG20JvdFTfOrB1p6ddZxz5y/JNnbf+9HoHhjhOVSX2hsJyA== + version "12.19.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.4.tgz#cdfbb62e26c7435ed9aab9c941393cc3598e9b46" + integrity sha512-o3oj1bETk8kBwzz1WlO6JWL/AfAA3Vm6J1B3C9CsdxHYp7XgPiH7OEXPUbZTndHlRaIElrANkQfe6ZmfJb3H2w== ajv@^6.12.3: version "6.12.6" @@ -178,9 +177,9 @@ aws-sign2@~0.7.0: integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" - integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== balanced-match@^1.0.0: version "1.0.0" @@ -392,12 +391,12 @@ debug@^2.2.0: dependencies: ms "2.0.0" -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== +debug@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== dependencies: - ms "^2.1.1" + ms "2.1.2" decamelize@^1.1.2: version "1.2.0" @@ -994,9 +993,9 @@ is-buffer@^1.1.5, is-buffer@~1.1.6: integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-core-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.0.0.tgz#58531b70aed1db7c0e8d4eb1a0a2d1ddd64bd12d" - integrity sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.1.0.tgz#a4cc031d9b1aca63eecbd18a650e13cb4eeab946" + integrity sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA== dependencies: has "^1.0.3" @@ -1410,7 +1409,7 @@ lodash.values@~2.4.1: dependencies: lodash.keys "~2.4.1" -lodash@^4.16.4: +lodash@^4.17.15: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -1572,12 +1571,12 @@ mocha-junit-reporter@^1.17.0: xml "^1.0.0" mocha-multi-reporters@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82" - integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI= + version "1.5.1" + resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.5.1.tgz#c73486bed5519e1d59c9ce39ac7a9792600e5676" + integrity sha512-Yb4QJOaGLIcmB0VY7Wif5AjvLMUFAdV57D2TWEva1Y0kU/3LjKpeRVmlMIfuO1SVbauve459kgtIizADqxMWPg== dependencies: - debug "^3.1.0" - lodash "^4.16.4" + debug "^4.1.1" + lodash "^4.17.15" mocha@^2.3.3: version "2.5.3" @@ -1605,7 +1604,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -2413,14 +2412,15 @@ vinyl@~2.0.1: replace-ext "^1.0.0" vscode-emmet-helper@~2.0.0: - version "2.0.8" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.0.8.tgz#7c3cf8027d1a75d29625e029d516da7bc56c1fdb" - integrity sha512-Wyf+b5pua+13eZSHCpmob1x915x/od4z6lIia9T2N4v7+CUYNxDisBu3/ShIM3qg3YiYvTCOm+Yx/CLd2khcVw== + version "2.0.9" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.0.9.tgz#16244c087cba4e379116f268384bb644649db6ad" + integrity sha512-S6RjnR9gUicl8LsYnQAMNqqOxolud9gcj+NpPyEnxfxp1YIBuC9oetj6l6N9VMZBWu6tL77wmf+/EJsRx1PDPA== dependencies: emmet "^2.1.5" jsonc-parser "^2.3.0" vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "^3.15.1" + vscode-nls "^5.0.0" vscode-uri "^2.1.2" vscode-html-languageservice@^3.0.3: diff --git a/extensions/git/package.json b/extensions/git/package.json index c9430e7c6..bea5f96a6 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -181,6 +181,12 @@ "category": "Git", "icon": "$(discard)" }, + { + "command": "git.rename", + "title": "%command.rename%", + "category": "Git", + "icon": "$(discard)" + }, { "command": "git.commit", "title": "%command.commit%", @@ -278,6 +284,11 @@ "title": "%command.checkout%", "category": "Git" }, + { + "command": "git.checkoutDetached", + "title": "%command.checkoutDetached%", + "category": "Git" + }, { "command": "git.branch", "title": "%command.branch%", @@ -368,6 +379,11 @@ "title": "%command.pushToForce%", "category": "Git" }, + { + "command": "git.pushTags", + "title": "%command.pushTags%", + "category": "Git" + }, { "command": "git.pushWithTags", "title": "%command.pushFollowTags%", @@ -378,6 +394,11 @@ "title": "%command.pushFollowTagsForce%", "category": "Git" }, + { + "command": "git.cherryPick", + "title": "%command.cherryPick%", + "category": "Git" + }, { "command": "git.addRemote", "title": "%command.addRemote%", @@ -605,6 +626,10 @@ "command": "git.cleanAllUntracked", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.rename", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file" + }, { "command": "git.commit", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -705,6 +730,10 @@ "command": "git.renameBranch", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.cherryPick", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, { "command": "git.pull", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -769,6 +798,10 @@ "command": "git.pushWithTagsForce", "when": "config.git.enabled && !git.missing && config.git.allowForcePush && gitOpenRepositoryCount != 0" }, + { + "command": "git.pushTags", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, { "command": "git.addRemote", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -1279,17 +1312,17 @@ { "command": "git.stageSelectedRanges", "group": "2_git@1", - "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.unstageSelectedRanges", "group": "2_git@2", - "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.revertSelectedRanges", "group": "2_git@3", - "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" } ], "scm/change/title": [ @@ -1657,21 +1690,27 @@ "scope": "resource" }, "git.checkoutType": { - "type": "string", - "enum": [ - "all", - "local", - "tags", - "remote" - ], - "enumDescriptions": [ - "%config.checkoutType.all%", - "%config.checkoutType.local%", - "%config.checkoutType.tags%", - "%config.checkoutType.remote%" - ], + "type": "array", + "items": { + "type": "string", + "enum": [ + "local", + "tags", + "remote" + ], + "enumDescriptions": [ + "%config.checkoutType.local%", + "%config.checkoutType.tags%", + "%config.checkoutType.remote%" + ] + }, + "uniqueItems": true, "markdownDescription": "%config.checkoutType%", - "default": "all" + "default": [ + "local", + "remote", + "tags" + ] }, "git.ignoreLegacyWarning": { "type": "boolean", @@ -1751,6 +1790,28 @@ "description": "%config.enableStatusBarSync%", "scope": "resource" }, + "git.followTagsWhenSync": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.followTagsWhenSync%" + }, + "git.promptToSaveFilesBeforeStash": { + "type": "string", + "enum": [ + "always", + "staged", + "never" + ], + "enumDescriptions": [ + "%config.promptToSaveFilesBeforeStash.always%", + "%config.promptToSaveFilesBeforeStash.staged%", + "%config.promptToSaveFilesBeforeStash.never%" + ], + "scope": "resource", + "default": "always", + "description": "%config.promptToSaveFilesBeforeStash%" + }, "git.promptToSaveFilesBeforeCommit": { "type": "string", "enum": [ @@ -1783,6 +1844,23 @@ "scope": "resource", "default": "none" }, + "git.openAfterClone": { + "type": "string", + "enum": [ + "always", + "alwaysNewWindow", + "whenNoFolderOpen", + "prompt" + ], + "enumDescriptions": [ + "%config.openAfterClone.always%", + "%config.openAfterClone.alwaysNewWindow%", + "%config.openAfterClone.whenNoFolderOpen%", + "%config.openAfterClone.prompt%" + ], + "default": "prompt", + "description": "%config.openAfterClone%" + }, "git.showInlineOpenFileAction": { "type": "boolean", "default": true, @@ -1840,6 +1918,12 @@ "default": false, "description": "%config.alwaysSignOff%" }, + "git.ignoreSubmodules": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.ignoreSubmodules%" + }, "git.ignoredRepositories": { "type": "array", "items": { @@ -1876,6 +1960,12 @@ "default": false, "description": "%config.fetchOnPull%" }, + "git.pruneOnFetch": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.pruneOnFetch%" + }, "git.pullTags": { "type": "boolean", "scope": "resource", @@ -1962,6 +2052,12 @@ "default": true, "description": "%config.terminalAuthentication%" }, + "git.useCommitInputAsStashMessage": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.useCommitInputAsStashMessage%" + }, "git.githubAuthentication": { "deprecationMessage": "This setting is now deprecated, please use `github.gitAuthentication` instead." }, @@ -2166,31 +2262,36 @@ { "view": "scm", "contents": "%view.workbench.scm.empty%", - "when": "config.git.enabled && git.state == initialized && workbenchState == empty", + "when": "config.git.enabled && workbenchState == empty", + "enablement": "git.state == initialized", "group": "2_open@1" }, { "view": "scm", "contents": "%view.workbench.scm.folder%", - "when": "config.git.enabled && git.state == initialized && workbenchState == folder", + "when": "config.git.enabled && workbenchState == folder", + "enablement": "git.state == initialized", "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.workspace%", - "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0", + "when": "config.git.enabled && workbenchState == workspace && workspaceFolderCount != 0", + "enablement": "git.state == initialized", "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.emptyWorkspace%", - "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount == 0", + "when": "config.git.enabled && workbenchState == workspace && workspaceFolderCount == 0", + "enablement": "git.state == initialized", "group": "2_open@1" }, { "view": "explorer", "contents": "%view.workbench.cloneRepository%", - "when": "config.git.enabled && git.state == initialized", + "when": "config.git.enabled", + "enablement": "git.state == initialized", "group": "5_scm@1" } ] diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index e2a46033b..6158a7e0c 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -23,6 +23,7 @@ "command.unstage": "Unstage Changes", "command.unstageAll": "Unstage All Changes", "command.unstageSelectedRanges": "Unstage Selected Ranges", + "command.rename": "Rename", "command.clean": "Discard Changes", "command.cleanAll": "Discard All Changes", "command.cleanAllTracked": "Discard All Tracked Changes", @@ -46,10 +47,12 @@ "command.restoreCommitTemplate": "Restore Commit Template", "command.undoCommit": "Undo Last Commit", "command.checkout": "Checkout to...", + "command.checkoutDetached": "Checkout to (Detached)...", "command.branch": "Create Branch...", "command.branchFrom": "Create Branch From...", "command.deleteBranch": "Delete Branch...", "command.renameBranch": "Rename Branch...", + "command.cherryPick": "Cherry Pick...", "command.merge": "Merge Branch...", "command.rebase": "Rebase Branch...", "command.createTag": "Create Tag", @@ -66,6 +69,7 @@ "command.pushToForce": "Push to... (Force)", "command.pushFollowTags": "Push (Follow Tags)", "command.pushFollowTagsForce": "Push (Follow Tags, Force)", + "command.pushTags": "Push Tags", "command.addRemote": "Add Remote...", "command.removeRemote": "Remove Remote", "command.sync": "Sync", @@ -100,11 +104,10 @@ "config.countBadge.all": "Count all changes.", "config.countBadge.tracked": "Count only tracked changes.", "config.countBadge.off": "Turn off counter.", - "config.checkoutType": "Controls what type of branches are listed when running `Checkout to...`.", - "config.checkoutType.all": "Show all references.", - "config.checkoutType.local": "Show only local branches.", - "config.checkoutType.tags": "Show only tags.", - "config.checkoutType.remote": "Show only remote branches.", + "config.checkoutType": "Controls what type of git refs are listed when running `Checkout to...`.", + "config.checkoutType.local": "Local branches", + "config.checkoutType.tags": "Tags", + "config.checkoutType.remote": "Remote branches", "config.branchValidationRegex": "A regular expression to validate new branch names.", "config.branchWhitespaceChar": "The character to replace whitespace in new branch names.", "config.ignoreLegacyWarning": "Ignores the legacy Git warning.", @@ -121,6 +124,11 @@ "config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.", "config.decorations.enabled": "Controls whether Git contributes colors and badges to the explorer and the open editors view.", "config.enableStatusBarSync": "Controls whether the Git Sync command appears in the status bar.", + "config.followTagsWhenSync": "Follow push all tags when running the sync command.", + "config.promptToSaveFilesBeforeStash": "Controls whether Git should check for unsaved files before stashing changes.", + "config.promptToSaveFilesBeforeStash.always": "Check for any unsaved files.", + "config.promptToSaveFilesBeforeStash.staged": "Check only for unsaved staged files.", + "config.promptToSaveFilesBeforeStash.never": "Disable this check.", "config.promptToSaveFilesBeforeCommit": "Controls whether Git should check for unsaved files before committing.", "config.promptToSaveFilesBeforeCommit.always": "Check for any unsaved files.", "config.promptToSaveFilesBeforeCommit.staged": "Check only for unsaved staged files.", @@ -129,6 +137,11 @@ "config.postCommitCommand.none": "Don't run any command after a commit.", "config.postCommitCommand.push": "Run 'Git Push' after a successful commit.", "config.postCommitCommand.sync": "Run 'Git Sync' after a successful commit.", + "config.openAfterClone": "Controls whether to open a repository automatically after cloning.", + "config.openAfterClone.always": "Always open in current window.", + "config.openAfterClone.alwaysNewWindow": "Always open in a new window.", + "config.openAfterClone.whenNoFolderOpen": "Only open in current window when no folder is opened.", + "config.openAfterClone.prompt": "Always prompt for action.", "config.showInlineOpenFileAction": "Controls whether to show an inline Open File action in the Git changes view.", "config.showPushSuccessNotification": "Controls whether to show a notification when a push is successful.", "config.inputValidation": "Controls when to show commit message input validation.", @@ -138,6 +151,7 @@ "config.detectSubmodulesLimit": "Controls the limit of git submodules detected.", "config.alwaysShowStagedChangesResourceGroup": "Always show the Staged Changes resource group.", "config.alwaysSignOff": "Controls the signoff flag for all commits.", + "config.ignoreSubmodules": "Ignore modifications to submodules in the file tree.", "config.ignoredRepositories": "List of git repositories to ignore.", "config.scanRepositories": "List of paths to search for git repositories in.", "config.showProgress": "Controls whether git actions should show progress.", @@ -145,6 +159,7 @@ "config.confirmEmptyCommits": "Always confirm the creation of empty commits for the 'Git: Commit Empty' command.", "config.fetchOnPull": "When enabled, fetch all branches when pulling. Otherwise, fetch just the current one.", "config.pullTags": "Fetch all tags when pulling.", + "config.pruneOnFetch": "Prune when fetching.", "config.autoStash": "Stash any changes before pulling and restore them after successful pull.", "config.allowForcePush": "Controls whether force push (with or without lease) is enabled.", "config.useForcePushWithLease": "Controls whether force pushing uses the safer force-with-lease variant.", @@ -164,6 +179,8 @@ "config.timeline.date": "Controls which date to use for items in the Timeline view", "config.timeline.date.committed": "Use the committed date", "config.timeline.date.authored": "Use the authored date", + "config.useCommitInputAsStashMessage": "Controls whether to use the message from the commit input box as the default stash message.", + "submenu.explorer": "Git", "submenu.commit": "Commit", "submenu.commit.amend": "Amend", "submenu.commit.signoff": "Sign Off", @@ -188,5 +205,5 @@ "view.workbench.scm.folder": "The folder currently open doesn't have a git repository. You can initialize a repository which will enable source control features powered by git.\n[Initialize Repository](command:git.init?%5Btrue%5D)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories. You can initialize a repository on a folder which will enable source control features powered by git.\n[Initialize Repository](command:git.init)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.emptyWorkspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Add Folder to Workspace](command:workbench.action.addRootFolder)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", - "view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).\n[Clone Repository](command:git.clone)" + "view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).\n[Clone Repository](command:git.clone 'Clone a repository once the git extension has activated')" } diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 8bcdba441..0a309c6f9 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -357,7 +357,7 @@ export function registerAPICommands(extension: GitExtensionImpl): Disposable { return; } - return pickRemoteSource(extension.model, opts); + return pickRemoteSource(extension.model, opts as any); })); return Disposable.from(...disposables); diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index f63b0b12c..b9a09632b 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -212,6 +212,7 @@ export interface RemoteSourceProvider { readonly icon?: string; // codicon name readonly supportsQuery?: boolean; getRemoteSources(query?: string): ProviderResult; + getBranches?(url: string): ProviderResult; publishRepository?(repository: Repository): Promise; } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 83fe906c8..2c24d9095 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { lstat, Stats } from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection } from 'vscode'; +import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider } from './api/git'; @@ -31,14 +30,14 @@ class CheckoutItem implements QuickPickItem { constructor(protected ref: Ref) { } - async run(repository: Repository): Promise { + async run(repository: Repository, opts?: { detached?: boolean }): Promise { const ref = this.ref.name; if (!ref) { return; } - await repository.checkout(ref); + await repository.checkout(ref, opts); } } @@ -55,7 +54,7 @@ class CheckoutRemoteHeadItem extends CheckoutItem { return localize('remote branch at', "Remote branch at {0}", this.shortCommit); } - async run(repository: Repository): Promise { + async run(repository: Repository, opts?: { detached?: boolean }): Promise { if (!this.ref.name) { return; } @@ -63,9 +62,9 @@ class CheckoutRemoteHeadItem extends CheckoutItem { const branches = await repository.findTrackingBranches(this.ref.name); if (branches.length > 0) { - await repository.checkout(branches[0].name!); + await repository.checkout(branches[0].name!, opts); } else { - await repository.checkoutTracking(this.ref.name); + await repository.checkoutTracking(this.ref.name, opts); } } } @@ -114,31 +113,21 @@ class RebaseItem implements QuickPickItem { } class CreateBranchItem implements QuickPickItem { - - constructor(private cc: CommandCenter) { } - get label(): string { return '$(plus) ' + localize('create branch', 'Create new branch...'); } get description(): string { return ''; } - get alwaysShow(): boolean { return true; } - - async run(repository: Repository): Promise { - await this.cc.branch(repository); - } } class CreateBranchFromItem implements QuickPickItem { - - constructor(private cc: CommandCenter) { } - get label(): string { return '$(plus) ' + localize('create branch from', 'Create new branch from...'); } get description(): string { return ''; } - get alwaysShow(): boolean { return true; } +} - async run(repository: Repository): Promise { - await this.cc.branch(repository); - } +class CheckoutDetachedItem implements QuickPickItem { + get label(): string { return '$(debug-disconnect) ' + localize('checkout detached', 'Checkout detached...'); } + get description(): string { return ''; } + get alwaysShow(): boolean { return true; } } class HEADItem implements QuickPickItem { @@ -217,18 +206,53 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ function createCheckoutItems(repository: Repository): CheckoutItem[] { const config = workspace.getConfiguration('git'); - const checkoutType = config.get('checkoutType') || 'all'; - const includeTags = checkoutType === 'all' || checkoutType === 'tags'; - const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; + const checkoutTypeConfig = config.get('checkoutType'); + let checkoutTypes: string[]; - const heads = repository.refs.filter(ref => ref.type === RefType.Head) - .map(ref => new CheckoutItem(ref)); - const tags = (includeTags ? repository.refs.filter(ref => ref.type === RefType.Tag) : []) - .map(ref => new CheckoutTagItem(ref)); - const remoteHeads = (includeRemotes ? repository.refs.filter(ref => ref.type === RefType.RemoteHead) : []) - .map(ref => new CheckoutRemoteHeadItem(ref)); + if (checkoutTypeConfig === 'all' || !checkoutTypeConfig || checkoutTypeConfig.length === 0) { + checkoutTypes = ['local', 'remote', 'tags']; + } else if (typeof checkoutTypeConfig === 'string') { + checkoutTypes = [checkoutTypeConfig]; + } else { + checkoutTypes = checkoutTypeConfig; + } - return [...heads, ...tags, ...remoteHeads]; + const processors = checkoutTypes.map(getCheckoutProcessor) + .filter(p => !!p) as CheckoutProcessor[]; + + for (const ref of repository.refs) { + for (const processor of processors) { + processor.onRef(ref); + } + } + + return processors.reduce((r, p) => r.concat(...p.items), []); +} + +class CheckoutProcessor { + + private refs: Ref[] = []; + get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(r)); } + constructor(private type: RefType, private ctor: { new(ref: Ref): CheckoutItem }) { } + + onRef(ref: Ref): void { + if (ref.type === this.type) { + this.refs.push(ref); + } + } +} + +function getCheckoutProcessor(type: string): CheckoutProcessor | undefined { + switch (type) { + case 'local': + return new CheckoutProcessor(RefType.Head, CheckoutItem); + case 'remote': + return new CheckoutProcessor(RefType.RemoteHead, CheckoutRemoteHeadItem); + case 'tags': + return new CheckoutProcessor(RefType.Tag, CheckoutTagItem); + } + + return undefined; } function sanitizeRemoteName(name: string) { @@ -246,6 +270,7 @@ enum PushType { Push, PushTo, PushFollowTags, + PushTags } interface PushOptions { @@ -254,9 +279,27 @@ interface PushOptions { silent?: boolean; } +class CommandErrorOutputTextDocumentContentProvider implements TextDocumentContentProvider { + + private items = new Map(); + + set(uri: Uri, contents: string): void { + this.items.set(uri.path, contents); + } + + delete(uri: Uri): void { + this.items.delete(uri.path); + } + + provideTextDocumentContent(uri: Uri): string | undefined { + return this.items.get(uri.path); + } +} + export class CommandCenter { private disposables: Disposable[]; + private commandErrors = new CommandErrorOutputTextDocumentContentProvider(); constructor( private git: Git, @@ -273,6 +316,8 @@ export class CommandCenter { return commands.registerCommand(commandId, command); } }); + + this.disposables.push(workspace.registerTextDocumentContentProvider('git-output', this.commandErrors)); } @command('git.setLogLevel') @@ -311,165 +356,14 @@ export class CommandCenter { } @command('git.openResource') - async openResource(resource: Resource, preserveFocus: boolean): Promise { + async openResource(resource: Resource): Promise { const repository = this.model.getRepository(resource.resourceUri); if (!repository) { return; } - const config = workspace.getConfiguration('git', Uri.file(repository.root)); - const openDiffOnClick = config.get('openDiffOnClick'); - - if (openDiffOnClick) { - await this._openResource(resource, undefined, preserveFocus, false); - } else { - await this.openFile(resource); - } - } - - private async _openResource(resource: Resource, preview?: boolean, preserveFocus?: boolean, preserveSelection?: boolean): Promise { - let stat: Stats | undefined; - - try { - stat = await new Promise((c, e) => lstat(resource.resourceUri.fsPath, (err, stat) => err ? e(err) : c(stat))); - } catch (err) { - // noop - } - - let left: Uri | undefined; - let right: Uri | undefined; - - if (stat && stat.isDirectory()) { - const repository = this.model.getRepositoryForSubmodule(resource.resourceUri); - - if (repository) { - right = toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: repository.root }); - } - } else { - if (resource.type !== Status.DELETED_BY_THEM) { - left = this.getLeftResource(resource); - } - - right = this.getRightResource(resource); - } - - const title = this.getTitle(resource); - - if (!right) { - // TODO - console.error('oh no'); - return; - } - - const opts: TextDocumentShowOptions = { - preserveFocus, - preview, - viewColumn: ViewColumn.Active - }; - - const activeTextEditor = window.activeTextEditor; - - // Check if active text editor has same path as other editor. we cannot compare via - // URI.toString() here because the schemas can be different. Instead we just go by path. - if (preserveSelection && activeTextEditor && activeTextEditor.document.uri.path === right.path) { - opts.selection = activeTextEditor.selection; - } - - if (!left) { - await commands.executeCommand('vscode.open', right, opts, title); - } else { - await commands.executeCommand('vscode.diff', left, right, title, opts); - } - } - - private getLeftResource(resource: Resource): Uri | undefined { - switch (resource.type) { - case Status.INDEX_MODIFIED: - case Status.INDEX_RENAMED: - case Status.INDEX_ADDED: - return toGitUri(resource.original, 'HEAD'); - - case Status.MODIFIED: - case Status.UNTRACKED: - return toGitUri(resource.resourceUri, '~'); - - case Status.DELETED_BY_THEM: - return toGitUri(resource.resourceUri, ''); - } - return undefined; - } - - private getRightResource(resource: Resource): Uri | undefined { - switch (resource.type) { - case Status.INDEX_MODIFIED: - case Status.INDEX_ADDED: - case Status.INDEX_COPIED: - case Status.INDEX_RENAMED: - return toGitUri(resource.resourceUri, ''); - - case Status.INDEX_DELETED: - case Status.DELETED: - return toGitUri(resource.resourceUri, 'HEAD'); - - case Status.DELETED_BY_US: - return toGitUri(resource.resourceUri, '~3'); - - case Status.DELETED_BY_THEM: - return toGitUri(resource.resourceUri, '~2'); - - case Status.MODIFIED: - case Status.UNTRACKED: - case Status.IGNORED: - case Status.INTENT_TO_ADD: - const repository = this.model.getRepository(resource.resourceUri); - - if (!repository) { - return; - } - - const uriString = resource.resourceUri.toString(); - const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); - - if (indexStatus && indexStatus.renameResourceUri) { - return indexStatus.renameResourceUri; - } - - return resource.resourceUri; - - case Status.BOTH_ADDED: - case Status.BOTH_MODIFIED: - return resource.resourceUri; - } - return undefined; - } - - private getTitle(resource: Resource): string { - const basename = path.basename(resource.resourceUri.fsPath); - - switch (resource.type) { - case Status.INDEX_MODIFIED: - case Status.INDEX_RENAMED: - case Status.INDEX_ADDED: - return localize('git.title.index', '{0} (Index)', basename); - - case Status.MODIFIED: - case Status.BOTH_ADDED: - case Status.BOTH_MODIFIED: - return localize('git.title.workingTree', '{0} (Working Tree)', basename); - - case Status.DELETED_BY_US: - return localize('git.title.theirs', '{0} (Theirs)', basename); - - case Status.DELETED_BY_THEM: - return localize('git.title.ours', '{0} (Ours)', basename); - - case Status.UNTRACKED: - return localize('git.title.untracked', '{0} (Untracked)', basename); - - default: - return ''; - } + await resource.open(); } async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean } = {}): Promise { @@ -531,35 +425,54 @@ export class CommandCenter { (progress, token) => this.git.clone(url!, { parentPath: parentPath!, progress, recursive: options.recursive }, token) ); - let message = localize('proposeopen', "Would you like to open the cloned repository?"); - const open = localize('openrepo', "Open"); - const openNewWindow = localize('openreponew', "Open in New Window"); - const choices = [open, openNewWindow]; + const config = workspace.getConfiguration('git'); + const openAfterClone = config.get<'always' | 'alwaysNewWindow' | 'whenNoFolderOpen' | 'prompt'>('openAfterClone'); - const addToWorkspace = localize('add', "Add to Workspace"); - if (workspace.workspaceFolders) { - message = localize('proposeopen2', "Would you like to open the cloned repository, or add it to the current workspace?"); - choices.push(addToWorkspace); + enum PostCloneAction { Open, OpenNewWindow, AddToWorkspace } + let action: PostCloneAction | undefined = undefined; + + if (openAfterClone === 'always') { + action = PostCloneAction.Open; + } else if (openAfterClone === 'alwaysNewWindow') { + action = PostCloneAction.OpenNewWindow; + } else if (openAfterClone === 'whenNoFolderOpen' && !workspace.workspaceFolders) { + action = PostCloneAction.Open; } - const result = await window.showInformationMessage(message, ...choices); + if (action === undefined) { + let message = localize('proposeopen', "Would you like to open the cloned repository?"); + const open = localize('openrepo', "Open"); + const openNewWindow = localize('openreponew', "Open in New Window"); + const choices = [open, openNewWindow]; + + const addToWorkspace = localize('add', "Add to Workspace"); + if (workspace.workspaceFolders) { + message = localize('proposeopen2', "Would you like to open the cloned repository, or add it to the current workspace?"); + choices.push(addToWorkspace); + } + + const result = await window.showInformationMessage(message, ...choices); + + action = result === open ? PostCloneAction.Open + : result === openNewWindow ? PostCloneAction.OpenNewWindow + : result === addToWorkspace ? PostCloneAction.AddToWorkspace : undefined; + } - const openFolder = result === open; /* __GDPR__ "clone" : { "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } */ - this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: openFolder ? 1 : 0 }); + this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: action === PostCloneAction.Open || action === PostCloneAction.OpenNewWindow ? 1 : 0 }); const uri = Uri.file(repositoryPath); - if (openFolder) { + if (action === PostCloneAction.Open) { commands.executeCommand('vscode.openFolder', uri, { forceReuseWindow: true }); - } else if (result === addToWorkspace) { + } else if (action === PostCloneAction.AddToWorkspace) { workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri }); - } else if (result === openNewWindow) { + } else if (action === PostCloneAction.OpenNewWindow) { commands.executeCommand('vscode.openFolder', uri, { forceNewWindow: true }); } } catch (err) { @@ -761,7 +674,10 @@ export class CommandCenter { try { document = await workspace.openTextDocument(uri); } catch (error) { - await commands.executeCommand('vscode.open', uri, opts); + await commands.executeCommand('vscode.open', uri, { + ...opts, + override: arg instanceof Resource && arg.type === Status.BOTH_MODIFIED ? false : undefined + }); continue; } @@ -801,7 +717,7 @@ export class CommandCenter { return; } - const HEAD = this.getLeftResource(resource); + const HEAD = resource.leftUri; const basename = path.basename(resource.resourceUri.fsPath); const title = `${basename} (HEAD)`; @@ -819,10 +735,6 @@ export class CommandCenter { @command('git.openChange') async openChange(arg?: Resource | Uri, ...resourceStates: SourceControlResourceState[]): Promise { - const preserveFocus = arg instanceof Resource; - const preview = !(arg instanceof Resource); - - const preserveSelection = arg instanceof Uri || !arg; let resources: Resource[] | undefined = undefined; if (arg instanceof Uri) { @@ -849,10 +761,33 @@ export class CommandCenter { } for (const resource of resources) { - await this._openResource(resource, preview, preserveFocus, preserveSelection); + await resource.openChange(); } } + @command('git.rename', { repository: true }) + async rename(repository: Repository, fromUri: Uri | undefined): Promise { + fromUri = fromUri ?? window.activeTextEditor?.document.uri; + + if (!fromUri) { + return; + } + + const from = path.relative(repository.root, fromUri.path); + let to = await window.showInputBox({ + value: from, + valueSelection: [from.length - path.basename(from).length, from.length] + }); + + to = to?.trim(); + + if (!to) { + return; + } + + await repository.move(from, to); + } + @command('git.stage') async stage(...resourceStates: SourceControlResourceState[]): Promise { this.outputChannel.appendLine(`git.stage ${resourceStates.length}`); @@ -1018,6 +953,10 @@ export class CommandCenter { @command('git.stageChange') async stageChange(uri: Uri, changes: LineChange[], index: number): Promise { + if (!uri) { + return; + } + const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0]; if (!textEditor) { @@ -1068,6 +1007,10 @@ export class CommandCenter { @command('git.revertChange') async revertChange(uri: Uri, changes: LineChange[], index: number): Promise { + if (!uri) { + return; + } + const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0]; if (!textEditor) { @@ -1397,7 +1340,7 @@ export class CommandCenter { ? localize('unsaved files single', "The following file has unsaved changes which won't be included in the commit if you proceed: {0}.\n\nWould you like to save it before committing?", path.basename(documents[0].uri.fsPath)) : localize('unsaved files', "There are {0} unsaved files.\n\nWould you like to save them before committing?", documents.length); const saveAndCommit = localize('save and commit', "Save All & Commit"); - const commit = localize('commit', "Commit Anyway"); + const commit = localize('commit', "Commit Staged Changes"); const pick = await window.showWarningMessage(message, { modal: true }, saveAndCommit, commit); if (pick === saveAndCommit) { @@ -1409,8 +1352,14 @@ export class CommandCenter { } } + if (!opts) { + opts = { all: noStagedChanges }; + } else if (!opts.all && noStagedChanges && !opts.empty) { + opts = { ...opts, all: true }; + } + // no changes, and the user has not configured to commit all in this case - if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit) { + if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit && !opts.empty) { const suggestSmartCommit = config.get('suggestSmartCommit') === true; if (!suggestSmartCommit) { @@ -1434,13 +1383,7 @@ export class CommandCenter { } } - if (!opts) { - opts = { all: noStagedChanges }; - } else if (!opts.all && noStagedChanges) { - opts = { ...opts, all: true }; - } - - // enable signing of commits if configurated + // enable signing of commits if configured opts.signCommit = enableCommitSigning; if (config.get('alwaysSignOff')) { @@ -1458,10 +1401,18 @@ export class CommandCenter { // no staged changes and no tracked unstaged changes || (noStagedChanges && smartCommitChanges === 'tracked' && repository.workingTreeGroup.resourceStates.every(r => r.type === Status.UNTRACKED)) ) + // amend allows changing only the commit message + && !opts.amend && !opts.empty ) { - window.showInformationMessage(localize('no changes', "There are no changes to commit.")); - return false; + const commitAnyway = localize('commit anyway', "Create Empty Commit"); + const answer = await window.showInformationMessage(localize('no changes', "There are no changes to commit."), commitAnyway); + + if (answer !== commitAnyway) { + return false; + } + + opts.empty = true; } if (opts.noVerify) { @@ -1484,9 +1435,9 @@ export class CommandCenter { } } - const message = await getCommitMessage(); + let message = await getCommitMessage(); - if (!message) { + if (!message && !opts.amend) { return false; } @@ -1523,7 +1474,7 @@ export class CommandCenter { let value: string | undefined = undefined; if (opts && opts.amend && repository.HEAD && repository.HEAD.commit) { - value = (await repository.getCommit(repository.HEAD.commit)).message; + return undefined; } const branchName = repository.headShortName; @@ -1690,20 +1641,38 @@ export class CommandCenter { } @command('git.checkout', { repository: true }) - async checkout(repository: Repository, treeish: string): Promise { - if (typeof treeish === 'string') { - await repository.checkout(treeish); + async checkout(repository: Repository, treeish?: string): Promise { + return this._checkout(repository, { treeish }); + } + + @command('git.checkoutDetached', { repository: true }) + async checkoutDetached(repository: Repository, treeish?: string): Promise { + return this._checkout(repository, { detached: true, treeish }); + } + + private async _checkout(repository: Repository, opts?: { detached?: boolean, treeish?: string }): Promise { + if (typeof opts?.treeish === 'string') { + await repository.checkout(opts?.treeish, opts); return true; } - const createBranch = new CreateBranchItem(this); - const createBranchFrom = new CreateBranchFromItem(this); - const picks = [createBranch, createBranchFrom, ...createCheckoutItems(repository)]; - const placeHolder = localize('select a ref to checkout', 'Select a ref to checkout'); + const createBranch = new CreateBranchItem(); + const createBranchFrom = new CreateBranchFromItem(); + const checkoutDetached = new CheckoutDetachedItem(); + const picks: QuickPickItem[] = []; + + if (!opts?.detached) { + picks.push(createBranch, createBranchFrom, checkoutDetached); + } + + picks.push(...createCheckoutItems(repository)); const quickpick = window.createQuickPick(); quickpick.items = picks; - quickpick.placeholder = placeHolder; + quickpick.placeholder = opts?.detached + ? localize('select a ref to checkout detached', 'Select a ref to checkout in detached mode') + : localize('select a ref to checkout', 'Select a ref to checkout'); + quickpick.show(); const choice = await new Promise(c => quickpick.onDidAccept(() => c(quickpick.activeItems[0]))); @@ -1717,8 +1686,31 @@ export class CommandCenter { await this._branch(repository, quickpick.value); } else if (choice === createBranchFrom) { await this._branch(repository, quickpick.value, true); + } else if (choice === checkoutDetached) { + return this._checkout(repository, { detached: true }); } else { - await (choice as CheckoutItem).run(repository); + const item = choice as CheckoutItem; + + try { + await item.run(repository, opts); + } catch (err) { + if (err.gitErrorCode !== GitErrorCodes.DirtyWorkTree) { + throw err; + } + + const force = localize('force', "Force Checkout"); + const stash = localize('stashcheckout', "Stash & Checkout"); + const choice = await window.showWarningMessage(localize('local changes', "Your local changes would be overwritten by checkout."), { modal: true }, force, stash); + + if (choice === force) { + await this.cleanAll(repository); + await item.run(repository, opts); + } else if (choice === stash) { + await this.stash(repository); + await item.run(repository, opts); + await this.stashPopLatest(repository); + } + } } return true; @@ -1849,8 +1841,8 @@ export class CommandCenter { @command('git.merge', { repository: true }) async merge(repository: Repository): Promise { const config = workspace.getConfiguration('git'); - const checkoutType = config.get('checkoutType') || 'all'; - const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; + const checkoutType = config.get('checkoutType'); + const includeRemotes = checkoutType === 'all' || checkoutType === 'remote' || checkoutType?.includes('remote'); const heads = repository.refs.filter(ref => ref.type === RefType.Head) .filter(ref => ref.name || ref.commit) @@ -1874,8 +1866,8 @@ export class CommandCenter { @command('git.rebase', { repository: true }) async rebase(repository: Repository): Promise { const config = workspace.getConfiguration('git'); - const checkoutType = config.get('checkoutType') || 'all'; - const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; + const checkoutType = config.get('checkoutType'); + const includeRemotes = checkoutType === 'all' || checkoutType === 'remote' || checkoutType?.includes('remote'); const heads = repository.refs.filter(ref => ref.type === RefType.Head) .filter(ref => ref.name !== repository.HEAD?.name) @@ -2068,7 +2060,7 @@ export class CommandCenter { forcePushMode = config.get('useForcePushWithLease') === true ? ForcePushMode.ForceWithLease : ForcePushMode.Force; if (config.get('confirmForcePush')) { - const message = localize('confirm force push', "You are about to force push your changes, this can be destructive and could inadvertedly overwrite changes made by others.\n\nAre you sure to continue?"); + const message = localize('confirm force push', "You are about to force push your changes, this can be destructive and could inadvertently overwrite changes made by others.\n\nAre you sure to continue?"); const yes = localize('ok', "OK"); const neverAgain = localize('never ask again', "OK, Don't Ask Again"); const pick = await window.showWarningMessage(message, { modal: true }, yes, neverAgain); @@ -2086,6 +2078,10 @@ export class CommandCenter { return; } + if (pushOptions.pushType === PushType.PushTags) { + await repository.pushTags(undefined, forcePushMode); + } + if (!repository.HEAD || !repository.HEAD.name) { if (!pushOptions.silent) { window.showWarningMessage(localize('nobranch', "Please check out a branch to push to a remote.")); @@ -2157,6 +2153,21 @@ export class CommandCenter { await this._push(repository, { pushType: PushType.PushFollowTags, forcePush: true }); } + @command('git.cherryPick', { repository: true }) + async cherryPick(repository: Repository): Promise { + const hash = await window.showInputBox({ + placeHolder: localize('commit hash', "Commit Hash"), + prompt: localize('provide commit hash', "Please provide the commit hash"), + ignoreFocusOut: true + }); + + if (!hash) { + return; + } + + await repository.cherryPick(hash); + } + @command('git.pushTo', { repository: true }) async pushTo(repository: Repository): Promise { await this._push(repository, { pushType: PushType.PushTo }); @@ -2167,6 +2178,11 @@ export class CommandCenter { await this._push(repository, { pushType: PushType.PushTo, forcePush: true }); } + @command('git.pushTags', { repository: true }) + async pushTags(repository: Repository): Promise { + await this._push(repository, { pushType: PushType.PushTags }); + } + @command('git.addRemote', { repository: true }) async addRemote(repository: Repository): Promise { const url = await pickRemoteSource(this.model, { @@ -2200,6 +2216,7 @@ export class CommandCenter { } await repository.addRemote(name, url); + await repository.fetch(name); return name; } @@ -2413,7 +2430,45 @@ export class CommandCenter { return; } - const message = await this.getStashMessage(); + const config = workspace.getConfiguration('git', Uri.file(repository.root)); + const promptToSaveFilesBeforeStashing = config.get<'always' | 'staged' | 'never'>('promptToSaveFilesBeforeStash'); + + if (promptToSaveFilesBeforeStashing !== 'never') { + let documents = workspace.textDocuments + .filter(d => !d.isUntitled && d.isDirty && isDescendant(repository.root, d.uri.fsPath)); + + if (promptToSaveFilesBeforeStashing === 'staged' || repository.indexGroup.resourceStates.length > 0) { + documents = documents + .filter(d => repository.indexGroup.resourceStates.some(s => pathEquals(s.resourceUri.fsPath, d.uri.fsPath))); + } + + if (documents.length > 0) { + const message = documents.length === 1 + ? localize('unsaved stash files single', "The following file has unsaved changes which won't be included in the stash if you proceed: {0}.\n\nWould you like to save it before stashing?", path.basename(documents[0].uri.fsPath)) + : localize('unsaved stash files', "There are {0} unsaved files.\n\nWould you like to save them before stashing?", documents.length); + const saveAndStash = localize('save and stash', "Save All & Stash"); + const stash = localize('stash', "Stash Anyway"); + const pick = await window.showWarningMessage(message, { modal: true }, saveAndStash, stash); + + if (pick === saveAndStash) { + await Promise.all(documents.map(d => d.save())); + } else if (pick !== stash) { + return; // do not stash on cancel + } + } + } + + let message: string | undefined; + + if (config.get('useCommitInputAsStashMessage') && (!repository.sourceControl.commitTemplate || repository.inputBox.value !== repository.sourceControl.commitTemplate)) { + message = repository.inputBox.value; + } + + message = await window.showInputBox({ + value: message, + prompt: localize('provide stash message', "Optionally provide a stash message"), + placeHolder: localize('stash message', "Stash message") + }); if (typeof message === 'undefined') { return; @@ -2422,13 +2477,6 @@ export class CommandCenter { await repository.createStash(message, includeUntracked); } - private async getStashMessage(): Promise { - return await window.showInputBox({ - prompt: localize('provide stash message', "Optionally provide a stash message"), - placeHolder: localize('stash message', "Stash message") - }); - } - @command('git.stash', { repository: true }) stash(repository: Repository): Promise { return this._stash(repository); @@ -2496,6 +2544,16 @@ export class CommandCenter { return; } + // request confirmation for the operation + const yes = localize('yes', "Yes"); + const result = await window.showWarningMessage( + localize('sure drop', "Are you sure you want to drop the stash: {0}?", stash.description), + yes + ); + if (result !== yes) { + return; + } + await repository.dropStash(stash.index); } @@ -2614,6 +2672,31 @@ export class CommandCenter { const outputChannel = this.outputChannel as OutputChannel; choices.set(openOutputChannelChoice, () => outputChannel.show()); + const showCommandOutputChoice = localize('show command output', "Show Command Output"); + if (err.stderr) { + choices.set(showCommandOutputChoice, async () => { + const timestamp = new Date().getTime(); + const uri = Uri.parse(`git-output:/git-error-${timestamp}`); + + let command = 'git'; + + if (err.gitArgs) { + command = `${command} ${err.gitArgs.join(' ')}`; + } else if (err.gitCommand) { + command = `${command} ${err.gitCommand}`; + } + + this.commandErrors.set(uri, `> ${command}\n${err.stderr}`); + + try { + const doc = await workspace.openTextDocument(uri); + await window.showTextDocument(doc); + } finally { + this.commandErrors.delete(uri); + } + }); + } + switch (err.gitErrorCode) { case GitErrorCodes.DirtyWorkTree: message = localize('clean repo', "Please clean your repository working tree before checkout."); diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index a4fbf0ad4..63c026051 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -15,18 +15,18 @@ class GitIgnoreDecorationProvider implements FileDecorationProvider { private static Decoration: FileDecoration = { color: new ThemeColor('gitDecoration.ignoredResourceForeground') }; - readonly onDidChange: Event; + readonly onDidChangeFileDecorations: Event; private queue = new Map>; }>(); private disposables: Disposable[] = []; constructor(private model: Model) { - this.onDidChange = fireEvent(anyEvent( + this.onDidChangeFileDecorations = fireEvent(anyEvent( filterEvent(workspace.onDidSaveTextDocument, e => /\.gitignore$|\.git\/info\/exclude$/.test(e.uri.path)), model.onDidOpenRepository, model.onDidCloseRepository )); - this.disposables.push(window.registerDecorationProvider(this)); + this.disposables.push(window.registerFileDecorationProvider(this)); } async provideFileDecoration(uri: Uri): Promise { @@ -93,14 +93,14 @@ class GitDecorationProvider implements FileDecorationProvider { }; private readonly _onDidChangeDecorations = new EventEmitter(); - readonly onDidChange: Event = this._onDidChangeDecorations.event; + readonly onDidChangeFileDecorations: Event = this._onDidChangeDecorations.event; private disposables: Disposable[] = []; private decorations = new Map(); constructor(private repository: Repository) { this.disposables.push( - window.registerDecorationProvider(this), + window.registerFileDecorationProvider(this), repository.onDidRunGitStatus(this.onDidRunGitStatus, this) ); } diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 1a4119959..af536fe6c 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -260,6 +260,7 @@ export interface IGitErrorData { exitCode?: number; gitErrorCode?: string; gitCommand?: string; + gitArgs?: string[]; } export class GitError { @@ -271,6 +272,7 @@ export class GitError { exitCode?: number; gitErrorCode?: string; gitCommand?: string; + gitArgs?: string[]; constructor(data: IGitErrorData) { if (data.error) { @@ -287,6 +289,7 @@ export class GitError { this.exitCode = data.exitCode; this.gitErrorCode = data.gitErrorCode; this.gitCommand = data.gitCommand; + this.gitArgs = data.gitArgs; } toString(): string { @@ -535,7 +538,8 @@ export class Git { stderr: result.stderr, exitCode: result.exitCode, gitErrorCode: getGitErrorCode(result.stderr), - gitCommand: args[0] + gitCommand: args[0], + gitArgs: args })); } @@ -1295,13 +1299,17 @@ export class Repository { await this.run(['update-index', add, '--cacheinfo', mode, hash, path]); } - async checkout(treeish: string, paths: string[], opts: { track?: boolean } = Object.create(null)): Promise { + async checkout(treeish: string, paths: string[], opts: { track?: boolean, detached?: boolean } = Object.create(null)): Promise { const args = ['checkout', '-q']; if (opts.track) { args.push('--track'); } + if (opts.detached) { + args.push('--detach'); + } + if (treeish) { args.push(treeish); } @@ -1317,23 +1325,30 @@ export class Repository { } catch (err) { if (/Please,? commit your changes or stash them/.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.DirtyWorkTree; + err.gitTreeish = treeish; } throw err; } } - async commit(message: string, opts: CommitOptions = Object.create(null)): Promise { - const args = ['commit', '--quiet', '--allow-empty-message', '--file', '-']; + async commit(message: string | undefined, opts: CommitOptions = Object.create(null)): Promise { + const args = ['commit', '--quiet', '--allow-empty-message']; if (opts.all) { args.push('--all'); } - if (opts.amend) { + if (opts.amend && message) { args.push('--amend'); } + if (opts.amend && !message) { + args.push('--amend', '--no-edit'); + } else { + args.push('--file', '-'); + } + if (opts.signoff) { args.push('--signoff'); } @@ -1350,8 +1365,11 @@ export class Repository { args.push('--no-verify'); } + // Stops git from guessing at user/email + args.splice(0, 0, '-c', 'user.useConfigOnly=true'); + try { - await this.run(args, { input: message || '' }); + await this.run(args, !opts.amend || message ? { input: message || '' } : {}); } catch (commitErr) { await this.handleCommitError(commitErr); } @@ -1414,6 +1432,11 @@ export class Repository { await this.run(args); } + async move(from: string, to: string): Promise { + const args = ['mv', from, to]; + await this.run(args); + } + async setBranchUpstream(name: string, upstream: string): Promise { const args = ['branch', '--set-upstream-to', upstream, name]; await this.run(args); @@ -1536,9 +1559,11 @@ export class Repository { await this.run(args); } - async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean } = {}): Promise { + async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean, readonly cancellationToken?: CancellationToken } = {}): Promise { const args = ['fetch']; - const spawnOptions: SpawnOptions = {}; + const spawnOptions: SpawnOptions = { + cancellationToken: options.cancellationToken, + }; if (options.remote) { args.push(options.remote); @@ -1635,7 +1660,7 @@ export class Repository { } } - async push(remote?: string, name?: string, setUpstream: boolean = false, tags = false, forcePushMode?: ForcePushMode): Promise { + async push(remote?: string, name?: string, setUpstream: boolean = false, followTags = false, forcePushMode?: ForcePushMode, tags = false): Promise { const args = ['push']; if (forcePushMode === ForcePushMode.ForceWithLease) { @@ -1648,10 +1673,14 @@ export class Repository { args.push('-u'); } - if (tags) { + if (followTags) { args.push('--follow-tags'); } + if (tags) { + args.push('--tags'); + } + if (remote) { args.push(remote); } @@ -1677,6 +1706,11 @@ export class Repository { } } + async cherryPick(commitHash: string): Promise { + const args = ['cherry-pick', commitHash]; + await this.run(args); + } + async blame(path: string): Promise { try { const args = ['blame', sanitizePath(path)]; @@ -1761,11 +1795,17 @@ export class Repository { } } - getStatus(limit = 5000): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> { + getStatus(opts?: { limit?: number, ignoreSubmodules?: boolean }): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> { return new Promise<{ status: IFileStatus[]; didHitLimit: boolean; }>((c, e) => { const parser = new GitStatusParser(); const env = { GIT_OPTIONAL_LOCKS: '0' }; - const child = this.stream(['status', '-z', '-u'], { env }); + const args = ['status', '-z', '-u']; + + if (opts?.ignoreSubmodules) { + args.push('--ignore-submodules'); + } + + const child = this.stream(args, { env }); const onExit = (exitCode: number) => { if (exitCode !== 0) { @@ -1775,13 +1815,15 @@ export class Repository { stderr, exitCode, gitErrorCode: getGitErrorCode(stderr), - gitCommand: 'status' + gitCommand: 'status', + gitArgs: args })); } c({ status: parser.status, didHitLimit: false }); }; + const limit = opts?.limit ?? 5000; const onStdoutData = (raw: string) => { parser.update(raw); @@ -1845,7 +1887,7 @@ export class Repository { args.push('--sort', `-${opts.sort}`); } - args.push('--format', '%(refname) %(objectname)'); + args.push('--format', '%(refname) %(objectname) %(*objectname)'); if (opts?.pattern) { args.push(opts.pattern); @@ -1860,12 +1902,12 @@ export class Repository { const fn = (line: string): Ref | null => { let match: RegExpExecArray | null; - if (match = /^refs\/heads\/([^ ]+) ([0-9a-f]{40})$/.exec(line)) { + if (match = /^refs\/heads\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { return { name: match[1], commit: match[2], type: RefType.Head }; - } else if (match = /^refs\/remotes\/([^/]+)\/([^ ]+) ([0-9a-f]{40})$/.exec(line)) { + } else if (match = /^refs\/remotes\/([^/]+)\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { return { name: `${match[1]}/${match[2]}`, commit: match[3], type: RefType.RemoteHead, remote: match[1] }; - } else if (match = /^refs\/tags\/([^ ]+) ([0-9a-f]{40})$/.exec(line)) { - return { name: match[1], commit: match[2], type: RefType.Tag }; + } else if (match = /^refs\/tags\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { + return { name: match[1], commit: match[3] ?? match[2], type: RefType.Tag }; } return null; diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts index b736f606e..cbe437d6a 100644 --- a/extensions/git/src/remoteSource.ts +++ b/extensions/git/src/remoteSource.ts @@ -59,7 +59,8 @@ class RemoteSourceProviderQuickPick { this.quickpick.items = remoteSources.map(remoteSource => ({ label: remoteSource.name, description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), - remoteSource + remoteSource, + alwaysShow: true })); } } catch (err) { @@ -80,12 +81,30 @@ class RemoteSourceProviderQuickPick { export interface PickRemoteSourceOptions { readonly providerLabel?: (provider: RemoteSourceProvider) => string; readonly urlLabel?: string; + readonly providerName?: string; + readonly branch?: boolean; // then result is PickRemoteSourceResult } -export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { +export interface PickRemoteSourceResult { + readonly url: string; + readonly branch?: string; +} + +export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions & { branch?: false | undefined }): Promise; +export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions & { branch: true }): Promise; +export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); quickpick.ignoreFocusOut = true; + if (options.providerName) { + const provider = model.getRemoteProviders() + .filter(provider => provider.name === options.providerName)[0]; + + if (provider) { + return await pickProviderSource(provider, options); + } + } + const providers = model.getRemoteProviders() .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); @@ -116,18 +135,48 @@ export async function pickRemoteSource(model: Model, options: PickRemoteSourceOp if (result.url) { return result.url; } else if (result.provider) { - const quickpick = new RemoteSourceProviderQuickPick(result.provider); - const remote = await quickpick.pick(); - - if (remote) { - if (typeof remote.url === 'string') { - return remote.url; - } else if (remote.url.length > 0) { - return await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); - } - } + return await pickProviderSource(result.provider, options); } } return undefined; } + +async function pickProviderSource(provider: RemoteSourceProvider, options: PickRemoteSourceOptions = {}): Promise { + const quickpick = new RemoteSourceProviderQuickPick(provider); + const remote = await quickpick.pick(); + + let url: string | undefined; + + if (remote) { + if (typeof remote.url === 'string') { + url = remote.url; + } else if (remote.url.length > 0) { + url = await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); + } + } + + if (!url || !options.branch) { + return url; + } + + if (!provider.getBranches) { + return { url }; + } + + const branches = await provider.getBranches(url); + + if (!branches) { + return { url }; + } + + const branch = await window.showQuickPick(branches, { + placeHolder: localize('branch name', "Branch name") + }); + + if (!branch) { + return { url }; + } + + return { url, branch }; +} diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 6b4fd0188..87f24395d 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -75,13 +75,21 @@ export class Resource implements SourceControlResourceState { return this._resourceUri; } - @memoize + get leftUri(): Uri | undefined { + return this.resources[0]; + } + + get rightUri(): Uri { + return this.resources[1]; + } + get command(): Command { - return { - command: 'git.openResource', - title: localize('open', "Open"), - arguments: [this] - }; + return this._commandResolver.resolveDefaultCommand(this); + } + + @memoize + private get resources(): [Uri | undefined, Uri] { + return this._commandResolver.getResources(this); } get resourceGroupType(): ResourceGroupType { return this._resourceGroupType; } @@ -262,12 +270,28 @@ export class Resource implements SourceControlResourceState { } constructor( + private _commandResolver: ResourceCommandResolver, private _resourceGroupType: ResourceGroupType, private _resourceUri: Uri, private _type: Status, private _useIcons: boolean, - private _renameResourceUri?: Uri + private _renameResourceUri?: Uri, ) { } + + async open(): Promise { + const command = this.command; + await commands.executeCommand(command.command, ...(command.arguments || [])); + } + + async openFile(): Promise { + const command = this._commandResolver.resolveFileCommand(this); + await commands.executeCommand(command.command, ...(command.arguments || [])); + } + + async openChange(): Promise { + const command = this._commandResolver.resolveChangeCommand(this); + await commands.executeCommand(command.command, ...(command.arguments || [])); + } } export const enum Operation { @@ -292,6 +316,7 @@ export const enum Operation { Fetch = 'Fetch', Pull = 'Pull', Push = 'Push', + CherryPick = 'CherryPick', Sync = 'Sync', Show = 'Show', Stage = 'Stage', @@ -315,6 +340,8 @@ export const enum Operation { Blame = 'Blame', Log = 'Log', LogFile = 'LogFile', + + Move = 'Move' } function isReadOnly(operation: Operation): boolean { @@ -550,6 +577,142 @@ class DotGitWatcher implements IFileWatcher { } } +class ResourceCommandResolver { + + constructor(private repository: Repository) { } + + resolveDefaultCommand(resource: Resource): Command { + const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const openDiffOnClick = config.get('openDiffOnClick', true); + return openDiffOnClick ? this.resolveChangeCommand(resource) : this.resolveFileCommand(resource); + } + + resolveFileCommand(resource: Resource): Command { + return { + command: 'vscode.open', + title: localize('open', "Open"), + arguments: [resource.resourceUri] + }; + } + + resolveChangeCommand(resource: Resource): Command { + const title = this.getTitle(resource); + + if (!resource.leftUri) { + return { + command: 'vscode.open', + title: localize('open', "Open"), + arguments: [resource.rightUri, { override: resource.type === Status.BOTH_MODIFIED ? false : undefined }, title] + }; + } else { + return { + command: 'vscode.diff', + title: localize('open', "Open"), + arguments: [resource.leftUri, resource.rightUri, title] + }; + } + } + + getResources(resource: Resource): [Uri | undefined, Uri] { + for (const submodule of this.repository.submodules) { + if (path.join(this.repository.root, submodule.path) === resource.resourceUri.fsPath) { + return [undefined, toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: this.repository.root })]; + } + } + + return [this.getLeftResource(resource), this.getRightResource(resource)]; + } + + private getLeftResource(resource: Resource): Uri | undefined { + switch (resource.type) { + case Status.INDEX_MODIFIED: + case Status.INDEX_RENAMED: + case Status.INDEX_ADDED: + return toGitUri(resource.original, 'HEAD'); + + case Status.MODIFIED: + case Status.UNTRACKED: + return toGitUri(resource.resourceUri, '~'); + + case Status.DELETED_BY_US: + case Status.DELETED_BY_THEM: + return toGitUri(resource.resourceUri, '~1'); + } + return undefined; + } + + private getRightResource(resource: Resource): Uri { + switch (resource.type) { + case Status.INDEX_MODIFIED: + case Status.INDEX_ADDED: + case Status.INDEX_COPIED: + case Status.INDEX_RENAMED: + return toGitUri(resource.resourceUri, ''); + + case Status.INDEX_DELETED: + case Status.DELETED: + return toGitUri(resource.resourceUri, 'HEAD'); + + case Status.DELETED_BY_US: + return toGitUri(resource.resourceUri, '~3'); + + case Status.DELETED_BY_THEM: + return toGitUri(resource.resourceUri, '~2'); + + case Status.MODIFIED: + case Status.UNTRACKED: + case Status.IGNORED: + case Status.INTENT_TO_ADD: + const uriString = resource.resourceUri.toString(); + const [indexStatus] = this.repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); + + if (indexStatus && indexStatus.renameResourceUri) { + return indexStatus.renameResourceUri; + } + + return resource.resourceUri; + + case Status.BOTH_ADDED: + case Status.BOTH_MODIFIED: + return resource.resourceUri; + } + + throw new Error('Should never happen'); + } + + private getTitle(resource: Resource): string { + const basename = path.basename(resource.resourceUri.fsPath); + + switch (resource.type) { + case Status.INDEX_MODIFIED: + case Status.INDEX_RENAMED: + case Status.INDEX_ADDED: + return localize('git.title.index', '{0} (Index)', basename); + + case Status.MODIFIED: + case Status.BOTH_ADDED: + case Status.BOTH_MODIFIED: + return localize('git.title.workingTree', '{0} (Working Tree)', basename); + + case Status.INDEX_DELETED: + case Status.DELETED: + return localize('git.title.deleted', '{0} (Deleted)', basename); + + case Status.DELETED_BY_US: + return localize('git.title.theirs', '{0} (Theirs)', basename); + + case Status.DELETED_BY_THEM: + return localize('git.title.ours', '{0} (Ours)', basename); + + case Status.UNTRACKED: + return localize('git.title.untracked', '{0} (Untracked)', basename); + + default: + return ''; + } + } +} + export class Repository implements Disposable { private _onDidChangeRepository = new EventEmitter(); @@ -680,6 +843,7 @@ export class Repository implements Disposable { private isRepositoryHuge = false; private didWarnAboutLimit = false; + private resourceCommandResolver = new ResourceCommandResolver(this); private disposables: Disposable[] = []; constructor( @@ -746,11 +910,12 @@ export class Repository implements Disposable { onConfigListener(updateIndexGroupVisibility, this, this.disposables); updateIndexGroupVisibility(); - const onConfigListenerForBranchSortOrder = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchSortOrder', root)); - onConfigListenerForBranchSortOrder(this.updateModelState, this, this.disposables); - - const onConfigListenerForUntracked = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.untrackedChanges', root)); - onConfigListenerForUntracked(this.updateModelState, this, this.disposables); + filterEvent(workspace.onDidChangeConfiguration, e => + e.affectsConfiguration('git.branchSortOrder', root) + || e.affectsConfiguration('git.untrackedChanges', root) + || e.affectsConfiguration('git.ignoreSubmodules', root) + || e.affectsConfiguration('git.openDiffOnClick', root) + )(this.updateModelState, this, this.disposables); const updateInputBoxVisibility = () => { const config = workspace.getConfiguration('git', root); @@ -864,6 +1029,12 @@ export class Repository implements Disposable { return; } + const path = uri.path; + + if (this.mergeGroup.resourceStates.some(r => r.resourceUri.path === path)) { + return undefined; + } + return toGitUri(uri, '', { replaceFileExtension: true }); } @@ -976,7 +1147,7 @@ export class Repository implements Disposable { await this.run(Operation.RevertFiles, () => this.repository.revert('HEAD', resources.map(r => r.fsPath))); } - async commit(message: string, opts: CommitOptions = Object.create(null)): Promise { + async commit(message: string | undefined, opts: CommitOptions = Object.create(null)): Promise { if (this.rebaseCommit) { await this.run(Operation.RebaseContinue, async () => { if (opts.all) { @@ -1053,6 +1224,14 @@ export class Repository implements Disposable { await this.run(Operation.RenameBranch, () => this.repository.renameBranch(name)); } + async cherryPick(commitHash: string): Promise { + await this.run(Operation.CherryPick, () => this.repository.cherryPick(commitHash)); + } + + async move(from: string, to: string): Promise { + await this.run(Operation.Move, () => this.repository.move(from, to)); + } + async getBranch(name: string): Promise { return await this.run(Operation.GetBranch, () => this.repository.getBranch(name)); } @@ -1081,12 +1260,12 @@ export class Repository implements Disposable { await this.run(Operation.DeleteTag, () => this.repository.deleteTag(name)); } - async checkout(treeish: string): Promise { - await this.run(Operation.Checkout, () => this.repository.checkout(treeish, [])); + async checkout(treeish: string, opts?: { detached?: boolean }): Promise { + await this.run(Operation.Checkout, () => this.repository.checkout(treeish, [], opts)); } - async checkoutTracking(treeish: string): Promise { - await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { track: true })); + async checkoutTracking(treeish: string, opts: { detached?: boolean } = {}): Promise { + await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { ...opts, track: true })); } async findTrackingBranches(upstreamRef: string): Promise { @@ -1119,21 +1298,31 @@ export class Repository implements Disposable { @throttle async fetchDefault(options: { silent?: boolean } = {}): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch(options)); + await this._fetch({ silent: options.silent }); } @throttle async fetchPrune(): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ prune: true })); + await this._fetch({ prune: true }); } @throttle async fetchAll(): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ all: true })); + await this._fetch({ all: true }); } async fetch(remote?: string, ref?: string, depth?: number): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ remote, ref, depth })); + await this._fetch({ remote, ref, depth }); + } + + private async _fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean } = {}): Promise { + if (!options.prune) { + const config = workspace.getConfiguration('git', Uri.file(this.root)); + const prune = config.get('pruneOnFetch'); + options.prune = prune; + } + + await this.run(Operation.Fetch, async () => this.repository.fetch(options)); } @throttle @@ -1169,11 +1358,12 @@ export class Repository implements Disposable { const fetchOnPull = config.get('fetchOnPull'); const tags = config.get('pullTags'); + // When fetchOnPull is enabled, fetch all branches when pulling if (fetchOnPull) { - await this.repository.pull(rebase, undefined, undefined, { unshallow, tags }); - } else { - await this.repository.pull(rebase, remote, branch, { unshallow, tags }); + await this.repository.fetch({ all: true }); } + + await this.repository.pull(rebase, remote, branch, { unshallow, tags }); }); }); } @@ -1199,6 +1389,10 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this._push(remote, undefined, false, true, forcePushMode)); } + async pushTags(remote?: string, forcePushMode?: ForcePushMode): Promise { + await this.run(Operation.Push, () => this._push(remote, undefined, false, false, forcePushMode, true)); + } + async blame(path: string): Promise { return await this.run(Operation.Blame, () => this.repository.blame(path)); } @@ -1229,11 +1423,18 @@ export class Repository implements Disposable { const config = workspace.getConfiguration('git', Uri.file(this.root)); const fetchOnPull = config.get('fetchOnPull'); const tags = config.get('pullTags'); + const followTags = config.get('followTagsWhenSync'); const supportCancellation = config.get('supportCancellation'); - const fn = fetchOnPull - ? async (cancellationToken?: CancellationToken) => await this.repository.pull(rebase, undefined, undefined, { tags, cancellationToken }) - : async (cancellationToken?: CancellationToken) => await this.repository.pull(rebase, remoteName, pullBranch, { tags, cancellationToken }); + const fn = async (cancellationToken?: CancellationToken) => { + // When fetchOnPull is enabled, fetch all branches when pulling + if (fetchOnPull) { + await this.repository.fetch({ all: true, cancellationToken }); + } + + await this.repository.pull(rebase, remoteName, pullBranch, { tags, cancellationToken }); + }; + if (supportCancellation) { const opts: ProgressOptions = { @@ -1256,7 +1457,7 @@ export class Repository implements Disposable { const shouldPush = this.HEAD && (typeof this.HEAD.ahead === 'number' ? this.HEAD.ahead > 0 : true); if (shouldPush) { - await this._push(remoteName, pushBranch); + await this._push(remoteName, pushBranch, false, followTags); } }); }); @@ -1418,9 +1619,9 @@ export class Repository implements Disposable { return ignored; } - private async _push(remote?: string, refspec?: string, setUpstream: boolean = false, tags = false, forcePushMode?: ForcePushMode): Promise { + private async _push(remote?: string, refspec?: string, setUpstream: boolean = false, followTags = false, forcePushMode?: ForcePushMode, tags = false): Promise { try { - await this.repository.push(remote, refspec, setUpstream, tags, forcePushMode); + await this.repository.push(remote, refspec, setUpstream, followTags, forcePushMode, tags); } catch (err) { if (!remote || !refspec) { throw err; @@ -1518,9 +1719,12 @@ export class Repository implements Disposable { @throttle private async updateModelState(): Promise { - const { status, didHitLimit } = await this.repository.getStatus(); - const config = workspace.getConfiguration('git'); const scopedConfig = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const ignoreSubmodules = scopedConfig.get('ignoreSubmodules'); + + const { status, didHitLimit } = await this.repository.getStatus({ ignoreSubmodules }); + + const config = workspace.getConfiguration('git'); const shouldIgnore = config.get('ignoreLimitWarning') === true; const useIcons = !config.get('decorations.enabled', true); this.isRepositoryHuge = didHitLimit; @@ -1595,36 +1799,36 @@ export class Repository implements Disposable { switch (raw.x + raw.y) { case '??': switch (untrackedChanges) { - case 'mixed': return workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.UNTRACKED, useIcons)); - case 'separate': return untracked.push(new Resource(ResourceGroupType.Untracked, uri, Status.UNTRACKED, useIcons)); + case 'mixed': return workingTree.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.UNTRACKED, useIcons)); + case 'separate': return untracked.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Untracked, uri, Status.UNTRACKED, useIcons)); default: return undefined; } case '!!': switch (untrackedChanges) { - case 'mixed': return workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.IGNORED, useIcons)); - case 'separate': return untracked.push(new Resource(ResourceGroupType.Untracked, uri, Status.IGNORED, useIcons)); + case 'mixed': return workingTree.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.IGNORED, useIcons)); + case 'separate': return untracked.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Untracked, uri, Status.IGNORED, useIcons)); default: return undefined; } - case 'DD': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_DELETED, useIcons)); - case 'AU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.ADDED_BY_US, useIcons)); - case 'UD': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.DELETED_BY_THEM, useIcons)); - case 'UA': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.ADDED_BY_THEM, useIcons)); - case 'DU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.DELETED_BY_US, useIcons)); - case 'AA': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_ADDED, useIcons)); - case 'UU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_MODIFIED, useIcons)); + case 'DD': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.BOTH_DELETED, useIcons)); + case 'AU': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.ADDED_BY_US, useIcons)); + case 'UD': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.DELETED_BY_THEM, useIcons)); + case 'UA': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.ADDED_BY_THEM, useIcons)); + case 'DU': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.DELETED_BY_US, useIcons)); + case 'AA': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.BOTH_ADDED, useIcons)); + case 'UU': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.BOTH_MODIFIED, useIcons)); } switch (raw.x) { - case 'M': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_MODIFIED, useIcons)); break; - case 'A': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_ADDED, useIcons)); break; - case 'D': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_DELETED, useIcons)); break; - case 'R': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_RENAMED, useIcons, renameUri)); break; - case 'C': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_COPIED, useIcons, renameUri)); break; + case 'M': index.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Index, uri, Status.INDEX_MODIFIED, useIcons)); break; + case 'A': index.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Index, uri, Status.INDEX_ADDED, useIcons)); break; + case 'D': index.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Index, uri, Status.INDEX_DELETED, useIcons)); break; + case 'R': index.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Index, uri, Status.INDEX_RENAMED, useIcons, renameUri)); break; + case 'C': index.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Index, uri, Status.INDEX_COPIED, useIcons, renameUri)); break; } switch (raw.y) { - case 'M': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); break; - case 'D': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; - case 'A': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; + case 'M': workingTree.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); break; + case 'D': workingTree.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; + case 'A': workingTree.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; } return undefined; diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index a315963a5..6e2408366 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -200,7 +200,7 @@ export class GitTimelineProvider implements TimelineProvider { if (working) { const date = new Date(); - const item = new GitTimelineItem('', index ? '~' : 'HEAD', localize('git.timeline.uncommitedChanges', 'Uncommited Changes'), date.getTime(), 'working', 'git:file:working'); + const item = new GitTimelineItem('', index ? '~' : 'HEAD', localize('git.timeline.uncommitedChanges', 'Uncommitted Changes'), date.getTime(), 'working', 'git:file:working'); // TODO@eamodio: Replace with a better icon -- reflecting its status maybe? item.iconPath = new (ThemeIcon as any)('git-commit'); item.description = ''; diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts index 98b715f80..b49942b52 100644 --- a/extensions/github-authentication/src/extension.ts +++ b/extensions/github-authentication/src/extension.ts @@ -40,6 +40,15 @@ export async function activate(context: vscode.ExtensionContext) { onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] }); return session; } catch (e) { + // If login was cancelled, do not notify user. + if (e.message === 'Cancelled') { + /* __GDPR__ + "loginCancelled" : { } + */ + telemetryReporter.sendTelemetryEvent('loginCancelled'); + throw e; + } + /* __GDPR__ "loginFailed" : { } */ diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index f28ac97c6..d1cf45b7d 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -180,9 +180,12 @@ export class GitHubAuthenticationProvider { } public async logout(id: string) { + Logger.info(`Logging out of ${id}`); const sessionIndex = this._sessions.findIndex(session => session.id === id); if (sessionIndex > -1) { this._sessions.splice(sessionIndex, 1); + } else { + Logger.error('Session not found'); } await this.storeSessions(); diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index 55c0e4ef4..b50285c9a 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -23,7 +23,7 @@ class UriEventHandler extends vscode.EventEmitter implements vscode. export const uriHandler = new UriEventHandler; -const onDidManuallyProvideToken = new vscode.EventEmitter(); +const onDidManuallyProvideToken = new vscode.EventEmitter(); @@ -91,7 +91,7 @@ export class GitHubServer { return Promise.race([ existingPromise, - promiseFromEvent(onDidManuallyProvideToken.event) + promiseFromEvent(onDidManuallyProvideToken.event, (token: string | undefined): string => { if (!token) { throw new Error('Cancelled'); } return token; }) ]).finally(() => { this._pendingStates.delete(scopes); this._codeExchangePromises.delete(scopes); @@ -147,7 +147,11 @@ export class GitHubServer { public async manuallyProvideToken() { const uriOrToken = await vscode.window.showInputBox({ prompt: 'Token', ignoreFocusOut: true }); - if (!uriOrToken) { return; } + if (!uriOrToken) { + onDidManuallyProvideToken.fire(undefined); + return; + } + try { const uri = vscode.Uri.parse(uriOrToken); if (!uri.scheme || uri.scheme === 'file') { throw new Error; } diff --git a/extensions/github/src/publish.ts b/extensions/github/src/publish.ts index 0abb606b5..cf8f306cf 100644 --- a/extensions/github/src/publish.ts +++ b/extensions/github/src/publish.ts @@ -9,6 +9,7 @@ import { API as GitAPI, Repository } from './typings/git'; import { getOctokit } from './auth'; import { TextEncoder } from 'util'; import { basename } from 'path'; +import { Octokit } from '@octokit/rest'; const localize = nls.loadMessageBundle(); @@ -57,9 +58,18 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) quickpick.show(); quickpick.busy = true; - const octokit = await getOctokit(); - const user = await octokit.users.getAuthenticated({}); - const owner = user.data.login; + let owner: string; + let octokit: Octokit; + try { + octokit = await getOctokit(); + const user = await octokit.users.getAuthenticated({}); + owner = user.data.login; + } catch (e) { + // User has cancelled sign in + quickpick.dispose(); + return; + } + quickpick.busy = false; let repo: string | undefined; @@ -139,7 +149,7 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) new Promise(c => quickpick.onDidHide(() => c(undefined))) ]); - if (!result) { + if (!result || result.length === 0) { return; } @@ -192,9 +202,9 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) } const openInGitHub = 'Open In GitHub'; - const action = await vscode.window.showInformationMessage(`Successfully published the '${owner}/${repo}' repository on GitHub.`, openInGitHub); - - if (action === openInGitHub) { - vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(githubRepository.html_url)); - } + vscode.window.showInformationMessage(`Successfully published the '${owner}/${repo}' repository on GitHub.`, openInGitHub).then(action => { + if (action === openInGitHub) { + vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(githubRepository.html_url)); + } + }); } diff --git a/extensions/github/src/pushErrorHandler.ts b/extensions/github/src/pushErrorHandler.ts index a08a8d45c..0b2f3b9de 100644 --- a/extensions/github/src/pushErrorHandler.ts +++ b/extensions/github/src/pushErrorHandler.ts @@ -40,8 +40,14 @@ async function handlePushError(repository: Repository, remote: Remote, refspec: // Issue: what if there's already another `origin` repo? await repository.addRemote('origin', ghRepository.clone_url); - await repository.fetch('origin', remoteName); - await repository.setBranchUpstream(localName, `origin/${remoteName}`); + + try { + await repository.fetch('origin', remoteName); + await repository.setBranchUpstream(localName, `origin/${remoteName}`); + } catch { + // noop + } + await repository.push('origin', localName, true); return [octokit, ghRepository]; diff --git a/extensions/github/src/remoteSourceProvider.ts b/extensions/github/src/remoteSourceProvider.ts index 9d0ab4c58..17b153006 100644 --- a/extensions/github/src/remoteSourceProvider.ts +++ b/extensions/github/src/remoteSourceProvider.ts @@ -8,6 +8,12 @@ import { getOctokit } from './auth'; import { Octokit } from '@octokit/rest'; import { publishRepository } from './publish'; +function parse(url: string): { owner: string, repo: string } | undefined { + const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\.git/i.exec(url) + || /^git@github\.com:([^/]+)\/([^/]+)\.git/i.exec(url); + return (match && { owner: match[1], repo: match[2] }) ?? undefined; +} + function asRemoteSource(raw: any): RemoteSource { return { name: `$(github) ${raw.full_name}`, @@ -28,17 +34,30 @@ export class GithubRemoteSourceProvider implements RemoteSourceProvider { async getRemoteSources(query?: string): Promise { const octokit = await getOctokit(); - const [fromUser, fromQuery] = await Promise.all([ + + if (query) { + const repository = parse(query); + + if (repository) { + const raw = await octokit.repos.get(repository); + return [asRemoteSource(raw.data)]; + } + } + + const all = await Promise.all([ this.getUserRemoteSources(octokit, query), this.getQueryRemoteSources(octokit, query) ]); - const userRepos = new Set(fromUser.map(r => r.name)); + const map = new Map(); - return [ - ...fromUser, - ...fromQuery.filter(r => !userRepos.has(r.name)) - ]; + for (const group of all) { + for (const remoteSource of group) { + map.set(remoteSource.name, remoteSource); + } + } + + return [...map.values()]; } private async getUserRemoteSources(octokit: Octokit, query?: string): Promise { @@ -61,6 +80,35 @@ export class GithubRemoteSourceProvider implements RemoteSourceProvider { return raw.data.items.map(asRemoteSource); } + async getBranches(url: string): Promise { + const repository = parse(url); + + if (!repository) { + return []; + } + + const octokit = await getOctokit(); + + const branches: string[] = []; + let page = 1; + + while (true) { + let res = await octokit.repos.listBranches({ ...repository, per_page: 100, page }); + + if (res.data.length === 0) { + break; + } + + branches.push(...res.data.map(b => b.name)); + page++; + } + + const repo = await octokit.repos.get(repository); + const defaultBranch = repo.data.default_branch; + + return branches.sort((a, b) => a === defaultBranch ? -1 : b === defaultBranch ? 1 : 0); + } + publishRepository(repository: Repository): Promise { return publishRepository(this.gitAPI, repository); } diff --git a/extensions/github/src/typings/git.d.ts b/extensions/github/src/typings/git.d.ts index 54f21b96d..b9a09632b 100644 --- a/extensions/github/src/typings/git.d.ts +++ b/extensions/github/src/typings/git.d.ts @@ -130,6 +130,7 @@ export interface CommitOptions { signoff?: boolean; signCommit?: boolean; empty?: boolean; + noVerify?: boolean; } export interface BranchQuery { @@ -211,6 +212,7 @@ export interface RemoteSourceProvider { readonly icon?: string; // codicon name readonly supportsQuery?: boolean; getRemoteSources(query?: string): ProviderResult; + getBranches?(url: string): ProviderResult; publishRepository?(repository: Repository): Promise; } diff --git a/extensions/html-language-features/client/src/htmlClient.ts b/extensions/html-language-features/client/src/htmlClient.ts index 6917b5695..494b935a7 100644 --- a/extensions/html-language-features/client/src/htmlClient.ts +++ b/extensions/html-language-features/client/src/htmlClient.ts @@ -27,8 +27,8 @@ namespace CustomDataChangedNotification { namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); } -namespace OnTypeRenameRequest { - export const type: RequestType = new RequestType('html/onTypeRename'); +namespace LinkedEditingRequest { + export const type: RequestType = new RequestType('html/linkedEditing'); } // experimental: semantic tokens @@ -44,7 +44,7 @@ namespace SemanticTokenLegendRequest { } namespace SettingIds { - export const renameOnType = 'editor.renameOnType'; + export const linkedRename = 'editor.linkedRename'; export const formatEnable = 'html.format.enable'; } @@ -169,10 +169,10 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua } }); - disposable = languages.registerOnTypeRenameProvider(documentSelector, { - async provideOnTypeRenameRanges(document, position) { + disposable = languages.registerLinkedEditingRangeProvider(documentSelector, { + async provideLinkedEditingRanges(document, position) { const param = client.code2ProtocolConverter.asTextDocumentPositionParams(document, position); - return client.sendRequest(OnTypeRenameRequest.type, param).then(response => { + return client.sendRequest(LinkedEditingRequest.type, param).then(response => { if (response) { return { ranges: response.map(r => client.protocol2CodeConverter.asRange(r)) @@ -301,7 +301,7 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua const promptForTypeOnRenameKey = 'html.promptForTypeOnRename'; const promptForTypeOnRename = extensions.getExtension('formulahendry.auto-rename-tag') !== undefined && (context.globalState.get(promptForTypeOnRenameKey) !== false) && - !workspace.getConfiguration('editor', { languageId: 'html' }).get('renameOnType'); + !workspace.getConfiguration('editor', { languageId: 'html' }).get('linkedRename'); if (promptForTypeOnRename) { const activeEditorListener = window.onDidChangeActiveTextEditor(async e => { @@ -309,9 +309,9 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua context.globalState.update(promptForTypeOnRenameKey, false); activeEditorListener.dispose(); const configure = localize('configureButton', 'Configure'); - const res = await window.showInformationMessage(localize('renameOnTypeQuestion', 'VS Code now has built-in support for auto-renaming tags. Do you want to enable it?'), configure); + const res = await window.showInformationMessage(localize('linkedRenameQuestion', 'VS Code now has built-in support for auto-renaming tags. Do you want to enable it?'), configure); if (res === configure) { - commands.executeCommand('workbench.action.openSettings', SettingIds.renameOnType); + commands.executeCommand('workbench.action.openSettings', SettingIds.linkedRename); } } }); diff --git a/extensions/html-language-features/client/src/node/htmlClientMain.ts b/extensions/html-language-features/client/src/node/htmlClientMain.ts index c58515b37..097bda1f6 100644 --- a/extensions/html-language-features/client/src/node/htmlClientMain.ts +++ b/extensions/html-language-features/client/src/node/htmlClientMain.ts @@ -24,7 +24,7 @@ export function activate(context: ExtensionContext) { const serverModule = context.asAbsolutePath(serverMain); // The debug options for the server - const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; + const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (8000 + Math.round(Math.random() * 999))] }; // If the extension is launch in debug mode the debug server options are use // Otherwise the run options are used diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 204ae49b1..c2c211ccb 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -138,6 +138,31 @@ ], "description": "%html.format.wrapAttributes.desc%" }, + "html.format.wrapAttributesIndentSize": { + "type": [ + "number", + "null" + ], + "scope": "resource", + "default": null, + "description": "%html.format.wrapAttributesIndentSize.desc%" + }, + "html.format.templating": { + "type": [ + "boolean" + ], + "scope": "resource", + "default": false, + "description": "%html.format.templating.desc%" + }, + "html.format.unformattedContentDelimiter": { + "type": [ + "string" + ], + "scope": "resource", + "default": "", + "markdownDescription": "%html.format.unformattedContentDelimiter.desc%" + }, "html.suggest.html5": { "type": "boolean", "scope": "resource", @@ -162,6 +187,18 @@ "default": true, "description": "%html.autoClosingTags%" }, + "html.hover.documentation": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%html.hover.documentation%" + }, + "html.hover.references": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%html.hover.references%" + }, "html.mirrorCursorOnMatchingTag": { "type": "boolean", "scope": "resource", @@ -204,7 +241,7 @@ "dependencies": { "vscode-extension-telemetry": "0.1.1", "vscode-languageclient": "7.0.0-next.5.1", - "vscode-nls": "^4.1.2" + "vscode-nls": "^5.0.0" }, "devDependencies": { "@types/node": "^12.11.7" diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index ff5815029..49c632091 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -20,11 +20,16 @@ "html.format.wrapAttributes.alignedmultiple": "Wrap when line length is exceeded, align attributes vertically.", "html.format.wrapAttributes.preserve": "Preserve wrapping of attributes", "html.format.wrapAttributes.preservealigned": "Preserve wrapping of attributes but align.", + "html.format.templating.desc": "Honor django, erb, handlebars and php templating language tags.", + "html.format.unformattedContentDelimiter.desc": "Keep text content together between this string.", + "html.format.wrapAttributesIndentSize.desc": "Alignment size when using 'force aligned' and 'aligned multiple' in `#html.format.wrapAttributes#` or `null` to use the default indent size.", "html.suggest.html5.desc": "Controls whether the built-in HTML language support suggests HTML5 tags, properties and values.", "html.trace.server.desc": "Traces the communication between VS Code and the HTML language server.", "html.validate.scripts": "Controls whether the built-in HTML language support validates embedded scripts.", "html.validate.styles": "Controls whether the built-in HTML language support validates embedded styles.", "html.autoClosingTags": "Enable/disable autoclosing of HTML tags.", "html.mirrorCursorOnMatchingTag": "Enable/disable mirroring cursor on matching HTML tag.", - "html.mirrorCursorOnMatchingTagDeprecationMessage": "Deprecated in favor of `editor.renameOnType`" + "html.mirrorCursorOnMatchingTagDeprecationMessage": "Deprecated in favor of `editor.linkedEditing`", + "html.hover.documentation": "Show tag and attribute documentation in hover.", + "html.hover.references": "Show references to MDN in hover." } diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 62bf41766..65ce7cc7e 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,8 +9,8 @@ }, "main": "./out/node/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^4.3.5", - "vscode-html-languageservice": "^3.1.4", + "vscode-css-languageservice": "^4.4.0", + "vscode-html-languageservice": "^3.2.0", "vscode-languageserver": "7.0.0-next.3", "vscode-nls": "^5.0.0", "vscode-uri": "^2.1.2" diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index 1ddfef6ce..7b76a8577 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -33,8 +33,8 @@ namespace CustomDataChangedNotification { namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); } -namespace OnTypeRenameRequest { - export const type: RequestType = new RequestType('html/onTypeRename'); +namespace LinkedEditingRequest { + export const type: RequestType = new RequestType('html/linkedEditing'); } // experimental: semantic tokens @@ -284,7 +284,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) if (!mode || !mode.doComplete) { return { isIncomplete: true, items: [] }; } - const doComplete = mode.doComplete!; + const doComplete = mode.doComplete; if (mode.getId() !== 'html') { /* __GDPR__ @@ -321,8 +321,10 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const document = documents.get(textDocumentPosition.textDocument.uri); if (document) { const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position); - if (mode && mode.doHover) { - return mode.doHover(document, textDocumentPosition.position); + const doHover = mode?.doHover; + if (doHover) { + const settings = await getDocumentSettings(document, () => doHover.length > 2); + return doHover(document, textDocumentPosition.position, settings); } } return null; @@ -508,15 +510,15 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }, null, `Error while computing rename for ${params.textDocument.uri}`, token); }); - connection.onRequest(OnTypeRenameRequest.type, (params, token) => { + connection.onRequest(LinkedEditingRequest.type, (params, token) => { return runSafe(async () => { const document = documents.get(params.textDocument.uri); if (document) { const pos = params.position; if (pos.character > 0) { const mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1)); - if (mode && mode.doOnTypeRename) { - return mode.doOnTypeRename(document, pos); + if (mode && mode.doLinkedEditing) { + return mode.doLinkedEditing(document, pos); } } } diff --git a/extensions/html-language-features/server/src/modes/cssMode.ts b/extensions/html-language-features/server/src/modes/cssMode.ts index 9b3ddd9b7..f7c5455b7 100644 --- a/extensions/html-language-features/server/src/modes/cssMode.ts +++ b/extensions/html-language-features/server/src/modes/cssMode.ts @@ -5,7 +5,7 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; import { Stylesheet, LanguageService as CSSLanguageService } from 'vscode-css-languageservice'; -import { LanguageMode, Workspace, Color, TextDocument, Position, Range, CompletionList, DocumentContext } from './languageModes'; +import { LanguageMode, Workspace, Color, TextDocument, Position, Range, CompletionList, DocumentContext, Settings } from './languageModes'; import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport'; export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegions: LanguageModelCache, workspace: Workspace): LanguageMode { @@ -25,9 +25,9 @@ export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegio const stylesheet = cssStylesheets.get(embedded); return cssLanguageService.doComplete2(embedded, position, stylesheet, documentContext) || CompletionList.create(); }, - async doHover(document: TextDocument, position: Position) { + async doHover(document: TextDocument, position: Position, settings?: Settings) { let embedded = embeddedCSSDocuments.get(document); - return cssLanguageService.doHover(embedded, position, cssStylesheets.get(embedded)); + return cssLanguageService.doHover(embedded, position, cssStylesheets.get(embedded), settings?.html?.hover); }, async findDocumentHighlight(document: TextDocument, position: Position) { let embedded = embeddedCSSDocuments.get(document); diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 96462f8e0..a8a17ca8c 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -8,7 +8,7 @@ import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration, SelectionRange, TextDocument, Position, Range, FoldingRange, - LanguageMode, Workspace + LanguageMode, Workspace, Settings } from './languageModes'; export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: Workspace): LanguageMode { @@ -31,8 +31,8 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: let completionList = htmlLanguageService.doComplete2(document, position, htmlDocument, documentContext, options); return completionList; }, - async doHover(document: TextDocument, position: Position) { - return htmlLanguageService.doHover(document, position, htmlDocuments.get(document)); + async doHover(document: TextDocument, position: Position, settings?: Settings) { + return htmlLanguageService.doHover(document, position, htmlDocuments.get(document), settings?.html?.hover); }, async findDocumentHighlight(document: TextDocument, position: Position) { return htmlLanguageService.findDocumentHighlights(document, position, htmlDocuments.get(document)); @@ -80,9 +80,9 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: const htmlDocument = htmlDocuments.get(document); return htmlLanguageService.findMatchingTagPosition(document, position, htmlDocument); }, - async doOnTypeRename(document: TextDocument, position: Position) { + async doLinkedEditing(document: TextDocument, position: Position) { const htmlDocument = htmlDocuments.get(document); - return htmlLanguageService.findOnTypeRenameRanges(document, position, htmlDocument); + return htmlLanguageService.findLinkedEditingRanges(document, position, htmlDocument); }, dispose() { htmlDocuments.dispose(); diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index 086b8a906..1fc33faed 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -6,7 +6,7 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; import { SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation, - Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString, + Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions, FoldingRange, FoldingRangeKind, SelectionRange, LanguageMode, Settings, SemanticTokenData, Workspace, DocumentContext } from './languageModes'; @@ -142,10 +142,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache Promise; doComplete?: (document: TextDocument, position: Position, documentContext: DocumentContext, settings?: Settings) => Promise; doResolve?: (document: TextDocument, item: CompletionItem) => Promise; - doHover?: (document: TextDocument, position: Position) => Promise; + doHover?: (document: TextDocument, position: Position, settings?: Settings) => Promise; doSignatureHelp?: (document: TextDocument, position: Position) => Promise; doRename?: (document: TextDocument, position: Position, newName: string) => Promise; - doOnTypeRename?: (document: TextDocument, position: Position) => Promise; + doLinkedEditing?: (document: TextDocument, position: Position) => Promise; findDocumentHighlight?: (document: TextDocument, position: Position) => Promise; findDocumentSymbols?: (document: TextDocument) => Promise; findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => Promise; diff --git a/extensions/html-language-features/server/src/test/semanticTokens.test.ts b/extensions/html-language-features/server/src/test/semanticTokens.test.ts index 6d815748e..548ce7d84 100644 --- a/extensions/html-language-features/server/src/test/semanticTokens.test.ts +++ b/extensions/html-language-features/server/src/test/semanticTokens.test.ts @@ -87,8 +87,8 @@ suite('HTML Semantic Tokens', () => { ]; await assertTokens(input, [ t(3, 11, 3, 'function.declaration'), t(3, 15, 2, 'parameter.declaration'), - t(4, 11, 3, 'function'), t(4, 15, 4, 'interface'), t(4, 20, 3, 'member'), t(4, 24, 2, 'parameter'), - t(6, 6, 6, 'variable'), t(6, 13, 8, 'property'), t(6, 24, 5, 'member'), t(6, 35, 7, 'member'), t(6, 43, 1, 'parameter.declaration'), t(6, 48, 3, 'function'), t(6, 52, 1, 'parameter') + t(4, 11, 3, 'function'), t(4, 15, 4, 'interface'), t(4, 20, 3, 'method'), t(4, 24, 2, 'parameter'), + t(6, 6, 6, 'variable'), t(6, 13, 8, 'property'), t(6, 24, 5, 'method'), t(6, 35, 7, 'method'), t(6, 43, 1, 'parameter.declaration'), t(6, 48, 3, 'function'), t(6, 52, 1, 'parameter') ]); }); @@ -115,9 +115,9 @@ suite('HTML Semantic Tokens', () => { t(3, 8, 1, 'class.declaration'), t(4, 11, 1, 'property.declaration.static'), t(5, 4, 1, 'property.declaration'), - t(6, 10, 1, 'member.declaration.async'), t(6, 23, 1, 'class'), t(6, 25, 1, 'property.static'), t(6, 40, 1, 'member.async'), + t(6, 10, 1, 'method.declaration.async'), t(6, 23, 1, 'class'), t(6, 25, 1, 'property.static'), t(6, 40, 1, 'method.async'), t(7, 8, 1, 'property.declaration'), t(7, 26, 1, 'property'), - t(8, 11, 1, 'member.declaration.static'), t(8, 28, 1, 'class'), t(8, 32, 1, 'property'), + t(8, 11, 1, 'method.declaration.static'), t(8, 28, 1, 'class'), t(8, 32, 1, 'property'), ]); }); @@ -157,7 +157,7 @@ suite('HTML Semantic Tokens', () => { t(3, 8, 1, 'variable.declaration.readonly'), t(4, 8, 1, 'class.declaration'), t(4, 28, 1, 'property.declaration.static.readonly'), t(4, 42, 3, 'property.declaration.static'), t(4, 47, 3, 'interface'), t(5, 13, 1, 'enum.declaration'), t(5, 17, 1, 'property.declaration.readonly'), t(5, 24, 1, 'property.declaration.readonly'), t(5, 28, 1, 'property.readonly'), - t(6, 2, 7, 'variable'), t(6, 10, 3, 'member'), t(6, 14, 1, 'variable.readonly'), t(6, 18, 1, 'class'), t(6, 20, 1, 'property.static.readonly'), t(6, 24, 1, 'class'), t(6, 26, 3, 'property.static'), t(6, 30, 6, 'property.readonly'), + t(6, 2, 7, 'variable'), t(6, 10, 3, 'method'), t(6, 14, 1, 'variable.readonly'), t(6, 18, 1, 'class'), t(6, 20, 1, 'property.static.readonly'), t(6, 24, 1, 'class'), t(6, 26, 3, 'property.static'), t(6, 30, 6, 'property.readonly'), ]); }); @@ -197,7 +197,7 @@ suite('HTML Semantic Tokens', () => { ]; await assertTokens(input, [ t(3, 11, 1, 'function.declaration'), t(3, 13, 1, 'typeParameter.declaration'), t(3, 16, 2, 'parameter.declaration'), t(3, 20, 1, 'typeParameter'), t(3, 24, 1, 'typeParameter'), t(3, 39, 2, 'parameter'), - t(6, 2, 6, 'variable'), t(6, 9, 5, 'member') + t(6, 2, 6, 'variable'), t(6, 9, 5, 'method') ]); }); @@ -215,7 +215,7 @@ suite('HTML Semantic Tokens', () => { /*9*/'', ]; await assertTokens(input, [ - t(3, 2, 6, 'variable'), t(3, 9, 5, 'member') + t(3, 2, 6, 'variable'), t(3, 9, 5, 'method') ], [Range.create(Position.create(2, 0), Position.create(4, 0))]); await assertTokens(input, [ diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 2bb5bba8e..6d17972d5 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -880,20 +880,20 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -vscode-css-languageservice@^4.3.5: - version "4.3.5" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.5.tgz#92f8817057dee7c381df2289aad539c7b553548a" - integrity sha512-g9Pjxt9T32jhY0nTOo7WRFm0As27IfdaAxcFa8c7Rml1ZqBn3XXbkExjzxY7sBWYm7I1Tp4dK6UHXHoUQHGwig== +vscode-css-languageservice@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.4.0.tgz#a7c5edf3057e707601ca18fa3728784a298513b4" + integrity sha512-jWi+297PJUUWTHwlcrZz0zIuEXuHOBJIQMapXmEzbosWGv/gMnNSAMV4hTKnl5wzxvZKZzV6j+WFdrSlKQ5qnw== dependencies: vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" vscode-nls "^5.0.0" vscode-uri "^2.1.2" -vscode-html-languageservice@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.1.4.tgz#0316dff77ee38dc176f40560cbf55e4f64f4f433" - integrity sha512-3M+bm+hNvwQcScVe5/ok9BXvctOiGJ4nlOkkFf+WKSDrYNkarZ/RByKOa1/iylbvZxJUPzbeziembWPe/dMvhw== +vscode-html-languageservice@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.2.0.tgz#e92269a04097d87bd23431e3a4e491a27b5447b9" + integrity sha512-aLWIoWkvb5HYTVE0kI9/u3P0ZAJGrYOSAAE6L0wqB9radKRtbJNrF9+BjSUFyCgBdNBE/GFExo35LoknQDJrfw== dependencies: vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 0d4b9507c..7d639c7b4 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -71,10 +71,10 @@ vscode-languageserver-types@3.16.0-next.2: resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== -vscode-nls@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" - integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== +vscode-nls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" + integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== zone.js@0.7.6: version "0.7.6" diff --git a/extensions/javascript/snippets/javascript.code-snippets b/extensions/javascript/snippets/javascript.code-snippets index b005c80c8..a48a64c28 100644 --- a/extensions/javascript/snippets/javascript.code-snippets +++ b/extensions/javascript/snippets/javascript.code-snippets @@ -35,7 +35,7 @@ "prefix": "forin", "body": [ "for (const ${1:key} in ${2:object}) {", - "\tif (${2:object}.hasOwnProperty(${1:key})) {", + "\tif (Object.hasOwnProperty.call(${2:object}, ${1:key})) {", "\t\tconst ${3:element} = ${2:object}[${1:key}];", "\t\t$0", "\t}", diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 9a991cde0..c729c8ff9 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/fa4e0d3a918db0eab8e5c5be952f3bd649968456", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/59b62cbc624e3a01368e5432d85af0c99a27d49d", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -128,8 +128,18 @@ "match": "(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js entity.name.function.js" @@ -487,7 +497,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js variable.other.constant.js entity.name.function.js" @@ -871,7 +881,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js" @@ -1111,7 +1121,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js entity.name.function.js" @@ -1251,6 +1261,9 @@ { "include": "#return-type" }, + { + "include": "#type-function-return-type" + }, { "include": "#decl-block" }, @@ -1434,7 +1447,7 @@ }, { "name": "meta.arrow.js", - "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -2469,7 +2482,7 @@ }, { "name": "string.regexp.js", - "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)*\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.js" @@ -2628,7 +2641,7 @@ }, { "name": "meta.object.member.js", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js" @@ -2719,7 +2732,7 @@ "end": "(?=,|\\})", "patterns": [ { - "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -2752,7 +2765,7 @@ ] }, { - "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -2788,7 +2801,7 @@ ] }, { - "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "meta.brace.round.js" @@ -2976,7 +2989,7 @@ "paren-expression-possibly-arrow": { "patterns": [ { - "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -3060,7 +3073,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js" @@ -3628,7 +3641,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3840,7 +3853,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.js meta.return.type.arrow.js keyword.operator.type.annotation.js" @@ -4159,7 +4172,7 @@ }, "patterns": [ { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", "captures": { "1": { "name": "storage.modifier.js" @@ -4677,7 +4690,7 @@ }, { "name": "string.regexp.js", - "begin": "((?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx entity.name.function.js.jsx" @@ -487,7 +497,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js.jsx", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx variable.other.constant.js.jsx entity.name.function.js.jsx" @@ -871,7 +881,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -1111,7 +1121,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js.jsx entity.name.function.js.jsx" @@ -1251,6 +1261,9 @@ { "include": "#return-type" }, + { + "include": "#type-function-return-type" + }, { "include": "#decl-block" }, @@ -1434,7 +1447,7 @@ }, { "name": "meta.arrow.js.jsx", - "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -2469,7 +2482,7 @@ }, { "name": "string.regexp.js.jsx", - "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)*\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.js.jsx" @@ -2628,7 +2641,7 @@ }, { "name": "meta.object.member.js.jsx", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js.jsx" @@ -2719,7 +2732,7 @@ "end": "(?=,|\\})", "patterns": [ { - "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -2752,7 +2765,7 @@ ] }, { - "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -2788,7 +2801,7 @@ ] }, { - "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "meta.brace.round.js.jsx" @@ -2976,7 +2989,7 @@ "paren-expression-possibly-arrow": { "patterns": [ { - "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -3060,7 +3073,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -3628,7 +3641,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3840,7 +3853,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.js.jsx meta.return.type.arrow.js.jsx keyword.operator.type.annotation.js.jsx" @@ -4159,7 +4172,7 @@ }, "patterns": [ { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -4677,7 +4690,7 @@ }, { "name": "string.regexp.js.jsx", - "begin": "((? { - window.showInformationMessage(`${message}\n${localize('configureLimit', 'Use setting \'{0}\' to configure the limit.', SettingIds.maxItemsComputed)}`); + client.onNotification(ResultLimitReachedNotification.type, async message => { + const shouldPrompt = context.globalState.get(StorageIds.maxItemsExceededInformation) !== false; + if (shouldPrompt) { + const ok = localize('ok', "Ok"); + const openSettings = localize('goToSetting', 'Open Settings'); + const neverAgain = localize('yes never again', "Don't Show Again"); + const pick = await window.showInformationMessage(`${message}\n${localize('configureLimit', 'Use setting \'{0}\' to configure the limit.', SettingIds.maxItemsComputed)}`, ok, openSettings, neverAgain); + if (pick === neverAgain) { + await context.globalState.update(StorageIds.maxItemsExceededInformation, false); + } else if (pick === openSettings) { + await commands.executeCommand('workbench.action.openSettings', SettingIds.maxItemsComputed); + } + } }); function updateFormatterRegistration() { @@ -315,6 +330,8 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua range: client.code2ProtocolConverter.asRange(range), options: client.code2ProtocolConverter.asFormattingOptions(options) }; + params.options.insertFinalNewline = workspace.getConfiguration('files', document).get('insertFinalNewline'); + return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( client.protocol2CodeConverter.asTextEdits, (error) => { diff --git a/extensions/json-language-features/client/src/node/jsonClientMain.ts b/extensions/json-language-features/client/src/node/jsonClientMain.ts index 6e9aed7f1..eba269138 100644 --- a/extensions/json-language-features/client/src/node/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/node/jsonClientMain.ts @@ -25,7 +25,7 @@ export function activate(context: ExtensionContext) { const serverModule = context.asAbsolutePath(serverMain); // The debug options for the server - const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; + const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (6000 + Math.round(Math.random() * 999))] }; // If the extension is launch in debug mode the debug server options are use // Otherwise the run options are used diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index db003f051..a8e3231a9 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -127,10 +127,10 @@ ] }, "dependencies": { - "request-light": "^0.3.0", + "request-light": "^0.4.0", "vscode-extension-telemetry": "0.1.1", "vscode-languageclient": "7.0.0-next.5.1", - "vscode-nls": "^4.1.2" + "vscode-nls": "^5.0.0" }, "devDependencies": { "@types/node": "^12.11.7" diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index 59729a0ee..9315f7f2f 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -13,5 +13,6 @@ "json.schemaResolutionErrorMessage": "Unable to resolve schema.", "json.clickToRetry": "Click to retry.", "json.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons).", + "json.maxItemsExceededInformation.desc": "Show notification when exceeding the maximum number of outline symbols and folding regions.", "json.enableSchemaDownload.desc": "When enabled, JSON schemas can be fetched from http and https locations." } diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index cb0dded17..86df32285 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,9 +12,9 @@ }, "main": "./out/node/jsonServerMain", "dependencies": { - "jsonc-parser": "^2.2.1", - "request-light": "^0.3.0", - "vscode-json-languageservice": "^3.9.1", + "jsonc-parser": "^3.0.0", + "request-light": "^0.4.0", + "vscode-json-languageservice": "^3.11.0", "vscode-languageserver": "7.0.0-next.3", "vscode-uri": "^2.1.2" }, diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 26191bc4c..e7d2526f1 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -141,7 +141,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) colorProvider: {}, foldingRangeProvider: true, selectionRangeProvider: true, - definitionProvider: true + documentLinkProvider: {} }; return { capabilities }; @@ -481,15 +481,15 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); }); - connection.onDefinition((params, token) => { + connection.onDocumentLinks((params, token) => { return runSafeAsync(async () => { const document = documents.get(params.textDocument.uri); if (document) { const jsonDocument = getJSONDocument(document); - return languageService.findDefinition(document, params.position, jsonDocument); + return languageService.findLinks(document, jsonDocument); } return []; - }, [], `Error while computing definitions for ${params.textDocument.uri}`, token); + }, [], `Error while computing links for ${params.textDocument.uri}`, token); }); // Listen on the connection diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index c438b03d5..ec82f87b5 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -61,36 +61,31 @@ https-proxy-agent@^2.2.4: agent-base "^4.3.0" debug "^3.1.0" -jsonc-parser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" - integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== - -jsonc-parser@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" - integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== +jsonc-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" + integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -request-light@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.3.0.tgz#04daa783e7f0a70392328dda4b546f3e27845f2d" - integrity sha512-xlVlZVT0ZvCT+c3zm3SjeFCzchoQxsUUmx5fkal0I6RIDJK+lmb1UYyKJ7WM4dTfnzHP4ElWwAf8Dli8c0/tVA== +request-light@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.4.0.tgz#c6b91ef00b18cb0de75d2127e55b3a2c9f7f90f9" + integrity sha512-fimzjIVw506FBZLspTAXHdpvgvQebyjpNyLRd0e6drPPRq7gcrROeGWRyF81wLqFg5ijPgnOQbmfck5wdTqpSA== dependencies: http-proxy-agent "^2.1.0" https-proxy-agent "^2.2.4" - vscode-nls "^4.1.1" + vscode-nls "^4.1.2" -vscode-json-languageservice@^3.9.1: - version "3.9.1" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.9.1.tgz#f72b581f8cd2bd9b47445ccf8b0ddcde6aba7483" - integrity sha512-oJkknkdCVitQ5XPSRa0weHjUxt8eSCptaL+MBQQlRsa6Nb8XnEY0S5wYnLUFHzEvKzwt01/LKk8LdOixWEXkNA== +vscode-json-languageservice@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.11.0.tgz#ad574b36c4346bd7830f1d34b5a5213d3af8d232" + integrity sha512-QxI+qV97uD7HHOCjh3MrM1TfbdwmTXrMckri5Tus1/FQiG3baDZb2C9Y0y8QThs7PwHYBIQXcAc59ZveCRZKPA== dependencies: - jsonc-parser "^2.3.1" + jsonc-parser "^3.0.0" vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" vscode-nls "^5.0.0" @@ -126,10 +121,10 @@ vscode-languageserver@7.0.0-next.3: dependencies: vscode-languageserver-protocol "3.16.0-next.4" -vscode-nls@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" - integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== +vscode-nls@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" + integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== vscode-nls@^5.0.0: version "5.0.0" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 29972b7df..a1e87bdbc 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -94,14 +94,14 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -request-light@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.3.0.tgz#04daa783e7f0a70392328dda4b546f3e27845f2d" - integrity sha512-xlVlZVT0ZvCT+c3zm3SjeFCzchoQxsUUmx5fkal0I6RIDJK+lmb1UYyKJ7WM4dTfnzHP4ElWwAf8Dli8c0/tVA== +request-light@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.4.0.tgz#c6b91ef00b18cb0de75d2127e55b3a2c9f7f90f9" + integrity sha512-fimzjIVw506FBZLspTAXHdpvgvQebyjpNyLRd0e6drPPRq7gcrROeGWRyF81wLqFg5ijPgnOQbmfck5wdTqpSA== dependencies: http-proxy-agent "^2.1.0" https-proxy-agent "^2.2.4" - vscode-nls "^4.1.1" + vscode-nls "^4.1.2" semver@^5.3.0: version "5.5.0" @@ -146,16 +146,16 @@ vscode-languageserver-types@3.16.0-next.2: resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== -vscode-nls@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" - integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== - vscode-nls@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== +vscode-nls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" + integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== + zone.js@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" diff --git a/extensions/json/cgmanifest.json b/extensions/json/cgmanifest.json index 53db20003..1af8426e5 100644 --- a/extensions/json/cgmanifest.json +++ b/extensions/json/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/json/package.json b/extensions/json/package.json index 73902a8ec..1b7331b20 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -33,8 +33,7 @@ ], "filenames": [ "composer.lock", - ".watchmanconfig", - ".ember-cli" + ".watchmanconfig" ], "mimetypes": [ "application/json", @@ -57,6 +56,9 @@ ".hintrc", ".babelrc" ], + "filenames": [ + ".ember-cli" + ], "configuration": "./language-configuration.json" } ], diff --git a/extensions/json/syntaxes/JSON.tmLanguage.json b/extensions/json/syntaxes/JSON.tmLanguage.json index 9454f0ed8..b53febdc8 100644 --- a/extensions/json/syntaxes/JSON.tmLanguage.json +++ b/extensions/json/syntaxes/JSON.tmLanguage.json @@ -210,4 +210,4 @@ ] } } -} +} \ No newline at end of file diff --git a/extensions/json/syntaxes/JSONC.tmLanguage.json b/extensions/json/syntaxes/JSONC.tmLanguage.json index bf65fce98..31828ba65 100644 --- a/extensions/json/syntaxes/JSONC.tmLanguage.json +++ b/extensions/json/syntaxes/JSONC.tmLanguage.json @@ -210,4 +210,4 @@ ] } } -} +} \ No newline at end of file diff --git a/extensions/make/language-configuration.json b/extensions/make/language-configuration.json index 802890392..82645b84d 100644 --- a/extensions/make/language-configuration.json +++ b/extensions/make/language-configuration.json @@ -3,8 +3,47 @@ "lineComment": "#" }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] + ], + "autoClosingPairs": [ + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string", + "comment" + ] + } ] -} \ No newline at end of file +} diff --git a/extensions/markdown-basics/cgmanifest.json b/extensions/markdown-basics/cgmanifest.json index 71df78ef4..5606dcc7c 100644 --- a/extensions/markdown-basics/cgmanifest.json +++ b/extensions/markdown-basics/cgmanifest.json @@ -26,7 +26,19 @@ ], "license": "TextMate Bundle License", "version": "0.0.0" + }, + { + "component": { + "type": "git", + "git": { + "name": "microsoft/vscode-markdown-tm-grammar", + "repositoryUrl": "https://github.com/microsoft/vscode-markdown-tm-grammar", + "commitHash": "11cf764606cb2cde54badb5d0e5a0758a8871c4b" + } + }, + "license": "MIT", + "version": "0.0.0" } ], "version": 1 -} \ No newline at end of file +} diff --git a/extensions/markdown-basics/snippets/markdown.code-snippets b/extensions/markdown-basics/snippets/markdown.code-snippets index b07f98413..f0753507b 100644 --- a/extensions/markdown-basics/snippets/markdown.code-snippets +++ b/extensions/markdown-basics/snippets/markdown.code-snippets @@ -64,6 +64,11 @@ "body": ["1. ${1:first}", "2. ${2:second}", "3. ${3:third}", "$0"], "description": "Insert ordered list" }, + "Insert definition list": { + "prefix": "definition list", + "body": ["${1:term}", ": ${2:definition}", "$0"], + "description": "Insert definition list" + }, "Insert horizontal rule": { "prefix": "horizontal rule", "body": "----------\n", @@ -83,5 +88,5 @@ "prefix": "strikethrough", "body": "~~${1:${TM_SELECTED_TEXT}}~~", "description": "Insert strikethrough" - } + }, } diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index e8feaa75f..13ee6f360 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/4be9cb335581f3559166c319607dac9100103083", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/f051a36bd9713dd722cbe1bdde9c8240d12f00b4", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -2190,9 +2190,18 @@ }, "13": { "name": "punctuation.definition.string.end.markdown" + }, + "14": { + "name": "string.other.link.description.title.markdown" + }, + "15": { + "name": "punctuation.definition.string.begin.markdown" + }, + "16": { + "name": "punctuation.definition.string.end.markdown" } }, - "match": "(?x)\n \\s* # Leading whitespace\n (\\[)([^]]+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in quotes…\n | ((\").+?(\")) # or in parens.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", + "match": "(?x)\n \\s* # Leading whitespace\n (\\[)([^]]+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", "name": "meta.link.reference.def.markdown" }, "list_paragraph": { @@ -2453,10 +2462,19 @@ "name": "punctuation.definition.string.markdown" }, "15": { + "name": "string.other.link.description.title.markdown" + }, + "16": { + "name": "punctuation.definition.string.markdown" + }, + "17": { + "name": "punctuation.definition.string.markdown" + }, + "18": { "name": "punctuation.definition.metadata.markdown" } }, - "match": "(?x)\n (\\!\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", + "match": "(?x)\n (\\!\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", "name": "meta.image.inline.markdown" }, "image-ref": { @@ -2616,10 +2634,19 @@ "name": "punctuation.definition.string.end.markdown" }, "16": { + "name": "string.other.link.description.title.markdown" + }, + "17": { + "name": "punctuation.definition.string.begin.markdown" + }, + "18": { + "name": "punctuation.definition.string.end.markdown" + }, + "19": { "name": "punctuation.definition.metadata.markdown" } }, - "match": "(?x)\n (\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n ((?>[^\\s()]+)|\\(\\g*\\))*)(>?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", + "match": "(?x)\n (\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n ((?>[^\\s()]+)|\\(\\g*\\))*)(>?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", "name": "meta.link.inline.markdown" }, "link-ref": { diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index f581cd002..edaa6f925 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -154,15 +154,13 @@ table { border-collapse: collapse; } -table > thead > tr > th { +th { text-align: left; border-bottom: 1px solid; } -table > thead > tr > th, -table > thead > tr > td, -table > tbody > tr > th, -table > tbody > tr > td { +th, +td { padding: 5px 10px; } @@ -217,22 +215,22 @@ pre code { border-color: rgb(0, 0, 0); } -.vscode-light table > thead > tr > th { +.vscode-light th { border-color: rgba(0, 0, 0, 0.69); } -.vscode-dark table > thead > tr > th { +.vscode-dark th { border-color: rgba(255, 255, 255, 0.69); } .vscode-light h1, .vscode-light hr, -.vscode-light table > tbody > tr + tr > td { +.vscode-light td { border-color: rgba(0, 0, 0, 0.18); } .vscode-dark h1, .vscode-dark hr, -.vscode-dark table > tbody > tr + tr > td { +.vscode-dark td { border-color: rgba(255, 255, 255, 0.18); } diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 34d6902fa..72b952e82 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -324,7 +324,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "highlight.js": "9.15.10", + "highlight.js": "10.1.2", "markdown-it": "^10.0.0", "markdown-it-front-matter": "^0.2.1", "vscode-extension-telemetry": "0.1.1", diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index c17dc17cb..79288f2d1 100644 --- a/extensions/markdown-language-features/src/commands/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/commands/openDocumentLink.ts @@ -12,10 +12,18 @@ import { TableOfContentsProvider } from '../tableOfContentsProvider'; import { isMarkdownFile } from '../util/file'; +type UriComponents = { + readonly scheme?: string; + readonly path: string; + readonly fragment?: string; + readonly authority?: string; + readonly query?: string; +}; + export interface OpenDocumentLinkArgs { - readonly path: {}; + readonly parts: UriComponents; readonly fragment: string; - readonly fromResource: {}; + readonly fromResource: UriComponents; } enum OpenMarkdownLinks { @@ -32,7 +40,7 @@ export class OpenDocumentLinkCommand implements Command { path: vscode.Uri, fragment: string, ): vscode.Uri { - const toJson = (uri: vscode.Uri) => { + const toJson = (uri: vscode.Uri): UriComponents => { return { scheme: uri.scheme, authority: uri.authority, @@ -42,7 +50,7 @@ export class OpenDocumentLinkCommand implements Command { }; }; return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ - path: toJson(path), + parts: toJson(path), fragment, fromResource: toJson(fromResource), }))}`); @@ -56,36 +64,58 @@ export class OpenDocumentLinkCommand implements Command { return OpenDocumentLinkCommand.execute(this.engine, args); } - public static async execute(engine: MarkdownEngine, args: OpenDocumentLinkArgs) { + public static async execute(engine: MarkdownEngine, args: OpenDocumentLinkArgs): Promise { const fromResource = vscode.Uri.parse('').with(args.fromResource); - const targetResource = vscode.Uri.parse('').with(args.path); + + const targetResource = reviveUri(args.parts); + const column = this.getViewColumn(fromResource); - try { - return await this.tryOpen(engine, targetResource, args, column); - } catch { - if (extname(targetResource.path) === '') { - return this.tryOpen(engine, targetResource.with({ path: targetResource.path + '.md' }), args, column); - } - await vscode.commands.executeCommand('vscode.open', targetResource, column); - return undefined; + + const didOpen = await this.tryOpen(engine, targetResource, args, column); + if (didOpen) { + return; + } + + if (extname(targetResource.path) === '') { + await this.tryOpen(engine, targetResource.with({ path: targetResource.path + '.md' }), args, column); + return; } } - private static async tryOpen(engine: MarkdownEngine, resource: vscode.Uri, args: OpenDocumentLinkArgs, column: vscode.ViewColumn) { - if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) { - if (vscode.window.activeTextEditor.document.uri.fsPath === resource.fsPath) { - return this.tryRevealLine(engine, vscode.window.activeTextEditor, args.fragment); + private static async tryOpen(engine: MarkdownEngine, resource: vscode.Uri, args: OpenDocumentLinkArgs, column: vscode.ViewColumn): Promise { + const tryUpdateForActiveFile = async (): Promise => { + if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) { + if (vscode.window.activeTextEditor.document.uri.fsPath === resource.fsPath) { + await this.tryRevealLine(engine, vscode.window.activeTextEditor, args.fragment); + return true; + } } + return false; + }; + + if (await tryUpdateForActiveFile()) { + return true; + } + + let stat: vscode.FileStat; + try { + stat = await vscode.workspace.fs.stat(resource); + } catch { + return false; } - const stat = await vscode.workspace.fs.stat(resource); if (stat.type === vscode.FileType.Directory) { - return vscode.commands.executeCommand('revealInExplorer', resource); + await vscode.commands.executeCommand('revealInExplorer', resource); + return true; } - return vscode.workspace.openTextDocument(resource) - .then(document => vscode.window.showTextDocument(document, column)) - .then(editor => this.tryRevealLine(engine, editor, args.fragment)); + try { + await vscode.commands.executeCommand('vscode.open', resource, column); + } catch { + return false; + } + + return tryUpdateForActiveFile(); } private static getViewColumn(resource: vscode.Uri): vscode.ViewColumn { @@ -101,7 +131,7 @@ export class OpenDocumentLinkCommand implements Command { } private static async tryRevealLine(engine: MarkdownEngine, editor: vscode.TextEditor, fragment?: string) { - if (editor && fragment) { + if (fragment) { const toc = new TableOfContentsProvider(engine, editor.document); const entry = await toc.lookup(fragment); if (entry) { @@ -122,6 +152,12 @@ export class OpenDocumentLinkCommand implements Command { } } +function reviveUri(parts: any) { + if (parts.scheme === 'file') { + return vscode.Uri.file(parts.path); + } + return vscode.Uri.parse('').with(parts); +} export async function resolveLinkToMarkdownFile(path: string): Promise { try { diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index 6d85e6135..becb16879 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -65,19 +65,6 @@ function getWorkspaceFolder(document: vscode.TextDocument) { || vscode.workspace.workspaceFolders?.[0]?.uri; } -function matchAll( - pattern: RegExp, - text: string -): Array { - const out: RegExpMatchArray[] = []; - pattern.lastIndex = 0; - let match: RegExpMatchArray | null; - while ((match = pattern.exec(text))) { - out.push(match); - } - return out; -} - function extractDocumentLink( document: vscode.TextDocument, pre: number, @@ -103,7 +90,7 @@ function extractDocumentLink( } export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; + private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]/g; private readonly definitionPattern = /^([\t ]*\[(?!\^)((?:\\\]|[^\]])+)\]:\s*)(\S+)/gm; @@ -124,7 +111,7 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { document: vscode.TextDocument, ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; - for (const match of matchAll(this.linkPattern, text)) { + for (const match of text.matchAll(this.linkPattern)) { const matchImage = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index); if (matchImage) { results.push(matchImage); @@ -144,7 +131,7 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { const results: vscode.DocumentLink[] = []; const definitions = this.getDefinitions(text, document); - for (const match of matchAll(this.referenceLinkPattern, text)) { + for (const match of text.matchAll(this.referenceLinkPattern)) { let linkStart: vscode.Position; let linkEnd: vscode.Position; let reference = match[3]; @@ -190,7 +177,7 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { private getDefinitions(text: string, document: vscode.TextDocument) { const out = new Map(); - for (const match of matchAll(this.definitionPattern, text)) { + for (const match of text.matchAll(this.definitionPattern)) { const pre = match[1]; const reference = match[2]; const link = match[3].trim(); diff --git a/extensions/markdown-language-features/src/features/foldingProvider.ts b/extensions/markdown-language-features/src/features/foldingProvider.ts index 553c1a325..62c245c9a 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -7,7 +7,6 @@ import { Token } from 'markdown-it'; import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; import { TableOfContentsProvider } from '../tableOfContentsProvider'; -import { flatten } from '../util/arrays'; const rangeLimit = 5000; @@ -27,7 +26,7 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi this.getHeaderFoldingRanges(document), this.getBlockFoldingRanges(document) ]); - return flatten(foldables).slice(0, rangeLimit); + return foldables.flat().slice(0, rangeLimit); } private async getRegions(document: vscode.TextDocument): Promise { diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index f2fb80034..9244546c5 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -408,7 +408,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { } } - OpenDocumentLinkCommand.execute(this.engine, { path: hrefPath, fragment, fromResource: this.resource.toJSON() }); + OpenDocumentLinkCommand.execute(this.engine, { parts: { path: hrefPath }, fragment, fromResource: this.resource.toJSON() }); } //#region WebviewResourceProvider diff --git a/extensions/markdown-language-features/src/features/smartSelect.ts b/extensions/markdown-language-features/src/features/smartSelect.ts index 0be50d0ca..12b2bdd78 100644 --- a/extensions/markdown-language-features/src/features/smartSelect.ts +++ b/extensions/markdown-language-features/src/features/smartSelect.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import { Token } from 'markdown-it'; import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; @@ -15,7 +14,7 @@ export default class MarkdownSmartSelect implements vscode.SelectionRangeProvide ) { } public async provideSelectionRanges(document: vscode.TextDocument, positions: vscode.Position[], _token: vscode.CancellationToken): Promise { - let promises = await Promise.all(positions.map((position) => { + const promises = await Promise.all(positions.map((position) => { return this.provideSelectionRange(document, position, _token); })); return promises.filter(item => item !== undefined) as vscode.SelectionRange[]; @@ -24,54 +23,206 @@ export default class MarkdownSmartSelect implements vscode.SelectionRangeProvide private async provideSelectionRange(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise { const headerRange = await this.getHeaderSelectionRange(document, position); const blockRange = await this.getBlockSelectionRange(document, position, headerRange); - return blockRange ? blockRange : headerRange ? headerRange : undefined; + const inlineRange = await this.getInlineSelectionRange(document, position, blockRange); + return inlineRange || blockRange || headerRange; + } + private async getInlineSelectionRange(document: vscode.TextDocument, position: vscode.Position, blockRange?: vscode.SelectionRange): Promise { + return createInlineRange(document, position, blockRange); } private async getBlockSelectionRange(document: vscode.TextDocument, position: vscode.Position, headerRange?: vscode.SelectionRange): Promise { const tokens = await this.engine.parse(document); - let blockTokens = getTokensForPosition(tokens, position); + const blockTokens = getBlockTokensForPosition(tokens, position, headerRange); if (blockTokens.length === 0) { return undefined; } - let parentRange = headerRange ? headerRange : createBlockRange(document, position.line, blockTokens.shift()); - let currentRange: vscode.SelectionRange | undefined; + let currentRange: vscode.SelectionRange | undefined = headerRange ? headerRange : createBlockRange(blockTokens.shift()!, document, position.line); - for (const token of blockTokens) { - currentRange = createBlockRange(document, position.line, token, parentRange); - if (currentRange) { - parentRange = currentRange; - } - } - if (currentRange) { - return currentRange; - } else { - return parentRange; - } - } - - private async getHeaderSelectionRange(document: vscode.TextDocument, position: vscode.Position): Promise { - const tocProvider = new TableOfContentsProvider(this.engine, document); - const toc = await tocProvider.getToc(); - - let headerInfo = getHeadersForPosition(toc, position); - - let headers = headerInfo.headers; - - let parentRange: vscode.SelectionRange | undefined; - let currentRange: vscode.SelectionRange | undefined; - - for (let i = 0; i < headers.length; i++) { - currentRange = createHeaderRange(i === headers.length - 1, headerInfo.headerOnThisLine, headers[i], parentRange, getFirstChildHeader(document, headers[i], toc)); - if (currentRange && currentRange.parent) { - parentRange = currentRange; - } + for (let i = 0; i < blockTokens.length; i++) { + currentRange = createBlockRange(blockTokens[i], document, position.line, currentRange); } return currentRange; } + + private async getHeaderSelectionRange(document: vscode.TextDocument, position: vscode.Position): Promise { + + const tocProvider = new TableOfContentsProvider(this.engine, document); + const toc = await tocProvider.getToc(); + + const headerInfo = getHeadersForPosition(toc, position); + + const headers = headerInfo.headers; + + let currentRange: vscode.SelectionRange | undefined; + + for (let i = 0; i < headers.length; i++) { + currentRange = createHeaderRange(headers[i], i === headers.length - 1, headerInfo.headerOnThisLine, currentRange, getFirstChildHeader(document, headers[i], toc)); + } + return currentRange; + } +} + +function getHeadersForPosition(toc: TocEntry[], position: vscode.Position): { headers: TocEntry[], headerOnThisLine: boolean } { + const enclosingHeaders = toc.filter(header => header.location.range.start.line <= position.line && header.location.range.end.line >= position.line); + const sortedHeaders = enclosingHeaders.sort((header1, header2) => (header1.line - position.line) - (header2.line - position.line)); + const onThisLine = toc.find(header => header.line === position.line) !== undefined; + return { + headers: sortedHeaders, + headerOnThisLine: onThisLine + }; +} + +function createHeaderRange(header: TocEntry, isClosestHeaderToPosition: boolean, onHeaderLine: boolean, parent?: vscode.SelectionRange, startOfChildRange?: vscode.Position): vscode.SelectionRange | undefined { + const range = header.location.range; + const contentRange = new vscode.Range(range.start.translate(1), range.end); + if (onHeaderLine && isClosestHeaderToPosition && startOfChildRange) { + // selection was made on this header line, so select header and its content until the start of its first child + // then all of its content + return new vscode.SelectionRange(range.with(undefined, startOfChildRange), new vscode.SelectionRange(range, parent)); + } else if (onHeaderLine && isClosestHeaderToPosition) { + // selection was made on this header line and no children so expand to all of its content + return new vscode.SelectionRange(range, parent); + } else if (isClosestHeaderToPosition && startOfChildRange) { + // selection was made within content and has child so select content + // of this header then all content then header + return new vscode.SelectionRange(contentRange.with(undefined, startOfChildRange), new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(range, parent)))); + } else { + // not on this header line so select content then header + return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(range, parent)); + } +} + +function getBlockTokensForPosition(tokens: Token[], position: vscode.Position, parent?: vscode.SelectionRange): Token[] { + const enclosingTokens = tokens.filter(token => token.map && (token.map[0] <= position.line && token.map[1] > position.line) && (!parent || (token.map[0] >= parent.range.start.line && token.map[1] <= parent.range.end.line + 1)) && isBlockElement(token)); + if (enclosingTokens.length === 0) { + return []; + } + const sortedTokens = enclosingTokens.sort((token1, token2) => (token2.map[1] - token2.map[0]) - (token1.map[1] - token1.map[0])); + return sortedTokens; +} + +function createBlockRange(block: Token, document: vscode.TextDocument, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + if (block.type === 'fence') { + return createFencedRange(block, cursorLine, document, parent); + } else { + let startLine = document.lineAt(block.map[0]).isEmptyOrWhitespace ? block.map[0] + 1 : block.map[0]; + let endLine = startLine === block.map[1] ? block.map[1] : block.map[1] - 1; + if (block.type === 'paragraph_open' && block.map[1] - block.map[0] === 2) { + startLine = endLine = cursorLine; + } else if (isList(block) && document.lineAt(endLine).isEmptyOrWhitespace) { + endLine = endLine - 1; + } + const range = new vscode.Range(startLine, 0, endLine, document.lineAt(endLine).text?.length ?? 0); + if (parent?.range.contains(range) && !parent.range.isEqual(range)) { + return new vscode.SelectionRange(range, parent); + } else if (parent?.range.isEqual(range)) { + return parent; + } else { + return new vscode.SelectionRange(range); + } + } +} + +function createInlineRange(document: vscode.TextDocument, cursorPosition: vscode.Position, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + const lineText = document.lineAt(cursorPosition.line).text; + const boldSelection = createBoldRange(lineText, cursorPosition.character, cursorPosition.line, parent); + const italicSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, true, parent); + let comboSelection: vscode.SelectionRange | undefined; + if (boldSelection && italicSelection && !boldSelection.range.isEqual(italicSelection.range)) { + if (boldSelection.range.contains(italicSelection.range)) { + comboSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, true, boldSelection); + } else if (italicSelection.range.contains(boldSelection.range)) { + comboSelection = createBoldRange(lineText, cursorPosition.character, cursorPosition.line, italicSelection); + } + } + const linkSelection = createLinkRange(lineText, cursorPosition.character, cursorPosition.line, comboSelection || boldSelection || italicSelection || parent); + const inlineCodeBlockSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, false, linkSelection || parent); + return inlineCodeBlockSelection || linkSelection || comboSelection || boldSelection || italicSelection; +} + +function createFencedRange(token: Token, cursorLine: number, document: vscode.TextDocument, parent?: vscode.SelectionRange): vscode.SelectionRange { + const startLine = token.map[0]; + const endLine = token.map[1] - 1; + const onFenceLine = cursorLine === startLine || cursorLine === endLine; + const fenceRange = new vscode.Range(startLine, 0, endLine, document.lineAt(endLine).text.length); + const contentRange = endLine - startLine > 2 && !onFenceLine ? new vscode.Range(startLine + 1, 0, endLine - 1, document.lineAt(endLine - 1).text.length) : undefined; + if (contentRange) { + return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent)); + } else { + if (parent?.range.isEqual(fenceRange)) { + return parent; + } else { + return new vscode.SelectionRange(fenceRange, parent); + } + } +} + +function createBoldRange(lineText: string, cursorChar: number, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + const regex = /(?:^|(?<=\s))(?:\*\*\s*([^*]+)(?:\*\s*([^*]+)\s*?\*)*([^*]+)\s*?\*\*)/g; + const matches = [...lineText.matchAll(regex)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length >= cursorChar); + if (matches.length > 0) { + // should only be one match, so select first and index 0 contains the entire match + const bold = matches[0][0]; + const startIndex = lineText.indexOf(bold); + const cursorOnStars = cursorChar === startIndex || cursorChar === startIndex + 1 || cursorChar === startIndex + bold.length || cursorChar === startIndex + bold.length - 1; + const contentAndStars = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex, cursorLine, startIndex + bold.length), parent); + const content = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex + 2, cursorLine, startIndex + bold.length - 2), contentAndStars); + return cursorOnStars ? contentAndStars : content; + } + return undefined; +} + +function createOtherInlineRange(lineText: string, cursorChar: number, cursorLine: number, isItalic: boolean, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + const regex = isItalic ? /(?:^|(?<=\s))(?:\*\s*([^*]+)(?:\*\*\s*([^*]+)\s*?\*\*)*([^*]+)\s*?\*)/g : /\`[^\`]*\`/g; + const matches = [...lineText.matchAll(regex)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length >= cursorChar); + if (matches.length > 0) { + // should only be one match, so select first and index 0 contains the entire match + const match = matches[0][0]; + const startIndex = lineText.indexOf(match); + const cursorOnType = cursorChar === startIndex || cursorChar === startIndex + match.length; + const contentAndType = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex, cursorLine, startIndex + match.length), parent); + const content = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex + 1, cursorLine, startIndex + match.length - 1), contentAndType); + return cursorOnType ? contentAndType : content; + } + return undefined; +} + +function createLinkRange(lineText: string, cursorChar: number, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + const regex = /(\[[^\(\)]*\])(\([^\[\]]*\))/g; + const matches = [...lineText.matchAll(regex)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length > cursorChar); + + if (matches.length > 0) { + // should only be one match, so select first and index 0 contains the entire match, so match = [text](url) + const link = matches[0][0]; + const linkRange = new vscode.SelectionRange(new vscode.Range(cursorLine, lineText.indexOf(link), cursorLine, lineText.indexOf(link) + link.length), parent); + + const linkText = matches[0][1]; + const url = matches[0][2]; + + // determine if cursor is within [text] or (url) in order to know which should be selected + const nearestType = cursorChar >= lineText.indexOf(linkText) && cursorChar < lineText.indexOf(linkText) + linkText.length ? linkText : url; + + const indexOfType = lineText.indexOf(nearestType); + // determine if cursor is on a bracket or paren and if so, return the [content] or (content), skipping over the content range + const cursorOnType = cursorChar === indexOfType || cursorChar === indexOfType + nearestType.length; + + const contentAndNearestType = new vscode.SelectionRange(new vscode.Range(cursorLine, indexOfType, cursorLine, indexOfType + nearestType.length), linkRange); + const content = new vscode.SelectionRange(new vscode.Range(cursorLine, indexOfType + 1, cursorLine, indexOfType + nearestType.length - 1), contentAndNearestType); + return cursorOnType ? contentAndNearestType : content; + } + return undefined; +} + +function isList(token: Token): boolean { + return token.type ? ['ordered_list_open', 'list_item_open', 'bullet_list_open'].includes(token.type) : false; +} + +function isBlockElement(token: Token): boolean { + return !['list_item_close', 'paragraph_close', 'bullet_list_close', 'inline', 'heading_close', 'heading_open'].includes(token.type); } function getFirstChildHeader(document: vscode.TextDocument, header?: TocEntry, toc?: TocEntry[]): vscode.Position | undefined { @@ -80,159 +231,9 @@ function getFirstChildHeader(document: vscode.TextDocument, header?: TocEntry, t let children = toc.filter(t => header.location.range.contains(t.location.range) && t.location.range.start.line > header.location.range.start.line).sort((t1, t2) => t1.line - t2.line); if (children.length > 0) { childRange = children[0].location.range.start; - let lineText = document.lineAt(childRange.line - 1).text; + const lineText = document.lineAt(childRange.line - 1).text; return childRange ? childRange.translate(-1, lineText.length) : undefined; } } return undefined; } - -function getTokensForPosition(tokens: Token[], position: vscode.Position): Token[] { - let enclosingTokens = tokens.filter(token => token.map && (token.map[0] <= position.line && token.map[1] > position.line) && isBlockElement(token)); - - if (enclosingTokens.length === 0) { - return []; - } - - let sortedTokens = enclosingTokens.sort((token1, token2) => (token2.map[1] - token2.map[0]) - (token1.map[1] - token1.map[0])); - return sortedTokens; -} - -function getHeadersForPosition(toc: TocEntry[], position: vscode.Position): { headers: TocEntry[], headerOnThisLine: boolean } { - let enclosingHeaders = toc.filter(header => header.location.range.start.line <= position.line && header.location.range.end.line >= position.line); - let sortedHeaders = enclosingHeaders.sort((header1, header2) => (header1.line - position.line) - (header2.line - position.line)); - let onThisLine = toc.find(header => header.line === position.line) !== undefined; - return { - headers: sortedHeaders, - headerOnThisLine: onThisLine - }; -} - -function isBlockElement(token: Token): boolean { - return !['list_item_close', 'paragraph_close', 'bullet_list_close', 'inline', 'heading_close', 'heading_open'].includes(token.type); -} - -function createHeaderRange(isClosestHeaderToPosition: boolean, onHeaderLine: boolean, header?: TocEntry, parent?: vscode.SelectionRange, childStart?: vscode.Position): vscode.SelectionRange | undefined { - if (header) { - let contentRange = new vscode.Range(header.location.range.start.translate(1), header.location.range.end); - let headerPlusContentRange = header.location.range; - let partialContentRange = childStart && isClosestHeaderToPosition ? contentRange.with(undefined, childStart) : undefined; - if (onHeaderLine && isClosestHeaderToPosition && childStart) { - return new vscode.SelectionRange(header.location.range.with(undefined, childStart), new vscode.SelectionRange(header.location.range, parent)); - } else if (onHeaderLine && isClosestHeaderToPosition) { - return new vscode.SelectionRange(header.location.range, parent); - } else if (parent && parent.range.contains(headerPlusContentRange)) { - if (partialContentRange) { - return new vscode.SelectionRange(partialContentRange, new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(headerPlusContentRange, parent)))); - } else { - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(headerPlusContentRange, parent)); - } - } else if (partialContentRange) { - return new vscode.SelectionRange(partialContentRange, new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(headerPlusContentRange)))); - } else { - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(headerPlusContentRange)); - } - } else { - return undefined; - } -} - -function createBlockRange(document: vscode.TextDocument, cursorLine: number, block?: Token, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { - if (block) { - if (block.type === 'fence') { - return createFencedRange(block, cursorLine, document, parent); - } else { - let startLine = document.lineAt(block.map[0]).isEmptyOrWhitespace ? block.map[0] + 1 : block.map[0]; - let endLine = startLine !== block.map[1] && isList(block.type) ? block.map[1] - 1 : block.map[1]; - let startPos = new vscode.Position(startLine, 0); - let endPos = new vscode.Position(endLine, getEndCharacter(document, startLine, endLine)); - let range = new vscode.Range(startPos, endPos); - if (parent && parent.range.contains(range) && !parent.range.isEqual(range)) { - return new vscode.SelectionRange(range, parent); - } else if (parent) { - if (rangeLinesEqual(range, parent.range)) { - return range.end.character > parent.range.end.character ? new vscode.SelectionRange(range) : parent; - } else if (parent.range.end.line + 1 === range.end.line) { - let adjustedRange = new vscode.Range(range.start, range.end.translate(-1, parent.range.end.character)); - if (adjustedRange.isEqual(parent.range)) { - return parent; - } else { - return new vscode.SelectionRange(adjustedRange, parent); - } - } else if (parent.range.end.line === range.end.line) { - let adjustedRange = new vscode.Range(parent.range.start, range.end.translate(undefined, parent.range.end.character)); - if (adjustedRange.isEqual(parent.range)) { - return parent; - } else { - return new vscode.SelectionRange(adjustedRange, parent.parent); - } - } else { - return parent; - } - } else { - return new vscode.SelectionRange(range); - } - } - } else { - return undefined; - } -} - -function createFencedRange(token: Token, cursorLine: number, document: vscode.TextDocument, parent?: vscode.SelectionRange): vscode.SelectionRange { - const startLine = token.map[0]; - const endLine = token.map[1] - 1; - let onFenceLine = cursorLine === startLine || cursorLine === endLine; - let fenceRange = new vscode.Range(new vscode.Position(startLine, 0), new vscode.Position(endLine, document.lineAt(endLine).text.length)); - let contentRange = endLine - startLine > 2 && !onFenceLine ? new vscode.Range(new vscode.Position(startLine + 1, 0), new vscode.Position(endLine - 1, getEndCharacter(document, startLine + 1, endLine))) : undefined; - if (parent && contentRange) { - if (parent.range.contains(fenceRange) && !parent.range.isEqual(fenceRange)) { - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent)); - } else if (parent.range.isEqual(fenceRange)) { - return new vscode.SelectionRange(contentRange, parent); - } else if (rangeLinesEqual(fenceRange, parent.range)) { - let revisedRange = fenceRange.end.character > parent.range.end.character ? fenceRange : parent.range; - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(revisedRange, getRealParent(parent, revisedRange))); - } else if (parent.range.end.line === fenceRange.end.line) { - parent.range.end.translate(undefined, fenceRange.end.character); - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent)); - } - } else if (contentRange) { - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange)); - } else if (parent) { - if (parent.range.contains(fenceRange) && !parent.range.isEqual(fenceRange)) { - return new vscode.SelectionRange(fenceRange, parent); - } else if (parent.range.isEqual(fenceRange)) { - return parent; - } else if (rangeLinesEqual(fenceRange, parent.range)) { - let revisedRange = fenceRange.end.character > parent.range.end.character ? fenceRange : parent.range; - return new vscode.SelectionRange(revisedRange, parent.parent); - } else if (parent.range.end.line === fenceRange.end.line) { - parent.range.end.translate(undefined, fenceRange.end.character); - return new vscode.SelectionRange(fenceRange, parent); - } - } - return new vscode.SelectionRange(fenceRange, parent); -} - -function isList(type: string): boolean { - return type ? ['ordered_list_open', 'list_item_open', 'bullet_list_open'].includes(type) : false; -} - -function getEndCharacter(document: vscode.TextDocument, startLine: number, endLine: number): number { - let startLength = document.lineAt(startLine).text ? document.lineAt(startLine).text.length : 0; - let endLength = document.lineAt(startLine).text ? document.lineAt(startLine).text.length : 0; - let endChar = Math.max(startLength, endLength); - return startLine !== endLine ? 0 : endChar; -} - -function getRealParent(parent: vscode.SelectionRange, range: vscode.Range) { - let currentParent: vscode.SelectionRange | undefined = parent; - while (currentParent && !currentParent.range.contains(range)) { - currentParent = currentParent.parent; - } - return currentParent; -} - -function rangeLinesEqual(range: vscode.Range, parent: vscode.Range) { - return range.start.line === parent.start.line && range.end.line === parent.end.line; -} diff --git a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts index f97bb0219..91f9dfcd3 100644 --- a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts +++ b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts @@ -9,7 +9,6 @@ import { isMarkdownFile } from '../util/file'; import { Lazy, lazy } from '../util/lazy'; import MDDocumentSymbolProvider from './documentSymbolProvider'; import { SkinnyTextDocument, SkinnyTextLine } from '../tableOfContentsProvider'; -import { flatten } from '../util/arrays'; export interface WorkspaceMarkdownDocumentProvider { getAllMarkdownDocuments(): Thenable>; @@ -136,7 +135,7 @@ export default class MarkdownWorkspaceSymbolProvider extends Disposable implemen } const allSymbolsSets = await Promise.all(Array.from(this._symbolCache.values()).map(x => x.value)); - const allSymbols = flatten(allSymbolsSets); + const allSymbols = allSymbolsSets.flat(); return allSymbols.filter(symbolInformation => symbolInformation.name.toLowerCase().indexOf(query.toLowerCase()) !== -1); } diff --git a/extensions/markdown-language-features/src/test/inMemoryDocument.ts b/extensions/markdown-language-features/src/test/inMemoryDocument.ts index 052216f90..8f5f6fe78 100644 --- a/extensions/markdown-language-features/src/test/inMemoryDocument.ts +++ b/extensions/markdown-language-features/src/test/inMemoryDocument.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; - +import * as os from 'os'; export class InMemoryDocument implements vscode.TextDocument { private readonly _lines: string[]; @@ -13,7 +13,7 @@ export class InMemoryDocument implements vscode.TextDocument { private readonly _contents: string, public readonly version = 1, ) { - this._lines = this._contents.split(/\n/g); + this._lines = this._contents.split(/\r\n|\n/g); } @@ -21,7 +21,7 @@ export class InMemoryDocument implements vscode.TextDocument { languageId: string = ''; isDirty: boolean = false; isClosed: boolean = false; - eol: vscode.EndOfLine = vscode.EndOfLine.LF; + eol: vscode.EndOfLine = os.platform() === 'win32' ? vscode.EndOfLine.CRLF : vscode.EndOfLine.LF; notebook: undefined; get fileName(): string { @@ -47,9 +47,9 @@ export class InMemoryDocument implements vscode.TextDocument { } positionAt(offset: number): vscode.Position { const before = this._contents.slice(0, offset); - const newLines = before.match(/\n/g); + const newLines = before.match(/\r\n|\n/g); const line = newLines ? newLines.length : 0; - const preCharacters = before.match(/(\n|^).*$/g); + const preCharacters = before.match(/(\r\n|\n|^).*$/g); return new vscode.Position(line, preCharacters ? preCharacters[0].length : 0); } getText(_range?: vscode.Range | undefined): string { diff --git a/extensions/markdown-language-features/src/test/smartSelect.test.ts b/extensions/markdown-language-features/src/test/smartSelect.test.ts index 787df59ac..6e77d56c2 100644 --- a/extensions/markdown-language-features/src/test/smartSelect.test.ts +++ b/extensions/markdown-language-features/src/test/smartSelect.test.ts @@ -14,10 +14,10 @@ const CURSOR = '$$CURSOR$$'; const testFileName = vscode.Uri.file('test.md'); -suite.only('markdown.SmartSelect', () => { +suite('markdown.SmartSelect', () => { test('Smart select single word', async () => { const ranges = await getSelectionRangesForDocument(`Hel${CURSOR}lo`); - assertNestedRangesEqual(ranges![0], [0, 1]); + assertNestedLineNumbersEqual(ranges![0], [0, 0]); }); test('Smart select multi-line paragraph', async () => { const ranges = await getSelectionRangesForDocument( @@ -26,12 +26,12 @@ suite.only('markdown.SmartSelect', () => { `For example, the[node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter]`, `(https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki).` )); - assertNestedRangesEqual(ranges![0], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [0, 2]); }); test('Smart select paragraph', async () => { const ranges = await getSelectionRangesForDocument(`Many of the core components and extensions to ${CURSOR}VS Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki).`); - assertNestedRangesEqual(ranges![0], [0, 1]); + assertNestedLineNumbersEqual(ranges![0], [0, 0]); }); test('Smart select html block', async () => { const ranges = await getSelectionRangesForDocument( @@ -40,7 +40,7 @@ suite.only('markdown.SmartSelect', () => { `${CURSOR}VS Code in action`, `

`)); - assertNestedRangesEqual(ranges![0], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [0, 2]); }); test('Smart select header on header line', async () => { const ranges = await getSelectionRangesForDocument( @@ -48,7 +48,7 @@ suite.only('markdown.SmartSelect', () => { `# Header${CURSOR}`, `Hello`)); - assertNestedRangesEqual(ranges![0], [0, 1]); + assertNestedLineNumbersEqual(ranges![0], [0, 1]); }); test('Smart select single word w grandparent header on text line', async () => { @@ -59,7 +59,7 @@ suite.only('markdown.SmartSelect', () => { `${CURSOR}Hello` )); - assertNestedRangesEqual(ranges![0], [2, 2], [1, 2]); + assertNestedLineNumbersEqual(ranges![0], [2, 2], [1, 2]); }); test('Smart select html block w parent header', async () => { const ranges = await getSelectionRangesForDocument( @@ -69,7 +69,7 @@ suite.only('markdown.SmartSelect', () => { `VS Code in action`, `

`)); - assertNestedRangesEqual(ranges![0], [1, 3], [1, 3], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 3], [0, 3]); }); test('Smart select fenced code block', async () => { const ranges = await getSelectionRangesForDocument( @@ -78,7 +78,7 @@ suite.only('markdown.SmartSelect', () => { `a${CURSOR}`, `~~~`)); - assertNestedRangesEqual(ranges![0], [0, 2]); + assertNestedLineNumbersEqual(ranges![0], [0, 2]); }); test('Smart select list', async () => { const ranges = await getSelectionRangesForDocument( @@ -87,8 +87,7 @@ suite.only('markdown.SmartSelect', () => { `- ${CURSOR}item 2`, `- item 3`, `- item 4`)); - - assertNestedRangesEqual(ranges![0], [1, 1], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [1, 1], [0, 3]); }); test('Smart select list with fenced code block', async () => { const ranges = await getSelectionRangesForDocument( @@ -100,7 +99,7 @@ suite.only('markdown.SmartSelect', () => { `- item 3`, `- item 4`)); - assertNestedRangesEqual(ranges![0], [1, 3], [0, 5]); + assertNestedLineNumbersEqual(ranges![0], [1, 3], [0, 5]); }); test('Smart select multi cursor', async () => { const ranges = await getSelectionRangesForDocument( @@ -112,8 +111,8 @@ suite.only('markdown.SmartSelect', () => { `- ${CURSOR}item 3`, `- item 4`)); - assertNestedRangesEqual(ranges![0], [0, 0], [0, 5]); - assertNestedRangesEqual(ranges![1], [4, 4], [0, 5]); + assertNestedLineNumbersEqual(ranges![0], [0, 0], [0, 5]); + assertNestedLineNumbersEqual(ranges![1], [4, 4], [0, 5]); }); test('Smart select nested block quotes', async () => { const ranges = await getSelectionRangesForDocument( @@ -122,7 +121,7 @@ suite.only('markdown.SmartSelect', () => { `> item 2`, `>> ${CURSOR}item 3`, `>> item 4`)); - assertNestedRangesEqual(ranges![0], [2, 4], [0, 4]); + assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [0, 3]); }); test('Smart select multi nested block quotes', async () => { const ranges = await getSelectionRangesForDocument( @@ -131,8 +130,7 @@ suite.only('markdown.SmartSelect', () => { `>> item 2`, `>>> ${CURSOR}item 3`, `>>>> item 4`)); - - assertNestedRangesEqual(ranges![0], [2, 3], [2, 4], [1, 4], [0, 4]); + assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [1, 3], [0, 3]); }); test('Smart select subheader content', async () => { const ranges = await getSelectionRangesForDocument( @@ -143,7 +141,7 @@ suite.only('markdown.SmartSelect', () => { `${CURSOR}content 2`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [3, 3], [2, 3], [1, 3], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [3, 3], [2, 3], [1, 3], [0, 3]); }); test('Smart select subheader line', async () => { const ranges = await getSelectionRangesForDocument( @@ -154,7 +152,7 @@ suite.only('markdown.SmartSelect', () => { `content 2`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [2, 3], [1, 3], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [2, 3], [1, 3], [0, 3]); }); test('Smart select blank line', async () => { const ranges = await getSelectionRangesForDocument( @@ -165,7 +163,7 @@ suite.only('markdown.SmartSelect', () => { `content 2`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [1, 3], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [1, 3], [0, 3]); }); test('Smart select line between paragraphs', async () => { const ranges = await getSelectionRangesForDocument( @@ -174,7 +172,7 @@ suite.only('markdown.SmartSelect', () => { `${CURSOR}`, `paragraph 2`)); - assertNestedRangesEqual(ranges![0], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [0, 2]); }); test('Smart select empty document', async () => { const ranges = await getSelectionRangesForDocument(``, [new vscode.Position(0, 0)]); @@ -196,7 +194,7 @@ suite.only('markdown.SmartSelect', () => { `more content`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [4, 6], [3, 9], [3, 10], [2, 10], [1, 10], [0, 10]); + assertNestedLineNumbersEqual(ranges![0], [4, 6], [3, 9], [3, 10], [2, 10], [1, 10], [0, 10]); }); test('Smart select list with one element without selecting child subheader', async () => { const ranges = await getSelectionRangesForDocument( @@ -209,8 +207,7 @@ suite.only('markdown.SmartSelect', () => { ``, `content 2`, `# main header 2`)); - - assertNestedRangesEqual(ranges![0], [2, 3], [1, 3], [1, 6], [0, 6]); + assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [1, 3], [1, 6], [0, 6]); }); test('Smart select content under header then subheaders and their content', async () => { const ranges = await getSelectionRangesForDocument( @@ -224,7 +221,7 @@ suite.only('markdown.SmartSelect', () => { `content 2`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [0, 3], [0, 6]); + assertNestedLineNumbersEqual(ranges![0], [0, 3], [0, 6]); }); test('Smart select last blockquote element under header then subheaders and their content', async () => { const ranges = await getSelectionRangesForDocument( @@ -242,7 +239,7 @@ suite.only('markdown.SmartSelect', () => { `content 2`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [4, 6], [2, 6], [1, 7], [1, 10], [0, 10]); + assertNestedLineNumbersEqual(ranges![0], [5, 5], [4, 5], [2, 5], [1, 7], [1, 10], [0, 10]); }); test('Smart select content of subheader then subheader then content of main header then main header', async () => { const ranges = await getSelectionRangesForDocument( @@ -266,7 +263,7 @@ suite.only('markdown.SmartSelect', () => { `- content 2`, `content 2`)); - assertNestedRangesEqual(ranges![0], [11, 12], [9, 12], [9, 17], [8, 17], [1, 17], [0, 17]); + assertNestedLineNumbersEqual(ranges![0], [11, 11], [9, 12], [9, 17], [8, 17], [1, 17], [0, 17]); }); test('Smart select last line content of subheader then subheader then content of main header then main header', async () => { const ranges = await getSelectionRangesForDocument( @@ -288,9 +285,9 @@ suite.only('markdown.SmartSelect', () => { `- content 2`, `- content 2`, `- content 2`, - `${CURSOR}content 2`)); + `- ${CURSOR}content 2`)); - assertNestedRangesEqual(ranges![0], [16, 17], [14, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); + assertNestedLineNumbersEqual(ranges![0], [17, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); }); test('Smart select last line content after content of subheader then subheader then content of main header then main header', async () => { const ranges = await getSelectionRangesForDocument( @@ -312,9 +309,9 @@ suite.only('markdown.SmartSelect', () => { `- content 2`, `- content 2`, `- content 2`, - `content 2${CURSOR}`)); + `- content 2${CURSOR}`)); - assertNestedRangesEqual(ranges![0], [16, 17], [14, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); + assertNestedLineNumbersEqual(ranges![0], [17, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); }); test('Smart select fenced code block then list then rest of content', async () => { const ranges = await getSelectionRangesForDocument( @@ -338,7 +335,7 @@ suite.only('markdown.SmartSelect', () => { `- content 2`, `- content 2`)); - assertNestedRangesEqual(ranges![0], [9, 11], [8, 12], [7, 17], [1, 17], [0, 17]); + assertNestedLineNumbersEqual(ranges![0], [9, 11], [8, 12], [8, 12], [7, 17], [1, 17], [0, 17]); }); test('Smart select fenced code block then list then rest of content on fenced line', async () => { const ranges = await getSelectionRangesForDocument( @@ -362,15 +359,289 @@ suite.only('markdown.SmartSelect', () => { `- content 2`, `- content 2`)); - assertNestedRangesEqual(ranges![0], [8, 12], [7, 17], [1, 17], [0, 17]); + assertNestedLineNumbersEqual(ranges![0], [8, 12], [7, 17], [1, 17], [0, 17]); + }); + test('Smart select without multiple ranges', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + ``, + `- ${CURSOR}paragraph`, + `- content`)); + + assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [1, 4], [0, 4]); + }); + test('Smart select on second level of a list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 0`, + ` * level 1`, + ` * level 1`, + ` * level 2`, + ` * level 1`, + ` * level ${CURSOR}1`, + `* level 0`)); + + assertNestedLineNumbersEqual(ranges![0], [5, 5], [1, 5], [0, 5], [0, 6]); + }); + test('Smart select on third level of a list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 0`, + ` * level 1`, + ` * level 1`, + ` * level ${CURSOR}2`, + ` * level 2`, + ` * level 1`, + ` * level 1`, + `* level 0`)); + assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [2, 4], [1, 6], [0, 6], [0, 7]); + }); + test('Smart select level 2 then level 1', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 1`, + ` * level ${CURSOR}2`, + ` * level 2`, + `* level 1`)); + assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 2], [0, 2], [0, 3]); + }); + test('Smart select last list item', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `- level 1`, + `- level 2`, + `- level 2`, + `- level ${CURSOR}1`)); + assertNestedLineNumbersEqual(ranges![0], [3, 3], [0, 3]); + }); + test('Smart select without multiple ranges', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + ``, + `- ${CURSOR}paragraph`, + `- content`)); + + assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [1, 4], [0, 4]); + }); + test('Smart select on second level of a list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 0`, + ` * level 1`, + ` * level 1`, + ` * level 2`, + ` * level 1`, + ` * level ${CURSOR}1`, + `* level 0`)); + + assertNestedLineNumbersEqual(ranges![0], [5, 5], [1, 5], [0, 5], [0, 6]); + }); + test('Smart select on third level of a list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 0`, + ` * level 1`, + ` * level 1`, + ` * level ${CURSOR}2`, + ` * level 2`, + ` * level 1`, + ` * level 1`, + `* level 0`)); + assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [2, 4], [1, 6], [0, 6], [0, 7]); + }); + test('Smart select level 2 then level 1', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 1`, + ` * level ${CURSOR}2`, + ` * level 2`, + `* level 1`)); + assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 2], [0, 2], [0, 3]); + }); + test('Smart select bold', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `stuff here **new${CURSOR}item** and here` + )); + assertNestedRangesEqual(ranges![0], [0, 13, 0, 30], [0, 11, 0, 32], [0, 0, 0, 41]); + }); + test('Smart select link', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `stuff here [text](https${CURSOR}://google.com) and here` + )); + assertNestedRangesEqual(ranges![0], [0, 18, 0, 46], [0, 17, 0, 47], [0, 11, 0, 47], [0, 0, 0, 56]); + }); + test('Smart select brackets', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `stuff here [te${CURSOR}xt](https://google.com) and here` + )); + assertNestedRangesEqual(ranges![0], [0, 12, 0, 26], [0, 11, 0, 27], [0, 11, 0, 47], [0, 0, 0, 56]); + }); + test('Smart select brackets under header in list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `- list`, + `paragraph`, + `## sub header`, + `- list`, + `- stuff here [te${CURSOR}xt](https://google.com) and here`, + `- list` + )); + assertNestedRangesEqual(ranges![0], [6, 14, 6, 28], [6, 13, 6, 29], [6, 13, 6, 49], [6, 0, 6, 58], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]); + }); + test('Smart select link under header in list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `- list`, + `paragraph`, + `## sub header`, + `- list`, + `- stuff here [text](${CURSOR}https://google.com) and here`, + `- list` + )); + assertNestedRangesEqual(ranges![0], [6, 20, 6, 48], [6, 19, 6, 49], [6, 13, 6, 49], [6, 0, 6, 58], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]); + }); + test('Smart select bold within list where multiple bold elements exists', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `- list`, + `paragraph`, + `## sub header`, + `- list`, + `- stuff here [text] **${CURSOR}items in here** and **here**`, + `- list` + )); + assertNestedRangesEqual(ranges![0], [6, 22, 6, 45], [6, 20, 6, 47], [6, 0, 6, 60], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]); + }); + test('Smart select link in paragraph with multiple links', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `This[extension](https://marketplace.visualstudio.com/items?itemName=meganrogge.template-string-converter) addresses this [requ${CURSOR}est](https://github.com/microsoft/vscode/issues/56704) to convert Javascript/Typescript quotes to backticks when has been entered within a string.` + )); + assertNestedRangesEqual(ranges![0], [0, 123, 0, 140], [0, 122, 0, 141], [0, 122, 0, 191], [0, 0, 0, 283]); + }); + test('Smart select bold link', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `**[extens${CURSOR}ion](https://google.com)**` + )); + assertNestedRangesEqual(ranges![0], [0, 3, 0, 22], [0, 2, 0, 23], [0, 2, 0, 43], [0, 2, 0, 43], [0, 0, 0, 45], [0, 0, 0, 45]); + }); + test('Smart select inline code block', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `[\`code ${CURSOR} link\`]` + )); + assertNestedRangesEqual(ranges![0], [0, 2, 0, 22], [0, 1, 0, 23], [0, 0, 0, 24]); + }); + test('Smart select link with inline code block text', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `[\`code ${CURSOR} link\`](http://example.com)` + )); + assertNestedRangesEqual(ranges![0], [0, 2, 0, 22], [0, 1, 0, 23], [0, 1, 0, 23], [0, 0, 0, 24], [0, 0, 0, 44], [0, 0, 0, 44]); + }); + test('Smart select italic', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `*some nice ${CURSOR}text*` + )); + assertNestedRangesEqual(ranges![0], [0, 1, 0, 25], [0, 0, 0, 26], [0, 0, 0, 26]); + }); + test('Smart select italic link', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `*[extens${CURSOR}ion](https://google.com)*` + )); + assertNestedRangesEqual(ranges![0], [0, 2, 0, 21], [0, 1, 0, 22], [0, 1, 0, 42], [0, 1, 0, 42], [0, 0, 0, 43], [0, 0, 0, 43]); + }); + test('Smart select italic on end', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `*word1 word2 word3${CURSOR}*` + )); + assertNestedRangesEqual(ranges![0], [0, 1, 0, 28], [0, 0, 0, 29], [0, 0, 0, 29]); + }); + test('Smart select italic then bold', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `outer text **bold words *italic ${CURSOR} words* bold words** outer text` + )); + assertNestedRangesEqual(ranges![0], [0, 25, 0, 48], [0, 24, 0, 49], [0, 13, 0, 60], [0, 11, 0, 62], [0, 0, 0, 73]); + }); + test('Smart select bold then italic', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `outer text *italic words **bold ${CURSOR} words** italic words* outer text` + )); + assertNestedRangesEqual(ranges![0], [0, 27, 0, 48], [0, 25, 0, 50], [0, 12, 0, 63], [0, 11, 0, 64], [0, 0, 0, 75]); + }); + test('Third level header from release notes', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `---`, + `Order: 60`, + `TOCTitle: October 2020`, + `PageTitle: Visual Studio Code October 2020`, + `MetaDescription: Learn what is new in the Visual Studio Code October 2020 Release (1.51)`, + `MetaSocialImage: 1_51/release-highlights.png`, + `Date: 2020-11-6`, + `DownloadVersion: 1.51.1`, + `---`, + `# October 2020 (version 1.51)`, + ``, + `**Update 1.51.1**: The update addresses these [issues](https://github.com/microsoft/vscode/issues?q=is%3Aissue+milestone%3A%22October+2020+Recovery%22+is%3Aclosed+).`, + ``, + ``, + ``, + `---`, + ``, + `Welcome to the October 2020 release of Visual Studio Code. As announced in the [October iteration plan](https://github.com/microsoft/vscode/issues/108473), we focused on housekeeping GitHub issues and pull requests as documented in our issue grooming guide.`, + ``, + `We also worked with our partners at GitHub on GitHub Codespaces, which ended up being more involved than originally anticipated. To that end, we'll continue working on housekeeping for part of the November iteration.`, + ``, + `During this housekeeping milestone, we also addressed several feature requests and community [pull requests](#thank-you). Read on to learn about new features and settings.`, + ``, + `## Workbench`, + ``, + `### More prominent pinned tabs`, + ``, + `${CURSOR}Pinned tabs will now always show their pin icon, even while inactive, to make them easier to identify. If an editor is both pinned and contains unsaved changes, the icon reflects both states.`, + ``, + `![Inactive pinned tabs showing pin icons](images/1_51/pinned-tabs.png)` + ) + ); + assertNestedRangesEqual(ranges![0], [27, 0, 27, 201], [26, 0, 29, 70], [25, 0, 29, 70], [24, 0, 29, 70], [23, 0, 29, 70], [10, 0, 29, 70], [9, 0, 29, 70]); }); }); -function assertNestedRangesEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number][]) { +function assertNestedLineNumbersEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number][]) { const lineage = getLineage(range); - assert.strictEqual(lineage.length, expectedRanges.length, `expected depth: ${expectedRanges.length}, but was ${lineage.length}`); + assert.strictEqual(lineage.length, expectedRanges.length, `expected depth: ${expectedRanges.length}, but was ${lineage.length} ${getValues(lineage)}`); for (let i = 0; i < lineage.length; i++) { - assertRangesEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][1], `parent at a depth of ${i}`); + assertLineNumbersEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][1], `parent at a depth of ${i}`); + } +} + +function assertNestedRangesEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number, number, number][]) { + const lineage = getLineage(range); + assert.strictEqual(lineage.length, expectedRanges.length, `expected depth: ${expectedRanges.length}, but was ${lineage.length} ${getValues(lineage)}`); + for (let i = 0; i < lineage.length; i++) { + assertLineNumbersEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][2], `parent at a depth of ${i}`); + assert(lineage[i].range.start.character === expectedRanges[i][1], `parent at a depth of ${i} on start char`); + assert(lineage[i].range.end.character === expectedRanges[i][3], `parent at a depth of ${i} on end char`); } } @@ -384,7 +655,13 @@ function getLineage(range: vscode.SelectionRange): vscode.SelectionRange[] { return result; } -function assertRangesEqual(selectionRange: vscode.SelectionRange, startLine: number, endLine: number, message: string) { +function getValues(ranges: vscode.SelectionRange[]): string[] { + return ranges.map(range => { + return range.range.start.line + ' ' + range.range.start.character + ' ' + range.range.end.line + ' ' + range.range.end.character; + }); +} + +function assertLineNumbersEqual(selectionRange: vscode.SelectionRange, startLine: number, endLine: number, message: string) { assert.strictEqual(selectionRange.range.start.line, startLine, `failed on start line ${message}`); assert.strictEqual(selectionRange.range.end.line, endLine, `failed on end line ${message}`); } diff --git a/extensions/markdown-language-features/src/util/arrays.ts b/extensions/markdown-language-features/src/util/arrays.ts index ec0ed25c5..b778a24ec 100644 --- a/extensions/markdown-language-features/src/util/arrays.ts +++ b/extensions/markdown-language-features/src/util/arrays.ts @@ -15,8 +15,4 @@ export function equals(one: ReadonlyArray, other: ReadonlyArray, itemEq } return true; -} - -export function flatten(arr: ReadonlyArray[]): T[] { - return ([] as T[]).concat.apply([], arr); } \ No newline at end of file diff --git a/extensions/markdown-language-features/test-workspace/a.md b/extensions/markdown-language-features/test-workspace/a.md index 9d7091806..568bad19d 100644 --- a/extensions/markdown-language-features/test-workspace/a.md +++ b/extensions/markdown-language-features/test-workspace/a.md @@ -1,4 +1,10 @@ [b](b) -[b](b.md) -[b](./b.md) -[b](/b.md) + +[b.md](b.md) + +[./b.md](./b.md) + +[/b.md](/b.md) + +[b#header1](b#header1) + diff --git a/extensions/markdown-language-features/test-workspace/b.md b/extensions/markdown-language-features/test-workspace/b.md index 730f53ee6..524a6cb26 100644 --- a/extensions/markdown-language-features/test-workspace/b.md +++ b/extensions/markdown-language-features/test-workspace/b.md @@ -1,3 +1,5 @@ # b -[](./a) \ No newline at end of file +[./a](./a) + +# header1 \ No newline at end of file diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index ec7424a40..b02362c2c 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -6,6 +6,8 @@ "lib": [ "es6", "es2015.promise", + "es2019.array", + "es2020.string", "dom" ] }, diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 5f1895e96..065943fa4 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -2131,10 +2131,10 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -highlight.js@9.15.10: - version "9.15.10" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2" - integrity sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw== +highlight.js@10.1.2: + version "10.1.2" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.1.2.tgz#c20db951ba1c22c055010648dfffd7b2a968e00c" + integrity sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA== hmac-drbg@^1.0.0: version "1.0.1" diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 8220adc6c..60455f02a 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -276,7 +276,7 @@ export class AzureActiveDirectoryService { } return new Promise(async (resolve, reject) => { - if (vscode.env.uiKind === vscode.UIKind.Web) { + if (vscode.env.remoteName !== undefined) { resolve(this.loginWithoutLocalServer(scope)); return; } diff --git a/extensions/npm/README.md b/extensions/npm/README.md index 144cdd275..82730c7e8 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -34,7 +34,7 @@ The extension fetches data from https://registry.npmjs.org and https://registry. - `npm.autoDetect` - Enable detecting scripts as tasks, the default is `on`. - `npm.runSilent` - Run npm script with the `--silent` option, the default is `false`. -- `npm.packageManager` - The package manager used to run the scripts: `npm`, `yarn` or `pnpm`, the default is `npm`. +- `npm.packageManager` - The package manager used to run the scripts: `auto`, `npm`, `yarn` or `pnpm`, the default is `auto`, which detects your package manager based on your files. - `npm.exclude` - Glob patterns for folders that should be excluded from automatic script detection. The pattern is matched against the **absolute path** of the package.json. For example, to exclude all test folders use '**/test/**'. - `npm.enableScriptExplorer` - Enable an explorer view for npm scripts. - `npm.scriptExplorerAction` - The default click action: `open` or `run`, the default is `open`. diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 575a2ef6d..5210c5296 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -18,15 +18,21 @@ "watch": "gulp watch-extension:npm" }, "dependencies": { + "find-up": "^4.1.0", + "find-yarn-workspace-root": "^2.0.0", "jsonc-parser": "^2.2.1", "minimatch": "^3.0.4", "request-light": "^0.4.0", - "vscode-nls": "^4.1.1" + "vscode-nls": "^4.1.1", + "which-pm": "^2.0.0" }, "devDependencies": { "@types/minimatch": "^3.0.3", "@types/node": "^12.11.7" }, + "resolutions": { + "which-pm/load-yaml-file/**/argparse": "1.0.9" + }, "main": "./out/npmMain", "browser": "./dist/browser/npmBrowserMain", "activationEvents": [ @@ -56,7 +62,7 @@ { "id": "npm", "name": "%view.name%", - "icon": "images/code.svg", + "icon": "$(code)", "visibility": "hidden" } ] @@ -217,11 +223,18 @@ "scope": "resource", "type": "string", "enum": [ + "auto", "npm", "yarn", "pnpm" ], - "default": "npm", + "enumDescriptions": [ + "%config.npm.packageManager.auto%", + "%config.npm.packageManager.npm%", + "%config.npm.packageManager.yarn%", + "%config.npm.packageManager.pnpm%" + ], + "default": "auto", "description": "%config.npm.packageManager%" }, "npm.exclude": { diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index 8ecf37462..53ea18481 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -4,6 +4,10 @@ "config.npm.autoDetect": "Controls whether npm scripts should be automatically detected.", "config.npm.runSilent": "Run npm commands with the `--silent` option.", "config.npm.packageManager": "The package manager used to run scripts.", + "config.npm.packageManager.npm": "Use npm as the package manager for running scripts.", + "config.npm.packageManager.yarn": "Use yarn as the package manager for running scripts.", + "config.npm.packageManager.pnpm": "Use pnpm as the package manager for running scripts.", + "config.npm.packageManager.auto": "Auto-detect which package manager to use for running scripts based on lock files and installed package managers.", "config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.", "config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts when there is no top-level 'package.json' file.", "config.npm.scriptExplorerAction": "The default click action used in the npm scripts explorer: `open` or `run`, the default is `open`.", diff --git a/extensions/npm/src/commands.ts b/extensions/npm/src/commands.ts index 7ca92879f..4f9131a44 100644 --- a/extensions/npm/src/commands.ts +++ b/extensions/npm/src/commands.ts @@ -15,7 +15,7 @@ import { const localize = nls.loadMessageBundle(); -export function runSelectedScript() { +export function runSelectedScript(context: vscode.ExtensionContext) { let editor = vscode.window.activeTextEditor; if (!editor) { return; @@ -27,15 +27,15 @@ export function runSelectedScript() { let script = findScriptAtPosition(contents, offset); if (script) { - runScript(script, document); + runScript(context, script, document); } else { let message = localize('noScriptFound', 'Could not find a valid npm script at the selection.'); vscode.window.showErrorMessage(message); } } -export async function selectAndRunScriptFromFolder(selectedFolder: vscode.Uri) { - let taskList: FolderTaskItem[] = await detectNpmScriptsForFolder(selectedFolder); +export async function selectAndRunScriptFromFolder(context: vscode.ExtensionContext, selectedFolder: vscode.Uri) { + let taskList: FolderTaskItem[] = await detectNpmScriptsForFolder(context, selectedFolder); if (taskList && taskList.length > 0) { const quickPick = vscode.window.createQuickPick(); diff --git a/extensions/npm/src/features/bowerJSONContribution.ts b/extensions/npm/src/features/bowerJSONContribution.ts index c3a827fd1..4fb0f0633 100644 --- a/extensions/npm/src/features/bowerJSONContribution.ts +++ b/extensions/npm/src/features/bowerJSONContribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkdownString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode'; +import { MarkdownString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace, Uri } from 'vscode'; import { IJSONContribution, ISuggestionsCollector } from './jsonContributions'; import { XHRRequest } from 'request-light'; import { Location } from 'jsonc-parser'; @@ -37,7 +37,7 @@ export class BowerJSONContribution implements IJSONContribution { return !!workspace.getConfiguration('npm').get('fetchOnlinePackageInfo'); } - public collectDefaultSuggestions(_resource: string, collector: ISuggestionsCollector): Thenable { + public collectDefaultSuggestions(_resource: Uri, collector: ISuggestionsCollector): Thenable { const defaultValue = { 'name': '${1:name}', 'description': '${2:description}', @@ -53,7 +53,7 @@ export class BowerJSONContribution implements IJSONContribution { return Promise.resolve(null); } - public collectPropertySuggestions(_resource: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, collector: ISuggestionsCollector): Thenable | null { + public collectPropertySuggestions(_resource: Uri, location: Location, currentWord: string, addValue: boolean, isLast: boolean, collector: ISuggestionsCollector): Thenable | null { if (!this.isEnabled()) { return null; } @@ -125,7 +125,7 @@ export class BowerJSONContribution implements IJSONContribution { return null; } - public collectValueSuggestions(_resource: string, location: Location, collector: ISuggestionsCollector): Promise | null { + public collectValueSuggestions(_resource: Uri, location: Location, collector: ISuggestionsCollector): Promise | null { if (!this.isEnabled()) { return null; } @@ -141,7 +141,7 @@ export class BowerJSONContribution implements IJSONContribution { return null; } - public resolveSuggestion(item: CompletionItem): Thenable | null { + public resolveSuggestion(_resource: Uri | undefined, item: CompletionItem): Thenable | null { if (item.kind === CompletionItemKind.Property && item.documentation === '') { return this.getInfo(item.label).then(documentation => { if (documentation) { @@ -182,7 +182,7 @@ export class BowerJSONContribution implements IJSONContribution { }); } - public getInfoContribution(_resource: string, location: Location): Thenable | null { + public getInfoContribution(_resource: Uri, location: Location): Thenable | null { if (!this.isEnabled()) { return null; } diff --git a/extensions/npm/src/features/jsonContributions.ts b/extensions/npm/src/features/jsonContributions.ts index 071d57b33..3954c2351 100644 --- a/extensions/npm/src/features/jsonContributions.ts +++ b/extensions/npm/src/features/jsonContributions.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Location, getLocation, createScanner, SyntaxKind, ScanError, JSONScanner } from 'jsonc-parser'; -import { basename } from 'path'; import { BowerJSONContribution } from './bowerJSONContribution'; import { PackageJSONContribution } from './packageJSONContribution'; import { XHRRequest } from 'request-light'; import { CompletionItem, CompletionItemProvider, CompletionList, TextDocument, Position, Hover, HoverProvider, - CancellationToken, Range, MarkedString, DocumentSelector, languages, Disposable + CancellationToken, Range, MarkedString, DocumentSelector, languages, Disposable, Uri } from 'vscode'; export interface ISuggestionsCollector { @@ -23,11 +22,11 @@ export interface ISuggestionsCollector { export interface IJSONContribution { getDocumentSelector(): DocumentSelector; - getInfoContribution(fileName: string, location: Location): Thenable | null; - collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable | null; - collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable | null; - collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable; - resolveSuggestion?(item: CompletionItem): Thenable | null; + getInfoContribution(resourceUri: Uri, location: Location): Thenable | null; + collectPropertySuggestions(resourceUri: Uri, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable | null; + collectValueSuggestions(resourceUri: Uri, location: Location, result: ISuggestionsCollector): Thenable | null; + collectDefaultSuggestions(resourceUri: Uri, result: ISuggestionsCollector): Thenable; + resolveSuggestion?(resourceUri: Uri | undefined, item: CompletionItem): Thenable | null; } export function addJSONProviders(xhr: XHRRequest, canRunNPM: boolean): Disposable { @@ -47,7 +46,6 @@ export class JSONHoverProvider implements HoverProvider { } public provideHover(document: TextDocument, position: Position, _token: CancellationToken): Thenable | null { - const fileName = basename(document.fileName); const offset = document.offsetAt(position); const location = getLocation(document.getText(), offset); if (!location.previousNode) { @@ -55,7 +53,7 @@ export class JSONHoverProvider implements HoverProvider { } const node = location.previousNode; if (node && node.offset <= offset && offset <= node.offset + node.length) { - const promise = this.jsonContribution.getInfoContribution(fileName, location); + const promise = this.jsonContribution.getInfoContribution(document.uri, location); if (promise) { return promise.then(htmlContent => { const range = new Range(document.positionAt(node.offset), document.positionAt(node.offset + node.length)); @@ -73,12 +71,14 @@ export class JSONHoverProvider implements HoverProvider { export class JSONCompletionItemProvider implements CompletionItemProvider { + private lastResource: Uri | undefined; + constructor(private jsonContribution: IJSONContribution) { } public resolveCompletionItem(item: CompletionItem, _token: CancellationToken): Thenable { if (this.jsonContribution.resolveSuggestion) { - const resolver = this.jsonContribution.resolveSuggestion(item); + const resolver = this.jsonContribution.resolveSuggestion(this.lastResource, item); if (resolver) { return resolver; } @@ -87,8 +87,8 @@ export class JSONCompletionItemProvider implements CompletionItemProvider { } public provideCompletionItems(document: TextDocument, position: Position, _token: CancellationToken): Thenable | null { + this.lastResource = document.uri; - const fileName = basename(document.fileName); const currentWord = this.getCurrentWord(document, position); let overwriteRange: Range; @@ -126,12 +126,12 @@ export class JSONCompletionItemProvider implements CompletionItemProvider { const scanner = createScanner(document.getText(), true); const addValue = !location.previousNode || !this.hasColonAfter(scanner, location.previousNode.offset + location.previousNode.length); const isLast = this.isLast(scanner, document.offsetAt(position)); - collectPromise = this.jsonContribution.collectPropertySuggestions(fileName, location, currentWord, addValue, isLast, collector); + collectPromise = this.jsonContribution.collectPropertySuggestions(document.uri, location, currentWord, addValue, isLast, collector); } else { if (location.path.length === 0) { - collectPromise = this.jsonContribution.collectDefaultSuggestions(fileName, collector); + collectPromise = this.jsonContribution.collectDefaultSuggestions(document.uri, collector); } else { - collectPromise = this.jsonContribution.collectValueSuggestions(fileName, location, collector); + collectPromise = this.jsonContribution.collectValueSuggestions(document.uri, location, collector); } } if (collectPromise) { diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index 7fc5475ab..3249cb765 100644 --- a/extensions/npm/src/features/packageJSONContribution.ts +++ b/extensions/npm/src/features/packageJSONContribution.ts @@ -3,13 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace, MarkdownString } from 'vscode'; +import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace, MarkdownString, Uri } from 'vscode'; import { IJSONContribution, ISuggestionsCollector } from './jsonContributions'; import { XHRRequest } from 'request-light'; import { Location } from 'jsonc-parser'; import * as cp from 'child_process'; import * as nls from 'vscode-nls'; +import { dirname } from 'path'; const localize = nls.loadMessageBundle(); const LIMIT = 40; @@ -35,7 +36,7 @@ export class PackageJSONContribution implements IJSONContribution { public constructor(private xhr: XHRRequest, private canRunNPM: boolean) { } - public collectDefaultSuggestions(_fileName: string, result: ISuggestionsCollector): Thenable { + public collectDefaultSuggestions(_resource: Uri, result: ISuggestionsCollector): Thenable { const defaultValue = { 'name': '${1:name}', 'description': '${2:description}', @@ -60,7 +61,7 @@ export class PackageJSONContribution implements IJSONContribution { } public collectPropertySuggestions( - _resource: string, + _resource: Uri, location: Location, currentWord: string, addValue: boolean, @@ -183,7 +184,7 @@ export class PackageJSONContribution implements IJSONContribution { return Promise.resolve(null); } - public async collectValueSuggestions(_fileName: string, location: Location, result: ISuggestionsCollector): Promise { + public async collectValueSuggestions(resource: Uri, location: Location, result: ISuggestionsCollector): Promise { if (!this.isEnabled()) { return null; } @@ -191,7 +192,7 @@ export class PackageJSONContribution implements IJSONContribution { if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { const currentKey = location.path[location.path.length - 1]; if (typeof currentKey === 'string') { - const info = await this.fetchPackageInfo(currentKey); + const info = await this.fetchPackageInfo(currentKey, resource); if (info && info.version) { let name = JSON.stringify(info.version); @@ -236,9 +237,9 @@ export class PackageJSONContribution implements IJSONContribution { return str; } - public resolveSuggestion(item: CompletionItem): Thenable | null { + public resolveSuggestion(resource: Uri | undefined, item: CompletionItem): Thenable | null { if (item.kind === CompletionItemKind.Property && !item.documentation) { - return this.fetchPackageInfo(item.label).then(info => { + return this.fetchPackageInfo(item.label, resource).then(info => { if (info) { item.documentation = this.getDocumentation(info.description, info.version, info.homepage); return item; @@ -266,13 +267,13 @@ export class PackageJSONContribution implements IJSONContribution { return false; } - private async fetchPackageInfo(pack: string): Promise { + private async fetchPackageInfo(pack: string, resource: Uri | undefined): Promise { if (!this.isValidNPMName(pack)) { return undefined; // avoid unnecessary lookups } let info: ViewPackageInfo | undefined; if (this.canRunNPM) { - info = await this.npmView(pack); + info = await this.npmView(pack, resource); } if (!info && this.onlineEnabled()) { info = await this.npmjsView(pack); @@ -280,10 +281,11 @@ export class PackageJSONContribution implements IJSONContribution { return info; } - private npmView(pack: string): Promise { + private npmView(pack: string, resource: Uri | undefined): Promise { return new Promise((resolve, _reject) => { const args = ['view', '--json', pack, 'description', 'dist-tags.latest', 'homepage', 'version']; - cp.execFile(process.platform === 'win32' ? 'npm.cmd' : 'npm', args, (error, stdout) => { + let cwd = resource && resource.scheme === 'file' ? dirname(resource.fsPath) : undefined; + cp.execFile(process.platform === 'win32' ? 'npm.cmd' : 'npm', args, { cwd }, (error, stdout) => { if (!error) { try { const content = JSON.parse(stdout); @@ -325,14 +327,14 @@ export class PackageJSONContribution implements IJSONContribution { return undefined; } - public getInfoContribution(_fileName: string, location: Location): Thenable | null { + public getInfoContribution(resource: Uri, location: Location): Thenable | null { if (!this.isEnabled()) { return null; } if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { const pack = location.path[location.path.length - 1]; if (typeof pack === 'string') { - return this.fetchPackageInfo(pack).then(info => { + return this.fetchPackageInfo(pack, resource).then(info => { if (info) { return [this.getDocumentation(info.description, info.version, info.homepage)]; } diff --git a/extensions/npm/src/npmMain.ts b/extensions/npm/src/npmMain.ts index 67cdf56c2..568c5ea3d 100644 --- a/extensions/npm/src/npmMain.ts +++ b/extensions/npm/src/npmMain.ts @@ -31,6 +31,7 @@ export async function activate(context: vscode.ExtensionContext): Promise const canRunNPM = canRunNpmInCurrentWorkspace(); context.subscriptions.push(addJSONProviders(httpRequest.xhr, canRunNPM)); + registerTaskProvider(context); treeDataProvider = registerExplorer(context); @@ -48,7 +49,6 @@ export async function activate(context: vscode.ExtensionContext): Promise } })); - registerTaskProvider(context); registerHoverProvider(context); context.subscriptions.push(vscode.commands.registerCommand('npm.runSelectedScript', runSelectedScript)); @@ -58,7 +58,7 @@ export async function activate(context: vscode.ExtensionContext): Promise })); context.subscriptions.push(vscode.commands.registerCommand('npm.packageManager', (args) => { if (args instanceof vscode.Uri) { - return getPackageManager(args); + return getPackageManager(context, args); } return ''; })); @@ -71,6 +71,7 @@ function canRunNpmInCurrentWorkspace() { return false; } +let taskProvider: NpmTaskProvider; function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposable | undefined { if (vscode.workspace.workspaceFolders) { let watcher = vscode.workspace.createFileSystemWatcher('**/package.json'); @@ -82,8 +83,8 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab let workspaceWatcher = vscode.workspace.onDidChangeWorkspaceFolders((_e) => invalidateScriptCaches()); context.subscriptions.push(workspaceWatcher); - let provider: vscode.TaskProvider = new NpmTaskProvider(); - let disposable = vscode.tasks.registerTaskProvider('npm', provider); + taskProvider = new NpmTaskProvider(context); + let disposable = vscode.tasks.registerTaskProvider('npm', taskProvider); context.subscriptions.push(disposable); return disposable; } @@ -92,7 +93,7 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab function registerExplorer(context: vscode.ExtensionContext): NpmScriptsTreeDataProvider | undefined { if (vscode.workspace.workspaceFolders) { - let treeDataProvider = new NpmScriptsTreeDataProvider(context); + let treeDataProvider = new NpmScriptsTreeDataProvider(context, taskProvider!); const view = vscode.window.createTreeView('npm', { treeDataProvider: treeDataProvider, showCollapseAll: true }); context.subscriptions.push(view); return treeDataProvider; diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index c1b5f6fdd..8617b7f7f 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -7,14 +7,18 @@ import { JSONVisitor, visit } from 'jsonc-parser'; import * as path from 'path'; import { commands, Event, EventEmitter, ExtensionContext, + Range, Selection, Task, - TaskGroup, tasks, TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, + TaskGroup, tasks, TextDocument, TextDocumentShowOptions, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, window, workspace, WorkspaceFolder } from 'vscode'; import * as nls from 'vscode-nls'; import { createTask, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, NpmTaskDefinition, - startDebugging + NpmTaskProvider, + startDebugging, + TaskLocation, + TaskWithLocation } from './tasks'; const localize = nls.loadMessageBundle(); @@ -43,7 +47,7 @@ class PackageJSON extends TreeItem { folder: Folder; scripts: NpmScript[] = []; - static getLabel(_folderName: string, relativePath: string): string { + static getLabel(relativePath: string): string { if (relativePath.length > 0) { return path.join(relativePath, packageName); } @@ -51,7 +55,7 @@ class PackageJSON extends TreeItem { } constructor(folder: Folder, relativePath: string) { - super(PackageJSON.getLabel(folder.label!, relativePath), TreeItemCollapsibleState.Expanded); + super(PackageJSON.getLabel(relativePath), TreeItemCollapsibleState.Expanded); this.folder = folder; this.path = relativePath; this.contextValue = 'packageJSON'; @@ -74,15 +78,20 @@ class NpmScript extends TreeItem { task: Task; package: PackageJSON; - constructor(_context: ExtensionContext, packageJson: PackageJSON, task: Task) { + constructor(_context: ExtensionContext, packageJson: PackageJSON, task: Task, public taskLocation?: TaskLocation) { super(task.name, TreeItemCollapsibleState.None); const command: ExplorerCommands = workspace.getConfiguration('npm').get('scriptExplorerAction') || 'open'; const commandList = { 'open': { title: 'Edit Script', - command: 'npm.openScript', - arguments: [this] + command: 'vscode.open', + arguments: [ + taskLocation?.document, + taskLocation ? { + selection: new Range(taskLocation.line, taskLocation.line) + } : undefined + ] }, 'run': { title: 'Run Script', @@ -123,7 +132,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { private _onDidChangeTreeData: EventEmitter = new EventEmitter(); readonly onDidChangeTreeData: Event = this._onDidChangeTreeData.event; - constructor(context: ExtensionContext) { + constructor(context: ExtensionContext, public taskProvider: NpmTaskProvider) { const subscriptions = context.subscriptions; this.extensionContext = context; subscriptions.push(commands.registerCommand('npm.runScript', this.runScript, this)); @@ -137,7 +146,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { } private async debugScript(script: NpmScript) { - startDebugging(script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder()); + startDebugging(this.extensionContext, script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder()); } private findScript(document: TextDocument, script?: NpmScript): number { @@ -181,7 +190,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { if (!uri) { return; } - let task = createTask('install', 'install', selection.folder.workspaceFolder, uri, undefined, []); + let task = await createTask(this.extensionContext, 'install', 'install', selection.folder.workspaceFolder, uri, undefined, []); tasks.executeTask(task); } @@ -228,7 +237,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { async getChildren(element?: TreeItem): Promise { if (!this.taskTree) { - let taskItems = await tasks.fetchTasks({ type: 'npm' }); + const taskItems = await this.taskProvider.tasksWithLocation; if (taskItems) { this.taskTree = this.buildTaskTree(taskItems); if (this.taskTree.length === 0) { @@ -265,7 +274,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { return fullName === task.name; } - private buildTaskTree(tasks: Task[]): Folder[] | PackageJSON[] | NoScripts[] { + private buildTaskTree(tasks: TaskWithLocation[]): Folder[] | PackageJSON[] | NoScripts[] { let folders: Map = new Map(); let packages: Map = new Map(); @@ -273,22 +282,22 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { let packageJson = null; tasks.forEach(each => { - if (isWorkspaceFolder(each.scope) && !this.isInstallTask(each)) { - folder = folders.get(each.scope.name); + if (isWorkspaceFolder(each.task.scope) && !this.isInstallTask(each.task)) { + folder = folders.get(each.task.scope.name); if (!folder) { - folder = new Folder(each.scope); - folders.set(each.scope.name, folder); + folder = new Folder(each.task.scope); + folders.set(each.task.scope.name, folder); } - let definition: NpmTaskDefinition = each.definition; + let definition: NpmTaskDefinition = each.task.definition; let relativePath = definition.path ? definition.path : ''; - let fullPath = path.join(each.scope.name, relativePath); + let fullPath = path.join(each.task.scope.name, relativePath); packageJson = packages.get(fullPath); if (!packageJson) { packageJson = new PackageJSON(folder, relativePath); folder.addPackage(packageJson); packages.set(fullPath, packageJson); } - let script = new NpmScript(this.extensionContext, packageJson, each); + let script = new NpmScript(this.extensionContext, packageJson, each.task, each.location); packageJson.addScript(script); } }); diff --git a/extensions/npm/src/preferred-pm.ts b/extensions/npm/src/preferred-pm.ts new file mode 100644 index 000000000..f9582bc70 --- /dev/null +++ b/extensions/npm/src/preferred-pm.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import findWorkspaceRoot = require('../node_modules/find-yarn-workspace-root'); +import findUp = require('find-up'); +import * as path from 'path'; +import whichPM = require('which-pm'); +import { Uri, workspace } from 'vscode'; + +async function pathExists(filePath: string) { + try { + await workspace.fs.stat(Uri.file(filePath)); + } catch { + return false; + } + return true; +} + +async function isPNPMPreferred(pkgPath: string) { + if (await pathExists(path.join(pkgPath, 'pnpm-lock.yaml'))) { + return true; + } + if (await pathExists(path.join(pkgPath, 'shrinkwrap.yaml'))) { + return true; + } + if (await findUp('pnpm-lock.yaml', { cwd: pkgPath })) { + return true; + } + + return false; +} + +async function isYarnPreferred(pkgPath: string) { + if (await pathExists(path.join(pkgPath, 'yarn.lock'))) { + return true; + } + + try { + if (typeof findWorkspaceRoot(pkgPath) === 'string') { + return true; + } + } catch (err) { } + + return false; +} + +const isNPMPreferred = (pkgPath: string) => { + return pathExists(path.join(pkgPath, 'package-lock.json')); +}; + +export async function findPreferredPM(pkgPath: string): Promise<{ name: string, multiplePMDetected: boolean }> { + const detectedPackageManagers: string[] = []; + + if (await isNPMPreferred(pkgPath)) { + detectedPackageManagers.push('npm'); + } + + if (await isYarnPreferred(pkgPath)) { + detectedPackageManagers.push('yarn'); + } + + if (await isPNPMPreferred(pkgPath)) { + detectedPackageManagers.push('pnpm'); + } + + const pmUsedForInstallation: { name: string } | null = await whichPM(pkgPath); + + if (pmUsedForInstallation && !detectedPackageManagers.includes(pmUsedForInstallation.name)) { + detectedPackageManagers.push(pmUsedForInstallation.name); + } + + const multiplePMDetected = detectedPackageManagers.length > 1; + + return { + name: detectedPackageManagers[0] || 'npm', + multiplePMDetected + }; +} diff --git a/extensions/npm/src/scriptHover.ts b/extensions/npm/src/scriptHover.ts index b1edc2752..48482660f 100644 --- a/extensions/npm/src/scriptHover.ts +++ b/extensions/npm/src/scriptHover.ts @@ -30,7 +30,7 @@ export function invalidateHoverScriptsCache(document?: TextDocument) { export class NpmScriptHoverProvider implements HoverProvider { - constructor(context: ExtensionContext) { + constructor(private context: ExtensionContext) { context.subscriptions.push(commands.registerCommand('npm.runScriptFromHover', this.runScriptFromHover, this)); context.subscriptions.push(commands.registerCommand('npm.debugScriptFromHover', this.debugScriptFromHover, this)); context.subscriptions.push(workspace.onDidChangeTextDocument((e) => { @@ -98,13 +98,13 @@ export class NpmScriptHoverProvider implements HoverProvider { return `${prefix}[${label}](command:${cmd}?${encodedArgs} "${tooltip}")`; } - public runScriptFromHover(args: any) { + public async runScriptFromHover(args: any) { let script = args.script; let documentUri = args.documentUri; let folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - let task = createTask(script, `run ${script}`, folder, documentUri); - tasks.executeTask(task); + let task = await createTask(this.context, script, `run ${script}`, folder, documentUri); + await tasks.executeTask(task); } } @@ -113,7 +113,7 @@ export class NpmScriptHoverProvider implements HoverProvider { let documentUri = args.documentUri; let folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - startDebugging(script, dirname(documentUri.fsPath), folder); + startDebugging(this.context, script, dirname(documentUri.fsPath), folder); } } } diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 02075cf12..fac16a396 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -5,13 +5,14 @@ import { TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, - DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem + DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem, window, Position, ExtensionContext, env } from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; import * as minimatch from 'minimatch'; import * as nls from 'vscode-nls'; import { JSONVisitor, visit, ParseErrorCode } from 'jsonc-parser'; +import { findPreferredPM } from './preferred-pm'; const localize = nls.loadMessageBundle(); @@ -27,20 +28,35 @@ export interface FolderTaskItem extends QuickPickItem { type AutoDetect = 'on' | 'off'; -let cachedTasks: Task[] | undefined = undefined; +let cachedTasks: TaskWithLocation[] | undefined = undefined; const INSTALL_SCRIPT = 'install'; +export interface TaskLocation { + document: Uri, + line: Position +} + +export interface TaskWithLocation { + task: Task, + location?: TaskLocation +} + export class NpmTaskProvider implements TaskProvider { - constructor() { + constructor(private context: ExtensionContext) { } - public provideTasks() { - return provideNpmScripts(); + get tasksWithLocation(): Promise { + return provideNpmScripts(this.context); } - public resolveTask(_task: Task): Task | undefined { + public async provideTasks() { + const tasks = await provideNpmScripts(this.context); + return tasks.map(task => task.task); + } + + public resolveTask(_task: Task): Promise | undefined { const npmTask = (_task.definition).script; if (npmTask) { const kind: NpmTaskDefinition = (_task.definition); @@ -54,7 +70,7 @@ export class NpmTaskProvider implements TaskProvider { } else { packageJsonUri = _task.scope.uri.with({ path: _task.scope.uri.path + '/package.json' }); } - return createTask(kind, `${kind.script === INSTALL_SCRIPT ? '' : 'run '}${kind.script}`, _task.scope, packageJsonUri); + return createTask(this.context, kind, `${kind.script === INSTALL_SCRIPT ? '' : 'run '}${kind.script}`, _task.scope, packageJsonUri); } return undefined; } @@ -107,8 +123,27 @@ export function isWorkspaceFolder(value: any): value is WorkspaceFolder { return value && typeof value !== 'number'; } -export function getPackageManager(folder: Uri): string { - return workspace.getConfiguration('npm', folder).get('packageManager', 'npm'); +export async function getPackageManager(extensionContext: ExtensionContext, folder: Uri): Promise { + let packageManagerName = workspace.getConfiguration('npm', folder).get('packageManager', 'npm'); + + if (packageManagerName === 'auto') { + const { name, multiplePMDetected } = await findPreferredPM(folder.fsPath); + packageManagerName = name; + const neverShowWarning = 'npm.multiplePMWarning.neverShow'; + if (multiplePMDetected && !extensionContext.globalState.get(neverShowWarning)) { + const multiplePMWarning = localize('npm.multiplePMWarning', 'Using {0} as the preferred package manager. Found multiple lockfiles for {1}.', packageManagerName, folder.fsPath); + const neverShowAgain = localize('npm.multiplePMWarning.doNotShow', "Do not show again"); + const learnMore = localize('npm.multiplePMWarning.learnMore', "Learn more"); + window.showInformationMessage(multiplePMWarning, learnMore, neverShowAgain).then(result => { + switch (result) { + case neverShowAgain: extensionContext.globalState.update(neverShowWarning, true); break; + case learnMore: env.openExternal(Uri.parse('https://nodejs.dev/learn/the-package-lock-json-file')); + } + }); + } + } + + return packageManagerName; } export async function hasNpmScripts(): Promise { @@ -132,10 +167,10 @@ export async function hasNpmScripts(): Promise { } } -async function detectNpmScripts(): Promise { +async function detectNpmScripts(context: ExtensionContext): Promise { - let emptyTasks: Task[] = []; - let allTasks: Task[] = []; + let emptyTasks: TaskWithLocation[] = []; + let allTasks: TaskWithLocation[] = []; let visitedPackageJsonFiles: Set = new Set(); let folders = workspace.workspaceFolders; @@ -149,7 +184,7 @@ async function detectNpmScripts(): Promise { let paths = await workspace.findFiles(relativePattern, '**/{node_modules,.vscode-test}/**'); for (const path of paths) { if (!isExcluded(folder, path) && !visitedPackageJsonFiles.has(path.fsPath)) { - let tasks = await provideNpmScriptsForFolder(path); + let tasks = await provideNpmScriptsForFolder(context, path); visitedPackageJsonFiles.add(path.fsPath); allTasks.push(...tasks); } @@ -163,7 +198,7 @@ async function detectNpmScripts(): Promise { } -export async function detectNpmScriptsForFolder(folder: Uri): Promise { +export async function detectNpmScriptsForFolder(context: ExtensionContext, folder: Uri): Promise { let folderTasks: FolderTaskItem[] = []; @@ -174,9 +209,9 @@ export async function detectNpmScriptsForFolder(folder: Uri): Promise = new Set(); for (const path of paths) { if (!visitedPackageJsonFiles.has(path.fsPath)) { - let tasks = await provideNpmScriptsForFolder(path); + let tasks = await provideNpmScriptsForFolder(context, path); visitedPackageJsonFiles.add(path.fsPath); - folderTasks.push(...tasks.map(t => ({ label: t.name, task: t }))); + folderTasks.push(...tasks.map(t => ({ label: t.task.name, task: t.task }))); } } return folderTasks; @@ -185,9 +220,9 @@ export async function detectNpmScriptsForFolder(folder: Uri): Promise { +export async function provideNpmScripts(context: ExtensionContext): Promise { if (!cachedTasks) { - cachedTasks = await detectNpmScripts(); + cachedTasks = await detectNpmScripts(context); } return cachedTasks; } @@ -223,8 +258,8 @@ function isDebugScript(script: string): boolean { return match !== null; } -async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise { - let emptyTasks: Task[] = []; +async function provideNpmScriptsForFolder(context: ExtensionContext, packageJsonUri: Uri): Promise { + let emptyTasks: TaskWithLocation[] = []; let folder = workspace.getWorkspaceFolder(packageJsonUri); if (!folder) { @@ -235,11 +270,13 @@ async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise return emptyTasks; } - const result: Task[] = []; + const result: TaskWithLocation[] = []; const prePostScripts = getPrePostScripts(scripts); - Object.keys(scripts).forEach(each => { - const task = createTask(each, `run ${each}`, folder!, packageJsonUri, scripts![each]); + + for (const each of scripts.keys()) { + const scriptValue = scripts.get(each)!; + const task = await createTask(context, each, `run ${each}`, folder!, packageJsonUri, scriptValue.script); const lowerCaseTaskName = each.toLowerCase(); if (isBuildTask(lowerCaseTaskName)) { task.group = TaskGroup.Build; @@ -251,13 +288,14 @@ async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise } // todo@connor4312: all scripts are now debuggable, what is a 'debug script'? - if (isDebugScript(scripts![each])) { + if (isDebugScript(scriptValue.script)) { task.group = TaskGroup.Rebuild; // hack: use Rebuild group to tag debug scripts } - result.push(task); - }); + result.push({ task, location: scriptValue.location }); + } + // always add npm install (without a problem matcher) - result.push(createTask(INSTALL_SCRIPT, INSTALL_SCRIPT, folder, packageJsonUri, 'install dependencies from package', [])); + result.push({ task: await createTask(context, INSTALL_SCRIPT, INSTALL_SCRIPT, folder, packageJsonUri, 'install dependencies from package', []) }); return result; } @@ -268,7 +306,7 @@ export function getTaskName(script: string, relativePath: string | undefined) { return script; } -export function createTask(script: NpmTaskDefinition | string, cmd: string, folder: WorkspaceFolder, packageJsonUri: Uri, detail?: string, matcher?: any): Task { +export async function createTask(context: ExtensionContext, script: NpmTaskDefinition | string, cmd: string, folder: WorkspaceFolder, packageJsonUri: Uri, detail?: string, matcher?: any): Promise { let kind: NpmTaskDefinition; if (typeof script === 'string') { kind = { type: 'npm', script: script }; @@ -276,27 +314,27 @@ export function createTask(script: NpmTaskDefinition | string, cmd: string, fold kind = script; } - function getCommandLine(folder: WorkspaceFolder, cmd: string): string { - let packageManager = getPackageManager(folder.uri); + const packageManager = await getPackageManager(context, folder.uri); + async function getCommandLine(cmd: string): Promise { if (workspace.getConfiguration('npm', folder.uri).get('runSilent')) { return `${packageManager} --silent ${cmd}`; } return `${packageManager} ${cmd}`; } - function getRelativePath(folder: WorkspaceFolder, packageJsonUri: Uri): string { + function getRelativePath(packageJsonUri: Uri): string { let rootUri = folder.uri; let absolutePath = packageJsonUri.path.substring(0, packageJsonUri.path.length - 'package.json'.length); return absolutePath.substring(rootUri.path.length + 1); } - let relativePackageJson = getRelativePath(folder, packageJsonUri); + let relativePackageJson = getRelativePath(packageJsonUri); if (relativePackageJson.length) { - kind.path = getRelativePath(folder, packageJsonUri); + kind.path = relativePackageJson; } let taskName = getTaskName(kind.script, relativePackageJson); let cwd = path.dirname(packageJsonUri.fsPath); - const task = new Task(kind, folder, taskName, 'npm', new ShellExecution(getCommandLine(folder, cmd), { cwd: cwd }), matcher); + const task = new Task(kind, folder, taskName, 'npm', new ShellExecution(await getCommandLine(cmd), { cwd: cwd }), matcher); task.detail = detail; return task; } @@ -337,33 +375,22 @@ async function exists(file: string): Promise { }); } -async function readFile(file: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(file, (err, data) => { - if (err) { - reject(err); - } - resolve(data.toString()); - }); - }); -} - -export function runScript(script: string, document: TextDocument) { +export async function runScript(context: ExtensionContext, script: string, document: TextDocument) { let uri = document.uri; let folder = workspace.getWorkspaceFolder(uri); if (folder) { - let task = createTask(script, `run ${script}`, folder, uri); + let task = await createTask(context, script, `run ${script}`, folder, uri); tasks.executeTask(task); } } -export function startDebugging(scriptName: string, cwd: string, folder: WorkspaceFolder) { +export async function startDebugging(context: ExtensionContext, scriptName: string, cwd: string, folder: WorkspaceFolder) { const config: DebugConfiguration = { type: 'pwa-node', request: 'launch', name: `Debug ${scriptName}`, cwd, - runtimeExecutable: getPackageManager(folder.uri), + runtimeExecutable: await getPackageManager(context, folder.uri), runtimeArgs: [ 'run', scriptName, @@ -378,10 +405,11 @@ export function startDebugging(scriptName: string, cwd: string, folder: Workspac export type StringMap = { [s: string]: string; }; -async function findAllScripts(buffer: string): Promise { - let scripts: StringMap = {}; +async function findAllScripts(document: TextDocument, buffer: string): Promise> { + let scripts: Map = new Map(); let script: string | undefined = undefined; let inScripts = false; + let scriptOffset = 0; let visitor: JSONVisitor = { onError(_error: ParseErrorCode, _offset: number, _length: number) { @@ -395,17 +423,18 @@ async function findAllScripts(buffer: string): Promise { onLiteralValue(value: any, _offset: number, _length: number) { if (script) { if (typeof value === 'string') { - scripts[script] = value; + scripts.set(script, { script: value, location: { document: document.uri, line: document.positionAt(scriptOffset) } }); } script = undefined; } }, - onObjectProperty(property: string, _offset: number, _length: number) { + onObjectProperty(property: string, offset: number, _length: number) { if (property === 'scripts') { inScripts = true; } else if (inScripts && !script) { script = property; + scriptOffset = offset; } else { // nested object which is invalid, ignore the script script = undefined; } @@ -493,7 +522,7 @@ export function findScriptAtPosition(buffer: string, offset: number): string | u return foundScript; } -export async function getScripts(packageJsonUri: Uri): Promise { +export async function getScripts(packageJsonUri: Uri): Promise | undefined> { if (packageJsonUri.scheme !== 'file') { return undefined; @@ -505,8 +534,9 @@ export async function getScripts(packageJsonUri: Uri): Promise)", - "decreaseIndentPattern": "^(.*\\*\\/)?\\s*((\\})|(\\)+[;,])|(\\][;,])|\\b(else:)|\\b((end(if|for(each)?|while|switch)|break);))" + "decreaseIndentPattern": "^(.*\\*\\/)?\\s*((\\})|(\\)+[;,])|(\\][;,])|\\b(else:)|\\b((end(if|for(each)?|while|switch));))" }, "folding": { "markers": { diff --git a/extensions/rust/cgmanifest.json b/extensions/rust/cgmanifest.json index 5181efba1..932a3926f 100644 --- a/extensions/rust/cgmanifest.json +++ b/extensions/rust/cgmanifest.json @@ -4,14 +4,14 @@ "component": { "type": "git", "git": { - "name": "language-rust", - "repositoryUrl": "https://github.com/zargony/atom-language-rust", - "commitHash": "7d59e2ad79fbe5925bd2fd3bd3857bf9f421ff6f" + "name": "rust-syntax", + "repositoryUrl": "https://github.com/dustypomerleau/rust-syntax", + "commitHash": "19f9aa86c0850b98db175754f019a2e79413353e" } }, "license": "MIT", - "description": "The files syntaxes/rust.tmLanguage.json was derived from the Atom package https://atom.io/packages/language-rust.", - "version": "0.4.12" + "description": "A TextMate-style grammar for Rust.", + "version": "0.2.13" } ], "version": 1 diff --git a/extensions/rust/package.json b/extensions/rust/package.json index 660783881..431e9fa20 100644 --- a/extensions/rust/package.json +++ b/extensions/rust/package.json @@ -5,21 +5,32 @@ "version": "1.0.0", "publisher": "vscode", "license": "MIT", - "engines": { "vscode": "*" }, + "engines": { + "vscode": "*" + }, "scripts": { - "update-grammar": "node ../../build/npm/update-grammar.js zargony/atom-language-rust grammars/rust.cson ./syntaxes/rust.tmLanguage.json" + "update-grammar": "node ../../build/npm/update-grammar.js dustypomerleau/rust-syntax syntaxes/rust.tmLanguage.json ./syntaxes/rust.tmLanguage.json" }, "contributes": { - "languages": [{ - "id": "rust", - "extensions": [".rs"], - "aliases": ["Rust", "rust"], - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "rust", - "path": "./syntaxes/rust.tmLanguage.json", - "scopeName":"source.rust" - }] + "languages": [ + { + "id": "rust", + "extensions": [ + ".rs" + ], + "aliases": [ + "Rust", + "rust" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "rust", + "path": "./syntaxes/rust.tmLanguage.json", + "scopeName": "source.rust" + } + ] } -} \ No newline at end of file +} diff --git a/extensions/rust/syntaxes/rust.tmLanguage.json b/extensions/rust/syntaxes/rust.tmLanguage.json index 784bd8c9a..fdced0797 100644 --- a/extensions/rust/syntaxes/rust.tmLanguage.json +++ b/extensions/rust/syntaxes/rust.tmLanguage.json @@ -1,690 +1,1135 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/zargony/atom-language-rust/blob/master/grammars/rust.cson", + "This file has been converted from https://github.com/dustypomerleau/rust-syntax/blob/master/syntaxes/rust.tmLanguage.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/zargony/atom-language-rust/commit/7d59e2ad79fbe5925bd2fd3bd3857bf9f421ff6f", + "version": "https://github.com/dustypomerleau/rust-syntax/commit/19f9aa86c0850b98db175754f019a2e79413353e", "name": "Rust", "scopeName": "source.rust", "patterns": [ { - "comment": "Implementation", - "begin": "\\b(impl)\\b", - "end": "\\{", + "comment": "boxed slice literal", + "begin": "(<)(\\[)", "beginCaptures": { "1": { - "name": "storage.type.rust" + "name": "punctuation.brackets.angle.rust" + }, + "2": { + "name": "punctuation.brackets.square.rust" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.brackets.angle.rust" } }, "patterns": [ { - "include": "#block_comment" + "include": "#block-comments" }, { - "include": "#line_comment" + "include": "#comments" }, { - "include": "#sigils" + "include": "#gtypes" }, { - "include": "#mut" + "include": "#lvariables" }, { - "include": "#dyn" + "include": "#lifetimes" }, { - "include": "#ref_lifetime" + "include": "#punctuation" }, { - "include": "#core_types" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" - }, - { - "include": "#where" - }, - { - "name": "storage.type.rust", - "match": "\\bfor\\b" - }, - { - "include": "#type" + "include": "#types" } ] }, { - "include": "#block_doc_comment" - }, - { - "include": "#block_comment" - }, - { - "include": "#line_doc_comment" - }, - { - "include": "#line_comment" - }, - { - "comment": "Attribute", - "name": "meta.attribute.rust", - "begin": "#\\!?\\[", - "end": "\\]", - "patterns": [ - { - "include": "#string_literal" - }, - { - "include": "#block_doc_comment" - }, - { - "include": "#block_comment" - }, - { - "include": "#line_doc_comment" - }, - { - "include": "#line_comment" - } - ] - }, - { - "comment": "Single-quote string literal (character)", - "name": "string.quoted.single.rust", - "match": "b?'([^'\\\\]|\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.))'" - }, - { - "include": "#string_literal" - }, - { - "include": "#raw_string_literal" - }, - { - "comment": "Floating point literal (fraction)", - "name": "constant.numeric.float.rust", - "match": "\\b[0-9][0-9_]*\\.[0-9][0-9_]*([eE][+-]?[0-9_]+)?(f32|f64)?\\b" - }, - { - "comment": "Floating point literal (exponent)", - "name": "constant.numeric.float.rust", - "match": "\\b[0-9][0-9_]*(\\.[0-9][0-9_]*)?[eE][+-]?[0-9_]+(f32|f64)?\\b" - }, - { - "comment": "Floating point literal (typed)", - "name": "constant.numeric.float.rust", - "match": "\\b[0-9][0-9_]*(\\.[0-9][0-9_]*)?([eE][+-]?[0-9_]+)?(f32|f64)\\b" - }, - { - "comment": "Integer literal (decimal)", - "name": "constant.numeric.integer.decimal.rust", - "match": "\\b[0-9][0-9_]*([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Integer literal (hexadecimal)", - "name": "constant.numeric.integer.hexadecimal.rust", - "match": "\\b0x[a-fA-F0-9_]+([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Integer literal (octal)", - "name": "constant.numeric.integer.octal.rust", - "match": "\\b0o[0-7_]+([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Integer literal (binary)", - "name": "constant.numeric.integer.binary.rust", - "match": "\\b0b[01_]+([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Static storage modifier", - "name": "storage.modifier.static.rust", - "match": "\\bstatic\\b" - }, - { - "comment": "Boolean constant", - "name": "constant.language.boolean.rust", - "match": "\\b(true|false)\\b" - }, - { - "comment": "Control keyword", - "name": "keyword.control.rust", - "match": "\\b(async|await|break|continue|else|if|in|for|loop|match|return|try|while)\\b" - }, - { - "comment": "Keyword", - "name": "keyword.other.rust", - "match": "\\b(crate|extern|mod|let|ref|use|super|move)\\b" - }, - { - "comment": "Reserved keyword", - "name": "invalid.deprecated.rust", - "match": "\\b(abstract|alignof|become|do|final|macro|offsetof|override|priv|proc|pure|sizeof|typeof|virtual|yield)\\b" - }, - { - "include": "#unsafe" - }, - { - "include": "#sigils" - }, - { - "include": "#self" - }, - { - "include": "#mut" - }, - { - "include": "#dyn" - }, - { - "include": "#impl" - }, - { - "include": "#box" - }, - { - "include": "#lifetime" - }, - { - "include": "#ref_lifetime" - }, - { - "include": "#const" - }, - { - "include": "#pub" - }, - { - "comment": "Miscellaneous operator", - "name": "keyword.operator.misc.rust", - "match": "(=>|::|\\bas\\b)" - }, - { - "comment": "Comparison operator", - "name": "keyword.operator.comparison.rust", - "match": "(&&|\\|\\||==|!=)" - }, - { - "comment": "Assignment operator", - "name": "keyword.operator.assignment.rust", - "match": "(\\+=|-=|/=|\\*=|%=|\\^=|&=|\\|=|<<=|>>=|=)" - }, - { - "comment": "Arithmetic operator", - "name": "keyword.operator.arithmetic.rust", - "match": "(!|\\+|-|/|\\*|%|\\^|&|\\||<<|>>)" - }, - { - "comment": "Comparison operator (second group because of regex precedence)", - "name": "keyword.operator.comparison.rust", - "match": "(<=|>=|<|>)" - }, - { - "include": "#core_types" - }, - { - "include": "#core_vars" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "comment": "Built-in macro", - "name": "support.function.builtin.rust", - "match": "\\b(macro_rules|compile_error|format_args|env|option_env|concat_idents|concat|line|column|file|stringify|include|include_str|include_bytes|module_path|cfg)!" - }, - { - "comment": "Core macro", - "name": "support.function.core.rust", - "match": "\\b(panic|assert|assert_eq|assert_ne|debug_assert|debug_assert_eq|debug_assert_ne|try|write|writeln|unreachable|unimplemented)!" - }, - { - "comment": "Standard library macro", - "name": "support.function.std.rust", - "match": "\\b(format|print|println|eprint|eprintln|select|vec)!" - }, - { - "comment": "Logging macro", - "name": "support.function.log.rust", - "match": "\\b(log|error|warn|info|debug|trace|log_enabled)!" - }, - { - "comment": "Invokation of a macro", - "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*\\!)\\s*[({\\[]", + "comment": "macro type metavariables", + "name": "meta.macro.metavariable.type.rust", + "match": "(\\$)((crate)|([A-Z][A-Za-z0-9_]*))((:)(block|expr|ident|item|lifetime|literal|meta|pat|path|stmt|tt|ty|vis))?", "captures": { "1": { + "name": "keyword.operator.macro.dollar.rust" + }, + "3": { + "name": "keyword.other.crate.rust" + }, + "4": { + "name": "entity.name.type.metavariable.rust" + }, + "6": { + "name": "keyword.operator.key-value.rust" + }, + "7": { + "name": "variable.other.metavariable.specifier.rust" + } + }, + "patterns": [ + { + "include": "#keywords" + } + ] + }, + { + "comment": "macro metavariables", + "name": "meta.macro.metavariable.rust", + "match": "(\\$)([a-z][A-Za-z0-9_]*)((:)(block|expr|ident|item|lifetime|literal|meta|pat|path|stmt|tt|ty|vis))?", + "captures": { + "1": { + "name": "keyword.operator.macro.dollar.rust" + }, + "2": { + "name": "variable.other.metavariable.name.rust" + }, + "4": { + "name": "keyword.operator.key-value.rust" + }, + "5": { + "name": "variable.other.metavariable.specifier.rust" + } + }, + "patterns": [ + { + "include": "#keywords" + } + ] + }, + { + "comment": "macro rules", + "name": "meta.macro.rules.rust", + "match": "\\b(macro_rules!)\\s+(([a-z0-9_]+)|([A-Z][a-z0-9_]*))\\s+(\\{)", + "captures": { + "1": { + "name": "entity.name.function.macro.rules.rust" + }, + "3": { "name": "entity.name.function.macro.rust" + }, + "4": { + "name": "entity.name.type.macro.rust" + }, + "5": { + "name": "punctuation.brackets.curly.rust" } } }, { - "comment": "Function call", - "match": "\\b([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)\\s*\\(", + "comment": "attributes", + "name": "meta.attribute.rust", + "begin": "(#)(\\!?)(\\[)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.attribute.rust" + }, + "2": { + "name": "keyword.operator.attribute.inner.rust" + }, + "3": { + "name": "punctuation.brackets.attribute.rust" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.brackets.attribute.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#gtypes" + }, + { + "include": "#types" + } + ] + }, + { + "comment": "modules", + "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)", "captures": { "1": { - "name": "entity.name.function.rust" + "name": "keyword.control.rust" + }, + "2": { + "name": "entity.name.module.rust" } } }, { - "comment": "Function call with type parameters", - "begin": "\\b([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)\\s*(::)(?=\\s*<.*>\\s*\\()", - "end": "\\(", - "captures": { - "1": { - "name": "entity.name.function.rust" - }, - "2": { - "name": "keyword.operator.misc.rust" - } - }, - "patterns": [ - { - "include": "#type_params" - } - ] - }, - { - "comment": "Function definition", - "begin": "\\b(fn)\\s+([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)", - "end": "[\\{;]", + "comment": "external crate imports", + "name": "meta.import.rust", + "begin": "\\b(extern)\\s+(crate)", "beginCaptures": { "1": { - "name": "keyword.other.fn.rust" + "name": "keyword.control.rust" }, "2": { - "name": "entity.name.function.rust" + "name": "keyword.other.crate.rust" } }, - "patterns": [ - { - "include": "#block_comment" - }, - { - "include": "#line_comment" - }, - { - "include": "#sigils" - }, - { - "include": "#self" - }, - { - "include": "#mut" - }, - { - "include": "#dyn" - }, - { - "include": "#impl" - }, - { - "include": "#ref_lifetime" - }, - { - "include": "#core_types" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" - }, - { - "include": "#const" - }, - { - "include": "#where" - }, - { - "include": "#unsafe" - }, - { - "comment": "Function arguments", - "match": "\bfn\b", - "name": "keyword.other.fn.rust" - } - ] - }, - { - "comment": "Type declaration", - "begin": "\\b(enum|struct|trait|union)\\s+([a-zA-Z_][a-zA-Z0-9_]*)", - "end": "[\\{\\(;]", - "beginCaptures": { - "1": { - "name": "storage.type.rust" - }, - "2": { - "name": "entity.name.type.rust" - } - }, - "patterns": [ - { - "include": "#block_comment" - }, - { - "include": "#line_comment" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" - }, - { - "include": "#core_types" - }, - { - "include": "#pub" - }, - { - "include": "#where" - } - ] - }, - { - "comment": "Type alias", - "begin": "\\b(type)\\s+([a-zA-Z_][a-zA-Z0-9_]*)", "end": ";", - "beginCaptures": { - "1": { - "name": "storage.type.rust" - }, - "2": { - "name": "entity.name.type.rust" + "endCaptures": { + "0": { + "name": "punctuation.semi.rust" } }, "patterns": [ { - "include": "#block_comment" + "include": "#block-comments" }, { - "include": "#line_comment" + "include": "#comments" }, { - "include": "#sigils" + "include": "#keywords" }, { - "include": "#mut" - }, - { - "include": "#dyn" - }, - { - "include": "#impl" - }, - { - "include": "#lifetime" - }, - { - "include": "#ref_lifetime" - }, - { - "include": "#core_types" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" + "include": "#punctuation" } ] + }, + { + "comment": "use statements", + "name": "meta.use.rust", + "begin": "\\b(use)\\s", + "beginCaptures": { + "1": { + "name": "keyword.control.rust" + } + }, + "end": ";", + "endCaptures": { + "0": { + "name": "punctuation.semi.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#types" + }, + { + "include": "#lvariables" + } + ] + }, + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#lvariables" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#types" + }, + { + "include": "#keywords" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#variables" } ], "repository": { - "block_doc_comment": { - "comment": "Block documentation comment", - "name": "comment.block.documentation.rust", - "begin": "/\\*[\\*!](?![\\*/])", - "end": "\\*/", + "comments": { "patterns": [ { - "include": "#block_doc_comment" + "comment": "documentation comments", + "name": "comment.line.documentation.rust", + "match": "^\\s*///.*" }, { - "include": "#block_comment" + "comment": "line comments", + "name": "comment.line.double-slash.rust", + "match": "\\s*//.*" } ] }, - "block_comment": { - "comment": "Block comment", - "name": "comment.block.rust", - "begin": "/\\*", - "end": "\\*/", + "block-comments": { "patterns": [ { - "include": "#block_doc_comment" + "comment": "block comments", + "name": "comment.block.rust", + "begin": "/\\*(?!\\*)", + "end": "\\*/", + "patterns": [ + { + "include": "#block-comments" + } + ] }, { - "include": "#block_comment" + "comment": "block documentation comments", + "name": "comment.block.documentation.rust", + "begin": "/\\*\\*", + "end": "\\*/", + "patterns": [ + { + "include": "#block-comments" + } + ] } ] }, - "line_doc_comment": { - "comment": "Single-line documentation comment", - "name": "comment.line.documentation.rust", - "begin": "//[!/](?=[^/])", - "end": "$" + "constants": { + "patterns": [ + { + "comment": "ALL CAPS constants", + "name": "constant.other.caps.rust", + "match": "\\b[A-Z]{2}[A-Z0-9_]*\\b" + }, + { + "comment": "constant declarations", + "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "captures": { + "1": { + "name": "keyword.control.rust" + }, + "2": { + "name": "constant.other.caps.rust" + } + } + }, + { + "comment": "decimal integers and floats", + "name": "constant.numeric.decimal.rust", + "match": "\\b\\d[\\d_]*(\\.?)[\\d_]*(?:(E)([+-])([\\d_]+))?(f32|f64|i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "punctuation.separator.dot.decimal.rust" + }, + "2": { + "name": "keyword.operator.exponent.rust" + }, + "3": { + "name": "keyword.operator.exponent.sign.rust" + }, + "4": { + "name": "constant.numeric.decimal.exponent.mantissa.rust" + }, + "5": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "hexadecimal integers", + "name": "constant.numeric.hex.rust", + "match": "\\b0x[\\da-fA-F_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "octal integers", + "name": "constant.numeric.oct.rust", + "match": "\\b0o[0-7_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "binary integers", + "name": "constant.numeric.bin.rust", + "match": "\\b0b[01_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "booleans", + "name": "constant.language.bool.rust", + "match": "\\btrue|false\\b" + } + ] }, - "line_comment": { - "comment": "Single-line comment", - "name": "comment.line.double-slash.rust", - "begin": "//", - "end": "$" - }, - "escaped_character": { + "escapes": { + "comment": "escapes: ASCII, byte, Unicode, quote, regex", "name": "constant.character.escape.rust", - "match": "\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)" - }, - "string_literal": { - "comment": "Double-quote string literal", - "name": "string.quoted.double.rust", - "begin": "b?\"", - "end": "\"", - "patterns": [ - { - "include": "#escaped_character" - } - ] - }, - "raw_string_literal": { - "comment": "Raw double-quote string literal", - "name": "string.quoted.double.raw.rust", - "begin": "b?r(#*)\"", - "end": "\"\\1" - }, - "sigils": { - "comment": "Sigil", - "name": "keyword.operator.sigil.rust", - "match": "[&*](?=[a-zA-Z0-9_\\(\\[\\|\\\"]+)" - }, - "self": { - "comment": "Self variable", - "name": "variable.language.rust", - "match": "\\bself\\b" - }, - "mut": { - "comment": "Mutable storage modifier", - "name": "storage.modifier.mut.rust", - "match": "\\bmut\\b" - }, - "dyn": { - "comment": "Dynamic modifier", - "name": "storage.modifier.dyn.rust", - "match": "\\bdyn\\b" - }, - "impl": { - "comment": "Existential type modifier", - "name": "storage.modifier.impl.rust", - "match": "\\bimpl\\b" - }, - "box": { - "comment": "Box storage modifier", - "name": "storage.modifier.box.rust", - "match": "\\bbox\\b" - }, - "const": { - "comment": "Const storage modifier", - "name": "storage.modifier.const.rust", - "match": "\\bconst\\b" - }, - "pub": { - "comment": "Visibility modifier", - "name": "storage.modifier.visibility.rust", - "match": "\\bpub\\b" - }, - "unsafe": { - "comment": "Unsafe code keyword", - "name": "keyword.other.unsafe.rust", - "match": "\\bunsafe\\b" - }, - "where": { - "comment": "Generic where clause", - "name": "keyword.other.where.rust", - "match": "\\bwhere\\b" - }, - "lifetime": { - "comment": "Named lifetime", - "name": "storage.modifier.lifetime.rust", - "match": "'([a-zA-Z_][a-zA-Z0-9_]*)\\b", + "match": "(\\\\)(?:(?:(x[0-7][0-7a-fA-F])|(u(\\{)[\\da-fA-F]{4,6}(\\}))|.))", "captures": { "1": { - "name": "entity.name.lifetime.rust" - } - } - }, - "ref_lifetime": { - "comment": "Reference with named lifetime", - "match": "&('([a-zA-Z_][a-zA-Z0-9_]*))\\b", - "captures": { - "1": { - "name": "storage.modifier.lifetime.rust" + "name": "constant.character.escape.backslash.rust" }, "2": { - "name": "entity.name.lifetime.rust" + "name": "constant.character.escape.bit.rust" + }, + "3": { + "name": "constant.character.escape.unicode.rust" + }, + "4": { + "name": "constant.character.escape.unicode.punctuation.rust" + }, + "5": { + "name": "constant.character.escape.unicode.punctuation.rust" } } }, - "core_types": { - "comment": "Built-in/core type", - "name": "storage.type.core.rust", - "match": "\\b(bool|char|usize|isize|u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|f32|f64|str|Self|Option|Result)\\b" - }, - "core_vars": { - "comment": "Core type variant", - "name": "support.constant.core.rust", - "match": "\\b(Some|None|Ok|Err)\\b" - }, - "core_marker": { - "comment": "Core trait (marker)", - "name": "support.type.marker.rust", - "match": "\\b(Copy|Send|Sized|Sync)\\b" - }, - "core_traits": { - "comment": "Core trait", - "name": "support.type.core.rust", - "match": "\\b(Drop|Fn|FnMut|FnOnce|Clone|PartialEq|PartialOrd|Eq|Ord|AsRef|AsMut|Into|From|Default|Iterator|Extend|IntoIterator|DoubleEndedIterator|ExactSizeIterator)\\b" - }, - "std_types": { - "comment": "Standard library type", - "name": "storage.class.std.rust", - "match": "\\b(Box|String|Vec|Path|PathBuf)\\b" - }, - "std_traits": { - "comment": "Standard library trait", - "name": "support.type.std.rust", - "match": "\\b(ToOwned|ToString)\\b" - }, - "type": { - "comment": "A type", - "name": "entity.name.type.rust", - "match": "\\b([A-Za-z][_A-Za-z0-9]*|_[_A-Za-z0-9]+)\\b" - }, - "type_params": { - "comment": "Type parameters", - "name": "meta.type_params.rust", - "begin": "<(?![=<])", - "end": "(?", + "functions": { "patterns": [ { - "include": "#block_comment" + "comment": "pub as a function", + "match": "\\b(pub)(\\()", + "captures": { + "1": { + "name": "keyword.other.rust" + }, + "2": { + "name": "punctuation.brackets.round.rust" + } + } }, { - "include": "#line_comment" + "comment": "function definition", + "name": "meta.function.definition.rust", + "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))", + "beginCaptures": { + "1": { + "name": "keyword.control.fn.rust" + }, + "2": { + "name": "entity.name.function.rust" + }, + "4": { + "name": "punctuation.brackets.round.rust" + }, + "5": { + "name": "punctuation.brackets.angle.rust" + } + }, + "end": "\\{|;", + "endCaptures": { + "0": { + "name": "punctuation.brackets.curly.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] }, { - "include": "#sigils" + "comment": "function/method calls, chaining", + "name": "meta.function.call.rust", + "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.rust" + }, + "2": { + "name": "punctuation.brackets.round.rust" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.brackets.round.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] }, { - "include": "#mut" + "comment": "function/method calls with turbofish", + "name": "meta.function.call.rust", + "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(?=::<.*>\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.rust" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.brackets.round.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] + } + ] + }, + "keywords": { + "patterns": [ + { + "comment": "control flow keywords", + "name": "keyword.control.rust", + "match": "\\b(async|await|break|continue|do|else|for|if|loop|match|move|return|try|where|while|yield)\\b" }, { - "include": "#dyn" + "comment": "storage keywords", + "name": "storage.type.rust", + "match": "\\b(const|enum|extern|let|macro|mod|struct|trait|type)\\b" }, { - "include": "#impl" + "comment": "storage modifiers", + "name": "storage.modifier.rust", + "match": "\\b(abstract|static)\\b" }, { - "include": "#lifetime" + "comment": "other keywords", + "name": "keyword.other.rust", + "match": "\\b(as|become|box|dyn|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual)\\b" }, { - "include": "#core_types" + "comment": "fn", + "name": "keyword.other.fn.rust", + "match": "\\bfn\\b" }, { - "include": "#core_marker" + "comment": "crate", + "name": "keyword.other.crate.rust", + "match": "\\bcrate\\b" }, { - "include": "#core_traits" + "comment": "mut", + "name": "storage.modifier.mut.rust", + "match": "\\bmut\\b" }, { - "include": "#std_types" + "comment": "math operators", + "name": "keyword.operator.math.rust", + "match": "(([+%]|(\\*(?!\\w)))(?!=))|(-(?!>))|(/(?!/))" }, { - "include": "#std_traits" + "comment": "logical operators", + "name": "keyword.operator.logical.rust", + "match": "(\\^|\\||\\|\\||&&|<<|>>|!)(?!=)" }, { - "include": "#type_params" + "comment": "logical AND, borrow references", + "name": "keyword.operator.borrow.and.rust", + "match": "&(?![&=])" + }, + { + "comment": "assignment operators", + "name": "keyword.operator.assignment.rust", + "match": "(-=|\\*=|/=|%=|\\^=|&=|\\|=|<<=|>>=)" + }, + { + "comment": "single equal", + "name": "keyword.operator.assignment.equal.rust", + "match": "(?])=(?!=|>)" + }, + { + "comment": "comparison operators", + "name": "keyword.operator.comparison.rust", + "match": "(=(=)?(?!>)|!=|<=|(?=)" + }, + { + "comment": "less than, greater than (special case)", + "match": "(?:\\b|(?:(\\))|(\\])|(\\})))[ \\t]+([<>])[ \\t]+(?:\\b|(?:(\\()|(\\[)|(\\{)))", + "captures": { + "1": { + "name": "punctuation.brackets.round.rust" + }, + "2": { + "name": "punctuation.brackets.square.rust" + }, + "3": { + "name": "punctuation.brackets.curly.rust" + }, + "4": { + "name": "keyword.operator.comparison.rust" + }, + "5": { + "name": "punctuation.brackets.round.rust" + }, + "6": { + "name": "punctuation.brackets.square.rust" + }, + "7": { + "name": "punctuation.brackets.curly.rust" + } + } + }, + { + "comment": "namespace operator", + "name": "keyword.operator.namespace.rust", + "match": "::" + }, + { + "comment": "dereference asterisk", + "match": "(\\*)(?=\\w+)", + "captures": { + "1": { + "name": "keyword.operator.dereference.rust" + } + } + }, + { + "comment": "subpattern binding", + "name": "keyword.operator.subpattern.rust", + "match": "@" + }, + { + "comment": "dot access", + "name": "keyword.operator.access.dot.rust", + "match": "\\.(?!\\.)" + }, + { + "comment": "ranges, range patterns", + "name": "keyword.operator.range.rust", + "match": "\\.{2}(=|\\.)?" + }, + { + "comment": "colon", + "name": "keyword.operator.key-value.rust", + "match": ":(?!:)" + }, + { + "comment": "dashrocket, skinny arrow", + "name": "keyword.operator.arrow.skinny.rust", + "match": "->" + }, + { + "comment": "hashrocket, fat arrow", + "name": "keyword.operator.arrow.fat.rust", + "match": "=>" + }, + { + "comment": "dollar macros", + "name": "keyword.operator.macro.dollar.rust", + "match": "\\$" + }, + { + "comment": "question mark operator, questionably sized, macro kleene matcher", + "name": "keyword.operator.question.rust", + "match": "\\?" + } + ] + }, + "interpolations": { + "comment": "curly brace interpolations", + "name": "meta.interpolation.rust", + "match": "({)[^\"{}]*(})", + "captures": { + "1": { + "name": "punctuation.definition.interpolation.rust" + }, + "2": { + "name": "punctuation.definition.interpolation.rust" + } + } + }, + "lifetimes": { + "patterns": [ + { + "comment": "named lifetime parameters", + "match": "(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b", + "captures": { + "1": { + "name": "punctuation.definition.lifetime.rust" + }, + "2": { + "name": "entity.name.type.lifetime.rust" + } + } + }, + { + "comment": "borrowing references to named lifetimes", + "match": "(\\&)(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b", + "captures": { + "1": { + "name": "keyword.operator.borrow.rust" + }, + "2": { + "name": "punctuation.definition.lifetime.rust" + }, + "3": { + "name": "entity.name.type.lifetime.rust" + } + } + } + ] + }, + "macros": { + "patterns": [ + { + "comment": "macros", + "name": "meta.macro.rust", + "match": "(([a-z_][A-Za-z0-9_]*!)|([A-Z_][A-Za-z0-9_]*!))", + "captures": { + "2": { + "name": "entity.name.function.macro.rust" + }, + "3": { + "name": "entity.name.type.macro.rust" + } + } + } + ] + }, + "namespaces": { + "patterns": [ + { + "comment": "namespace (non-type, non-function path segment)", + "match": "(?", + "endCaptures": { + "0": { + "name": "punctuation.brackets.angle.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#lifetimes" + }, + { + "include": "#punctuation" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] + }, + { + "comment": "primitive types", + "name": "entity.name.type.primitive.rust", + "match": "\\b(bool|char|str)\\b" + }, + { + "comment": "trait declarations", + "match": "\\b(trait)\\s+([A-Z][A-Za-z0-9]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.trait.rust" + } + } + }, + { + "comment": "struct declarations", + "match": "\\b(struct)\\s+([A-Z][A-Za-z0-9]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.struct.rust" + } + } + }, + { + "comment": "enum declarations", + "match": "\\b(enum)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.enum.rust" + } + } + }, + { + "comment": "type declarations", + "match": "\\b(type)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.declaration.rust" + } + } + }, + { + "comment": "types", + "name": "entity.name.type.rust", + "match": "\\b[A-Z][A-Za-z0-9]*\\b(?!!)" + } + ] + }, + "gtypes": { + "patterns": [ + { + "comment": "option types", + "name": "entity.name.type.option.rust", + "match": "\\b(Some|None)\\b" + }, + { + "comment": "result types", + "name": "entity.name.type.result.rust", + "match": "\\b(Ok|Err)\\b" + } + ] + }, + "punctuation": { + "patterns": [ + { + "comment": "comma", + "name": "punctuation.comma.rust", + "match": "," + }, + { + "comment": "curly braces", + "name": "punctuation.brackets.curly.rust", + "match": "[{}]" + }, + { + "comment": "parentheses, round brackets", + "name": "punctuation.brackets.round.rust", + "match": "[()]" + }, + { + "comment": "semicolon", + "name": "punctuation.semi.rust", + "match": ";" + }, + { + "comment": "square brackets", + "name": "punctuation.brackets.square.rust", + "match": "[\\[\\]]" + }, + { + "comment": "angle brackets", + "name": "punctuation.brackets.angle.rust", + "match": "(?]" + } + ] + }, + "strings": { + "patterns": [ + { + "comment": "double-quoted strings and byte strings", + "name": "string.quoted.double.rust", + "begin": "(b?)(\")", + "beginCaptures": { + "1": { + "name": "string.quoted.byte.raw.rust" + }, + "2": { + "name": "punctuation.definition.string.rust" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.rust" + } + }, + "patterns": [ + { + "include": "#escapes" + }, + { + "include": "#interpolations" + } + ] + }, + { + "comment": "double-quoted raw strings and raw byte strings", + "name": "string.quoted.double.rust", + "begin": "(b?r)(#*)(\")", + "beginCaptures": { + "1": { + "name": "string.quoted.byte.raw.rust" + }, + "2": { + "name": "punctuation.definition.string.raw.rust" + }, + "3": { + "name": "punctuation.definition.string.rust" + } + }, + "end": "(\")(\\2)", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.rust" + }, + "2": { + "name": "punctuation.definition.string.raw.rust" + } + } + }, + { + "comment": "characters and bytes", + "name": "string.quoted.single.char.rust", + "begin": "(b)?(')", + "beginCaptures": { + "1": { + "name": "string.quoted.byte.raw.rust" + }, + "2": { + "name": "punctuation.definition.char.rust" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.char.rust" + } + }, + "patterns": [ + { + "include": "#escapes" + } + ] + } + ] + }, + "lvariables": { + "patterns": [ + { + "comment": "self", + "name": "variable.language.self.rust", + "match": "\\b[Ss]elf\\b" + }, + { + "comment": "super", + "name": "variable.language.super.rust", + "match": "\\bsuper\\b" + } + ] + }, + "variables": { + "patterns": [ + { + "comment": "variables", + "name": "variable.other.rust", + "match": "\\b(?", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -56,13 +100,13 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -88,7 +132,18 @@ } }, { - "c": ": ", + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -110,7 +165,18 @@ } }, { - "c": "{ }", + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -121,14 +187,25 @@ } }, { - "c": "impl", - "t": "source.rust storage.type.rust", + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "impl", + "t": "source.rust keyword.other.rust", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -154,8 +231,52 @@ } }, { - "c": "", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -177,13 +298,13 @@ }, { "c": "for", - "t": "source.rust storage.type.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -221,13 +342,13 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -253,7 +374,18 @@ } }, { - "c": ": ", + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -275,7 +407,18 @@ } }, { - "c": "{ }", + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -286,14 +429,25 @@ } }, { - "c": "impl", - "t": "source.rust storage.type.rust", + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "impl", + "t": "source.rust keyword.other.rust", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -319,8 +473,52 @@ } }, { - "c": "", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -342,13 +540,13 @@ }, { "c": "for", - "t": "source.rust storage.type.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -375,7 +573,7 @@ }, { "c": "{", - "t": "source.rust", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -397,18 +595,18 @@ }, { "c": "fn", - "t": "source.rust keyword.other.fn.rust", + "t": "source.rust meta.function.definition.rust keyword.control.fn.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { "c": " ", - "t": "source.rust", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -419,7 +617,7 @@ }, { "c": "foo", - "t": "source.rust entity.name.function.rust", + "t": "source.rust meta.function.definition.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -429,8 +627,8 @@ } }, { - "c": "", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -440,8 +638,19 @@ } }, { - "c": " -> C", - "t": "source.rust", + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust meta.function.definition.rust punctuation.comma.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -450,9 +659,75 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "->", + "t": "source.rust meta.function.definition.rust keyword.operator.arrow.skinny.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "C", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, { "c": " ", - "t": "source.rust", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -463,18 +738,18 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust meta.function.definition.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " A: B", - "t": "source.rust", + "c": " ", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -484,7 +759,73 @@ } }, { - "c": " { }", + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust meta.function.definition.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.rust meta.function.definition.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -496,7 +837,18 @@ }, { "c": "}", - "t": "source.rust", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,13 +859,222 @@ }, { "c": "fn", - "t": "source.rust keyword.other.fn.rust", + "t": "source.rust meta.function.definition.rust keyword.control.fn.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "foo", + "t": "source.rust meta.function.definition.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "<", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust meta.function.definition.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "->", + "t": "source.rust meta.function.definition.rust keyword.operator.arrow.skinny.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "C", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "where", + "t": "source.rust meta.function.definition.rust keyword.control.rust", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust meta.function.definition.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.rust meta.function.definition.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { @@ -528,74 +1089,8 @@ } }, { - "c": "foo", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "", - "t": "source.rust meta.type_params.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " -> C", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "where", - "t": "source.rust keyword.other.where.rust", - "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" - } - }, - { - "c": " A: B", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "{ }", - "t": "source.rust", + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -628,6 +1123,28 @@ }, { "c": "Foo", + "t": "source.rust entity.name.type.struct.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", "t": "source.rust entity.name.type.rust", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -638,8 +1155,30 @@ } }, { - "c": "", - "t": "source.rust meta.type_params.rust", + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -661,17 +1200,17 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " A: B", + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -682,7 +1221,29 @@ } }, { - "c": "{ }", + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -692,6 +1253,50 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "trait", "t": "source.rust storage.type.rust", @@ -716,6 +1321,28 @@ }, { "c": "Foo", + "t": "source.rust entity.name.type.trait.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", "t": "source.rust entity.name.type.rust", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -726,8 +1353,8 @@ } }, { - "c": "", - "t": "source.rust meta.type_params.rust", + "c": ",", + "t": "source.rust punctuation.comma.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -737,7 +1364,29 @@ } }, { - "c": " : C", + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -747,6 +1396,39 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "C", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, { "c": " ", "t": "source.rust", @@ -760,17 +1442,17 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " A: B", + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -781,7 +1463,29 @@ } }, { - "c": "{ }", + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -790,5 +1494,49 @@ "light_vs": "default: #000000", "hc_black": "default: #FFFFFF" } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } } ] \ No newline at end of file diff --git a/extensions/rust/test/colorize-results/test_rs.json b/extensions/rust/test/colorize-results/test_rs.json index 977fe6bd2..82615d435 100644 --- a/extensions/rust/test/colorize-results/test_rs.json +++ b/extensions/rust/test/colorize-results/test_rs.json @@ -1,18 +1,18 @@ [ { "c": "use", - "t": "source.rust keyword.other.rust", + "t": "source.rust meta.use.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " std", - "t": "source.rust", + "c": " ", + "t": "source.rust meta.use.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -21,9 +21,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "std", + "t": "source.rust meta.use.rust entity.name.namespace.rust", + "r": { + "dark_plus": "entity.name.namespace: #4EC9B0", + "light_plus": "entity.name.namespace: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.namespace: #4EC9B0" + } + }, { "c": "::", - "t": "source.rust keyword.operator.misc.rust", + "t": "source.rust meta.use.rust keyword.operator.namespace.rust", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -33,8 +44,19 @@ } }, { - "c": "io;", - "t": "source.rust", + "c": "io", + "t": "source.rust meta.use.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust meta.use.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -45,18 +67,18 @@ }, { "c": "fn", - "t": "source.rust keyword.other.fn.rust", + "t": "source.rust meta.function.definition.rust keyword.control.fn.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { "c": " ", - "t": "source.rust", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -67,7 +89,7 @@ }, { "c": "main", - "t": "source.rust entity.name.function.rust", + "t": "source.rust meta.function.definition.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -77,8 +99,30 @@ } }, { - "c": "() {", - "t": "source.rust", + "c": "()", + "t": "source.rust meta.function.definition.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.rust meta.function.definition.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -100,18 +144,18 @@ }, { "c": "println!", - "t": "source.rust support.function.std.rust", + "t": "source.rust meta.macro.rust entity.name.function.macro.rust", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.rust", + "t": "source.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -121,7 +165,18 @@ } }, { - "c": "\"Guess the number!\"", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Guess the number!", "t": "source.rust string.quoted.double.rust", "r": { "dark_plus": "string: #CE9178", @@ -132,8 +187,30 @@ } }, { - "c": ");", - "t": "source.rust", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,18 +232,18 @@ }, { "c": "println!", - "t": "source.rust support.function.std.rust", + "t": "source.rust meta.macro.rust entity.name.function.macro.rust", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.rust", + "t": "source.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -176,7 +253,18 @@ } }, { - "c": "\"Please input your guess.\"", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Please input your guess.", "t": "source.rust string.quoted.double.rust", "r": { "dark_plus": "string: #CE9178", @@ -187,8 +275,30 @@ } }, { - "c": ");", - "t": "source.rust", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -210,13 +320,13 @@ }, { "c": "let", - "t": "source.rust keyword.other.rust", + "t": "source.rust storage.type.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" } }, { @@ -242,7 +352,29 @@ } }, { - "c": " guess ", + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "guess", + "t": "source.rust variable.other.rust", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -254,7 +386,7 @@ }, { "c": "=", - "t": "source.rust keyword.operator.assignment.rust", + "t": "source.rust keyword.operator.assignment.equal.rust", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -276,18 +408,18 @@ }, { "c": "String", - "t": "source.rust storage.class.std.rust", + "t": "source.rust entity.name.type.rust", "r": { - "dark_plus": "storage: #569CD6", - "light_plus": "storage: #0000FF", - "dark_vs": "storage: #569CD6", - "light_vs": "storage: #0000FF", - "hc_black": "storage: #569CD6" + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" } }, { "c": "::", - "t": "source.rust keyword.operator.misc.rust", + "t": "source.rust keyword.operator.namespace.rust", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -298,139 +430,7 @@ }, { "c": "new", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "();", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " io", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "::", - "t": "source.rust keyword.operator.misc.rust", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "stdin", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "().", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "read_line", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "(", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "&", - "t": "source.rust keyword.operator.sigil.rust", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "mut", - "t": "source.rust storage.modifier.mut.rust", - "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" - } - }, - { - "c": " guess)", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " .", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "ok", - "t": "source.rust entity.name.function.rust", + "t": "source.rust meta.function.call.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -441,6 +441,28 @@ }, { "c": "()", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -451,8 +473,41 @@ } }, { - "c": " .", - "t": "source.rust", + "c": "io", + "t": "source.rust entity.name.namespace.rust", + "r": { + "dark_plus": "entity.name.namespace: #4EC9B0", + "light_plus": "entity.name.namespace: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.namespace: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.rust keyword.operator.namespace.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "stdin", + "t": "source.rust meta.function.call.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -462,8 +517,19 @@ } }, { - "c": "expect", - "t": "source.rust entity.name.function.rust", + "c": ".", + "t": "source.rust keyword.operator.access.dot.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "read_line", + "t": "source.rust meta.function.call.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -474,6 +540,72 @@ }, { "c": "(", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "&", + "t": "source.rust meta.function.call.rust keyword.operator.borrow.and.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "mut", + "t": "source.rust meta.function.call.rust storage.modifier.mut.rust", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6" + } + }, + { + "c": " ", + "t": "source.rust meta.function.call.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "guess", + "t": "source.rust meta.function.call.rust variable.other.rust", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -484,8 +616,85 @@ } }, { - "c": "\"Failed to read line\"", - "t": "source.rust string.quoted.double.rust", + "c": ".", + "t": "source.rust keyword.operator.access.dot.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "ok", + "t": "source.rust meta.function.call.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.rust keyword.operator.access.dot.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "expect", + "t": "source.rust meta.function.call.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.rust meta.function.call.rust string.quoted.double.rust punctuation.definition.string.rust", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -495,8 +704,41 @@ } }, { - "c": ");", - "t": "source.rust", + "c": "Failed to read line", + "t": "source.rust meta.function.call.rust string.quoted.double.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.rust meta.function.call.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -518,18 +760,18 @@ }, { "c": "println!", - "t": "source.rust support.function.std.rust", + "t": "source.rust meta.macro.rust entity.name.function.macro.rust", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.rust", + "t": "source.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -539,7 +781,18 @@ } }, { - "c": "\"You guessed: {}\"", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "You guessed: ", "t": "source.rust string.quoted.double.rust", "r": { "dark_plus": "string: #CE9178", @@ -550,7 +803,40 @@ } }, { - "c": ", guess);", + "c": "{}", + "t": "source.rust string.quoted.double.rust meta.interpolation.rust punctuation.definition.interpolation.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -560,9 +846,42 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "guess", + "t": "source.rust variable.other.rust", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "}", - "t": "source.rust", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index 7b43da243..9635a6c51 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -3,6 +3,7 @@ "displayName": "%displayName%", "description": "%description%", "version": "1.0.0", + "enableProposedApi": true, "publisher": "vscode", "license": "MIT", "engines": { diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 0c0c9b85f..6bc5a573c 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -8,7 +8,8 @@ import * as pathUtils from 'path'; const FILE_LINE_REGEX = /^(\S.*):$/; const RESULT_LINE_REGEX = /^(\s+)(\d+)(:| )(\s+)(.*)$/; -const SEARCH_RESULT_SELECTOR = { language: 'search-result' }; +const ELISION_REGEX = /⟪ ([0-9]+) characters skipped ⟫/g; +const SEARCH_RESULT_SELECTOR = { language: 'search-result', exclusive: true }; const DIRECTIVES = ['# Query:', '# Flags:', '# Including:', '# Excluding:', '# ContextLines:']; const FLAGS = ['RegExp', 'CaseSensitive', 'IgnoreExcludeSettings', 'WordMatch']; @@ -80,12 +81,18 @@ export function activate(context: vscode.ExtensionContext) { return lineResult.allLocations; } - const translateRangeSidewaysBy = (r: vscode.Range, n: number) => - r.with({ start: new vscode.Position(r.start.line, Math.max(0, n - r.start.character)), end: new vscode.Position(r.end.line, Math.max(0, n - r.end.character)) }); + const location = lineResult.locations.find(l => l.originSelectionRange.contains(position)); + if (!location) { + return []; + } + const targetPos = new vscode.Position( + location.targetSelectionRange.start.line, + location.targetSelectionRange.start.character + (position.character - location.originSelectionRange.start.character) + ); return [{ - ...lineResult.location, - targetSelectionRange: translateRangeSidewaysBy(lineResult.location.targetSelectionRange!, position.character - 1) + ...location, + targetSelectionRange: new vscode.Range(targetPos, targetPos), }]; } }), @@ -93,7 +100,7 @@ export function activate(context: vscode.ExtensionContext) { vscode.languages.registerDocumentLinkProvider(SEARCH_RESULT_SELECTOR, { async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { return parseSearchResults(document, token) - .filter(({ type }) => type === 'file') + .filter(isFileLine) .map(({ location }) => ({ range: location.originSelectionRange!, target: location.targetUri })); } }), @@ -162,7 +169,7 @@ function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | u } type ParsedSearchFileLine = { type: 'file', location: vscode.LocationLink, allLocations: vscode.LocationLink[], path: string }; -type ParsedSearchResultLine = { type: 'result', location: vscode.LocationLink, isContext: boolean, prefixRange: vscode.Range }; +type ParsedSearchResultLine = { type: 'result', locations: Required[], isContext: boolean, prefixRange: vscode.Range }; type ParsedSearchResults = Array; const isFileLine = (line: ParsedSearchResultLine | ParsedSearchFileLine): line is ParsedSearchFileLine => line.type === 'file'; const isResultLine = (line: ParsedSearchResultLine | ParsedSearchFileLine): line is ParsedSearchResultLine => line.type === 'result'; @@ -211,17 +218,35 @@ function parseSearchResults(document: vscode.TextDocument, token?: vscode.Cancel const lineNumber = +_lineNumber - 1; const resultStart = (indentation + _lineNumber + seperator + resultIndentation).length; const metadataOffset = (indentation + _lineNumber + seperator).length; + const targetRange = new vscode.Range(Math.max(lineNumber - 3, 0), 0, lineNumber + 3, line.length); - const location: vscode.LocationLink = { - targetRange: new vscode.Range(Math.max(lineNumber - 3, 0), 0, lineNumber + 3, line.length), - targetSelectionRange: new vscode.Range(lineNumber, metadataOffset, lineNumber, metadataOffset), - targetUri: currentTarget, - originSelectionRange: new vscode.Range(i, resultStart, i, line.length), - }; + let lastEnd = resultStart; + let offset = 0; + let locations: Required[] = []; + ELISION_REGEX.lastIndex = resultStart; + for (let match: RegExpExecArray | null; (match = ELISION_REGEX.exec(line));) { + locations.push({ + targetRange, + targetSelectionRange: new vscode.Range(lineNumber, offset, lineNumber, offset), + targetUri: currentTarget, + originSelectionRange: new vscode.Range(i, lastEnd, i, ELISION_REGEX.lastIndex - match[0].length), + }); - currentTargetLocations?.push(location); + offset += (ELISION_REGEX.lastIndex - lastEnd - match[0].length) + Number(match[1]); + lastEnd = ELISION_REGEX.lastIndex; + } - links[i] = { type: 'result', location, isContext: seperator === ' ', prefixRange: new vscode.Range(i, 0, i, metadataOffset) }; + if (lastEnd < line.length) { + locations.push({ + targetRange, + targetSelectionRange: new vscode.Range(lineNumber, offset, lineNumber, offset), + targetUri: currentTarget, + originSelectionRange: new vscode.Range(i, lastEnd, i, line.length), + }); + } + + currentTargetLocations?.push(...locations); + links[i] = { type: 'result', locations, isContext: seperator === ' ', prefixRange: new vscode.Range(i, 0, i, metadataOffset) }; } } diff --git a/extensions/search-result/syntaxes/generateTMLanguage.js b/extensions/search-result/syntaxes/generateTMLanguage.js index fb74d3696..c0170e514 100644 --- a/extensions/search-result/syntaxes/generateTMLanguage.js +++ b/extensions/search-result/syntaxes/generateTMLanguage.js @@ -83,6 +83,7 @@ const scopes = { meta: 'meta.resultLine.search', metaSingleLine: 'meta.resultLine.singleLine.search', metaMultiLine: 'meta.resultLine.multiLine.search', + elision: 'comment meta.resultLine.elision', prefix: { meta: 'constant.numeric.integer meta.resultLinePrefix.search', metaContext: 'meta.resultLinePrefix.contextLinePrefix.search', @@ -220,6 +221,10 @@ const plainText = [ '4': { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaContext].join(' ') }, '5': { name: scopes.resultBlock.result.prefix.lineNumber }, } + }, + { + match: '⟪ [0-9]+ characters skipped ⟫', + name: [scopes.resultBlock.meta, scopes.resultBlock.result.elision].join(' '), } ]; diff --git a/extensions/search-result/syntaxes/searchResult.tmLanguage.json b/extensions/search-result/syntaxes/searchResult.tmLanguage.json index e2687fe8a..e319191c4 100644 --- a/extensions/search-result/syntaxes/searchResult.tmLanguage.json +++ b/extensions/search-result/syntaxes/searchResult.tmLanguage.json @@ -263,6 +263,10 @@ "name": "meta.resultLinePrefix.lineNumber.search" } } + }, + { + "match": "⟪ [0-9]+ characters skipped ⟫", + "name": "meta.resultBlock.search comment meta.resultLine.elision" } ], "repository": { diff --git a/extensions/sql/build/update-grammar.js b/extensions/sql/build/update-grammar.js new file mode 100644 index 000000000..7f95e256b --- /dev/null +++ b/extensions/sql/build/update-grammar.js @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +var updateGrammar = require('../../../build/npm/update-grammar'); +updateGrammar.update('microsoft/vscode-mssql', 'syntaxes/SQL.plist', './syntaxes/sql.tmLanguage.json', undefined, 'main'); + + diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index 110a300af..3f0ac384a 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/sql/package.json b/extensions/sql/package.json index 5063c9d73..437d114c4 100644 --- a/extensions/sql/package.json +++ b/extensions/sql/package.json @@ -5,21 +5,32 @@ "version": "1.0.0", "publisher": "vscode", "license": "MIT", - "engines": { "vscode": "*" }, + "engines": { + "vscode": "*" + }, "scripts": { - "update-grammar": "node ../../build/npm/update-grammar.js microsoft/vscode-mssql syntaxes/SQL.plist ./syntaxes/sql.tmLanguage.json" + "update-grammar": "node ./build/update-grammar.js" }, "contributes": { - "languages": [{ - "id": "sql", - "extensions": [ ".sql", ".dsql" ], - "aliases": [ "SQL" ], - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "sql", - "scopeName": "source.sql", - "path": "./syntaxes/sql.tmLanguage.json" - }] + "languages": [ + { + "id": "sql", + "extensions": [ + ".sql", + ".dsql" + ], + "aliases": [ + "SQL" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "sql", + "scopeName": "source.sql", + "path": "./syntaxes/sql.tmLanguage.json" + } + ] } } diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index 446d20081..76b4c39b5 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.tmLanguage.json @@ -17,7 +17,7 @@ "name": "text.bracketed" }, { - "match": "\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|alter|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference|automatic|autopilot|availability|availability_mode|backup_priority|base64|basic|batches|batchsize|before|between|bigint|binary|binding|bit|block|blocksize|bmk|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|cast|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|cleanup_policy|clear|clear_port|close|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\s+or\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|days|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare(\\s+cursor)?|decrypt|decrypt_a|decryption|default_database|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distinct|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|except|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|first_row|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hours|http|identity|identity_value|if|ifnull|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intersect|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minutes|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|restart|restore|restricted_user|resume|retaindays|retention|return|revert|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|schema|schemabinding|scoped|scroll|scroll_locks|sddl|secexpr|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|tran|transaction|transfer|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|waitfor|webmethod|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|windows|with|within|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|zone)\\b", + "match": "\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|add|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|alter|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference|automatic|autopilot|availability|availability_mode|backup_priority|base64|basic|batches|batchsize|before|between|bigint|binary|binding|bit|block|blocksize|bmk|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|cleanup_policy|clear|clear_port|close|clustered|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\s+or\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|days|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare|decrypt|decrypt_a|decryption|default_database|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distinct|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|except|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|first_row|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hours|http|identity|identity_value|if|ifnull|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|index|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intersect|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|matched|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minutes|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nonclustered|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|restart|restore|restricted_user|resume|retaindays|retention|return|revert|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|schema|schemabinding|scoped|scroll|scroll_locks|sddl|secexpr|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|tran|transaction|transfer|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|unique|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|waitfor|webmethod|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|windows|with|within|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|zone)\\b", "name": "keyword.other.sql" }, { @@ -131,7 +131,7 @@ "match": "(?xi)\n\n\t\t\t\t# normal stuff, capture 1\n\t\t\t\t \\b(bigint|bigserial|bit|boolean|box|bytea|cidr|circle|date|double\\sprecision|inet|int|integer|line|lseg|macaddr|money|oid|path|point|polygon|real|serial|smallint|sysdate|text)\\b\n\n\t\t\t\t# numeric suffix, capture 2 + 3i\n\t\t\t\t|\\b(bit\\svarying|character\\s(?:varying)?|tinyint|var\\schar|float|interval)\\((\\d+)\\)\n\n\t\t\t\t# optional numeric suffix, capture 4 + 5i\n\t\t\t\t|\\b(char|number|varchar\\d?)\\b(?:\\((\\d+)\\))?\n\n\t\t\t\t# special case, capture 6 + 7i + 8i\n\t\t\t\t|\\b(numeric|decimal)\\b(?:\\((\\d+),(\\d+)\\))?\n\n\t\t\t\t# special case, captures 9, 10i, 11\n\t\t\t\t|\\b(times?)\\b(?:\\((\\d+)\\))?(\\swith(?:out)?\\stime\\szone\\b)?\n\n\t\t\t\t# special case, captures 12, 13, 14i, 15\n\t\t\t\t|\\b(timestamp)(?:(s|tz))?\\b(?:\\((\\d+)\\))?(\\s(with|without)\\stime\\szone\\b)?\n\n\t\t\t" }, { - "match": "(?i:\\b((?:primary|foreign)\\s+key|references|on\\sdelete(\\s+cascade)?|check|constraint)\\b)", + "match": "(?i:\\b((?:primary|foreign)\\s+key|references|on\\sdelete(\\s+cascade)?|nocheck|check|constraint|collate|default)\\b)", "name": "storage.modifier.sql" }, { @@ -139,7 +139,7 @@ "name": "constant.numeric.sql" }, { - "match": "(?i:\\b(select(\\s+distinct)?|insert\\s+(ignore\\s+)?into|update|delete|from|set|where|group\\sby|or|like|and|union(\\s+all)?|having|order\\sby|limit|(inner|cross)\\s+join|join|straight_join|full\\s+outer\\s+join|(left|right)(\\s+outer)?\\s+join|natural(\\s+(left|right)(\\s+outer)?)?\\s+join)\\b)", + "match": "(?i:\\b(select(\\s+distinct)?|insert\\s+(ignore\\s+)?into|update|delete|from|set|where|group\\s+by|or|like|and|union(\\s+all)?|having|order\\s+by|limit|(inner|cross)\\s+join|join|straight_join|full\\s+outer\\s+join|(left|right)(\\s+outer)?\\s+join|natural(\\s+(left|right)(\\s+outer)?)?\\s+join)\\b)", "name": "keyword.other.DML.sql" }, { @@ -239,7 +239,7 @@ "name": "support.function.security.sql" }, { - "match": "(?i)\\b(ascii|char|charindex|concat|difference|format|left|len|lower|ltrim|nchar|patindex|quotename|replace|replicate|reverse|rtrim|soundex|space|str|string_agg|string_escape|string_split|stuff|substring|translate|trim|unicode|upper)\\b", + "match": "(?i)\\b(ascii|char|charindex|concat|difference|format|left|len|lower|ltrim|nchar|nodes|patindex|quotename|replace|replicate|reverse|right|rtrim|soundex|space|str|string_agg|string_escape|string_split|stuff|substring|translate|trim|unicode|upper)\\b", "name": "support.function.string.sql" }, { @@ -516,4 +516,4 @@ ] } } -} +} \ No newline at end of file diff --git a/extensions/theme-abyss/package.json b/extensions/theme-abyss/package.json index bdeb6a2a6..9543824f3 100644 --- a/extensions/theme-abyss/package.json +++ b/extensions/theme-abyss/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Abyss", + "id": "Abyss", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/abyss-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-abyss/package.nls.json b/extensions/theme-abyss/package.nls.json index 48fbcd858..a25492d70 100644 --- a/extensions/theme-abyss/package.nls.json +++ b/extensions/theme-abyss/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Abyss Theme", - "description": "Abyss theme for Visual Studio Code" -} \ No newline at end of file + "description": "Abyss theme for Visual Studio Code", + "themeLabel": "Abyss" +} diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 642a9e3d9..8e97bbb6d 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -172,14 +172,14 @@ "scope": "invalid", "settings": { "fontStyle": "", - "foreground": "#F8F8F0" + "foreground": "#A22D44" } }, { "name": "Invalid deprecated", "scope": "invalid.deprecated", "settings": { - "foreground": "#F8F8F0" + "foreground": "#A22D44" } }, { diff --git a/extensions/theme-defaults/package.json b/extensions/theme-defaults/package.json index 56d5fa3f4..75e158342 100644 --- a/extensions/theme-defaults/package.json +++ b/extensions/theme-defaults/package.json @@ -11,31 +11,31 @@ "themes": [ { "id": "Default Dark+", - "label": "Dark+ (default dark)", + "label": "%darkPlusColorThemeLabel%", "uiTheme": "vs-dark", "path": "./themes/dark_plus.json" }, { "id": "Default Light+", - "label": "Light+ (default light)", + "label": "%lightPlusColorThemeLabel%", "uiTheme": "vs", "path": "./themes/light_plus.json" }, { "id": "Visual Studio Dark", - "label": "Dark (Visual Studio)", + "label": "%darkColorThemeLabel%", "uiTheme": "vs-dark", "path": "./themes/dark_vs.json" }, { "id": "Visual Studio Light", - "label": "Light (Visual Studio)", + "label": "%lightColorThemeLabel%", "uiTheme": "vs", "path": "./themes/light_vs.json" }, { "id": "Default High Contrast", - "label": "High Contrast", + "label": "%hcColorThemeLabel%", "uiTheme": "hc-black", "path": "./themes/hc_black.json" } @@ -43,9 +43,9 @@ "iconThemes": [ { "id": "vs-minimal", - "label": "Minimal (Visual Studio Code)", + "label": "%minimalIconThemeLabel%", "path": "./fileicons/vs_minimal-icon-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-defaults/package.nls.json b/extensions/theme-defaults/package.nls.json index f03211eed..4cf3da530 100644 --- a/extensions/theme-defaults/package.nls.json +++ b/extensions/theme-defaults/package.nls.json @@ -1,4 +1,10 @@ { "displayName": "Default Themes", - "description": "The default Visual Studio light and dark themes" -} \ No newline at end of file + "description": "The default Visual Studio light and dark themes", + "darkPlusColorThemeLabel": "Dark+ (default dark)", + "lightPlusColorThemeLabel": "Light+ (default light)", + "darkColorThemeLabel": "Dark (Visual Studio)", + "lightColorThemeLabel": "Light (Visual Studio)", + "hcColorThemeLabel": "High Contrast", + "minimalIconThemeLabel": "Minimal (Visual Studio Code)" +} diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index d0382cec2..ea03fb3fa 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -9,21 +9,14 @@ "statusBarItem.remoteBackground": "#00000000", "sideBarTitle.foreground": "#FFFFFF" }, - "settings": [ - { - "settings": { - "foreground": "#FFFFFF", - "background": "#000000" - } - }, + "tokenColors": [ { "scope": [ "meta.embedded", "source.groovy.embedded" ], "settings": { - "foreground": "#FFFFFF", - "background": "#000000" + "foreground": "#FFFFFF" } }, { diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json index 0f82502db..05fce6cd1 100644 --- a/extensions/theme-defaults/themes/light_defaults.json +++ b/extensions/theme-defaults/themes/light_defaults.json @@ -21,9 +21,8 @@ "sideBarSectionHeader.background": "#0000", "sideBarSectionHeader.border": "#61616130", "tab.lastPinnedBorder": "#61616130", - "notebook.focusedCellBackground": "#c8ddf150", - "notebook.cellBorderColor": "#dae3e9", - "notebook.outputContainerBackgroundColor": "#c8ddf150" + "notebook.cellBorderColor": "#E8E8E8", + "statusBarItem.errorBackground": "#c72e0f" }, "semanticHighlighting": true } diff --git a/extensions/theme-kimbie-dark/package.json b/extensions/theme-kimbie-dark/package.json index 1031c34d2..7c7ff5ea7 100644 --- a/extensions/theme-kimbie-dark/package.json +++ b/extensions/theme-kimbie-dark/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Kimbie Dark", + "id": "Kimbie Dark", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/kimbie-dark-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-kimbie-dark/package.nls.json b/extensions/theme-kimbie-dark/package.nls.json index 85c736cee..0d96b6f4a 100644 --- a/extensions/theme-kimbie-dark/package.nls.json +++ b/extensions/theme-kimbie-dark/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Kimbie Dark Theme", - "description": "Kimbie dark theme for Visual Studio Code" -} \ No newline at end of file + "description": "Kimbie dark theme for Visual Studio Code", + "themeLabel": "Kimbie Dark" +} diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index 8997b80df..3453c53dc 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -390,7 +390,7 @@ }, { "name": "Invalid", - "scope": "invalid.illegal", + "scope": "invalid", "settings": { "foreground": "#dc3958" } diff --git a/extensions/theme-monokai-dimmed/package.json b/extensions/theme-monokai-dimmed/package.json index 66c4711d9..43d950eb8 100644 --- a/extensions/theme-monokai-dimmed/package.json +++ b/extensions/theme-monokai-dimmed/package.json @@ -11,10 +11,11 @@ "contributes": { "themes": [ { - "label": "Monokai Dimmed", + "id": "Monokai Dimmed", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/dimmed-monokai-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-monokai-dimmed/package.nls.json b/extensions/theme-monokai-dimmed/package.nls.json index 3d93898e2..47baa2267 100644 --- a/extensions/theme-monokai-dimmed/package.nls.json +++ b/extensions/theme-monokai-dimmed/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Monokai Dimmed Theme", - "description": "Monokai dimmed theme for Visual Studio Code" -} \ No newline at end of file + "description": "Monokai dimmed theme for Visual Studio Code", + "themeLabel": "Monokai Dimmed" +} diff --git a/extensions/theme-monokai/package.json b/extensions/theme-monokai/package.json index 13b2db10d..b21aded1b 100644 --- a/extensions/theme-monokai/package.json +++ b/extensions/theme-monokai/package.json @@ -11,10 +11,11 @@ "contributes": { "themes": [ { - "label": "Monokai", + "id": "Monokai", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/monokai-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-monokai/package.nls.json b/extensions/theme-monokai/package.nls.json index 8e8d73c75..a5a17dc57 100644 --- a/extensions/theme-monokai/package.nls.json +++ b/extensions/theme-monokai/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Monokai Theme", - "description": "Monokai theme for Visual Studio Code" -} \ No newline at end of file + "description": "Monokai theme for Visual Studio Code", + "themeLabel": "Monokai" +} diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index aa30b85b1..969455d01 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -36,7 +36,7 @@ "tab.border": "#1e1f1c", "tab.inactiveForeground": "#ccccc7", // needs to be bright so it's readable when another editor group is focused "tab.lastPinnedBorder": "#414339", - "widget.shadow": "#000000", + "widget.shadow": "#00000098", "progressBar.background": "#75715E", "badge.background": "#75715E", "badge.foreground": "#f8f8f2", @@ -284,14 +284,14 @@ "scope": "invalid", "settings": { "fontStyle": "", - "foreground": "#F8F8F0" + "foreground": "#F44747" } }, { "name": "Invalid deprecated", "scope": "invalid.deprecated", "settings": { - "foreground": "#F8F8F0" + "foreground": "#F44747" } }, { diff --git a/extensions/theme-quietlight/package.json b/extensions/theme-quietlight/package.json index 0263925ee..f2e66e7a9 100644 --- a/extensions/theme-quietlight/package.json +++ b/extensions/theme-quietlight/package.json @@ -11,10 +11,11 @@ "contributes": { "themes": [ { - "label": "Quiet Light", + "id": "Quiet Light", + "label": "%themeLabel%", "uiTheme": "vs", "path": "./themes/quietlight-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-quietlight/package.nls.json b/extensions/theme-quietlight/package.nls.json index 1873df058..30354d629 100644 --- a/extensions/theme-quietlight/package.nls.json +++ b/extensions/theme-quietlight/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Quiet Light Theme", - "description": "Quiet light theme for Visual Studio Code" -} \ No newline at end of file + "description": "Quiet light theme for Visual Studio Code", + "themeLabel": "Quiet Light" +} diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index 2a12b3698..e395b99e3 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -45,6 +45,13 @@ "foreground": "#448C27" } }, + { + "name": "Invalid", + "scope": "invalid", + "settings": { + "foreground": "#cd3131" + } + }, { "name": "Invalid - Illegal", "scope": "invalid.illegal", @@ -409,16 +416,37 @@ }, { "name": "Extra: Diff From", - "scope": "meta.diff.header.from-file", + "scope": ["meta.diff.header.from-file", "punctuation.definition.from-file.diff"], "settings": { - "foreground": "#434343" + "foreground": "#4B69C6" } }, { "name": "Extra: Diff To", - "scope": "meta.diff.header.to-file", + "scope": ["meta.diff.header.to-file", "punctuation.definition.to-file.diff"], "settings": { - "foreground": "#434343" + "foreground": "#4B69C6" + } + }, + { + "name": "diff: deleted", + "scope": "markup.deleted.diff", + "settings": { + "foreground": "#C73D20" + } + }, + { + "name": "diff: changed", + "scope": "markup.changed.diff", + "settings": { + "foreground": "#9C5D27" + } + }, + { + "name": "diff: inserted", + "scope": "markup.inserted.diff", + "settings": { + "foreground": "#448C27" } }, { diff --git a/extensions/theme-red/package.json b/extensions/theme-red/package.json index ba751a33e..a9920fdfd 100644 --- a/extensions/theme-red/package.json +++ b/extensions/theme-red/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Red", + "id": "Red", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/Red-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-red/package.nls.json b/extensions/theme-red/package.nls.json index 680fde603..d58a547e8 100644 --- a/extensions/theme-red/package.nls.json +++ b/extensions/theme-red/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Red Theme", - "description": "Red theme for Visual Studio Code" -} \ No newline at end of file + "description": "Red theme for Visual Studio Code", + "themeLabel": "Red" +} diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index 20f54daa8..4548b9812 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "7ea773d195eac3f40261897b49a2499815e9346c" + "commitHash": "4bbf2132df28c71302e305077ce20a811bf7d64b" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index 021e527020083f1efad65391f16c21f9951fe533..82e4a516665550fa3a2f7da6d0ded7b7e6dbdb4a 100644 GIT binary patch delta 34284 zcmV)UK(N2;lmgtA0u*;oMn(Vu00000i(CK;00000(_oPlKYvGKZDDW#00D#m00Te( z016cOCE4a@YUZFG150EGkq001oj00LlED*ym&Z)0Hq0EILF00A-p00A-& zape+iVR&!=05*nb0000V0000W0&xlvZeeX@004%F0003V0005z-bxwFaBp*T004)m z000BB000G8vJ;2RlL!Gp0)EevO93Z;pZNul8wDnT8bJgAQnC!!004NL?bL-<99bAe z;f(|$B*fj_-QC??iMzWyF=9j+=hga6GIeUtV+^dXZqa|=eW6z^fDS-SVRE4ss#~cl z?x<=hUacx$uc34KdTmux`JURmUTOdJtGHI>TKrsFUaO1u))vJ&>gh?~ z8O&rBvzfzO<}sfIEMyUjS;A75vAo#7f|aadHEUSQI@Ys+jm32no7uwFV%^4ecCeFO z>}C&p*~fkkaF9bB<_Jw3S|UJKW_S z_j$lW9`TqbJmneBdBIDXc~$)7uX)2;-qFH)KJbxGe5REzeB~S8`N2=xijS|3|Gye? zsc-)8r}jKyDxbM(k+nv7b}O~YQ&{PsJlB;vk+oiA?I^N#5?MPd&wHhRi^$rb{C1VD zB5OC1wNYg4F0%FzS$m4Cy+qdDD);tLdEZxL-cMxSUt~T&WIj-2K1gIfSY$p#WIj}6 zK1^gjTx32%WIj@4K1yUhT4X*(WIk48K2BslUSvK&WIj=3K1pOgS!6y%WIk2--&L6= zGM_FopCK}zDKej>@_x2|%KJGY`{s)5nBKsDK>{}$VZ?VX}B_jKlitJk^ zvTwP_z7-<-R*LLfC9-d|$i6iyKmWBNjddc8^&*W8B8`nAjZGqr%_5C0B8{ygjcp>0 z?JD21L!`G;q_<0?w_BvQN9FtXinRBMwD*g&4~Vo6inI@jv=57aw2z3in?%}2McT(i z+Q&uOCq&vOMcSuC+NVX@XGGd(McU^?+UG^u7ev|@McS7{+LuMzS47%ZMcUUy+Sf(e zH$>VuMcTJS+P6j8cSPEEMcVg7+V@4;4@BAzMcR)<+K)xrPej^JMcU6q+RsJWFGSie zMcU0G?N=i0*COqIHzMt~BJFn~?G};tdy)1Bk@iQG|Jy%_oby@aoK}%@zKERjRpgv+ zBIkS;Ip>GSIX^|tX%ji;H<5FG7dht-k#qhOIp;5tbN&`N=O2-C+C|&HAMxN1c$}2I z3A`Lvc_-RuuYKv-x_a-f+uir-?!8{=O7~jV+9gZKmMnRH6A0Ugc)@n;gxDZru)&Z( z?8M*%#s;2wW`@LIhXewHV`j+s;lv&YTf&5dz`(@B1ZLppgy4DZH{Yr1E1CD;z28&c z?yjynb?PkN^8c1|l%qNLM-2RI&d3#W+jIMKIn-Q0{Wa9BBHu!!X(7Lgdfm~m-)To- z_Nt*|5w$yi?O7BgZZA9M3l6VN60D=)XxJHc+6j8$(4B`4-T8a+qp|JPTerS?+f{>Y z+XfnAI#)QxyKm@mP8C*s<$o|;)6*?e&zsJ0+cu#vfsiFQI^MnXHJA2!@X@}ylsl2T8T!Wq z*-{G$K8u=*xSccww-N6o%}#ssHR!i@p#G>oT0^7ZD(coy*b94I=vy}%0}H_|737D3 z{FN+4B*;d9i)7Fr4bQzocyd0meP%O5^9-|W3)@p;6Fy8w^=yxaj;C@$xUy|Jc4}Ha zG&y&FRI?oIC@Q*AEU9gks6?q)1&ZDk`X$9s9GhT4RX?y5%OvO;&X*Q^iIWtZZn8*S=5_0-nWHjT6fC25;v@!3?2ojf!m{^!d2js9Kz2Vuz-t z3nMmFDlo|*IfnSZlSWR55$x%x=SFTca!2@o*Kd09#hXse&!0ly8b60(v~zX*+r1vT zX*HM8)&jmUCvsYDn7c5S^G3ZW=_F#*>qG+>PZ-_-&{axIa@2a=C`jkSmqb=Yfe!juD zDa{1XyNZFNi@1 znxnMfSE*&Hj3Eszs@@615P{mZ3lyVc18N%@r4%KqwFBlH$j&R16Lsq5nQ%BYEJiJ# zS~_MZ$LY}-jO>paCOs>VM%3|MmysWN>eiKR6UK|044%1h@V9Pqbxm8^va%k3V#H4! z7EEO(5C~daKYh&Pib7bazGKNW*5*{ExoV+Z8WrH1ilD7jU?|h^w8){{O}V$^eji42 zGz3OVnNE&d&;=R@V6KH^u&CoFLDYmR0Srozh#(4v>o^R0@JmvLAIY153E^@t=*jz_ zY;QCKMwN1?I~)Pc42Ib}ETTStFs)?vWKsa;gpacMO1X9&^<_oFp1jf<^x)GM%|R0I zgyYX_D^RChJ$cP5rW%MM%0LDgz9|@R9PxAn5)BThtE;r%#{y#-6c1jDfjlu~RG3)s zq8^4WoeT0UTgPR0dHGu6`QpI#rNZh?h2W)yojdG&({%DtWpx%~p$nyd>pDEl7i$D& zi)f5iY=*BLwC9DY)NPo)B+f%apecmg8VE$pG4WKSP!5N1QyxT1+oKE^p6=>B^=ZVA z6rEUg0t>6v7&?z#-83+R8E#D-+*5N^JV<49s=4|@Aq9RpPAupmP>a+m(35_95T*^znv}kJ(5a{w0)=+EYpC7i ze%OI7MzYRMn>YmI`j%s})CcZ%Fu@du<+cHg8;fXrsbUd_6Ms1$htcV^YK72kTG?>h z@IXWe%pEHklzAS1keZ@$OK6|~6-qTzA$ia_P+xw*;*|i%pb%o?cfA+^&48LzByED` zV6$ekSl~oZ3_2MrjzSHgn}MZS4moB}GCu`ch}fhYI%Oa=#Q;Lm%5dsb*sL!$>i;hG zRv0|qR|TQW@SsQ7FM*1i>U-3PP zb>e_p)1V&7UxvvkGBtu#AaPbL#oBCA-N-MXk%1PM@$!hDJ=RLa#8h z9HwcMF;zuS0wYkaHuM){Ro*QEUI1Ppphc<6L01wSM)~jx6)}S&s!C}_FxiMoHfYnx zs5wvt5(emhR9|yc#np={=Dm`G5iNn-g~D7lfQvwm!m-e)cB;A1Xe`w~%Yk2of~kQ` zqH0+23@us2Y)gFw!>)Gt}SY(mHv^1 z?ul;q#I+|2ADSMZ2d=sb702&DC$3tb9wz0vy<1YO)!Q=Ml-2mhxw+hd++on(eaW@* zK_b=Jpr|bz_`oj!4@XhT37Y__m55}N40`FiC(<_vaQROBQ3uX{v$Zy#j1~{ZyA?;% zd@IaZ9_3a^i0iYo9#tDyvzD9Frvh%%IMZZ_nqiErgdo zaA{bORk3u;_5)60IW-sND!EIbUZB^}fQg{xaS@FsLlla3M~pz-wLEZdG`Ng#H{fEKA)*uoWTtuma~HD7gfmx&9}*BX}|cAlAwzm}l)#_D=p zC>I)DL7#bmbxy|A*bo}ao6GHa%@8XD;}~#<8H0*Jpy;Z8i}LlK)GSZxB2fJW^QXB@ zeZPeNYi>(!NA6NdwaF&PUO*CJr&>UN0zcVD0MrvK4h2Y(>I?wlDdd)typ=+C4kH7i zyDG(c)RQ%!7h3(|mS#K)MucYj(--Vfj?CQjYO~oa=gwyI%xs)du&AW?Ph5d+Bb;=JSQ5ktIAP2H{^u9+}@3>ddP zGX56#sOsdCeKElGkcn$fx|YW=LMw!vBpOl50yMahJ7_^Oa!0G1ez_M(TGM>o{HrD*(n#e|}|<)bSKO^Uy{ zp_0k(K}fs3%NS{SVKLjQ!}hBI0lUd(o@MNie%Sagb2GpV@0Avq4XoCNNtfC`RqtKdjw+n(v^j_`_yKc&pR}2U0-lah z*qy*>_*y`~6jIYlB%Xk(1}YMM4QJRp@F(OllqA+kxobHE5m6OOcFrmp^qZ*-*zPpt zlO#oAQ&CpDS<4Nl$~{3q9~2AF&GJfH30ta`FU|O&>xTKbHvVs1CCwGvZxufp@iOu1 z2^eCk4cc(#;6m{>%L>$g>HR=OQ=DjK9&`YER2J_*=On?#+X%&K^HxXX;Gz)mRAj-2vdO)mPt(;fl71@P_%JbA!wc&M2%g* zbtF|@VBZPfv5;yMTmq*sE*S=~U!#UjtmQRZ(fmr)Q88j1;iS}Ys2 zw!PT*i$@J1j6k9TxZTjXI6s#bv2uglp4`>a7R;sxUOuodjHnz`iKr$1Lg@^^YuJ{s zY=VTkLsG?SIK7q@kK`pFy1}q2X+!==^BYij1r5-5+e%qkS!@S0s_dM-KBz{OGMFPX zJYqX8Z#s$&3=CI)tVU(j9Wdvw(9Rp~ym1ZTjp5dvYu8olKLyybH6|eURLpu!I}x_Y zbYf=rG=_VL#a>p~%bo(lGJE=#MwOEBx7V**M>}uckd_;F~Yny|#UN%anTG?%UhRY!JEz=BSk{U%WVoSL+oZV1zDO-$38< zmZ~Ce0$A~X>%~6`FWA2xx}jAEW5FP^5d+2``K61O+Ea}ZDU`RG`HS9ss8JiK96fcx zC}g01pb7BoJ=@CB7GY}aqBpnNoAU81_)kF(*D?vgLG}v?jTa|$10qQxKhY5#{1rqF z0*rhA`?$i~RxDI<#~CKr^Ln)KiQ|uKJC5Ff$zD)@Ouzdc#@wX97`^RkM!$`=hI1di z_@U9o=XG^LcIRAp=5lUbdMFU!C%`w+sEXDwz$q6dVhzh7wlMj**MRh;*}}Yt;I*AM z4ZOL(ss5MQ>&_fb?Dba-M^|nM58v>b9i`rdQ^PCg-KkF$|7hyu@tqa3e`J2)P?Fzy z`LSYuRc{9?<4bR~{kPxMeX;b@re8aB=K9OJ<1_1LZafmsUNvg%>X*?itJmBx z^M&X?mz{Y1_+|TN#*g)mU%ru_YR})$38H^Qg|+eb&dd25fKOj3`_yTthHqwF^)dkn zrimmPuz)h-OFF>X>6L7_JA;0bIWu^6vxFso3(`Oa4qihOt6hKvgf3=^uV{;Y;x+YX z?#8pX?QMG^@SIvfm);{JsLBe=HU+J1u$ZX?kTUYR$(CX{_5<$gexWjZ{Hmx>j=ff~ zsDo9)!UAwr0tN~^+bGRmxo{byuUcI3Y>6)vJ&#MHm3REH-kQhI&5)MY3L9Y!S_pRA;2i>vCs8F~SjEzUHUE#~gZ zbZc=AbcJOKGepC=ZWsPOGgw_6Y{zu<)DGWiOEZNKuIkAU%oIPb>iHuLKW|$NyI|Gk z?i5V3SUFkek7O>H{DNROhmm+aZgnAyyyx9B*WZA0?|u(_W$nR#kN-=ql*;K}Wa5nyv5pdfHsZsl?4zfD;}h{ocptvp zJ9<@mbk;uMAm{AH;hlHDznzCS&YnH{#Y`&+br*k}`6T-k+Yvrav4;;*$kkpbw<49zV zsT77z$RwqYj_hVH0&UcPOKGT=3Pm`!WCUdgT1DL|2CXrRMgd&Ieb5+)Ot)@d-`(Fm z-)a;>UBktwSZ{5C6BoR(J%i_CZB~otV&d5UGX#;P3XO&=X!z<)Gqz(YKr7~!$k8=U zsis+&?q6G7QV|TNuuPdwtg-~0`vOAoR-T&-+e%DE?^8W3;OBN5P$Qb-}}JF-+8dzI{WbY z@Y3Vx#W%eLy|7WeV*7{2pE`OT8@ z`vE39O5+ddwNx2o7eH75_FLT6H9|v}jA~7Us2NCq@T&|c zNEAA=EAQI5t>2vv)oBg*Uvd8>SZGg5<_Dp2JgzzwG#bKz@?hU{9GkRyb$ods|I|MqywWlr0aJR)?4HR;8@q zR|Z;z1)8I_H}W%`S|va1t?gVICZ03hx;VnLTHZ5}abx^tkocC2MFt`YI!p|JFbC1| zPe1U$)7XFdz3+V*ho3%q*Ig$+P2h{%b;YO7$;U5{BQV3$(xbN-3KtoFtj>r!Yc$Ly zNySG8#Bv43pa!a4Hz{vdm)e(f`pKMQdHLz~l`Vj0`(~Voh{v4)eymh+>r?r9wYBAN ze>Th$!=9Sof5EW>hxc7?Cv!{Vf{Kiq=QMIZ)1D2`R_EID^IH=E(vfbFNkkO@Y)51s zl0jeMpuP-hpKtOB#r}nV4TjovZT(1XvEt<0_2MA1%GFZ&!Xu5T>ipEoyz|@Vx^tCc z|Dw(`X6GAd*RVF_8zQvha^M{>1;22mG3S`UmXd9ce{-{|nbTp4OhJ!XPML68n9HfC z(TN5v$gcgEko z@7MpYI;y?&IkXd5AH4Uzd(qNc-#Y&GUFY@3JpMkQB{vu3a-$582dOw~6Tp8iAi*ap z+fGJTL}%9*`|+3Fwzl@-;+4NSb^jE3`t-qzX9>FEi6_R-t*xD_ZwKfj2JLpG1SVd? zW1Yb$3R*z~WQRW~GXB5^K7dZ&KmOEHPdzpM)LnPoMa=PYGLZh=o4ZRx#8@p?yJ@?GaY-nw@*2+|K-hz|w94LRD z)2Bg2|KOf`u!pp_-Tk(=A>;IuSHa;uZ@cHbJeVgtB>DrBNdTrAbw=og(_eq6{khMz zAN*^w{i)gmU%%#>uRl=R)NS|$auWDh`q<0Tr_zx@_JDa(E_zb6%YpES2M`YL+Pl#d zc`^RU=l@!D$?pHgOkLO6cRv65%dmeb@TD7Xy-3%;{4eMYO^uu5o8Cb%Se@i;=ndny z;w>gI&ie{}g?PF5W#pBAEoZnPFTae&dYzYOopAd zq}6paU_1pcpo^n$I0>qz@MS1tsIq!4vdU*uxeLW7F$LMQA-R=e4Qp#yD-fmP5-rxs z^Cpjl&&5e@~FDualW3Ii7%#nfHJC8TPZI_)hA3m@G8l z@l1vtYZXQDLT+7y(V2U~*>eBR+h>(eBg(XZsZ?QH(~Z1lJ?UXvFra`SHiIjM%yd;~5ncSV`;xs^p zCjjW&VVm4NzIDDazK(x9f#-OF^XL7LE`AntZY#GhcL?rGCfKZof;2^9 z9i^#U5+X{B+Zilk!Mekd*OP9y^t|*J^;0rWL<&@-)dhwSzWGNiQ)KrxV zz5cx)Hgqlz0iF!5x#s@48ah7yRJEZxj>DlKSTtIz8|gq)&i(h@cWzvKQp}wMIh6JY zN8LbbMaeW0@>`$*W$-fZ9nHgT`-14A)%F2ZUHfQ)_uJ%dS~2|DaO&9uOyBF@t$! z^AE64(GOLH1H^v_;jNSbP{WM<2Ioq#ly^!+4Sv0=D?|5Q!0LG?z+^x;)Jf8Kp5jBL zKP;F3Q6&OFQLkwK0aJ|0IP}Su+;wS;XTshx#cBw!JIR0I9!Wdxa%TcRgA8^>sW)Gy zM=Yl?$W9`IlBr1RjsQDMki{AXVIqrWA(R~m9)RaWL97QQi*Tk>O&UhJh&{p&?1wIT zs%;w#j5@Hp7|0A`VJdb^PKsf{amqGBgvY8@FO(fqZMXI{rzqKV%kEcPbl`QN@B7ys zFiZlH;u?Rr(^(v@8HQp3JZWM|v1yQe9xNy@gD^4b4d_Ox%0u4hNdHkxkj8kSOhIJX z6(5FhyAarly1RBC-+TMTmL12p9=_+Izl44ny%D_w-G|;MZOwmlT;u=>X*72HusiI_ypT?6Me~U- zFY2e69{|0gE}RbJH7L_e1$ELK_195%Am#or>ZacOB#V)8nTiDHf6$gWN?^oxrFw74eZFB1rNl2tbxz>T9cAeK5cMyWE8-)X=l2!WUVFzU)Q&J-a+ zgA#wq-u1=#isb1Gyh{ZWHKj*1l0MRahusOuNLp+H zCAOnMe}V7-ZuG+uG_eDc%)%mdrbwr>>}Y24jQUN#2e)MAPd5}@xF$`@Flx#!!C1*W zoRQz`_ks{OxDRKbA9#B)Vbr%~124c)pzVLuZCw|N{dV>#;inC$_U055hc&BTe|H-Pj6=PA+A!gV#1XbkX%ZaM*QX<85}B)lwRC_yj6if@xFEX3D9+c6O9dr{M#aMjDV62JyL!utvaoc~h26xZzqxa6TG4+O3)^Q7T(9VB zvll4#RfrGj>lTj+Q&R(6#RkR(nU-HVnISqR8o3-aC(tQq(_(d0oF+VR6khg);V8@P zof_zowLRO{Eu$GtvwubHq_=Oao(OZ_+R61fVC+9aM36>PvDS-=y5p73=V`o-P_Slh z0IA+t26+QAa+FFqUs`|AX#~Na4B8}TF+JZ)qyr4Z>vEVm7XdM3VsRCv`3+qeBR_wf zogbV7@c8zYn1!?Ng5E+_pJ@*R;n3a{SJTM$yn@cBp@)TGm%1#|mK(ZI~@{ zdTSe|QFv7&AfzQ4)}@xB7&hot;C7wV3Z`L(N4u9^c{4B&t#mpS$~V6S-Lbr_vax$K znBKGpuLqntojaVnMWW0!S~lrp=KUKrUZz#js2M1#%@AXdot4A8c}ug?9j1vYtEkoM zX4&^J=&7QRx+s66=Cp3mm*~4H(P{!3DuAmQyF6;SK1N>%+pk!rL|5{H*)|J-Y|tAi z2$#59$DF8wyUnt#0$3o}bU;!Pyn7m?)X|snhd}{!j(|+^Y-vu=*)1SM3?`W2j7kVm zR3|FnQPryj)lx4hG;8g-Zn7&W^eWA0%G81HfiaLvb%uXOmeO(aMri2-B);X@o>y>T zri*(i2tu|qktu|65Z6pac%D$eaA{l2*@={i4LU~-()?rd3len;Wa_{Y#GkD z4&wb&P{JgiQe~2;6_HPnD?UXc%V5uWan*V!x*0u$>BHPyo;OXZ9_1P-wpv9)9A%7{ z=IpW!SA~&Xy_ij|@{0vG%ph1U!aQ=c0anELNfjg13PrV&S6~mL?K0PO{N%$7xM_T= z=Uq(5d4A84`P_$ckHC|lD8vCLlVvG(0S1%YDM)`;KMG?O)|)GPcIeXm4tP%!2+?htF=TR=ISRMA&;bSFpGwyzZ^iRRggI7hvaEVS5t zGkz2y_};kuyl?wilFwa{`}J(?fHdS~bQOQ3JnqPdL6T;&NUvXJmIHIbtSy7Bn&r53 zLC}e1`Mum9ElaP4UrST2($xdHrRQsdHjgYMiZbIoC{2yPS%pmHL!q^Ls4@r_zH_FAPzm+0b6^vSZ~=i%C)Ygy#6nPB5XBNI z)-*u#(0cqC=O+!sYo93Sim8IGQ5-PBxuRQ@$U3d60uVpvLIKMHEC|I|z-qX=oCh$b z>yE1{e1b3EO3nbc7&!+xrIKrBF~fg}CEMie9HIKiXtL6r|ZJ_0HNUqxmqa+0m z{UtwL6_m&oQ*woe)J;-C4%(ewl$8vCHbyNF+r0p2RDPo;z)~2VoP8a!R$+fC;e))c za5{=k7g{%Jo_^-Q?i-1@UBqKz|NGlFee(Em&}kUvv6joV29YFZ;U+FneYV`um8+b(o#Chl3L<_D zQe%~)%XO=^?ZB4sf-6oPyzp?}zCzpf#K$i~P9gD}@joNYGwjIJD(HXiarN--i;nKx z)SvT&q%n?MD~)gn=@N|aedDJed=RZX_>V`9y#L6Ne*md#jeqnHMEX^3T+?2%{|LJ{_2B#SVU!(~`4J)Zs4; zUcC#{P@&?rWcH+|YLvN-I^bJd!?lI}>zNx=!Qj`1E!`X|HHFtiYp%wLr@i_nl^Wb5 z2UqX7!onXmLDg#OBtX&ZYc-o!3QkOHPt#qSYIoMA_cjh6e2;%!3{({qU3+AoR%cYD zthACjo)v%r+mUk*+rQbk)91ZnP){NJoK9q!f=tADxEelU-|;>{-6eu3Dgjy8=(0;ikl2` z9iVL)Om#vRLFIoyV=+2GjSwI*E`w4IG}{au>rVjL8X6+A3}J?U_|gyVM@(PTrSEPx z2qfaS=ltk&M5s)og@Zd7vH^dyXBb>08>dXqHdVv)0OWAI5`!3Ap3p%B{}}8vBj*8~ z=TIh#{IDg{gc@!~bOek_&u!rMMUBQ{qcQ$d^v&@K`uKm%yMN6O|9$Au@nfjGDUX)O7NE6mZh!9LR2NA}G@BVK74)(|y_8Aq0cuWC zB-6BL=^Pz3WrBHU0LKB0G{C=EVE#d8v$bTN%{Q*x+Ng9dKXfAG+U)4!tqa{>e#K5t zt^pfAy>EZV_T77Ttnc4@doqRhZ(jpTCAC^k$2C>+%1>MqRqJ!rI(GK&UEXP_>dOAt z)V%JA?MLfA(~j-D0;Z)Y)bZc6k~{9ZBWdBi+jq$C(*U#3Gnk;*(C+i%_@(-69sXVx z2L<^N=azCiB!{J5<4G!V+UH@WLs6O$Dwh#N(yV^~{Tc?@s&OncycYlzdW%gj-hcJ+ zeS5deqAgqY?)!atH2!?ruV34HbRDqYLSl$`&+Yq?)hX26wpV_x;y2}YEt$jF%{$V2 zj~^I6j#|yzf9u!wqB&1(F42{(`)=Qj-Zr^w+uj|?L?$hetzdt?SbU0aLEMG}8 z$Q*y&LFNFTT!7PA>`p3wE|L@jN_@B(z)LZ{jJ2i-8ZyKN2yr-&%Ma7lq-*IiaS7I? zQ96mZ$x~!rWkAnhm~bjI&V=RxJOsS0Qq2{SWfvXOcZvok)EZbyo~zU*q+Tl4Xvo^d zW_7Ms%yR>DH|3a56}9}Jy<%`Od*q-?HBo;+x}uwEc|p)bCH<<s;~pqLeMUC`hpLT6bG;O*0)VxM&%{PSSQa3QlFT#0M7O7E%ib)Yc6cat z4D-Z6k13iVjR>tm&3ttV+X}^6TroB@W`hAD2=$>~=j^HmY2^GAN2HYFXX`kI>0W;V zowgTRxWn6>ktCeq4%9<09Nn{PdS+&N*Pe$r4~oQY&J`*Hucg<$xV%p4KXW-f7(ZHA zNL-AgaKUmKjXyhwA^#bF60polnv3J7>omdWO_QD{D@6L~x^wAH7a0kpZA=sDvs_Sg z&GD(mhCQXL@NmohU>@wCYPFo4x#)kw!JNnQdJvW>jxpU`Y|k91)MFn%{=E3$O0%fTE_tO z@S6|_+&Y*`REbbn+T3DQ$Ga?lOo61$- zh{cKnCYuHdMeL!(rizgU3Dg~Kp5hR(xtR!bFKBpS$&@*aojeAU!!ts#?Z*#W6lpRA z0A8kPbEX=39a@2?^j&{5OhDr!q-}}ZwVLf(Dg$wWOh6Kd5*FFI+X%fVZ}}7&AIP+^ zWz9A2QKiRBp##|$MW2%^a;4$I6jd%x1`A0EDpl1Yh^Y!A!BQe)00;~+#axC`*n~l5 z7;y~=WY}PnwO)s?$qPyqo`Y~`3ya7FS>{owj0))_BGfh{c&f>8s4$Ln0UduWk&CkON`VA6!*iHaIm{qY%)IzF#P%ITjcc(^ z02iSws{nCz{QExC!No5ZY)2DZEnPmdlB_`GqNG?cAy!FsTufD!b;>Hhr@T#J>}H|8 zh6w6zqd%k35)^;mL!sXUS`Q{DHLSYK3MEKj`4n8TjOdHy5X?v!%0U1uBhPX$HYkuA z@M@&{s9+G4GvbvEl{+HH%ajA6sE(;A^OcpUSaqSFier>PX*mIvLq%wGof>;C!Z?&G zJtazYOq#BKrqa{tMis8+e<=OWB%e&RLTHqn!MAZ8hP{6VZP^CRqa_Q@egciS3fBQd zu3ZlBP|^#g3mC~1XQgBtE<*o`x$oru zDfd5eFXUd5ncJJ9DqUt9Z04heJ&=We9=62k?YWFOcsjl->&&(HuxXJ^--_ zcclSzP)dW2T>AzXYk88Yb3U0?O2;A0)gbEmfiDB_i4-*>XvjK>psmSZ6$Q|bwiIq~ zJ2?oo0%NgR0z*Av1Qw;0QP@FXd9i!1+x=^BvFwenOII&mUM#6 z0-t8xQWd2;1TaMhE>Kyc5s=FJSut+UxKJ3X0TxF{NyQk!ZB`nx0&_H+A`iKi9YyT~ z10CZM5bi5Lh~T0QJgdkJD;QmYLWXe5X#DnkG_xCU5lBDV5L3)>?~db(V)`mXok&-0 z9e;lfYV)R`dze^7oydk#rkS!Q+(^Tk&{K_HOpLsWR3Jm4ayPV#0I~o}QNUe7a04yO z%;P!3bXDe3(^gE)62QcQnP!n5S5M|W*U8H@(!>JsW*1DMTdraoAX5kPOS4jz*+|WU zOtF0`d};u_Y;L{ zq>9j2_gsi~JzyIQSr$F-lg48rawD0+;iXA8?S6_>V6u}eMfhBFK64}UFkShYUYE}4 ze$r|Na{G(`?2UnhI&xErbVXsd)y_bIADQWsu2o8_={;YU!BD)?i_OrJfoR97R;7Q) zkfR@(@oU8ZI|D+li;d7Dbn`0l10(LP03Cn`21~@&12yqHS1c{8#xXZ+g`0V;4JQ=G zZ|Ql{uyq#4tEZZ+WRWkbiRv{^4dSi|=5xz;6ybVpEpKvXsX$g2meT8iZlAYJ5dLSd z`eKPJJwMIp=`SD)vvbEy3w2)pENDP_c`?gYw9E>6<7&{-J#1=6ABM@z>Gx_*>&KZch9;Pm(Kh_vC**pZgj- z7i`cZO*RST!ip?Lc5d$!ka2@P*qnnjd~0PH!A1g-lSGweR;A&-baR&Ua=ViXxxaEVf1tYI&8c{<{rgLy?aTO#8#cEtH z-e3Z>k$&8WSWG1p>i`@yB*RCbPZ^_hx?0#KL))vn8GMEOAn3hv^*?{2G*fg#dgMuh zPxf|_Rw+@dl?9cSr{`z7py2btuB2Ut_Lv%IPiUpEBG+W*GV_>k`N6TR@POHJ=}bTN z6&{P?nUb^90T|dCHg@c3NNg3G0jKHGU2FirQfvTvigf4tI#rlQ0e+}vbm90Z1LTTj zf$^rM8-Y<%Fa-V5G){k1jlsRzkpn$ThU>fgj&_N{?CthrU~(>fnfnZE&WFnmDBTki zU+@&z|A%vTqw$bxM^+31T;yyqcxlgsfn4MrXxsa{kgDP=(T?nx#Be2^GSv!x`R-t1 zvrEM(&n94=JKTQ+I90A;>Jcm|t?6PJ6y$G8fT^l+z696`s26_fhBGfyf@~lFqUK$d z#3Ij3(1Rds!PxiqsoEo`{7hwSJA5s$K>Rv#QCtJn<H=wto-$frrzmFb8e~dnZ{uKQgdKUc^`djp0(R1jZ(2oIIzLeT8 zU^QS1dw^&oT);7|;u@a9E!@VlcnJ^iHoT5^;r;k9z677dm*LCtEAgxFwfHsodVDj! z1-~A@5ue3x!oLnS*_-h_`0e<&@V)pw_yPPNeh7a*jQ=hEDE=6J41WTD68{N)0{VOPeVLoof`H>!X;Uj;o zGCh*_?m$aFb52$gh(w0M1~!ssvSZO*PI~LeUl#CWxgKG(4DZ1bTng6Fl7L_EC9j_Q zf>KZe+}4?#=M;Zbu@e=y8JP<#t5lXHFbD3U^(6SQ<=+b`pO7Fgbxk^1lZz-VH-v*Fcn@heyG_dRa}^+ZXc&*b zpIY;20wn*eip7Ia1g^jS1Zna`oR-pr4L8!7ENJ9)80wHo!QU>^2?b4jWU|kpfU;@| zx>$`g;XGTFN#A%cA^TuN*cwY4f}Vm25#ys+m^$U?TA5ra#Q00GA*aWK$wG%80Fw|w zCL;&<`=q`Vp&SkxOOts39UMO_dO#t05R+L!Suh4im%`ZL@7Bj( zKOraP__3oY5uk(n(#c9c0WZO@tvxM||9v*a>E{RJt@u10oTroGK}mnTc(5O#{Y+Hq z1kaZDy)0g3=L#~`-fCtsxHOpKTgb~Y?)&YYOeN3KH_)w zbb3DYybE>(H@xx8DFX6YT@M0W$SbD0e6g12*Bn>X^K0Yg@w2%wSIkv&vuVU-b0fW; z+yWrolW-Lc(uL3I;^&6+kxg)bCsnu1p93fm;9jB8C^%KIsEv~HQW^e!=XV}mT%226 zM7NADziu!D`;r_tSh34OM)SkF7jYEL~ zqxQ=9V}9xI5&me|JHK}75I&hZBKI@wWQ!}p^ZvGUf6;W~139E?*$!1v(C(klhE82{ z?MW!4GszE>cRWX!E%)crY+kq3yyiGNXJW#u3%!n-sJ3GSs(%1KK)}DI$}D)(3tFI& zxTSm5MkzVAbx>>h)8S}oJC*zNYM}}W>LrsfB#Z!Lnuu-1;RXv^G3U*uQd2C3^-xn7 z27RQ~Wz;2!ZB=0^BWAm3sg9Z#z8kK2x=9%mrStP7fKR4!o!n|}U+$NrRVQN`6&X(& zq!2H0v%UDzyg0WnZMc6%)Cs)FUS4u9q->khWU@E7+}Q$&l&NOrE&*vJa`snctVhBx zc)|cz$o)id+*`fy`44asn&fMqB4R2KD&BhQEEv_j-Hf&Now{rrW4n8?>b2I}?ezoe zt=78TQcXi^$#ojjimofuZ~!uC9>nSWgrB0EFNXPBUiv#Q7bt%-^*kr<$`zJG1%^}4Ih3DpoYx~lYN^W%kYv#7{Gww(!(bc$fpn$a-^PEZWdiQ_>+%!b6(|PMltK{v zKMp?fNjN~d@@e^H5)ykDZS$%zq2KHA zJvjlrkgFg%z{BajJdFWMH9d3ekuM%U_6Vq| z_Vmo$^znZe#~+`W!S}p4H}le$ZomDJN9MlJz4>nd}!o z^2^7MeVCx;|MwF!8UMHV%jtfkGUGrx_Btf;I>T%map8fkXq$fH70tk!yW*BBX4D{F zD0u4P6*uEk#%!ZMtZy;K-$+o~Xx3+DYKcAmo92Ic-|03Ya=)GXGwIQ3x&6(I2r$F^ zBu#1Y!*#SN4!gB%cjas$d~b9v)EjMX>5(lPO+%(txqW1s8Y4Gtlxs`VmFdzZ4gHC` zI$KJZdS|-Q(HLzC+Fp0CfCjCR+_pAbl9)QBd6e$vn5J$HrK>0D4T3%>jiL13z@wvf zr=NeMO0LZ({+;Z(K%+cO%-P&bLwbYAJf$TyUAi?6j6`mt?xpggzXbaRBxWKx}hb821Sz4F-m($>1NCTY$q`uB6tvqk!ts zU667rKv_F3XBpZOTxzmECRSNqlYmdf6~MpnFvD{Q4^>gnh>RDfpj^7RfPW03u*-i* z$(4aasv#A7(A-rG%TyPgNPi_@Kq~)R1_O*7gASn4!r=9SKyqJ}ng^qfbfSccEGoAG z6b$S$qT2b~WFIJ#=qaYF0N-^0S1f|u8p&L@rZnp2!#2ut*L*H_q6*G`M2XzJ z>--ykc6?o9e!hY3Z_MMl@w*#Kfa-r1Pd@z=N&o)(smVX0%{Rt3rzP)e%#ZWSJC~O? zmYy994#Qu!``tk}h~X`pnE6WYBL@Ka^m99MdvaIguFd5}NvbZTvmA)M7g$8nF_1b? zWtw+BSR;e}0`3fYsk5=Q*#ok9&;mgs6INuLuiq2{V83o425FE7c;UH2Zbg67bVJq3 z`9p3sc5KtAx%eZxrd8ZSPCd4rxauCtmo?RR>37Pl_O@2Lnth>vcRgr57=SErf2N)S3BP4cVOGz(FQOWj#^18i5#!C9xx{Yk-oo{~tlNXz6IEQJ= zVK&QCh0y@8yiPSBWC93HjTV4Onj6j7<3b~hazPw2)u`6t3rPNd_4O`rj$P%Qpw2n> zo^#*#N>}$@Nu?^4q>@zchg6cfUES4ox7%*JU)XN2!C=SyY#?^b+9Z$xLSh36gA-zA z3}ztqW)m156UYDwGdO=ZhIm8bELj|~VKT9^WJt`0SteO#hF!qf7W@6rm8uPy+3MEo zzRv4A-{YL``+wb%--oH0HY)SQV%3pe5!2XAjnhu)4RcFU3Rf6P6wUhN3C8AX@bPdg z!%rR}qsb<|B4F0h%w=`oQ&fr<8~k+FU310Sd~2ng2wRox$gh9Sms)MHI)G0BX-J+- zT@zawFwk5l6~2aQ2!*k{GGB3tDV4mx_4!z;Vc*en?OCC$3ll`W$b_LFff~p@yfN$HN{mNWUktm9$0iLo%nGh<}$F#!IK(v)@*;?ad^p;k_H!;+eTc=)+5Qa4x}?mB|`jmr$PSFCu|W1B;t4sA4oNa@gO8VQo* zN0-{r5DYn5+g~|V1q>IQ!3+`AA+AZLE0-7(r!tgd6c*9)uAKLzDq8O9 z(%j;%Z306KvDG}bLx(i1&F!l!&jk8|Ynt;LFLgqbnbKemSvGt$M=F$8w9dJnsbVXT z)y2@03Gpr~?lxfPnm8KWO@mnyfS4ziNG-R-PnwoZRk5eEaKAV=#bhhQ&7_Gq6<~ix zzKt(j{Oi9$GzvTMmwzeF{?T#zH_KTPiE*Jp+Qp&56@^<1|3l%QXun~aA-g)1GRJ`I zwN)|ek4LlUAp<91I)r?$_EEU&}1wRs8ADJ?;qXI-w=mM zwwO6~xl)Tufn_Bd+Z)4$&Q9-*DVu*=7}c_><&;b9=H-`#Niv@#B%BcP`h(bVvSykV z;}g9;TWSo~w>Qc`sWcb5mp9v`vSU@V+OSdMx6o}1Nm4TaKVb3?U&+#iEDN2Id4lE~ zGcG8FT(y*=GRY}MJIns(?)hO zT^^jf?C9?HYQ4&REOkyk64V>5rPbzQ6dCT$+C8uRp<8D%rYFjzQ8|bQ=?%@qXU$>r z>(BiSyKnMy{N&`f*je^7Z+_sPfA7Ly7H>H4lX`mDn1MexO895f494JLP zVw)_D&OYNeO`cXRznewkkA#1zhX1EoEXU?WDSw8PUMbnDiy?dTCT=VC|5{8=Zjl}< zv*&D=+4kffJGDP&+3Ypu;*4hgTNtGp^KbUbrdWTS>2FXWi=KOq`A!qmbYz8R~{|gg{F4~K%o#;2>b9n0_zfCqyM`PK8bWr0K;K;Q{KRiT$ z$3Mo2u6YplHT%N9KvTUuzqs;}<;D4O(RY0}R1PMqsi4CJwEKPnsE-%+y z_D^anoyvS?uGWc*OL2ewOy{z_&Y8ONXCCRW+3l^d>m5Ja@1H&H`B$IcvX7YMIC5)~ z-)yWk=>Jps2S6Iapyc?zQwl;qu2h#=Yn{tB4-ZZrJG?(Sx^wiz4J&hdffF=4lW#9C zH7_rfMx$bUc{7SvHs)e)Z_kV8D$;TsD_vS%?DV|7vwQyXQn!CsJpQ85e|Caar}@UT z(O6$tNb`>=&f{b_w|tJadLyaN3JDZ5XqFUQiyhR>@T6d}Dvvcav)3`pX*#wv=6kT|xNg z7#fXi@=?a>rD}DlR{L_dRI4pjt98ad^+nT{(l>*>R@*0YSkeEIA4jddcN%s*r3afb z+8X=kQ5O~v-}7YqS~AY5h?ErTVo7?$JOeUEU4A|oZTQtp|?nQ+9|ov7+km6U(|S;IaF zw-{rW)?o_b!9q!|m>1$fgTZmH8*)G?4|^KPxJa+R{yw{d{qEyjypnzY^RIf<=Pz7O zf8v*)xZ`=7}(;J_QG{(cLiuCq`2|qpUw--#pgr+4c1} zlwq{?8ItPkE@jpm(UIL+$t4lL)J*RS%2DXJ!M*$WrDR~`m-0LRdzW&f*F5BO8`b@l zZA*Vda9mz)+rDso>ASY;-_u+YpL4>f6!7pHz2;&2M=vEsG%)q0_by(#l)B5;^5Y`^ z9`V(!!r{Uhn%%ck&%L?uPSPfSweYVBpUtC>e+UGBo;7o`C(rT-^t|C15Ee88$C!@2 z{5$o`FhPdQtUTE1qyL5){Z=aNeW#bCc6)!rZoAh`aU6h3X#=b4qux+^>|q~&x4XN& z#`NRJ+8RpzBRzvg#WqW(JWTo-)_Ht>)KFT1ZNzX()6vpodcAKhtJuy$ zYs0UVN1MZAhw4$gIG%E(n^IT$I=i zRGS!)@hbVcZHp3Fe6i0KjmV@+sVaX@QcYu+?Vhl|!mvmt>4@zAy#`Z2eY+gHsY+=TBhYheOGG4l?;_psfx@H2{l8dvrttN`Hw*t);lCEXSNH*T zQj$xsFq%|0`FQ5ZmOI1c2w~I6km&}c_Tkmsdx|yP>tns+AW8~xJStBu(BOZ$`q%@M z@+_W8!}|=T;$Gj;8R>wu@d375nwjvD)22^O$@D05<`J5&;p&oR%et3z2@+1&`Jpdy zTa%n_ok}}u-0h5sl*jsLmw!@{;y71c%7EJ%*0<-G?8enRVfO0Cta)lx=A zM!&w;$mU3=;q`$Vr)lg}ON)P>F-e8+Z6ZYxE*kEzUiHaXw}r3+&mo&b#_x^o`k_Xe zM%9qr=fyOhvFBBrL9JTsCXtvFb7OQ@m)D}&RxpG)2ZX0P(t%NldHSBYfrx=1j zq|vC96ECw$i&8B8ykE`AeH&2`th^e-MO`UJRf+KZ)2C!rs_6BGRkVK)^w2qEO#U;y zslTD)zO1kZgn9JnGB;hP5~`}_nx5U=Kk4~iHQ7msHa&4N?bL{oMsAraqZ=i**=zKj zvajr_pJ+6j}Ce6G?v7SW4JtMm!)2{9O2kF-RtelmU4D7P1cIC-p zsi?x8v=)&CD3obS(#F`(s#~h!|DZshZoHv_dU2?Z9CymEJZ}5*1Ha>wIx>sZ7aJtV zfJn6jYpd7<(mjGj$GPsS=(WasdPfA#pXzab@5Kk7*^HAU{>6Xa{n^*O?*1<{uWE1Y z-?hEFCN?fU_?>IwcHHl8?68ggi4)&mUmm9idj8X-|DFUAc_*JxQ(xnJ|Dvn22<19a ze`=o%bsVQg7n<^KV*9Mw*8z0w05}y>aR{lHz+sI)c7fOdbz&o1s`|uVBB%{*3)c_I)H@<7Gb25ht2tgME&; zX%d^HaVURmBqgMW3>F0BZ&HSY%%cne`B0{_b#@R0iJkg<)H%{mQR9hak^&=>1&#no zGU4R_0TCM4lJp)8XqkIWwskOY{=KE+huW>74)qT8Qhs;X+l7S9b5oJH<_NhVU9EML z=@`cLZoAj&YAh>kx|uJvPS@Iz*xn4Q@gNIYFO>1VSzdH!J` z%vcA%3t4x-!m<1s6W6u?y;3Es#Y65pWjjVycyyszLh3S(B`3r-89I?ChnsR2yIC}+ zVgn(Hw)8zL>|=!=NJ0Xxipc0`c*5n8oeFr#apHdpY-VyF)y z>NrmkI^amU&@`(lRGvDV%xr1cv8nyo46}vJI%2%BAZ^B0(nEYBBZ~>^ZP*Dw5?@Pd z$15MOlStXdJa}y*VR+!0E_}`Dve;CUfBE;Zy%2=96H?bmU6W}hhhU$5PSV8ol+T^2mynooQxcyt>Wazx zZFoZRs?V9t`0?B5*C|f{o|XFVoOnEkkYw-5Q6(qOTKI6^>l9tb_P?TV(&2v{!@W*T z`vOg$`jkq>_XkHWN)& zcMKW_`bp*^bw-s0q;DguhEYw0TmVTx*Rx~vo*s!ywfT{8FlZPwjR~?fiBm#=cmC2Gr5`3A7ePYKR zQJ7kRHVpD3PQ4Ntw*9vy>Q9{ul<}z1;ryXBL=X^>g+h;zH~BC4be%bRb7?p~pj;<% zKZaAGJC!%z;vXs8mghGmUVwl7y#848n=-PZ@3H=LrDAF~>df2Pj%sV(qAefY0z9O66 z!m7As+R^;6ghVoRcOX1JmZs_XB&CRY%|RQ}>Vsxcng*sf*^>!8Iz4|=DZdmXRmW9c z91%;P>7q<3z_9`|2+QmFGlO__4y>c^*<3pzZZxbl{c3-_m7y5>y!7QZ< zYwI5ebTLlkG(o*2RgHg;CTBnzk$>psK3V0bHrrd3-hvS#7j>{8`_*VKS~^|Phs$>6$Ww2E{$jZ=~&Qr_`482qL(EDc-oHf%IGwo8{Dtl0w}r(ZY*#C7=dV*W4i zEC)bs1EpGrmxg>c>3gl-CMR8L{KELm$L-+cZKK6nWpD!-H%_9XkRPVfs@iW~y<1#) zQ(PV0v@L%xY@EO4{MIJDZSuuxt+M;#KG}ySZXK`0+da5dKa8+{Usg$ss|Pns8C-q) zrexkGZh8g1otxztO6SierAX6 zQ`6Y6Nf)$}eBG>T{?G-N4j(*=y|&inygR_#$31^%dD-!Jeez}2cRcSM)q_)$t33yK znQ#`@c#}WV_w5q5?l9}ibi%v%jORS~AYJjzR&Br5Y|>lny4dmdJ^Js&w)Yl&NT0lp ze(K{kUGSDx^%$-{R&DJa^X=I7Q+gfqFZsNgLK2ljV-JuKOy^$9R6_lrlN!l%Bhu0Yn?@!Ckurg*d}oiOA$KGNV^3Tb3ZTS zJ#K4Em}~}&3m*-vR{-p_JsLkN+=UK%H%Z%-s;PvtP);WI=wISGr`(K50eA|2ORdt@ zyRSa;?`15H)YjO#zGJyq5{ZvwWYKii%7<1u}KvxH!TwWVRikqV{gm=|E_$}u)U`%)F!<|F_cqL#uFjuba6>z|VQs75f77FmdGyE|f8y3yTJWmi zS#nFIm8_R&CX0A^P+e*6pYXgYGD3eEo%7jZxmUhwxiZfJccDV&^eeBpX7Wkfk4nDF zzjfW-nJa(d+9nO2@!2=re8u%ia8<+&WoE`FZ#h?voJi4t%RCH0+E-nE=4DZ{=O6Ak zK=3vfOGl1XGat@u)%C$Yi()TKwfULP|3qNN*w+4K&BAJ7P&lHouCR00>kfa9F*P0y zw^%mApilnM7cv6LG!(4Y84nt49J6u8zIgidht6|$e)1xEdyQ9g-J6<^pUus!KeWT=Y8xN2=yK2dDLenjf3e$N^wL}W$*b7I z8-w#V-5Suh_ik(zF7e@akZgaveTKC(1yi0a?+v9+(=apk`#K(%gw$?VGrVJ5A75ZE z$n^@M-=)!?qGnzaXT9An86tkEcR-^7tb=?a%B)uw69*#tZW)4X^!d}q1`NZ#@isGC-*@_r!AO{9A3fA;__Y&e}hh$_~Z7jleW z$OEkH4qAwB7{Iq)r+9ym!nf=7Zln7%KXsy)x~|>sthX0V9(v^X!e0~bt|DJg%Oc1tL-e#qXX9+a*f zl}HCLVDIX{EyLSRtFu0R z`4HruI@-d}sa=2gbe*Hlux@inXU1wq9;!Y#0D{rGBzn1A=lIQ03q)_1twZZ**L#ED8YdeZBk3KzoB zrl^y#8JVAKmI3o;>FI;+ehhj zNym6Of1D`GF0=}#3O@-sJLvRO2H$#8o8YmFZS6s?tp@pY+hcKlaGud=$XbnUq8G$^ z(`1X|HvE9cnSMfx##i$!XkN*H*fs9cNl1y$VJ&})MJM0I9$DM+PENe_2UbY<+D1oy z$L{ZZub(~jpu zvN7Q(ttW|gN;t4HQlPv~&2Oyhbw}(wpY9w|VyirQ^%bHr&u z{9?@|_Plbe2^1)08zO2nI84ku-LXUo5La(q+RS?SD2PN^EWMc-0x-vuA`pQ>^5$LVMr?o!$m{=ffIxN)4-q%_3ZE_f zZ-s9a{tGi%i;dU`_7e6=_LJYwYj1;ye5bew)S{csG9!{{a6j z{uuv#{_prR{A>Jg`1klll57<*FIu7}M&gJ#E>4Lv;s)_E;$7lB;$MhI#IK2uh>wfk z7LSQP5Pu=QCjLe|Z!8!sW5u}IIBWcv@mk|8#{I@G81FJZVSLK?H^%Q6e_($+Yy6e* z4bwF@&2#1j^Ct73nIAAeVSdW|ee=uaSIw`R-!Q)|$@G+qa!IbptL0hwQhAH~arsvH zfP6%LOnyTCj{IZ!f5>ksL83WO^J-0PslFPi>(oosPpH?ax2j)I?^5qpA61`Mf2f{O zf2IB(^$%9f>R7AR5$l9?jdg#$b*uFn>-E-KthZb5w|?FF4eQg^6V{ikKe7Iu^)>4` z>pxn5Z+*}DfoIY?}zRWNK;b=${y zA9cyFE8Obga@>>UD#s9;Uc35O2IQWR*5Lu*F4gXIuK<3B^GxPjC+!xU8EL4}ZL-x> zS&<@-y_8mWS0!jmKgEBY@ewLa?5C?>)n(J? z^;*CwQ@9azBWBa3qX;F;61XF`M?ItmAF1u#0XjzMAfmK4O7jH}>N-UCK;z1Q&p6Fa zH|pwphST1oCh|cl%Wxxydu`GKdPa|ULH-mf6M$>uY8cgEOS!xNEKW4sGBFG&?Ja)`Sk z4ymPaDyHC-Z~z6JlvNv`Gq`6o?)4MZ9*kj@(W?iCkP+1MZnoPUs4^m>vo19UyD1|o zFGs%tL;ve%o61BqH67o z2W17~)*0XErmPJB{4w#>F#uD0J4u^rGVCI;yP}$m8f4sN5?L*Wqh6}3w>umsZR+2o zoFK55e&c(FsE$5}FMvBmEAMP90Ogh^-9H|TNFk%@4YGf(fus%?Lk5VN);Pnf}z~rHc7WJ&C52@nP?Zr9EKDjVaPBx>XN*mma85Tone}k zsjJ9l?@>?hl2)HDst$5>5R+P`x5miLqxas6d*%&Gmo$Vm61``_mbV79w_uNJjJnx zM$>$_t1}W(tMV=;QWz&YJ}W^lIbvwy5l@+4pd;J zL5zFd9nia8eZ`wx8q7ZsEx-BD`J(5@{f$ zI4ff_a+EiJyp!+uqFYA8bf0wrv_eCOk)x*utUGEi@T1U2wfaXLwF~YI)&(lTV89h% zxZ8iHiHTw|s*=)s;bc6bwak@J+z9E0dsC9K8?@$+@0>Hf{COpO%Xb54d{CI~7 z9nzy|)cVAVBqobKIpxoXXv^|+0J-0D7VHWc8G!! z>gcRhhNuEl08^s6??QqlxuvZT-Iny)DSptvCd4+z$&~$ekzIbMO*ZXEOZRtmUL$|B zk(g+27jQEpAoS|Cl~GgTV{HXyL8DA^cNhL~UBr@Q+u5Miz)m)u>norTXd+fE&9B=% z1S=6^7|V2oksoWdx!ohu#0*mOyfO$UYXhRG5BW;Gg34%*Qc2yi3rZ!4qh(9pEyO%% zfHRCW@z^r@lGpg7bQ<UZAy%0o0fOs<8dKNvg=w3Xh17mn zhkP|QgI{8pu3?(D0lVcj%;2VN*}f5Avzb|Rn~SY-ZrM`fx7ZR8E(Tn!$s9DWR>XY7 z)G$-pZMwpXfTM$aOUOrTry;H-a0CHcT_p-T*x z10Go9@NgV?BJm6zO+z;VWXXbUCJbK10t8cv^F;5jYZw=S3}u-`z@W^|5o2`>eGg<%cG1v!hRcBDs&sw4Dm)75n~U~rAAazmUmdYx9b1;S)XSs zT)dyYz6_C1H1Zl4N0=@(7w}6X#?m64&y_b$XG3}_|)pe%V z+XX_=HHqj>5F((D<0$P*sCy2_m16~eTK5tM^$M2vGu$39Oo;E6DCi-^-jz2SRHUD& zDW95klD-_}dPjfV)YTWJcpvAp!P8jYLKjDGREy6t(m-`6I@3Y%`oo-#Cx(|=#Qv1- z{K~X6&i)A9fq_*H9YDB0+hY7z7<->Sk1}rBNG_Pn59OaEi&f7(?FQh-ls}C$oypFF%^GQZG=8x!cz(V-n8i=`3E3E zT`6><5FcdhlYr4?xR7ah(>bMQtd-GsTk_vPhojP8X5S=6rEvh3HDzPmK%<{U3zbG< z;*aUZ(NTlC@|{R)gf<(Nk+%UiVQ_aCdm1D}&;6(Lvof781~p3eILKH7of)R&zm4Z9 zTaPb8+H-&NJ=3Q#R%h&fa6C;T891S)5;$PUCeItp~XZhe|ja zk)g{pB$fq8Ge4iz0!+aET;3mW!$P1jS0zpSpX7gf;I+FBm40@{Pc#K{>XWln)Du8b z7dh87F#@_U;lS3ZrMg9QFnTlxdUm&{Q!Nf40R0SWh#Gc5>i)+H33;I&4fZFG3TOm$968f1$jxeMG7h@h05}-D$m<4sia1@P!Q?fhMMSNl13VIGwPS{Rp%P5eh zK)k780T;oO%5@!onvqpxk=5y1Au)_q6{uy2OwdSPP$fcgLWyX#fJjbYhAA6x-&c1# zbf*VDKi<;ZjcyLZt4VYidj3mNH(N4vsDT!ZFI*3)<16%&*og$G3Y{;|0OA$m;Y>$s zTJRWU{sJJd!@yZ+VHiA&5VSVK0W%`bu84?2j$K#7RUUDFRB#ijv;*H&;aCYb0!szN znCYpIG!`VK0l!DhiG#A3%A!!vT;xDYgr6)jhDcIjEnR0VkW5MJz@(1kI!}@1A_aiq zx=aK6VxA~hN>_3b2Gmu|!Uh@Uh;51}gj+phkpwaWx9MoHsC6?$6OnurDuCO)#nb{Y zgE7OQ;ektkU=WmLDBI+YPNKr3E=Oy8_-{FMFWfjo63D^`JmhE>yhgne2Y~1&8kb?> zfRYTK2-9;dbf52Ns2gfhSvSk#OH_hs!x_rOq_U9&7<#MW(a?*M2qoZ^(G5hUmed*| zG*J@|qIpG=(vpVqi6kp7aStW}=*h@;DsvW}Gc}uk2FZ)9A{D^hDh-N{kb4>v;0zg5 z8P_V4RHK<5dvHZWehs1b)|ulI=`uQk1Sm?2GR-*3)OW;4OfA0vvtz1UBGO%p)v{VkscRRuD)BRFtg!C+}p z7-ErsD~o!Q>kJgooS|@Nmu9#IxDW%DmhQxH0kusJA`ZnC%a&WD0WNEW08`0U$UX-( zP@5j4YAV;%`aDe>Q{?s4x-*DRL(fuo7_y{s5Ku4(PD;ljK5D~i!YoHBdQ0bnaUi$d zG=#R~6S)9WMR>&8O(R4y33G0)NK|8L(?XGd3SuxjH{wFfYUFLFx$Ggr{s%=6OJ!opS&f*dv?=6znmPVf;`lK>{ zgOIoidK8ip3NA7YQNMa7(%EzHd77ghvpg$=pA&=3;4aruwtBD(^t`F@R$x0R=$oeF zfif!=qvmUePt$OSMHm58SfH`sUa7Sv4K0f?Gqtxos8E}SNGl95MQG9xs!U%d`zzZ=!YW+8U&`3m}nzZ%O5^dpKYj|8d+5v zy+8Ul^~qS`zg4i{f63ZK`ZsJQ*>1NtOKXSl9>npswZBG=aFi!Fn|ij42ik>yqdy+8 zZ{Bt8PS*I2rKD$-tt+~%mCl=&JEv@?d}J}UBCmGz^0?I9KfbbMoKEe9Vv97mpPoF) z_75Nanj4V-5SEDR^J`o6db_zDl%24WmI5hFt2ExJ^t}3FXSfIxIiBSo{*D+F_6lbU zw-tV}@J{T%9RRk3Z0d-Vht)5Ca0Jq5kDLC1_uWBgxB*|9{?kaNBKs% zPPU!9SZh2jc<;~=q&U-QEvAq$39l`8!#NTnL@J&vEoW9V?+YM5b2ZnlaVIJ@ivU&Q z{#tjd(|z-5Q0uOpuX<5w#XQ>SCXrflOQmuWCZ=3Z4NDc3LrmN000`=TEkwp<$(=Jr z5=6FlqcllA#nr$K0!u6X{3cuZl_Yaq>8P+4n(*x-HMM93sa=kJ&-RcD*bKbgUehpx zt^2mFUs$FJ-F&(gJ4R)F$BUhiOvWHSy|G{$N;#!&qwSCcwoio?-7f63m4;=%nBDGp z0h+ZIyCq4wfKM8j?acgteL@iw;=%$Db!X0xx+k?qyC@Ehv2Em5-DTf7^VV*Uvoptz zpF4AZr_0&dV^{oZaZ!km-o&3hd&P-!r+;>-i_dF+ZmG+jn*7U;iF*%mV^+?u2rthc z%i^}`wVN($ld62vuU9ef{&+`5=%bUH!-8%ldTg|%ZJI(*)(AKK6Wv{-- z38pDevtBQoJoOXw_xPJ?`>n?x-`aZksslfaeg4UUS141-1=dsTS$V)WAJDf!kKWja zkL@2jcYJ;S*#7!|@$<}i%h|KhE28tCsWd9=@bk~JpMIA9ChufFJ$dIVUh&8;{nE@g zk?FX~AI26z4q-JMXGN8m*|4XISt2@#+F)cG^o9*K9;)6rQ~d9rf8^dB3F@-v2mT74+s;1R>F8y8eloavFb((nY)LE;h&46I<-N#@+2yYL zhKk&%mjTR_kVyblh`6@Z`qTMzW4;JuZZx-Dv0zAGO^PjueX`h!jXEj5wy0FC`S#q| zodxTE{@Up{tQ%^PR9$Adq1#oPi%Ae`gFxPu`6SarhYr?tJQp=7$W()^7PHW$r&2l{KHA;yRiH|L_UAU|sS6|zUE$u!#Qt*C!4e-YNzyn) zwqSP{KZt4GyyV+9%}YIGoubb~r}y{|=#MDw`pIvy8yLOuueQI-=DyqhDob}Jf6f;t zf3fxbOMdSgqF2}{{A}R~c$p$K1l<4I!88b8uCd|*AcIqll#HE~J|c39S>M!RtlTDl z;RGk9X?!L&PZ6_PI+fo)Bu>X#2wnP zZZyq;LC5|(r*xIZeX$X^-&iB%(p`7g;`Qpuh9|s!73sPfTXtG!{5hc32{!p?9jim6 zzSuI>8DHoo5o3YAN=91Ar@`(BSWQTON+TLbBW-g;Rx)0Vpxzz-)?=TBMev!&{tYxF z`kw#(bpHD>#=k)n`S$Ca@KBDDM+&^0ggViUgX}Uxtp(eH-6huFr4BhmmE#ddGEojp6EsRa}4pH0YIjgq-6i82hS*+_A z&U#n>-Xc1jVKZ%r`NDSLRN+D5kMAq|O5s-v9|XcXLL1gtg27W5t73mNIN&7vVmwGR zu=31v@kd{sT^*{8e1Yv2v9=bxN;Wk=Lv(YZHykxkF5npv0*sjx6RalaJMS`DjF?QNj*BZ;Z;hO)h@bQ*_JQ%SmEK#PlQ)0}d9JnHBgjcp4UQllE=- zAOGUyM$&hqeN28^D7?avzy37$2^a z?tLY;RFYdnyxc}rkY^z^V*`02TplehAT(C6 zp{_F&Lw!{3O$eVZYwM^oJuUh=EhK#5NBxv--F@rJ?iAOF^&2lcbcJC4bxwTEWim4O zt!H2Uf#z0&H@BMnaemo<_^~^VzxL%>TAzHhfB3jdg5lh;8?MTlih48c1<4FoAdgTR{uB004NLV_;-pV1B^>1S}gsB=cnkMg~+c0{}kd z0}zuagdh#Q!g-+V2dM=B%f*NMlU0PefAlX_FOV|0iPvaRpi|6n>UcifZ2}kfU zUcsw)4X@)3yotB)Hr~Ozcn`aH9}EpR1PCoU^pKchfWin31B;_@@Cb;QVU7isIED}K zAwI&#_ynKgGklIO@P8$~!q@l)-{L!bk00~lOjaL=l@jY`EJm+kndIh_-w}*I7d}z#KC+fYB zM1Aro=TpBZxH5dG5>BI|ecTAz;YyAy(DvTO6LY+uaHB*Uy?->iG3Y>K85)Uv3;0YTOgXCXbd!;LLPs({=+5;7Qf;&qz( zl9XIXw}0(+J$KTEjMP|=bHtraC^?$M1>`c~~!)51~P_%MV`6@ZvA5}%?q7Hc%m92Zm2gP)k_iR*?K23(S z)Sa(-<_A(LoiC_N#h4*xJuUJ+jH;5IHjc!$R4V%5SZc9I^mdd>-I^kCAbCDzf)dQw zWCiO^t10P0YF|vHp0a%EjV`89NmdNZjjS9@Nc~qza(~jEN+pj*DJDwJX#IV@8f>N_ ztH&gURw_z?8&*0hMm8gzjl$GpDvBjV)SBiyt4o=dW35e~#+V_~cHB<3d!mb4M?H;v z@P*-tgu)0GSZ;+-nH6nr$*C>dn_zrzY?kzw+r-!;L1>K|rJ2~3Vtt?JDy@@#WR-Ph zN<5^kQGce(Z0t%K6SwJP8T!=p-araRjE6d5;$c)}x~!tjU2K*Ke3*Eq$fqQna?g_b zd=m=KnPTNy!L_FlR^ux6vgb)WW0~^1uq=HlMR!|1EETOZRj#7wD<7M5i!wj!3O|=^ z*a}H{Y}HB@(tv|W3?l+C*o kOPvPd^SN;%TPU9`MwKs_)7f%V*)eH1HvR)@KcMCS0D=>oQ~&?~ delta 34045 zcmV)PK()WzmICaQ0u*;oMn(Vu00000ifjN200000(d3a7KYvDJZDDW#00D#m00TGx z015c`j8%MRYkLPFf#-QC^YL)?wHyAvZG#(B4XlT4l3^B4o`t6TKncVFmL3!oEFSD0F;Ky4c} z#T_-R#j7>t>veQ3U$4|Om+z_M^=ikjU&Xa1*W%|&d95wpTPce5G|-tY#lCKTG}4_O zG|`h@^rla7*F<0X(VqbfWDtWH!cc}WoDqy<6r&l#SjI7)2~1=XlbKTd4%3*<3}%+! zvzW~s<}#1@EMOsvSj-ZZvW(@dU}dp?6{}gpTGp|i4Qyl+n~Uofwz7@w#kzx?>|!^2 z*vmflbAW>!qM5@S;V8#A&IwL`a*ETO;VkDk&jl`WiOaNbg{xfSIybnqXWEk+rkP+C^mTsyyq}ZX#=cqw?ETyNj$n zMAjyewWrA1OJwaWvi1>K`>NdAPvw1ok@*0T`9P8RAd&fCk@*mj`B0JhFp>Fik@*Ob z`ACuZD3SSSk@*;r`B;(pIFb2yk@*CX`9zWVB$4@Kk@*yn`BahlG?Dpq<^NW7hRA%T z$b6Q_e74AZj>`MFD(~liiR_y%vTuRNzJ((D7K!XzEV6Hj$iAf_`<999TQ0J1g~+~@ zBKuZ}>{~6eZ;i;lwIci0sr>xci!?TfG&YJfHi%GJABnUdi?pAJw4aK!pNX`ei?m;ev|oy}Ux~C| zi?rW}v|B~mZ$;W!??l?~McN-k+8FL ziHQl!z~_YEdG7cAr>d`H-Ush}Pkp<)y6V)av;3Fee>q1vnuCADz|ZE4Trsyjw?CId z%>~q7L)|L!Ekv3Y@~f!V9S!@Pb`)l>8h=U_QM=QgMM2{BvU9%R@aiPNIvS3Konfb) zpcf9^dFarczbii)+g`nO>#Mh2J=nHwpfRR%g=4(?h92isVZ~SeN7FSu-7@vO=?u4R z6ABXuS%RYz-OFBkS+55lT{PcZ-MyOB8h19UU&okG$_Tmjw5AdMX0(ckP#u4(?|*87 zo*g2DnJ`>VpnEv&BO*(=lewFre>{*awUFSmsJV#SNmFne@lMk0v^QUaetQS%kNTrE zG#aj=ZViRKu-ApYb+a+B5ZqEhei+DK$x=juYy`MS2K~|S{40bf=M&p!HZwHOFw3^E zJvBDr!*o>7_IT)cDkp?1+oofurhnx_lXFKk%h8UaqASIc+E$55l!{fL=)TY|DTdYg#hMWCEUs#tP_8$4 zJ1+`m2yJUrYzv{!$JIvF;uI4*G&Nlqv8hslNe;;|#Q%ddaypD)Pe(mBa(|P1N>5u;uw8o+qM z@D6~kQeu*$*6T(=Iv>6yvMLIE7|^6S8uX%3FGvO==>(rSHhXL?x-6{ktlz#=@@*J# zHSlVI9miYR%`I`>&=uXwcYmsu757^2-`4GSXSzub-SwUC?AUSnRe#iW6s9Q#*A&I^ z9KUMkl_`U(K36owtA|ce~s^G_wXmS0_ag!?wVWs+xCDT}&Q<>(fg?4FF zfNv^-wo-wiOvlqAhjKUN-j@4)7|qcT7%gQwIc`B0Xdr;O7LviDj-Lck6Rrd>C_y5E zC>XBeFzCTANf~}5ZvrNS%e|l{?}M_v(GVC_%AxLX1T-@kW`Fari2A^^lG&3<0hkj$ z%H}KO+I7^I6%BjxN^j7EPhT_#Nx&11KeMesop$xqwXc|JAc`mh8D#jTV8C(2(-BBC zIH0bs(taNcjA>9j_! z!_GHNCm&T-XMZskx=^~V!^3>BMqsvx#%RT6_%(y}yik?84bzvzd1weUg>YK~frvRK zo{AL8;Sg@hgJ@}clmWxjUA?D1jTn-m6RS>OVYM1V=dr7s24*nBt*L{1YObohXQ^gR zMMRvrN-JTi>FWHGk>f>}dRB+r%v8iF(F|q_gPFQ87k?EwtxCo#M9aM)_ZzuC$UT$$ zD)LYnbzqiLp}L5gZ2{C?C8J)(8|$boM}L6~(gLYi&J;{Il}YCbi38 z)Q1WNL&=DeOGaIg&MC3mR7R(o zt3MP{;D7hz<07~pTGN3CgUt8g2B4XCryKgb+3rJIdQmrowsw**6;SQOf-VBJNUZ`r z>9+@A+Tg57>8l5wih3bXXs5e|+D-0<9q3{t>+H0NLqM)?IX+8$;BE&KOmSFl8^E}+ zh_;t17I8T7m-BHLoms0^2;HWY4Yv&sM1;WHv44_5ndbqiDJr*w1`1H2R5KNl2b}}; zeYc`7oP6WlEld<9`)DXHESeoUK;|3-3Q=o;2 zP0FED22xWDASA5}r%s2>`eLL0?_zI-!Q*{Z5XuY>dW8KFC>qwC+O>7LNMg-1^R}3( z$A5w6t0p(Z;l?~PqkiO1Y!CSr-=kP34yZK^>XH0qn4BV0BUl9zXVp@y%@#SgJ)jRc zK7*PL3};=^x?<_fNvmXN#KbD}3Ny=LnnoE@RRkq40_AE$e?eB|-6G%x;1vQ|l)4;r zCBb2o53f`aGdQBElx75zji_XUHjRv$1AkQ@VSrBcHAhuky{KZ|D>)d^63AUB%vA%p z2;?Xn3!Q4Gn+uJ`QvI_W_*E#F8rUSNh854yl10q6)K@@$0AFGPtpoucs-wN|#F<)~#T`#b9Rp0@$i?emxP>c{gQ#0CT zdy!8sm@T6Q@98d|>6$I!)_D#6Wc$@?zTJusa}yJxY00RzujYB0mOtF^J;$os1-sU% z9z59WHV+=mzHoa_S9UMMHJ`>m%0=+QLWyK=*~4G~j)6scFp~Jw&Apvpd4I<{exk!pk1IEG)>XSUP6=0VlDXnhSH4+@(-2(CcWxM9}iM zh(?nk3PrmkMxgH76ZIsT(3v0vRg=mPz!*)5B|zA8r9gyXPNB6d+QAQMQE=z@=Is5IbW)i_|P^;R?1^8P4mP zuR6NR#Kq`qjmr)@&&IoKy0whUw1_1FCa!X0xN})T4kpaV(RpE>zD!O&KeuQ^zVZ$p#y8NG5_)i6BO!pRwss5#%+&`zr{VOI{9Q@3~)VU;@VTL<#CMA3L&S6MwGGu z4Q}Ml+aO!WmRyj_^kG9zu3MGru8}H3w@(1=Hk$2@3y87T03virJK8h8I^-9DC5EHD z=uzg;4RvoR8vkxFp{Q>8=*mKq;%{!Kq_i=Wy!PGJnMaI@```@{aTLPFBf6 zt`0xeP*3XA3~LLHL8HNVrxOAePd`MDV20myuT=_+f^Ktt+jqBlbHUSMeKVa^YHnUK`(=iIW6F3cD3kaA(YFdfJ6HwJaMZ&M)40{Lugj|M_ z#5yT=EvFzNs$$8`StWyhGqnNRou+(}q)2Qk%8EB@x#3i~CkW_+Vgb5YUTG^~OV#qF z89#L0Fdx^(|DCI(xnldR;zuK1CSE-OLrk?n8_pbDD1Y8&S%EseAE;=G6V1$n4!~BV ztHRF*#Bw@9V1;lqwyl7ujjc<&H!Rw0fwhH&aj-Z0OdZ4+v#K%ZqGvHJs?^c)sv#6% zDp0~Qsc9oniLL>PHZCg!%~OM@u?x74q{<8IJK;MPQjLO3;1tFs!yxu+)zFExyk;w! zU#U7OMt_VWJe7CmK!17dg4b4yWuw-%7yEwks3C+ANOSs(1~j*V5vVyaYry7*-{1$UkX*0}8L8 z0s3xRDJv_B?O;ZgowL^m)u>Vib7Y1`Y{wN%M}N_Qf#HhPsEoP;=KK}fdBdGIt|7cJ z+`4n^x@!HW09&@k1mvEIS+8j)!Zw*s%Q$Sc|Pv6q0QZoMb`gQAQ z=gk|kQj9JduB;N3wxq~oL^W9wSuy3xGd5AgN6-bm?Gq(u0mogGdo_%h9G(i2)^9pA zpntE!C>xk;uskq@lI8+R-3X~QEQvaE!i;#*CQU9cz0MFjDtsHF;rp7LLYpcB}AHRbC6!dT{ zlMozazmU*)aY8pBk|gpI9nryGLF6F7xc9%0E6i=hLM3;cWr97gM+=`k@z}N#=zmR^ z>;=X2JMUr4O$v_e1Yd=Wg5E_C(-0wSq3aM@UeW6_{-bTH9bTQwbnt zFoUnqK@ zxgZP?L8`egl-92z4B<@M%ztw0?S)R?8^kl^re=DqI$swQA>GQCg$87d(RqDbAo<)w zxksfwUPNHC^3^%4vgZRQW6j;{;vk7P#>M&s&aG4nzD3G6531AhdL^vQZ zeqJRUj5vd4mB0_8|MUFGz-!K>pXixiaEA$&E0=d@oT>W%$HNF8lx} zy<FVhnzSEXw z3L#w8lOdQXeqPn{M;d7ykFh(0;W4u@^7944t}+-2BQ{j_-c*N%ZDdj$iSf_q=D)|Lk5f=e_Li&IqKK z2ct0RoUi0tfB1(pZ+qL3Km5b#x4!k|Rqc7tyJxPy0p;HP9{9@Iga01?*IX%;)4#~X z8zo{LB?4{4hksAoN6*A3<5TcHe1&)P>h$QGebPbBxsAg+?|^?h4{w}1ckam}N5E#v z@P$6Hb8TpMls*eAUe9f~45}YZ9`p0Qzk$2s=?{MJ8$a)lXfJ&g&)qsRGc(wHPft(h zct$6Lfwk7kP3P9Y+C3?$C56V3$Q)BC44sfkN*^8B&3|45+NhV&kd%pVXvqM|4z!B8 zRSZgF7L9^lcMbQYVong5Zr#4VyT5zB)hL9zhKo_L-r52uE_!o&2G7UZtQOD3#IgTZ z2r|o6X~=?xuii9cJEj7(Vs42XUE`D~f?AmFUt3*Ml)>RD!l9ouF4=H>#*!nehPJlj zB3i56bAPnkJ-XjzswNEo;NDWfHlxZu;gqI|wz%mOQSwz6VBqiGNU zT?!IFjm^1j6>IbpojFzWSAG|Fi(49IL0da8xb)zTeHZC0HUjsvpKmYk=kO#$+zpp zL1dMyrSio`8dKHzsg-%>x6XIxYQ_FVooUR@H_)zOZOS)9XvgKiJ75Za@k(RPF@r57 z+aCYMW>+((!xWi<9;j}Q9Q&FQ64O+-WZbRCHC~9=vQ3Jg&{x)8_>CW+I;rmtN zAHU`P`|ltB<$v9%{`R}ZFQV^^zx}|k{ae)|6D+VPgJ&@imr^#tuOZDFWs}Y_Tu7IzcKaT6nXm0!DF)o zUHQZl%IU=#(dAOf<(zX><~zz05n&OA7id>k7Ep@Rn> zI+L6nGbp=YKpEZAiiH^mv|$WPgl|*Z>SgE~8@p?yJ@?GaY-nw@*2+|K-iDL+94LR@ zGiN|W|KQ$xv4^yK?!M<9WSn{OYB;?2o_jCIgL$$;qCYU11YoLBXM|oj^YusCpZi?< z;lC!^pQ=6d^=q&F`a`u%-G*Nvr+|;8kG(8?DjgYQ511$Aq9;|m90-qi0O9biy&FxD z7vrCN{;yS+?Ede})ODSG=kuSx9GiavU$*h~OLYCq|AOAs)VMjm>74|F)k*F_ZyLWH zZ!w8+!B_Ar#LK-W_tD&c$UU3;YU;%m$*=%QG6F!&4KiPj z7-fi}(@Q}`>TyjyJyH+UWCFmNj2K9(GYgQ8BmlKhZv^K1D0P>1!W!;%UY37lGVHV^ zt*)a1<0*InT^xnONl-O~FGCqamDO{RRX&@_T_`?@DafV`$*mM?SX;wdfhZN1Xt7qF zH^JOC0u)DULbWJZFQ7h&a44u$8APO17`W&trtT^(Ayv!NaZSd*RZVxw%q)|^jS}_7x?ANx2uJV%^%WB&jA7CzmWzu<>-@w1?FTe*F?LvUv@!DclSq$v{X zC{5*(5K&^>&R`J>)*X(#o^-pV=cT`>pOSeZQlKKOE--}fgJ%sw5g;h*jK_8BEP0)! z8|u3Z(-4T4=Vr(MCaCG}^zJiUrj(v7n<@|yao*)=9*@s`e>;C)K_4kgpEm=frm9@% z_3!<#p>uf%@MLi9wGYnK(24PF9(>?|^W)-^V(t{kp|nRh z>IPCPN~V#J-$JRKmc^c+w?PzCfoDl0w4rJ@GE?(53Xy%(^0-U%=n5Ds;cG@hsUQ0WiL z<$qL(Kv2{x+JC?lV=@kXvL$z28snL;w@k4b0_;w*xJQ4|PP^Qhz|SCqT~X@Im+2A9 zX$-QH$e?5@(z+wS4ijXthC!IfqFD%K2Z9ISIZ+VnLCGSV=~R=3kuGA7@B{myi=JxR z1_PrG>@Ef}!&sP#9g~w{Sa6)O%@E1wmwxaH?-6!_me$2At_}0UhTypT%m}%BQ-v%`ur26H5k`sV6;#?=UoI9F3le;zd zw%kvVfJFsbA4c8Oo1bJcGA>h*0R0czGDiuF*sfHsFqkq< zutvgwB%L&AY(1URzVu}R;ZCyZh6A{9lm^67r^YB%2J$-%xC9~avL8lWnZ}tSL}*YV z`FnpesWAhW?M0lm6KrMa^c37n!*cx$yH}Asoq>0$V4|k9%$ID#g2K$2Nlq|OxSl$IUMOrBA{>G$B4%>3zwq6^ogX&FXM*(De&nTIp- zoBdu80tffu4DGk#EVeiLtqAj zV*GfqQ1o3@h`R5U1X)o{GY?Q{*)o4S`6>eoEnX07s$qDBq1Hg&5km4BqlAK-;&~Gk zyAYI;CRa5Ka+3)Y8r|hg0X8)ajG$E-$bO2AJds&72#-sIF=kpitt?xC5$E@L28A*j zsFL;CaMqwi#6}z#jQL8*;zfmNW~&ZYw~Y3+DZvhvk7S$>H6Q3rrxXnmDkXoj3SBa} z6umJk5tTB6u|-75^Xryw`_R*y*?c(&F{|b~rga)X3}|*hEs7@7Dd1A?Jc!OI#v%<@ zOnj6d$M=KIk8(-w(%frvZvcNS*91Yain_oGiTF9kf$)=78cWNNU?3TA9c6(YC=i@? zZ>8p9DiflKh|jW8Gr$VsMC!;?5`oaT0)#4}?DS3ROQ*2U7 zb!~cj0X2&AHRCctiJ?*PC_+kQ`S7mZvZ5?3U2<_Zap`aDoSRnk#lnB~nFH4=`r7P8 zihVWWgZjF~qr%kG09Ubru|cNgmriDgj)_LD0L=+>3fi<-9TleuPaK7pePKAta(kx+ zdSq?S_I1l>M$_zHQ9J4FTdOC++_!dWeGVA=j}Q^0(NwJU;-c<&r3-l)uO}3&nHxZ= zca}lkfQ%fa63&+vbQ*s_@F#;d$yrR#_Y&y<1M#{XX3j-G44GJ5MQMIRSH{RM9B1bT z=K(yvy=A#TzJD5S-(HZJb}Gq7<(W?8m)!RDWc&7ghizS5R%#Xkja>ABR4HwC!f4rC z*jsA`h*LoSh|I0dQ9=8ZBRaQj4&XKIF@YSvbFxu1G?^W0sg{5B)yT0z7(^Rp%becY zhG`UD)d&b_iH3EVr6`6CdKI``C$)lUnBmdxWmnw{3`8rPPKEN#Z$Wn~Z>wzV9u1~9 z?ZN8-XHMr1=WdZGGmVx_`j~nDMva$gl{9JwifS{&7-VPV@NVAH>~x1|qRJ|2^}1R1 zJq&uPD5NgRs5yVF8}udmu1d6;fQAa-YR0aJTCR`L7sB={mMPJdykNG?LLeLTW(vY3 z?$$9Us^D(3Y^wkk2sRy%lmzdd1}Sy)rTk$~0G%TslRR6R6LfY92oZw`W;mk~LKM}B z3V2lYYC*NsOA5_ed#;=8N(#M7Gnz7W;Co;UjT$t(N zUJ8Pc?M!3}VI0IYQxTpg6fj)c7IStYWnzQQkwdu+;Qy;~XR~bdPFvz~nH7^|v37>- z)QOnJBrTLd_ht%xW%vRpSEdI7LxU+9rhY{k$o0B4)E#+J9S5$Qf*>KI#194}wGBF+ z#EqRnBOreus|3?{XcfDmO78=e(&|r<`>-HUw_pZz^J1jX`(Svd`Yrf2gJsKb#&r@sld36^bBgC9l99McZYr>-fn>8F171R?j;| z$OV4Sk@?(*a*x52t0=?)7n5))b^!&G=P5{k4IhOu3+v65Jv(&ieh0j#3G&B(Y>1PX zGq7qX*NsI(Q-o?*%0Q!*`7m@iK7PreBdD&$>KPYITtZA$TLVxd!(s^#y3Ey2Izk>v<6ssINq;$jQmY_w&=1MelCqljfPsyl z0kPS9#}pAyBQk^VQ?I_B{O6sQlS5B&@-)Uz6V!a?OHX6;47?lfXf8he6h4KmW z%kq1S&1y~S@v4GWZcR3GWOxGP( zSNH^9zMY%}ZZUEWa7rcD&SHjt6HB(q*~@=KFaLqA!L^sZJzm-E0;7n6evKB{^A8hYau9%W5Jfv=t5^~V)^rEa}0JJe`f!OW^K%?>-Jpq=&@YL+tYg~3Z)+oz6hCmU@Co& zcM-Q@JxtzsEy?fA*Lo!~d)XEEf9xYqAheH6^t8;8jB-U7$9kHr-+^`}Hd;hUP=ihc zy>%pG;)`g+I+2J(E9ww`ZEcObmz+BXALoy*e*F3I;qBjl{PFLjKU#erz42$SrbqWZ zzWV(0t4}|^{rlhFzUh<49|xU=VIFI_Tx$?Xau#ml0@Y{B9bLJ~soNQjdY~ZU*CI7m zIl4l(YTFKM2`{?x^udb{_w6gSZBKmSa^w^e&l&$S(mcbCOs#@{?jBbU@4n>d&Q1L} zPe>Z$$hFc4hmbD82tP1>`r(Js%ESM7GUpcg)K|NWoA-s4BlojdwCdHndfbH^W_&`5~C0;4*e+nPHrb@o6iTAn&e zCVDk$W+8nk3D)I*%AMheM*@P;~8)eMX&8m9o-G z=6GHlv8bVXju~99ZpI=k{6m=O#oR@?1Gy_Cr@9T9z$ZJ@Z_lEpjisoDu4YI=Mh7C9 zLZ9*pA7vM1UPO8}>|~x5nefnWN(jS6YN~YF#C+)sxch?|NG4E2jBbGD_b6^M%yoda zWiZtVT?CbX1C7P#BsD^S$hZtjInZn~aI8N8WNT=M%rb--{^3hMcn~ptQJ22E-5`*N z-=6cMGZCROjTR2>V8{mi&7NU!k!+kcJ=;_b(*uyh@k$J0aCt%p75rnc(~O)4be=<* zEb_yaOcQFj9nldmDm}M>-xoC+i;c$kPtiBWE9et{H}C%STTyqsYdg9VeX$M~7VCd7 zK8NlK#vi-$=z|ZSkBlEjf#=X(e|&a7WU@d$;e9-)8`3p=U5bv7tTS#qmq^**g5aEDj3t zBhD@5c1R9Oy~dMNLy^6EtLq4G`jRAeSGet4Y_=W#SU7OQUoW zag(RWyvl%{!7$-eXq*Yn19%8{Tcw&SBFip1rtcIDOsF-mlss3dO-Q{|tkICQi_Pj> zt(fNq=x)j}pDJqkL3_pEWcJ8GmujMafOJJS)$)R%iAwrap$m_9rlofe-LbgpX^!o+ zc*k!Rb;dnVfclJX01j0fjplkOR0RNQ5uc5Xg0L(=7A2Wygo$oX)t0?kK<)5Q>KNw9 zgC0{fLmClUg_`;56t)$LwYXwzXv_u!L=fsjzs}oL4bsT@DUL`f$IsSr4AZ@T1UhXm zv~Y*FJ0nRr!yTxHUO2jE*YwQH^sYURZXOhg-JB~_23||AdvST4)PLr3dNBS-VIgrb zj=}}YX*B-qJcj&d{KtT0R?=J?KV7E@MsJ$*JXs;qPuHDGce=<(AZ=rsP@m<3qH9k~ zH8$)iU4@5R?g#T=2UV-(y?qYl9K&2l0_~SpkB&jpDOSSV` zx|&dec4SN)>aKgc2^haYQ&$~t*y+|1uzhkeFAK(LH+dZ(@L_H@*!5TEZp!^V+KTp} ztI@Bbcc6#R$I;VLx~*ne!^3PPt;DO7M4>D}JCTuO>1>jz9iu^C`tTQjaZ|FZv>rhf z@(D%uGdtFgnic7bf_q1x^JD^ZRvReN(39Q-fKtOnoQ4dthb9+N!Tli0?AXmjm}n9! z3nuG#;cwJUzk8W`YdBh#Y0fa*a-BldU50624m14LN<$Z&rR)Kj^_^C_oF*~I5IN{E zX^w6BC2J_-%i)E*m(68=bBJe6NPniaqyv`bazyPk+%665G*Mf+OxhWzXR-J}#1{LFY0O<#XuawRY68zg(ESm*kQ;E7B^H=YC)%e!f;+FRE{UMON>)P z_wk+Bjs+2+XB%=6DhEpi1Fsr{-NOrkaGbW{`G!Euu@NcGT%?7?xj7?JF|esz1&&y( zIAF4ApismfN^GhaX^=qO;pQn05u2NdK=*=%7nV$!!`R7VFgZLU1lxZ6s6~+`Qvl#) znl@*uk=LOWh)UmoHNylnK0?}-$X%=1uB9>%7sv!8fhb{-t-FoTi}IFFq49xC8(Y>~ z<6c#I%oIA1eNpr|xgu8@E=*D7;$*Opl%P^oErOV;FcK^!G6sOaAXCg`D1}WJWQGyf zkU)kFHd*WS2%Eg1RN*-YhqkbYT##iRh03UqP9j2W!?C7+Voj~%)7Y0WXc3gC5YT=` zSUxayW=WhQ5QV3j42KHiNEguY*AclWE3XttU^6_2NtMG462;7me?x5FLDYB+)(PMu zlw}nlu8x1-hdQ|U<$~>Kf~%z~W>%6Fs9cm3D<;G$sg8@Osj#wk@ww&5c5uShOxH-daV%vbAC;8uG(w)ey&H|!Apo7|Ujf0g@A?w@l1 zGxtL7C7HRsDXP+Cw!vmTYS;r=2uddtbs(4oSYIIMGM<%U#81PK@<`hLqh6;cdV>+b zwb3MhRZfOeCs9_g*}(jiZ#a~0>=0yaSEMez%y$4!==1{ljza0ZkP*#+1mpt{t8iBu zKnJBX=*YEifU%Y*sX7;uX{B@=!dwlao*(!!0G~)vGlGV!qX^oX3|3J9{b)Hgtzy6AaLc3-Zfw-B;flY<*JQG{CtqNd&KZLf?d0a3%AxIJx8IpcF##+^~Yt6)0o~w~WT`$VW4~0T+SvvkfuD4EOFhz9govLezYL);d7R)q@^n`jU@3~H1u8}4dh&Q`n3f*!Q+W?t5m|vQevdl(m9%PE` z8xdi;V)~ZFbix&aFB7UMfP$&qgF|wEf{?sv3kE}1AOAkW7sCy)Dc1*MF?f(DTq9M4 zzPjgPyz3#`V92uQ1)nq?6OkLq3=S_%x@q@Qqym$jWGTYuqw|>?nTP4h*Yvt{PWO{m zGmzV71YmCrB-D|cQlu*iv#oXp68y+apLDHKT21fyx(tTmm0oOyo(x1gR<$aBMTQ*x z(2QRv2G|)8a$RhM9-*68i60nocLnGGL@-z)wjQX7=ec5OVKt7qVJqCsYi&58IDSjd zn})5kI9@&7Y$c0)QB72@d3q3cO)#HZzM}}&YioIvJ4*$!y0Da94|My2ZG!MWgVh&H zWa;^7Mo)hMS(u$aZd$1G@*mHCHC8rO8tDgWUpRj7Wv5@?*gUGsgY>vwU-^DRex{|) z=U(A25-M{YQQ-9ky{Jb9Bk3tc@!7@r`iDRLk;hKN1U)pq{o#jE=)Cyr_nvPzsFE~~ z6@cGJ#SIm^=sPHnoR_|d^5Y-MH*S6>`x<{8O^?4d9^>Z3pYtTSDtB*x?(?~?!E?a| zP10nOP%f;p5+w89|DPV%>0QaQuSbD^> z%Mui=(JZqB%-ptwKB=+m^|Q57P#A0^AUR1?S!Pul?n^gkNiVlMsh|s_^GANdMT>PeB!CZu#c0V}EC5S;n#uBO zpxK#Vj>?!XqX#x4DwWDoXPN5l+_2LaC zKpW}DjfllmLa`3OK|?Zp1p1UQN~f!ZZ8EgIx|_jQ$Pa?vD_8%26G}5hH>5|NB=}Ts zCux-uwOUzFX?c2nrV9!_AM8rnWoVD7f%b$}3M+C=W-c?2`Ia9X-wF?yEtk&rV_)I1 zD4s1jI~{<5tzl!wo`%F$u^DihF5Sfj04&7@pr=T8uCG&tc@*G>YDO22pE5wMSQZ#> zYPt~^H3dV^FHPfrMAaDFs~tJevt+owyYFb1D9qk&KMp46(wDiV%uthD<<{q&r%}sgRnO>1jHGbk?5_yMjv}+&>if~t{>D;6?el;_gLl~z(bid?e0I-vY9h43=wqmL@{GtfC)JSF^G=n=bsRoL`$8)AC7e5+yBv-NuO`BaR zPI)!~^W5QoCcvq34O5R`QE5#V%b*~CLjp`yjq@eIRzSV*Q#YJ>nG$3J0T4Cssw5V9 zW`Z6BVGG8-w=cg~ll$Aq2ddBrA*e9RT;|lsUAzbbc>jo!DA)7?f;`zW1}Gn(E3aRy z5Kv?aQ5tH^JG^K>#cY1LYQkAfHDgfL%oHU{bh*?UYE<1S6{j3FgH-c{fn5Y0qbOlb22Y4gCnkX< zcf(PE9r^fG8`9n(QmKKKa3xL!H?p9i$97#h9AeD#D9$c1V4fQ6hDbS zi~loz3O|FN#s39=L28q3x){4R9Be}aIPUI1{Z%AghP`cR>nL+c@}wcT1AaH;??%&q zMt;<5Y+jT5(j?Mb+&=F$UWEq&+8B1+bQ8QVaI+292JSF|)6gX*D7_--&iY zLLRt&0uPrrCNpX2ZiwW$Zaf z3_5-=5Tkxq41JM)cKqf@jO0foWC3|-qY)HKI?aTkQLr?Sw=DINo|ybdkGt@Hkye== z$$NL8rJp$`D+xp*Ltz6O$urrp=q@L{b>uG#c(PoNFj|K9Uu5>9FZhyI&woKF zr~z*4Oipxo`i=eNK`>fQ3!E>F(F-_rZKRfg>It0s?h$H&z(@#HgivXkk5DBRg4p7>07JGvOxaTt_sHZIh4J$QEQs;*Qci#rLx4la4|9@Gf_{&anm;vv%KXbu zNHIC?%3MCN%A_{|MAGvqZueA55i5g)x;I$U$)>MsEZ|_W>xuzyjKUM}3x2Xy3w0W4v*2HyF@t_dbFi$JKMU$)_Q- zLFNEM;Zbu@e=m~=6<#t5l zXHFhJ3U?hlEDfeZ^7j1{W`^vApBJsk-@kdM+&U&(&wlN2K~KSii1AS@Or3J{ z8kt-v#Q00GA*aWK$wG%80+Sj+CL@RV`=q`Vp&SkxOOts39UMO@ zdO#t07?Wc`SuhGmm%-TK@7Bj(KPe~X#POpk5uk(n(#c9c0WZO@tvxM||3fy#>E{RJ zt@r{RT%eQeK}ml-HrS8QekLk)f@jP7UKX#ia|Ib|Z#A)J|B5;GnfXn4mR)wY_}N^TE9R=X*)-y^xshH^ZUK<)Nw|sz>B8r9@pD7^$R;?zld4yydc1Y|N9~pI$NbXaBm5&}@50)tL-rR4b5L9OLahohzKQ~)SI*T3%5tA#2ksFzH_kT3#} zX(F~2hZ`(x#hf>rN=>mC)DCq^WvW@ZOF&wQoc&c9>yhvao-n`_@*q(h_f{`_{sWwZCi$ADh?ok5inrc63r2Ns zH)E}Rr!U{e*zR7edad<#d;P$AtF>;oRMXH}a-GJsqU*{u9Dq!k2XXo!;ioC*i(&qj zm;MgS1`V?xf?_ZmGqwcD6 zLN&yUZtF7wj2wq@`@){#41YY=N+aNMt&7NV#Ilf5wkuDR5>9rdE!bqM+Vzw6+FC(#_u3BuLkJ4dTgoYx)fc}9P!IK5sO|8=)M8oX(^H5pab@-sKw z7{z)i@O^|o-b9pgRafV1+m4!cY$bUYyI#$#!p?mY~@b9s?qZepx^O<3; znfmH}{xjJxe&m-=9RDyu&HwKwX0q{b@t4zmJY}R_I>b68@;bw8`)%QYjBT5K;}y-o zn!ECrD`(UoUMP6#;*~e!)5dJ0Kdf&t#@~NPP}^wMXJ%@NJ^q{K1)t+KB67c#`!neY zX}SH)Oad@p{3J~U@xyhrDMq@rY**rJ0eNq9K13RAZiSI8w@gEgRk`hAno=S+JCtiO z(-qy)mJ9ufn>Sm6mwGk2(m@w(3aDOpuz&`wk=*VzTgsO@b9t2Rf|#Z}4yB7E=?#B^ zJ_y^P^m@RfqjsmCq{^YqCw`IaxnTJ5G@)g46AI}?A@h`$)O5+yI4}~q`Mjej0B;qA zr8~;|dg_tj3ekayOu1}H4Q=5Dj%MmU!wMt$qACp61po*jP&H#Fm<3o%kV?M`SdTgK zewh$yP|a37Ehw5n9>juasd+=C<0gNFB9%tX5w7&A2$Z*VW|uAL@sryg62{RZ@EnS@ zvSF%#JUmr~Ad#ZUKw@l`>ZYMsT3&!Wdx2Od(hlf4a*b}ok%|)U?(d7I|BoZ1A`8r(Zb;MfE z?%)($_=pm@>(zxf{_Oa=#{7SL13lQ7$8+O%HPxA-!8Y>+gj~v_J#i4^|-Zf zu;VS)ylpi))_ml;i$|rXHuIZPhvufQ9d3J7XZDhoUfg-|?6w`(%x&2nqvOMCX66n} zY4PsMuGz8e?8%))y>)-dZ0A*aXSLnl-X4ExdbQnI?daS0?%DQc&)z+MjTQX+qh(E5 zj>^6BGSmmTa~j6xw%pxv?^G{K=99kF!6fOdGg-{l%`y(swMRjwiOz3!lwlPM$;6~I zT+mL^0ZkVyq)VKe>FE}n8)aD&0M0>1NS)7?l3teDk?o=6b$fq_j4|}H^$yvNHQxXx zFE-V14%3#yY?h}AqXA%GooYbH1dxpyEdY}=H=41>g+>_Vf;eQVQLV!lkbFCDcBP3H z)(REZjWuq#l8sqN4{G^0l%_Z*I-!ENtUP`ap;A%iMraB(g9pLbP(iK`X$}GBqOxfS zc#7dse6X$0>STYm(wy-FqKdrgm~kcFOvwM;*Smm8a#d%7dGC$5H{KDMkr9zul~q}p zS(#byM?Sk%-BnVzT573ZNNSKkAX$78*pgu+Z14camH?YUHWo7iF<`M?8-~XkynxLN zvSq+pW6O&d8IKu{B@Z5KF=jr-US@_}K>j+}^WVs<7I=SVt2{gD^JZG4vzz8*8<|i_ig48f? zo?Tz7#t3V-OfRwxZ>B?(N#FVz8oNTfm4{+)v*9d=0GlUWo&Q0HH|4%O#Jp#S@iU3B zh#laTcD8@PRv!sUY^==O()4ke&=bMI2OUipnkWY2%lM5rMuxx#wyEMpco4c-$qwxP zLKbyo>@*$HT#dQ8a_Dl;VLU1?mzNsd%~Xb|ifZ-PQ$gqjdVzzdoX5j1F-XJV*hq=E zNi5pkc7-o9Gd?~~Y_8;!Zs&Ol#&J)Sk*>6 zmu*yvb|`Cu%1UK1w4*GX8y#CH9qD_oD$hsFs<(3fY}<0wL+@gPmKxfz%e_M?|aEywYn(HqF~;o zFG>3wrr`HFCR-(8VQeq296h}nrd7qOkRE@A;}!#wTwZf7sVi)nH%UgBsX{s!g9q=Y zvAc(JKXjYfRHs?vPZ!=*_-Nrba|BGi(UQ&JyQXYG@5F%F%%FUy22YmGqW^K@Fwb~^ zV3;&FI|o<{o@xF7Af{vnz^)>7gT>+IBA8#l!Z5qVidQ|@8v1l-vmQiB zha}TTkR(68+=7N+$kF=#>gg)rtKdXsh^Pi}O)_1%#F#jhp&YB&+#r*R#J*ZwGPoIp zKQB44AX8xfQ5dFelzhvvxh)-b|B`?0OJO*+l8zk-8VGAS<4l=AjT4SbL+({KuReYC zf+tnca@UsUmj<>8>@CD3^Vkj@(y+F6uCY85s0prVE^NNo2~B27gE?dq@X{}rM**vY^AOL6XxPSC$uu8&BJ3w6>ijufsc++O%6g@3C3ZE1!K zbO>UOo!D)uV%8gvW>GhybD4fHwBy<8jGD>tZDjDsy`%e>g~^l;_KTp&SRPWLCb;fD zx}U!;4wGytbL?_uE-nR@m27|RYz`OOyWQ8PY<_VxmsKsNTxvD0ydq4Jg(M;2giz7% z$Ci^d(zF<#?Dp7leYml+Sq@62`Ov+x(JGZ4tD4OX>vQ}zx@|E@<_v`GoBTspvve`b zLZ@V&q&dfoOV5e9+S<7T#i_+V$PR^>jH2qzy7YW3#wT4O1S40m_^-dF#?tuq9K=y{ zhi2ll#<20VXa9!XKlxdHYVw=x9Q)}vKKL)bd+{%eHx*`M_dmke)R=X%S2o3}YfpcJ z8d&t~v&?_;$;oHG^re3{Jo@PG{T{n`=be*R(!t)r0jut@)nn{n@5y8PdwXR4?|p8s za9EzHaT(VOrwiu`cj(yF*B`E8$!6}jc{wULn?Yj3f4~GHiS~kOCHjr{4cbs5&DzRZSi>1lW#pRVbm;KYZ)pli}JwMlui_3BCZ2OA6_Su^AXCCR2*`3X?>zz2) z>zzB{`PW|9wvUJffIi;+LLdsEH|z!l}4jtd}SkwS2yQlZ*R|w=PS~394lR3S!#E^y>om1 z%5tY$Jn@3je|3UpyYc$8Uf)<;O!JQ^PT{Zf&l9E83s)3wF1)z#tA!61K3@1_;kOH) zFMNT<;3!kXn)SAP*33QNXJGrSQD>ZoJYb7#nuQ_{cG!Pso%Wy)EkwI=Y&r|A-Q`8c&=Gm9zc%e!vszpbX0+unm$;wBR1{PqzA&lL z+i9< z*zbQn&c(~w_dfTESA6c`jr1pe>9cn|$BdVM?zvaI`4u0!Y2w~E)6JN$3Y%o9-bJ#c z!18_9bws%9DC~akxQ@Ni8_p>_Kkx^*RE~Ep$wz-Pm^?I_b7j}R*#Mi=1MM!+T})ie^8D>#|`e=&o3oI zC%=^6{Xe^u8{Nhcr&F))ukKhPg5&K<%l3uiOW(C!|K7&3_#G#VN&yeQ-fbMUfA~^T z2m@1Jdf(FJOR2kjz29 za`Pk4QV8_7;TRAMGy}(&j@|q_^~^9qhR3Yj-|eCQh8n$9D(yX{mxc!2VW-t?r8o{i zptO$F;Zb*}JoA8P&K z@OXD;-VWUPV#$$A_@rGa>CeBgb9{eJ82r+=;kZ$^9BbhPbr1Pg>X!UYRK>R_Fb(sQ zGL^9%1k#LOlLgpGYziP=7{Z86?x<}#kL+2Ya4rgg@IKF=QL)XEDUXt#g!LAmA2*a% zB8}!I4_jDlTNzUawt!;d*41=_WRL+G>AbC922F zhdn84l1i_B2CnylrX?38b`#YmMr5~2zGmB^M3z_Vvn3-k=~Al76I9a}=Cdd4FEcDw zNjf6lD`H7iCF4on0HZg8j&EH}`lrDZP|L2wZlF?HJqYyN+7=PX-g`(#d$90+;{IPK zJYD!&;TwhjyYSx&-z|I}I{|;mWmpqUDqDO!^Tf)XxpIW7X=KB6gH8LOYVJM7O78Zs z?r<38gE$nGL*v(ZuEuo%1w4xf((p9>skqm7v`0FiYJ7-ImS!frytE0DQ!+iuoOv|n zYpJ@V+4AaTU4n!Yb}r~k+|necTc^^t8h6@bBIU6@+Tov&q`=LWmotChj)t|J1tvRj zwOZzBM8+pvlbBNLwQ9ALk+R>bE!DGm(rI|D@5X5wd)3m?r%h5Je49v7giD4ytW|w7 znr$KMz;j6Vm+`w}yLP0WrcpIy_j@soXY6^^Mle?`c9O|Qdo?nROG{NhP3D$NjTv%O zC~g~RP_2Y>DyrM(Cr^Je1TjdXUMVMDW|fwtSpGS`nw5Jty!u&rEryG=QjV$;;rnM! z%c@k-YYnSt!D+vJ#F+e7dQ*Qx$9-904+``6@fB{mP9;=T&m~j7vwzC-y=t1oj%#r>Trz3q)r++xBJLU46`6Ky z=RZof=4a)sL`J?{R@II?RV)=%xSP&JWQhr7+LE*}HneJ%s`%e8(5D-3s-RXJs$(ad z^2<-y{zBhx`=o!4%wo;OCJ8biDlNn6DK-Grj$p@euKOx_t@EzlnSk@6dYs>P>7l2$ z;v|WG;dg)LHLrQ#^Nnj-+xz$I4A#ZwrH8(KUEGR$z0F;=**kgiTN^9m^w6LCDbjyW z0ZF@^PpGM{Y`%}s(OGbEov1gplZHADQKREb`8TnB*64rf05En4{E4YJgj7u6aK#_H zNbG<*F>{JQp!Dq`S&)~@vUrJMHnO>OFZN|h_9G)9M${%IUsisr zT=mLHquiaB$6oR^JKTsaeUvPnpt5-Vg#|KPErl(9s@Ghj7`i^5Dr`K+CAuIxO)kh< zt$q+KC3b&1E-8zwE5w>s_ZK>0*_Ohbm*R{!*kuc6OWET3`tFce z`$U8zV(L|TJ@U`4v4Po%RV32<=ZXDC%o6_4TkwB&m(~`yYD>PMZ7iajq1a$AW53G& zjQwZ!JtPm~Wxl`>=a^*ueU7+a5}TxPC~W8?q=)nu1>|p1hJ?(cNYzXONs6~1Z^79;m&|AK+f9!2rjc&%Gu}R zJ~ou7(9s^e$bm43eW^ZK4MWnt5MMJIY6o2;%%tPPd^arLR_Yz_eOBi>rLuiL-6&~ITGnOk?9pl_j0^{+z%Ik{-90eD7|Xlf)ZM1RHb1C54s*2(Lu+im+>!V zppNa?T0}fuzUXDMIB)(TANZ#KC}OA&BEmRN;ri}Ky3jPMDO8>soTO}N*s-boune<_O*3M=u^?^6 zR?t0xqM-o~~YR4fTu!(<2ImSGA9V6j&;F>OcrRlQRRFi-8_p!Yggtik> z*GOREQel=snvPy`1{#pVLX#wt^8Bq6BKFM4tO+mP?MimX%N#+Lo9ULkBS&DL{f?xG z?J1u-RWBhidF^|2l?yn40zlnm+Z3qBaq0q^}OKQeWyEYlcfYCJA&;n3ndEL!Mw) zaSLH_aNZUv)+;3mJ8bHitmYUr4)l}EN9v3!2}s{YRtTe-3b_c9fUal9=si6WmuvGw z<6zJ*Xc`k_YZ9l5B$cHH2QGhZo=$hhbrlSFoX0jvW4fvKe2Sn-pmiyb8c`nxpEgmY zN@4pmCT>nPH5INszUY^_LpUX*9ta%-1j5U&C@`o*v!PfeZ3?X{*CP>Ip}Q7Pp)`=P z)KQbK3M-Q(ha~t46Z^!DJ)$tR3T+tVN1S>!GHm;AOVpn_mnGv-z0H65!|U*XCn5`l z9wBe?-|*==^YrG@aK2BuPULap^ z`KISk4=X!zgW8JeE3$v-Ev!ykrX4LDBqWlly943*u{2H3Cn-hLYxY~1Rv$2n(lju= z$(~H$k?5I9`K2JKB71ur5lf)yqD(5lu>vy)=dA$!XxJq`wn=UEv2((>XD_sRTugP9 zXGK={k;1F;JZ$+YKe+0_OzSv6UYFSx)_7_NEY6UW=-+hnYKnhhHnTvrs})H!eGk$R z45vr)hwy%>JuZdraG6YJ8P}ZHv(uu*F@KD;#j2esFI+u+$Ekf)m&;vFh!SZSMKoD@2BGTU#ueJPKQhOI`3qH*2nLEU%k} zGD>R=r>rc`29|#bcjgzXVYHDhuN^ciG0 z_|-9HWxZ&KHSCxyEvqz1jQLJR0sAL$2YdHLaq?67U1{&D}D#YO`!En^S zya+eSlKgnS&^H|QXDLZoOaC~ei*X{S3Dza4YWxE$lw}er?>2wCTby*M@$=&gAGd>3cZ`Yd{1TjJ{ImK}L<^TKTxwzueQi!W8@DuWmH$UZ!I`*=0p>B24e z5rnP#vPxoHJ+x`c;MzO4B=fd#)63}X{47Tn|3<+pq=hBDVs=&GeBl616mPkr2`3*OYM9^ilagKBf{z_(-DPw92wU-msSg(ND6#vUM} zmChZOsf7ALC*`5fKjr;NdvA~3J$dBWXW91T%R(YQ*y-)w_R5*n)2o-)I*T+<77M$u zP2wV!B6OUL-kvY!{#MA_+tQdO*$mhfz7klk09a{jG=4_7i*5E!lD4Z=Qwe9WoJ@c2 z)xX3IPPq}20`MgKhnl7BcV2t;-^*AYn_Fia`i_-iNd(=%n%v6_)=Wc#ehGG~S6wfY zMblm{A6YBKZuypSR8w`=r_#lzJR-s&jr_6ZQ5Y^2e9S~?S? zKD4LfVCWG0_KBh;f(Gn$W4$@JyT5rdmYV$Ln|B1lPt*!mM8iln& zzi>=@s$%D^+Zi4(H69JOSvJE^PyW#tG6Kmo6s+4G_v>sNvvJ10aOTVhFK~8Y@&bDM zQLpH_Meq6V?8+DIRC)F)mUJ$C?M1s)mc457&CbvAFKmu_Y<_;@;az_|Kezcoi!S%P zpS1Ij{1?0P1uwqMpS*%Svf00I%k2Sud-vvc;W8h78_C8yXIMc~FyPtp-caf^12bd4 zr{hUUNDVrg;T>>&e33mbS1E{oK%+lJeY`Brx`PfGB7Ujl#N+PHqR&FR6xg9bwve4x zqt0%1!wWbhy(&*0kJf)I0nL^Ek#R-RB&n?pN`9&6k>)G{$EShuRiH*I$Cu!5$UX5^ zDaq*CyKwQ9y(QP@+_Q?NL)x36>bu9UuHUrVH6&?WJdTT#Cl&%MK`Q`f8-dxp!n<*B z(@v;tCTBIVBdD;6s>FJ1{Bmd+s(wY+}}N~V>nydOw&6RF;U z9~`1x4X4uwQL#F6K#oxhd4RRueiQ!refWm!6a`ZFcCFT_cYgXOPj*w+wOj3t*5aun zkDgfki(=wccLugB8KSit+mVb)9V2l{c0_Wtu|E>TUtHyf>P^Sbe9zG5YI_Djdk*qkpJW?Qg%vU*BI%kh{bJs@f> zCuP%a3Psir*}K$((zT-!=>P`oT^)F2csprz)@O5KLFaa&SppCw_^-obW!xw>*egG_ z(du42w>6htS3kk-n>@tdTE8xv+dOk|rL}eEfo#oA#CdwG< zy=*#KfWweBARndn^-yiB=ny@?et@~uVmVB>hWp&b(*RHs7$25al*{WGo zWF%zOjZOYeSbeeSO55^1Zj%vdk~UYhou)0;(cCicCT1l zld+RHQHe%RdcD)(VmR6oH8Pewv3tuiZVimz6Lsdp*Wb|$o*k5n!QAw>|X~`=h6xx^(=hr=EN2 zDgM-t{mI>Uv9t8{5qe$LF<#0aC(5!5&BE!zPe9K0+g+8x$DPzBc+O&5yWef8em>py zSe$vQ}er=moLvG+E%d1%KRerk~KH@zp#Fnpe^%c8&XV5>4WBSj%G3sdunP z*SEb>6K~_eRT93o(U#w~+rls%yXt>uSn?I`bU0asy(}>VZ`;q*>tFR&viF5s+xER- zAo-b|pb6zgcSNGI>f9Fvrnu^e?S#>t?EQZ$r%s7xdu8Q|h8Lc`FIupP3r@apGk2os zfhcnL8OL)fcdkCUdgFZgRi{oh+uV5dX`zI<+BSy0jSXhranZ5chq@H}?~+2NaDBec zOC(P!6qrvVcfT7KlXC8$Og1JQmh~jjP6>y0MhcYjsrij{z0Qbz`%~>BN^G~u!k&Np z%J$w?CuO(UzHr^iuX&C*BZyzDyTqPX4;nx*Qnn$Ydc7TcOWtV`!@}QcNq_Qa=cp;J z*kpIC98r_M{Z#DR>^9GFyd&p{RT(LfR((5#^Q3T*#wOWZ?^jAZzFP^~e_s0#WpM^O z3O(JrS=00w(y;1ev0VExZ;8P^*Gqp6L%oBAe8gxj*KipSVH5UW!g4vBlJZ19q&d#6z`O}uN z%8OaCLPoY}+n!I8-xVe`oZIEabiU&|rt2$T8AhPD4xiHcbTJ}f>aeoKEmCjLPDh4`xY8}Xd6Xf%yg<67gK@gv5ojW-z&7(Z{k z!}z%IN#kD|zis@1@r?0T#@9{P+%nIb7tLGDe{R0d{J8l^^Y_g!nO`x#W`5oLmL$_t zF3DxNDzBC2o$TK6Y!vla^s=;)x z0RDdqOy*lBttOorX{gN|vei{tks^=1lxAn3611h4;?8ky2k2U0EiPZzAC=Rj*Qd*O zJ7W}`(4+VW6(;u6QLyT=>GQfx;A|<}h`JH8>CjPx!et5Ef?K05(p!(z&Y+KuQ976> z?T*rX0ff2^Q8s_jxH8}iPP40xI{KdBwD+iqyr0T4+_~ZAnskBw&?R1wKZVK!VAwbt zjHy{zF$;N@>M44)%>{TO?9>;%lwtk0u+|KS8 zTWfeC(n5>}Q1g;h0po?ZE8>Bg8fRe&z6eK5&`DXf0GfY-dq(4KFHx=j7-kv0x^M^? zK~3*ugHB(S5!st{s5#h88BuvTdJ7;f=>DDKbhITqqimGz;uKkHV*oqiSqZS%$Ad9d zpI#|OjR+V^Gcrq%hl;ATJMNbih+AhYqnol80NlsKSH}SS?CvHls>!f}#N>)s(s5`P#vDcy zp;^d~HR_POpq8sH5_Mskl&P!8X75r@4@j%e7gc}zxjKkRtQXn1^$^jmT?NQX-OOXG zPi17(_1$F9)dPh-hQ~Jc&}f-9$>J0g}$FSqMBINcL-B*N=vAdxykVzV+fBS(4j$Gd;|elNOZG)(td2S6b-gcvz`YQQ?9)*?R+ zeN?M|)KLR)Z?G;<2?hhM0K-9#CMJr>s7gxjg_B9q-8sexYYRk)80d+E5JbG>Gg4M2Zh zmGz2bHe!NSC4(ZV}YCU2_5|c%robqd;R3eBWTso+s#0gr8)aj9G&K8AQ zGj!PFY}1oqotub1!m*+Wl8~~YlLnDlOAQ^< zP?Y8;NlEGTB^7^&58_$Wv%MkF7sOi&ctwyjyCb3zxt+U#(llt1Fr@(_ z0@2I-$ajioP-8k;pUD*JN!4r-v5)u2)Z%?~R@Mj0z>sR9=_5@Nu>hj#a@N$e+TYQM zLyM6T8%5&&sp+r|`ATdCzsN9M!!+*z_Q;PigPXQx`$mAxW@gcCF1E_KWlN1e zVoN}f7;v>FbI`zA5%Yf$+rms~x9AEl0uBuF1tFiVmE(lqDMBB!xZ~4_8rQ(VR4y|B z5N2`&8vr9GJkRcE!sFsjx|Ng0XaIjJ*hSS1`+97eGN~;= zJ5T!}&>g9cKBQAl4&c*eyqx3MAh_@fTt+`!dJrBUG(wUT!Hz~@VWy#v!c{=#b;DB} zM`%BR{0fQg6VsDoiUow{Xs-^ge;SI~c^(BZvqLJ6YF2Z!FxquHEF_sqTgl=FNRWpp2GWL1K z-pe@Mr(IZdQbaU{O8p!vMsFWxZFITDuhF;Z1v@>?Q&bytj(CFSr&taH^<^?7*(02< z;3+HY-E{1;(~kOGU1xf|RUjl>lZfsFAp-h1fzrN+y61mzQaRS`r*toIP_JNlKf~<- z!-P0%iGm(x>>YWtK}GtRn)0bxr|8RZu6NW;U43DS_i#QNJdH&ybaC`XwfGDp4OEAO zGaa0+Kg{WPVtA=V>`&>=FHcM3?2phL7+B@d0fgPNZN`6@vG?fnC{y%JoL73rS{Z$}CI5AFI4b=m_6=fG zIv#&TQ#Qs8H2N8|P-!F~{+MnY9W|&c-;T6AXtRG|8F?FU69#vev8O;n^xS_&KP%Ju zVo;-WkAsXgP?up!{#$sSvi0~fr0q7}HGLXmHO3wQ$I~>DffH&ffdhtY@Ejs*I#HP7 zuOnuzrxtgxd0?Y40xW<%#d44)Hibl^xBmm~HHcLry>T7U`IzmxX|+^`Vv%T-Ae|0lQ}c>h?lq9iUWr{yUeW|}<#%?9~ zU}C&qlcQ8g?y}c1)BFY9!yT$BUuGn{s9G)dE-J*fNebE6gHaU$^maoi+ko#I(YvkT z>Ajew3gG0;MDS*dxkQJaa_slRN$D4;C5GaTZI~I7)hh8>Jx@9FJxnob)5A6d#V>y{ z*1)WIG5ogmd~0ibHnyh0w5VDjJi28El|`UJ8TSM9wKCKJ#>-ULB&4ns>jJV` znhBkPvw2e>IVC*Kh$vmmYSZ*EG>$12SVUtP1SDA$dN2_&k5I6ohY(cGV}vb1pwZ3NSm<2V%a1@P!Q?lFC zMSO9?3c3zbCfH7B%P5ehK-_<)VF91OwjI*~4cRC_vdn-Irp290`h^)NQ&t2Bk#s5F ziyDztWRcbGSRpZtH5I58iA=@-->(uOIiW3_brPshdq1I@Cam#uu)K)bSPiN$f;|RE5rWX8^GY@o=W2H7$R5j52=_5YA!X zEH*I=9!3aSo8f>N5ocFKL?OqntKlk-I4ZaaRoa2?s&K4?8-b+)V$AeZNE!>0(tzKi z=EOl+Ol46hXfAS~{lQNb880NMu$Hc~7D%Qfc3@IRa-C<#asgp)xGvMcw3sK#mC}`5 zgaLIGv#>#iIbxe43gLfN&sZdZkicy^S}bba4ADd+AB76wc5gAY2+Ui|aAiVhg?1@M!2oNrV#cz~}~|QcHho4H25Ci3icVqDg5< zL-|CK6_>aN69M#O`n@MVV$CW$HU(B&L>MfV(kOE)i)kku98fhE6V`rI;QWi9L1C z(_AIjsxitIhXa4KfU|B9za-E{CA0NlSeX8nPlKugncEQ@IHh2)G${W;ODTQ{5uE7*h(&T0zzz5v8Ev^Z;PWXl}rB z6k#SLinL$Wg1w9H$2?ZCK za;RTD6Y1DF_&m)~k6E4-!q168W^kA5C|f;P272DqxF@ik6!cBg@j#gsi&68n!>4ID z#3GCUDlAY@aIe%_lZHmcn3>vJ9#p8!L!`w87$AQ%X$V!OFC^90@R%&>nmG*pX#%Jg z#%`T0!9=y7lJi_&%QfYh{V@BPzj0$~Vn9Q8oyzp0Jjq9BDJsTCV%fZH0eXv6B4KKy z4C)(6U}_$TDw$i5AU>v70QAEV1PubyNjS6-s^t%#sn0f4PK^wzjou&roBCud^WQ93 z@V|d#ts?y!Hj-@6>CV!!A-o51ye;joks}S|3A(19E#tm+;pmM=>>Kx-znj&+Z7Jzl zW$UU=bG7}(mG)`dDIZ&kt;m}@eq~(h?4MZOHqNB>VzEgY+)quOVEadpe$|ag00>LO zwT1QVTCLUC3Cd1bNlSs0rd1m6R=QqosXc#Of{7f@^5=eA^b321bA>w!KT&u)_TLVH zQ$jX%M9RbJ7dZlHw8u^F(EIK%w6d>#1>r&BQ-2lR1Pt1rwu@+HxU_|C3oHwNf6oI&C(?K6juW` z2rRAi^IL4?SCY(erK7^R(1dRvsi{RPNbPd$d$xyMwr1cBx(&k&w(sA*adCwzbnBUB z>=>1eT`zV*G8u#T%;utPDCLwo^_G7_64*W+T6DXx(`Fi${bF{f;{|BedhC`Y=>k4! zV74>!_X$N%hzpBAgq=A*>Ymgd?V{K}U^~bSI$+;E`{quUv$F>$&Yyjt-Qn!q!Bzje zxFp0!ZsE_IyXxfmGe5K3!RPfqyWC+fY^ zg+<{OeQ$MJ^x0DVXL#@Fm96g4P7QyZ?MBV?oyPxpWP8oow%1$V0$pzL`tyy`%=kC+DewW_ZhYt1+&Y#%WKiJgV-X-fvbk& ztf&$*8+KJOOGG zevWeilImp5nSVU8ne2aaM9%>^Uhgm@hq#aZlqgVV6E`(*RFeCQ;e9mCL_3X??slYh zDbr4@C>`s2T<5=O{9HEpnv7$k9?X6uI`OddX6;c;%QC+ZGQSthd?784&Dd`MW(c4nvX5<>~ z0#GRaR+YRpHEt&XuUr-RbEb7X(23SgkQ}nKIYmlzc`x!}cBSjSt|B*UWdI~4WD-CX zBCc&U|8ybUTqu9Sm>bRSR4f=0Sd(IlVxKIwV!cL+uPrK7YoRrNZg8K_KtRe3I#*LkDX*o{O3kWU9{AidpE=)6G^m_al(3Tn^_pi&{fd z+-C=^O4?h3y=Ys?2t+XosMOP?u*$A84PTT5nMx#>sE6n;52A2|@kaGj-4kA~igZ);Z9Ax?CIlZdfE zUnL{0}>iK89|3%eL@)*)}w3`6k9Ddq=2k3!K%O0PLfuEY)-jXRV`uZxbEPu$eZ+ zLSd(Hy6_P3$M+O|x$rB64*;PYp$+RS!Qd&3Rk1hfA99j?G48_?k;rT6x%k7c_CSYf zBi~)CNvy32uaYgz&k)_5=nY2=Sc`b?-XMR+vmA_bzQ~b|r@zQp(i#?r{RAo9hx8pg zZer7L!f%H{X2m;&&$hhH-b> zU22O+5(i_|n8vgQBUSp&wv)t;i0Mfp1}rNqGb`|6@YF5hCapX2KmLWu&7|)}`QhK%bsNZi%;hmQ=0hcO#mTI!EZnJ()TsC>%6hu z;E(ew#*f`~;?*zB(%R%By`v{w5)9`LZn`FGELAFv#$mkvKk#oAUQ60l?n@2L6dE~A zX8IWEoQBgtj9iP=PG@-Q=@6Pcjcb|%U#fuT08%;}F!%KAk8~#9snpGV!*YLxxHn=y zW#t=QYA%yL4P4Rmtc;WK7e^_LK~dvp7=Xo9^=&do0|$BfY{XEKjaV7OvNsZ%@?>aP zt$C~DgrV*DcC>o)F|yEmNNMiXT1}F=ZpmHThn-Y##rizv2Er`g&W~JY; zCBI49w!HLlY4g`i-pYUOO0?$(y)x}F!FKhRYT)S4GFDvo zGc1i}{{IJy*;rEm004NLV_;-pU;yIpH)9UQ^VKqT{J21W){Fbx1c;{y(p1ce|Ct;2Vq z><6g@0E_*GCX+FRx_|O7EH7j)k}&8oJTcBPt}}Ks{51kK95vuI`Zh8)@HiScL^$3# z@;iJxvOGjQm_K|#2tc$z{6T&}+CpkVkV5K1PD9#6LPXj{bVZg%ct)H@Hb`G| zoL6jDpjW{i+o7^@Ky>~L~?Ilf`GBfuIDKj&tj8|D2+uD^SonRqlW^U7ex7yp}x9a;eGnU7r zd2gOKHaFI5yz&3*1UA7?phSflBWz)eZ5+TsoQ6X zj|cD|9>T+T1drk|JdP*uB%Z?4cm~hnIXsUS@FHHq%XkIH@G4%z>v#ii;w`+5cknLW z!~6IEyZ8_s4Fn_zEjsj2m|zNx0R|Qhd+-Q|NSI-c1(rCDkMJ=*!Ke5PpW_RBiLZa~ zHNL^O_zvIW2mFYi@H2kFulNnW;}86azwkHy!3q4kfnz0iPE*Z&C+*xY9)^-9schNs zhIHB3IeFFuU5lO2bMGJu!KXA@nO)brBcdlUZR}zvnf;LD=+sdmCSg$uZN;?Cc9J|D z5LVKT&}!h3w)YNE)zX|+)P;M(m5;gySw^iY!b z)>jm?GI2z)Cr@dp+cgDs%V#QA8MVq&!voc;ptXyt%^1iRtu>K#Dpq;-so8(*EbHvi zs`FfGS~;adot^9VblLf2V$mh-Mm-l(%}rPIe9+@QPlk*#U8s8=rlfT~Ur?2svD3tQ zS`>X8bgepLJ;`mQbqdk*46)4gc2p?S+A?#XL^0u#vYGMm%B+#r6Eag}LOD@p!i!0; zrko_Lcsb>M;MLT|jK9`OkO_ZOX|

9BVbB_4mbUuuLaD8nPH#r6~t~y3$cO@EI94 zNIM#GQ#>nTWN5y#N@7M0%(!7Y@7X3rAjWx!y(_ zdh1x0UEwlO<7{GU{h;jFt%Mr-%u?;VosfuC_S$yLrLJ-*bD2+;vCn@X_XkjV;v$Z+ z&A10$)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.ts entity.name.function.ts" @@ -484,7 +494,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.ts", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.ts variable.other.constant.ts entity.name.function.ts" @@ -868,7 +878,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.ts" @@ -1108,7 +1118,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.ts entity.name.function.ts" @@ -1248,6 +1258,9 @@ { "include": "#return-type" }, + { + "include": "#type-function-return-type" + }, { "include": "#decl-block" }, @@ -1431,7 +1444,7 @@ }, { "name": "meta.arrow.ts", - "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -2466,7 +2479,7 @@ }, { "name": "string.regexp.ts", - "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)*\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.ts" @@ -2625,7 +2638,7 @@ }, { "name": "meta.object.member.ts", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.ts" @@ -2716,7 +2729,7 @@ "end": "(?=,|\\})", "patterns": [ { - "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -2749,7 +2762,7 @@ ] }, { - "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -2785,7 +2798,7 @@ ] }, { - "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "meta.brace.round.ts" @@ -2973,7 +2986,7 @@ "paren-expression-possibly-arrow": { "patterns": [ { - "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -3057,7 +3070,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.ts" @@ -3677,7 +3690,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3889,7 +3902,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.ts meta.return.type.arrow.ts keyword.operator.type.annotation.ts" @@ -4208,7 +4221,7 @@ }, "patterns": [ { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", "captures": { "1": { "name": "storage.modifier.ts" @@ -4726,7 +4739,7 @@ }, { "name": "string.regexp.ts", - "begin": "((?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx entity.name.function.tsx" @@ -487,7 +497,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.tsx", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx variable.other.constant.tsx entity.name.function.tsx" @@ -871,7 +881,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -1111,7 +1121,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.tsx entity.name.function.tsx" @@ -1251,6 +1261,9 @@ { "include": "#return-type" }, + { + "include": "#type-function-return-type" + }, { "include": "#decl-block" }, @@ -1434,7 +1447,7 @@ }, { "name": "meta.arrow.tsx", - "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -2469,7 +2482,7 @@ }, { "name": "string.regexp.tsx", - "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)*\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.tsx" @@ -2628,7 +2641,7 @@ }, { "name": "meta.object.member.tsx", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.tsx" @@ -2719,7 +2732,7 @@ "end": "(?=,|\\})", "patterns": [ { - "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -2752,7 +2765,7 @@ ] }, { - "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -2788,7 +2801,7 @@ ] }, { - "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "meta.brace.round.tsx" @@ -2976,7 +2989,7 @@ "paren-expression-possibly-arrow": { "patterns": [ { - "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -3060,7 +3073,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -3628,7 +3641,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3840,7 +3853,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.tsx meta.return.type.arrow.tsx keyword.operator.type.annotation.tsx" @@ -4159,7 +4172,7 @@ }, "patterns": [ { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -4677,7 +4690,7 @@ }, { "name": "string.regexp.tsx", - "begin": "((? { public static readonly cancelledCommand: vscode.Command = { // Cancellation is not an error. Just show nothing until we can properly re-compute the code lens @@ -47,7 +47,7 @@ export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensP return this.onDidChangeCodeLensesEmitter.event; } - async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { const filepath = this.client.toOpenedFilePath(document); if (!filepath) { return []; diff --git a/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts index f06411ec5..4e5293e7f 100644 --- a/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts @@ -19,11 +19,9 @@ const localize = nls.loadMessageBundle(); export default class TypeScriptImplementationsCodeLensProvider extends TypeScriptBaseCodeLensProvider { public async resolveCodeLens( - inputCodeLens: vscode.CodeLens, + codeLens: ReferencesCodeLens, token: vscode.CancellationToken, ): Promise { - const codeLens = inputCodeLens as ReferencesCodeLens; - const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); const response = await this.client.execute('implementation', args, token, { lowPriority: true, cancelOnResourceChange: codeLens.document }); if (response.type !== 'response' || !response.body) { diff --git a/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts index 8fe118de5..57a9e599c 100644 --- a/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts @@ -26,8 +26,7 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens super(client, _cachedResponse); } - public async resolveCodeLens(inputCodeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise { - const codeLens = inputCodeLens as ReferencesCodeLens; + public async resolveCodeLens(codeLens: ReferencesCodeLens, token: vscode.CancellationToken): Promise { const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); const response = await this.client.execute('references', args, token, { lowPriority: true, @@ -42,12 +41,9 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens } const locations = response.body.refs + .filter(reference => !reference.isDefinition) .map(reference => - typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference)) - .filter(location => - // Exclude original definition from references - !(location.uri.toString() === codeLens.document.toString() && - location.range.start.isEqual(codeLens.range.start))); + typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference)); codeLens.command = { title: this.getCodeLensLabel(locations), diff --git a/extensions/typescript-language-features/src/languageFeatures/completions.ts b/extensions/typescript-language-features/src/languageFeatures/completions.ts index 73f54f20c..e316dd6ff 100644 --- a/extensions/typescript-language-features/src/languageFeatures/completions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/completions.ts @@ -62,6 +62,13 @@ class MyCompletionItem extends vscode.CompletionItem { // De-prioritze auto-imports // https://github.com/microsoft/vscode/issues/40311 this.sortText = '\uffff' + tsEntry.sortText; + + // Render "fancy" when source is a workspace path + const qualifierCandidate = vscode.workspace.asRelativePath(tsEntry.source); + if (qualifierCandidate !== tsEntry.source) { + this.label2 = { name: tsEntry.name, qualifier: qualifierCandidate }; + } + } else { this.sortText = tsEntry.sortText; } @@ -561,7 +568,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< type: response?.type ?? 'unknown', count: response?.type === 'response' && response.body ? response.body.entries.length : 0, updateGraphDurationMs: response?.type === 'response' ? response.performanceData?.updateGraphDurationMs : undefined, - createAutoImportProviderProgramDurationMs: response?.type === 'response' ? (response.performanceData as Proto.PerformanceData & { createAutoImportProviderProgramDurationMs?: number })?.createAutoImportProviderProgramDurationMs : undefined, + createAutoImportProviderProgramDurationMs: response?.type === 'response' ? response.performanceData?.createAutoImportProviderProgramDurationMs : undefined, includesPackageJsonImport: includesPackageJsonImport ? 'true' : undefined, }); } diff --git a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index 03ebff6dd..05fa16cf9 100644 --- a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -13,18 +13,6 @@ import { isTypeScriptDocument } from '../utils/languageModeIds'; import { equals } from '../utils/objects'; import { ResourceMap } from '../utils/resourceMap'; -namespace Experimental { - // https://github.com/microsoft/TypeScript/pull/37871/ - export interface UserPreferences extends Proto.UserPreferences { - readonly provideRefactorNotApplicableReason?: boolean; - } - - // https://github.com/microsoft/TypeScript/issues/41208 - export interface FormatCodeSettings extends Proto.FormatCodeSettings { - readonly insertSpaceAfterOpeningAndBeforeClosingEmptyBraces?: boolean; - } -} - interface FileConfiguration { readonly formatOptions: Proto.FormatCodeSettings; readonly preferences: Proto.UserPreferences; @@ -141,7 +129,7 @@ export default class FileConfigurationManager extends Disposable { private getFormatOptions( document: vscode.TextDocument, options: vscode.FormattingOptions - ): Experimental.FormatCodeSettings { + ): Proto.FormatCodeSettings { const config = vscode.workspace.getConfiguration( isTypeScriptDocument(document) ? 'typescript.format' : 'javascript.format', document.uri); @@ -185,8 +173,9 @@ export default class FileConfigurationManager extends Disposable { isTypeScriptDocument(document) ? 'typescript.preferences' : 'javascript.preferences', document.uri); - const preferences: Experimental.UserPreferences = { + const preferences: Proto.UserPreferences = { quotePreference: this.getQuoteStylePreference(preferencesConfig), + // @ts-expect-error until TypeScript 4.2 API importModuleSpecifierPreference: getImportModuleSpecifierPreference(preferencesConfig), importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(preferencesConfig), allowTextChangesInNewFiles: document.uri.scheme === fileSchemes.file, @@ -210,6 +199,7 @@ export default class FileConfigurationManager extends Disposable { function getImportModuleSpecifierPreference(config: vscode.WorkspaceConfiguration) { switch (config.get('importModuleSpecifier')) { + case 'project-relative': return 'project-relative'; case 'relative': return 'relative'; case 'non-relative': return 'non-relative'; default: return undefined; diff --git a/extensions/typescript-language-features/src/languageFeatures/folding.ts b/extensions/typescript-language-features/src/languageFeatures/folding.ts index 5b18decb4..5dc6dbd1c 100644 --- a/extensions/typescript-language-features/src/languageFeatures/folding.ts +++ b/extensions/typescript-language-features/src/languageFeatures/folding.ts @@ -54,14 +54,24 @@ class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { } const start = range.start.line; - // workaround for #47240 - const end = (range.end.character > 0 && ['}', ']'].includes(document.getText(new vscode.Range(range.end.translate(0, -1), range.end)))) - ? Math.max(range.end.line - 1, range.start.line) - : range.end.line; - + const end = this.adjustFoldingEnd(range, document); return new vscode.FoldingRange(start, end, kind); } + private static readonly foldEndPairCharacters = ['}', ']', ')', '`']; + + private adjustFoldingEnd(range: vscode.Range, document: vscode.TextDocument) { + // workaround for #47240 + if (range.end.character > 0) { + const foldEndCharacter = document.getText(new vscode.Range(range.end.translate(0, -1), range.end)); + if (TypeScriptFoldingProvider.foldEndPairCharacters.includes(foldEndCharacter)) { + return Math.max(range.end.line - 1, range.start.line); + } + } + + return range.end.line; + } + private static getFoldingRangeKind(span: Proto.OutliningSpan): vscode.FoldingRangeKind | undefined { switch (span.kind) { case 'comment': return vscode.FoldingRangeKind.Comment; diff --git a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts index 0965a5747..2af23b270 100644 --- a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts +++ b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts @@ -51,6 +51,9 @@ class ApplyCodeActionCommand implements Command { } } +type ApplyFixAllCodeAction_args = { + readonly action: VsCodeFixAllCodeAction; +}; class ApplyFixAllCodeAction implements Command { public static readonly ID = '_typescript.applyFixAllCodeAction'; @@ -61,14 +64,7 @@ class ApplyFixAllCodeAction implements Command { private readonly telemetryReporter: TelemetryReporter, ) { } - public async execute( - file: string, - tsAction: Proto.CodeFixAction, - ): Promise { - if (!tsAction.fixId) { - return; - } - + public async execute(args: ApplyFixAllCodeAction_args): Promise { /* __GDPR__ "quickFixAll.execute" : { "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, @@ -78,25 +74,12 @@ class ApplyFixAllCodeAction implements Command { } */ this.telemetryReporter.logTelemetry('quickFixAll.execute', { - fixName: tsAction.fixName + fixName: args.action.tsAction.fixName }); - const args: Proto.GetCombinedCodeFixRequestArgs = { - scope: { - type: 'file', - args: { file } - }, - fixId: tsAction.fixId, - }; - - const response = await this.client.execute('getCombinedCodeFix', args, nulToken); - if (response.type !== 'response' || !response.body) { - return undefined; + if (args.action.combinedResponse) { + await applyCodeActionCommands(this.client, args.action.combinedResponse.body.commands, nulToken); } - - const edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body.changes); - await vscode.workspace.applyEdit(edit); - await applyCodeActionCommands(this.client, response.body.commands, nulToken); } } @@ -134,13 +117,25 @@ class VsCodeCodeAction extends vscode.CodeAction { constructor( public readonly tsAction: Proto.CodeFixAction, title: string, - kind: vscode.CodeActionKind, - public readonly isFixAll: boolean, + kind: vscode.CodeActionKind ) { super(title, kind); } } +class VsCodeFixAllCodeAction extends VsCodeCodeAction { + constructor( + tsAction: Proto.CodeFixAction, + public readonly file: string, + title: string, + kind: vscode.CodeActionKind + ) { + super(tsAction, title, kind); + } + + public combinedResponse?: Proto.GetCombinedCodeFixResponse; +} + class CodeActionSet { private readonly _actions = new Set(); private readonly _fixAllActions = new Map<{}, VsCodeCodeAction>(); @@ -202,7 +197,7 @@ class SupportedCodeActionProvider { } } -class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { +class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { public static readonly metadata: vscode.CodeActionProviderMetadata = { providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] @@ -257,6 +252,28 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { return allActions; } + public async resolveCodeAction(codeAction: VsCodeCodeAction, token: vscode.CancellationToken): Promise { + if (!(codeAction instanceof VsCodeFixAllCodeAction) || !codeAction.tsAction.fixId) { + return codeAction; + } + + const arg: Proto.GetCombinedCodeFixRequestArgs = { + scope: { + type: 'file', + args: { file: codeAction.file } + }, + fixId: codeAction.tsAction.fixId, + }; + + const response = await this.client.execute('getCombinedCodeFix', arg, token); + if (response.type === 'response') { + codeAction.combinedResponse = response; + codeAction.edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body.changes); + } + + return codeAction; + } + private async getFixesForDiagnostic( document: vscode.TextDocument, file: string, @@ -295,7 +312,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { diagnostic: vscode.Diagnostic, tsAction: Proto.CodeFixAction ): VsCodeCodeAction { - const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix, false); + const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix); codeAction.edit = getEditForCodeAction(this.client, tsAction); codeAction.diagnostics = [diagnostic]; codeAction.command = { @@ -328,14 +345,16 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { return results; } - const action = new VsCodeCodeAction( + const action = new VsCodeFixAllCodeAction( tsAction, + file, tsAction.fixAllDescription || localize('fixAllInFileLabel', '{0} (Fix all in file)', tsAction.description), - vscode.CodeActionKind.QuickFix, true); + vscode.CodeActionKind.QuickFix); + action.diagnostics = [diagnostic]; action.command = { command: ApplyFixAllCodeAction.ID, - arguments: [file, tsAction], + arguments: [{ action }], title: '' }; results.addFixAllAction(tsAction.fixId, action); @@ -370,7 +389,7 @@ function isPreferredFix( action: VsCodeCodeAction, allActions: readonly VsCodeCodeAction[] ): boolean { - if (action.isFixAll) { + if (action instanceof VsCodeFixAllCodeAction) { return false; } @@ -384,7 +403,7 @@ function isPreferredFix( return true; } - if (otherAction.isFixAll) { + if (otherAction instanceof VsCodeFixAllCodeAction) { return true; } diff --git a/extensions/typescript-language-features/src/languageFeatures/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts index ab0186c8f..cc8c0a5d3 100644 --- a/extensions/typescript-language-features/src/languageFeatures/refactor.ts +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -20,11 +20,6 @@ import FormattingOptionsManager from './fileConfigurationManager'; const localize = nls.loadMessageBundle(); -namespace Experimental { - export interface RefactorActionInfo extends Proto.RefactorActionInfo { - readonly notApplicableReason?: string; - } -} interface DidApplyRefactoringCommand_Args { readonly codeAction: InlinedCodeAction @@ -58,10 +53,13 @@ class DidApplyRefactoringCommand implements Command { const renameLocation = args.codeAction.renameLocation; if (renameLocation) { - await vscode.commands.executeCommand('editor.action.rename', [ - args.codeAction.document.uri, - typeConverters.Position.fromLocation(renameLocation) - ]); + // Disable renames in interactive playground https://github.com/microsoft/vscode/issues/75137 + if (args.codeAction.document.uri.scheme !== fileSchemes.walkThroughSnippet) { + await vscode.commands.executeCommand('editor.action.rename', [ + args.codeAction.document.uri, + typeConverters.Position.fromLocation(renameLocation) + ]); + } } } } @@ -354,7 +352,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider string | undefined, onCaseInsenitiveFileSystem: boolean ) { - this._pending = new ResourceMap(undefined, { + this._pending = new ResourceMap(pathNormalizer, { onCaseInsenitiveFileSystem }); } @@ -280,7 +281,7 @@ class PendingDiagnostics extends ResourceMap { .sort((a, b) => a.value - b.value) .map(entry => entry.resource); - const map = new ResourceMap(undefined, this.config); + const map = new ResourceMap(this._normalizePath, this.config); for (const resource of orderedResources) { map.set(resource, undefined); } @@ -367,7 +368,7 @@ export default class BufferSyncSupport extends Disposable { const pathNormalizer = (path: vscode.Uri) => this.client.normalizedPath(path); this.syncedBuffers = new SyncedBufferMap(pathNormalizer, { onCaseInsenitiveFileSystem }); this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer, { onCaseInsenitiveFileSystem }); - this.synchronizer = new BufferSynchronizer(client, onCaseInsenitiveFileSystem); + this.synchronizer = new BufferSynchronizer(client, pathNormalizer, onCaseInsenitiveFileSystem); this.updateConfiguration(); vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this, this._disposables); @@ -482,7 +483,7 @@ export default class BufferSyncSupport extends Disposable { } } - public interuptGetErr(f: () => R): R { + public interruptGetErr(f: () => R): R { if (!this.pendingGetErr || this.client.configuration.enableProjectDiagnostics // `geterr` happens on seperate server so no need to cancel it. ) { diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts index bc06d18ee..33845073e 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts @@ -3,10 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; import { TypeScriptServiceConfiguration } from '../utils/configuration'; +import { memoize } from '../utils/memoize'; import { TsServerProcess, TsServerProcessKind } from './server'; + +const localize = nls.loadMessageBundle(); + declare const Worker: any; declare type Worker = any; @@ -37,6 +43,11 @@ export class WorkerServerProcess implements TsServerProcess { args: readonly string[], ) { worker.addEventListener('message', (msg: any) => { + if (msg.data.type === 'log') { + this.output.appendLine(msg.data.body); + return; + } + for (const handler of this._onDataHandlers) { handler(msg.data); } @@ -44,6 +55,11 @@ export class WorkerServerProcess implements TsServerProcess { worker.postMessage(args); } + @memoize + private get output(): vscode.OutputChannel { + return vscode.window.createOutputChannel(localize('channelName', 'TypeScript Server Log')); + } + write(serverRequest: Proto.Request): void { this.worker.postMessage(serverRequest); } diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index a3ad0abb1..58fd431ed 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -128,7 +128,7 @@ export class TypeScriptServerSpawner { const apiVersion = version.apiVersion || API.defaultVersion; const canceller = cancellerFactory.create(kind, this._tracer); - const { args, tsServerLogFile } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager, canceller.cancellationPipeName); + const { args, tsServerLogFile, tsServerTraceDirectory } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager, canceller.cancellationPipeName); if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) { if (tsServerLogFile) { @@ -138,6 +138,14 @@ export class TypeScriptServerSpawner { } } + if (configuration.enableTsServerTracing) { + if (tsServerTraceDirectory) { + this._logger.info(`<${kind}> Trace directory: ${tsServerTraceDirectory}`); + } else { + this._logger.error(`<${kind}> Could not create trace directory`); + } + } + this._logger.info(`<${kind}> Forking...`); const process = this._factory.fork(version.tsServerPath, args, kind, configuration, this._versionManager); this._logger.info(`<${kind}> Starting...`); @@ -173,9 +181,10 @@ export class TypeScriptServerSpawner { apiVersion: API, pluginManager: PluginManager, cancellationPipeName: string | undefined, - ): { args: string[], tsServerLogFile: string | undefined } { + ): { args: string[], tsServerLogFile: string | undefined, tsServerTraceDirectory: string | undefined } { const args: string[] = []; let tsServerLogFile: string | undefined; + let tsServerTraceDirectory: string | undefined; if (kind === TsServerProcessKind.Syntax) { if (apiVersion.gte(API.v401)) { @@ -216,6 +225,13 @@ export class TypeScriptServerSpawner { } } + if (configuration.enableTsServerTracing && !isWeb()) { + tsServerTraceDirectory = this._logDirectoryProvider.getNewLogDirectory(); + if (tsServerTraceDirectory) { + args.push('--traceDirectory', tsServerTraceDirectory); + } + } + if (!isWeb()) { const pluginPaths = this._pluginPathsProvider.getPluginPaths(); @@ -251,7 +267,7 @@ export class TypeScriptServerSpawner { args.push('--validateDefaultNpmLocation'); } - return { args, tsServerLogFile }; + return { args, tsServerLogFile, tsServerTraceDirectory }; } private static isLoggingEnabled(configuration: TypeScriptServiceConfiguration) { diff --git a/extensions/typescript-language-features/src/tsServer/versionStatus.ts b/extensions/typescript-language-features/src/tsServer/versionStatus.ts index 20b9debc2..1d4e12894 100644 --- a/extensions/typescript-language-features/src/tsServer/versionStatus.ts +++ b/extensions/typescript-language-features/src/tsServer/versionStatus.ts @@ -58,7 +58,6 @@ class ProjectStatusCommand implements Command { public async execute(): Promise { const info = this._delegate(); - const result = await vscode.window.showQuickPick(coalesce([ this.getProjectItem(info), this.getVersionItem(), @@ -73,7 +72,7 @@ class ProjectStatusCommand implements Command { private getVersionItem(): QuickPickItem { return { label: localize('projectQuickPick.version.label', "Select TypeScript Version..."), - description: this._client.apiVersion.displayName, + description: localize('projectQuickPick.version.description', "[current = {0}]", this._client.apiVersion.displayName), run: () => { this._client.showVersionPicker(); } @@ -169,7 +168,7 @@ export default class VersionStatus extends Disposable { const doc = vscode.window.activeTextEditor.document; if (isTypeScriptDocument(doc)) { - const file = this._client.normalizedPath(doc.uri); + const file = this._client.toOpenedFilePath(doc, { suppressAlertOnFailure: true }); if (file) { this._statusBarEntry.show(); if (!this._ready) { diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index d225ef602..76471a204 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -30,12 +30,6 @@ import * as typeConverters from './utils/typeConverters'; import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus'; import * as ProjectStatus from './utils/largeProjectStatus'; -namespace Experimental { - export interface Diagnostic extends Proto.Diagnostic { - readonly reportsDeprecated?: {} - } -} - // Style check diagnostics that can be reported as warnings const styleCheckDiagnostics = new Set([ ...errorCodes.variableDeclaredButNeverUsed, @@ -256,7 +250,7 @@ export default class TypeScriptServiceClientHost extends Disposable { return diagnostics.map(tsDiag => this.tsDiagnosticToVsDiagnostic(tsDiag, source)); } - private tsDiagnosticToVsDiagnostic(diagnostic: Experimental.Diagnostic, source: string): vscode.Diagnostic & { reportUnnecessary: any, reportDeprecated: any } { + private tsDiagnosticToVsDiagnostic(diagnostic: Proto.Diagnostic, source: string): vscode.Diagnostic & { reportUnnecessary: any, reportDeprecated: any } { const { start, end, text } = diagnostic; const range = new vscode.Range(typeConverters.Position.fromLocation(start), typeConverters.Position.fromLocation(end)); const converted = new vscode.Diagnostic(range, text, this.getDiagnosticSeverity(diagnostic)); diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 58a78ad19..588925ec6 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -147,7 +147,9 @@ export interface ITypeScriptServiceClient { * * @return The normalized path or `undefined` if the document is not open on the server. */ - toOpenedFilePath(document: vscode.TextDocument): string | undefined; + toOpenedFilePath(document: vscode.TextDocument, options?: { + suppressAlertOnFailure?: boolean + }): string | undefined; /** * Checks if `resource` has a given capability. diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 2d9dad639..e4c8c840e 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -189,9 +189,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.tracer.updateConfiguration(); if (this.serverState.type === ServerState.Type.Running) { - if (this._configuration.checkJs !== oldConfiguration.checkJs - || this._configuration.experimentalDecorators !== oldConfiguration.experimentalDecorators - ) { + if (!this._configuration.implictProjectConfiguration.isEqualTo(oldConfiguration.implictProjectConfiguration)) { this.setCompilerOptionsForInferredProjects(this._configuration); } @@ -663,14 +661,14 @@ export default class TypeScriptServiceClient extends Disposable implements IType return this.normalizedPath(resource); } - public toOpenedFilePath(document: vscode.TextDocument): string | undefined { + public toOpenedFilePath(document: vscode.TextDocument, options: { suppressAlertOnFailure?: boolean } = {}): string | undefined { if (!this.bufferSyncSupport.ensureHasBuffer(document.uri)) { - if (!fileSchemes.disabledSchemes.has(document.uri.scheme)) { + if (!options.suppressAlertOnFailure && !fileSchemes.disabledSchemes.has(document.uri.scheme)) { console.error(`Unexpected resource ${document.uri}`); } return undefined; } - return this.toPath(document.uri) || undefined; + return this.toPath(document.uri); } public hasCapabilityForResource(resource: vscode.Uri, capability: ClientCapability): boolean { @@ -778,7 +776,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } public interruptGetErr(f: () => R): R { - return this.bufferSyncSupport.interuptGetErr(f); + return this.bufferSyncSupport.interruptGetErr(f); } private fatalError(command: string, error: unknown): void { diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index f549ff6f9..c43a12107 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -7,7 +7,6 @@ import * as os from 'os'; import * as path from 'path'; import * as vscode from 'vscode'; import * as objects from '../utils/objects'; -import * as arrays from './arrays'; export enum TsServerLogLevel { Off, @@ -51,6 +50,43 @@ export const enum SeparateSyntaxServerConfiguration { Enabled, } +export class ImplicitProjectConfiguration { + + public readonly checkJs: boolean; + public readonly experimentalDecorators: boolean; + public readonly strictNullChecks: boolean; + public readonly strictFunctionTypes: boolean; + + constructor(configuration: vscode.WorkspaceConfiguration) { + this.checkJs = ImplicitProjectConfiguration.readCheckJs(configuration); + this.experimentalDecorators = ImplicitProjectConfiguration.readExperimentalDecorators(configuration); + this.strictNullChecks = ImplicitProjectConfiguration.readImplicitStrictNullChecks(configuration); + this.strictFunctionTypes = ImplicitProjectConfiguration.readImplicitStrictFunctionTypes(configuration); + } + + public isEqualTo(other: ImplicitProjectConfiguration): boolean { + return objects.equals(this, other); + } + + private static readCheckJs(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('js/ts.implicitProjectConfig.checkJs') + ?? configuration.get('javascript.implicitProjectConfig.checkJs', false); + } + + private static readExperimentalDecorators(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('js/ts.implicitProjectConfig.experimentalDecorators') + ?? configuration.get('javascript.implicitProjectConfig.experimentalDecorators', false); + } + + private static readImplicitStrictNullChecks(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('js/ts.implicitProjectConfig.strictNullChecks', false); + } + + private static readImplicitStrictFunctionTypes(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('js/ts.implicitProjectConfig.strictFunctionTypes', true); + } +} + export class TypeScriptServiceConfiguration { public readonly locale: string | null; public readonly globalTsdk: string | null; @@ -58,8 +94,7 @@ export class TypeScriptServiceConfiguration { public readonly npmLocation: string | null; public readonly tsServerLogLevel: TsServerLogLevel = TsServerLogLevel.Off; public readonly tsServerPluginPaths: readonly string[]; - public readonly checkJs: boolean; - public readonly experimentalDecorators: boolean; + public readonly implictProjectConfiguration: ImplicitProjectConfiguration; public readonly disableAutomaticTypeAcquisition: boolean; public readonly separateSyntaxServer: SeparateSyntaxServerConfiguration; public readonly enableProjectDiagnostics: boolean; @@ -67,6 +102,7 @@ export class TypeScriptServiceConfiguration { public readonly enablePromptUseWorkspaceTsdk: boolean; public readonly watchOptions: protocol.WatchOptions | undefined; public readonly includePackageJsonAutoImports: 'auto' | 'on' | 'off' | undefined; + public readonly enableTsServerTracing: boolean; public static loadFromWorkspace(): TypeScriptServiceConfiguration { return new TypeScriptServiceConfiguration(); @@ -81,8 +117,7 @@ export class TypeScriptServiceConfiguration { this.npmLocation = TypeScriptServiceConfiguration.readNpmLocation(configuration); this.tsServerLogLevel = TypeScriptServiceConfiguration.readTsServerLogLevel(configuration); this.tsServerPluginPaths = TypeScriptServiceConfiguration.readTsServerPluginPaths(configuration); - this.checkJs = TypeScriptServiceConfiguration.readCheckJs(configuration); - this.experimentalDecorators = TypeScriptServiceConfiguration.readExperimentalDecorators(configuration); + this.implictProjectConfiguration = new ImplicitProjectConfiguration(configuration); this.disableAutomaticTypeAcquisition = TypeScriptServiceConfiguration.readDisableAutomaticTypeAcquisition(configuration); this.separateSyntaxServer = TypeScriptServiceConfiguration.readUseSeparateSyntaxServer(configuration); this.enableProjectDiagnostics = TypeScriptServiceConfiguration.readEnableProjectDiagnostics(configuration); @@ -90,24 +125,11 @@ export class TypeScriptServiceConfiguration { this.enablePromptUseWorkspaceTsdk = TypeScriptServiceConfiguration.readEnablePromptUseWorkspaceTsdk(configuration); this.watchOptions = TypeScriptServiceConfiguration.readWatchOptions(configuration); this.includePackageJsonAutoImports = TypeScriptServiceConfiguration.readIncludePackageJsonAutoImports(configuration); + this.enableTsServerTracing = TypeScriptServiceConfiguration.readEnableTsServerTracing(configuration); } public isEqualTo(other: TypeScriptServiceConfiguration): boolean { - return this.locale === other.locale - && this.globalTsdk === other.globalTsdk - && this.localTsdk === other.localTsdk - && this.npmLocation === other.npmLocation - && this.tsServerLogLevel === other.tsServerLogLevel - && this.checkJs === other.checkJs - && this.experimentalDecorators === other.experimentalDecorators - && this.disableAutomaticTypeAcquisition === other.disableAutomaticTypeAcquisition - && arrays.equals(this.tsServerPluginPaths, other.tsServerPluginPaths) - && this.separateSyntaxServer === other.separateSyntaxServer - && this.enableProjectDiagnostics === other.enableProjectDiagnostics - && this.maxTsServerMemory === other.maxTsServerMemory - && objects.equals(this.watchOptions, other.watchOptions) - && this.enablePromptUseWorkspaceTsdk === other.enablePromptUseWorkspaceTsdk - && this.includePackageJsonAutoImports === other.includePackageJsonAutoImports; + return objects.equals(this, other); } private static fixPathPrefixes(inspectValue: string): string { @@ -145,14 +167,6 @@ export class TypeScriptServiceConfiguration { return configuration.get('typescript.tsserver.pluginPaths', []); } - private static readCheckJs(configuration: vscode.WorkspaceConfiguration): boolean { - return configuration.get('javascript.implicitProjectConfig.checkJs', false); - } - - private static readExperimentalDecorators(configuration: vscode.WorkspaceConfiguration): boolean { - return configuration.get('javascript.implicitProjectConfig.experimentalDecorators', false); - } - private static readNpmLocation(configuration: vscode.WorkspaceConfiguration): string | null { return configuration.get('typescript.npm', null); } @@ -198,4 +212,8 @@ export class TypeScriptServiceConfiguration { private static readEnablePromptUseWorkspaceTsdk(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get('typescript.enablePromptUseWorkspaceTsdk', false); } + + private static readEnableTsServerTracing(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('typescript.tsserver.enableTracing', false); + } } diff --git a/extensions/typescript-language-features/src/utils/resourceMap.ts b/extensions/typescript-language-features/src/utils/resourceMap.ts index ea4889ded..1b7285aaa 100644 --- a/extensions/typescript-language-features/src/utils/resourceMap.ts +++ b/extensions/typescript-language-features/src/utils/resourceMap.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as fileSchemes from '../utils/fileSchemes'; /** * Maps of file resources @@ -12,10 +13,18 @@ import * as vscode from 'vscode'; * file systems. */ export class ResourceMap { - private readonly _map = new Map(); + + private static readonly defaultPathNormalizer = (resource: vscode.Uri): string => { + if (resource.scheme === fileSchemes.file) { + return resource.fsPath; + } + return resource.toString(true); + }; + + private readonly _map = new Map(); constructor( - private readonly _normalizePath: (resource: vscode.Uri) => string | undefined = (resource) => resource.fsPath, + protected readonly _normalizePath: (resource: vscode.Uri) => string | undefined = ResourceMap.defaultPathNormalizer, protected readonly config: { readonly onCaseInsenitiveFileSystem: boolean, }, diff --git a/extensions/typescript-language-features/src/utils/tsconfig.ts b/extensions/typescript-language-features/src/utils/tsconfig.ts index f6802ca3c..9d1c309e5 100644 --- a/extensions/typescript-language-features/src/utils/tsconfig.ts +++ b/extensions/typescript-language-features/src/utils/tsconfig.ts @@ -32,17 +32,25 @@ export function inferredProjectCompilerOptions( jsx: 'preserve' as Proto.JsxEmit, }; - if (serviceConfig.checkJs) { + if (serviceConfig.implictProjectConfiguration.checkJs) { projectConfig.checkJs = true; if (projectType === ProjectType.TypeScript) { projectConfig.allowJs = true; } } - if (serviceConfig.experimentalDecorators) { + if (serviceConfig.implictProjectConfiguration.experimentalDecorators) { projectConfig.experimentalDecorators = true; } + if (serviceConfig.implictProjectConfiguration.strictNullChecks) { + projectConfig.strictNullChecks = true; + } + + if (serviceConfig.implictProjectConfiguration.strictFunctionTypes) { + projectConfig.strictFunctionTypes = true; + } + if (projectType === ProjectType.TypeScript) { projectConfig.sourceMap = true; } diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index ad1d70446..82681ca03 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -1014,4 +1014,38 @@ suite('vscode API - workspace', () => { } }); + + test('issue #110141 - TextEdit.setEndOfLine applies an edit and invalidates redo stack even when no change is made', async () => { + const file = await createRandomFile('hello\nworld'); + + const document = await vscode.workspace.openTextDocument(file); + await vscode.window.showTextDocument(document); + + // apply edit + { + const we = new vscode.WorkspaceEdit(); + we.insert(file, new vscode.Position(0, 5), '2'); + await vscode.workspace.applyEdit(we); + } + + // check the document + { + assert.equal(document.getText(), 'hello2\nworld'); + assert.equal(document.isDirty, true); + } + + // apply no-op edit + { + const we = new vscode.WorkspaceEdit(); + we.set(file, [vscode.TextEdit.setEndOfLine(vscode.EndOfLine.LF)]); + await vscode.workspace.applyEdit(we); + } + + // undo + { + await vscode.commands.executeCommand('undo'); + assert.equal(document.getText(), 'hello\nworld'); + assert.equal(document.isDirty, false); + } + }); }); diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 20586e5c5..9e9d4faf7 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" - integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== +typescript@4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9" + integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ== diff --git a/package.json b/package.json index 28f8a69a2..c856e311d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.51.1", - "distro": "d32b7eb5181a656f005556e20c54bc5fd73fd080", + "version": "1.52.1", + "distro": "11fd6f1230738827638f000f641d01b2b178db20", "author": { "name": "Microsoft Corporation" }, @@ -37,14 +37,15 @@ "smoketest": "cd test/smoke && node test/index.js", "download-builtin-extensions": "node build/lib/builtInExtensions.js", "monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit", - "tsec-compile-check": "node_modules/tsec/bin/tsec -p src/tsconfig.json --noEmit", + "tsec-compile-check": "node node_modules/tsec/bin/tsec -p src/tsconfig.json --noEmit", "valid-layers-check": "node build/lib/layersChecker.js", "strict-function-types-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictFunctionTypes", "update-distro": "node build/npm/update-distro.js", "web": "node resources/web/code-web.js", "compile-web": "gulp compile-web --max_old_space_size=4095", "watch-web": "gulp watch-web --max_old_space_size=4095", - "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions" + "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions", + "electron-rebuild": "electron-rebuild --arch=arm64 --force --version=11.0.2" }, "dependencies": { "applicationinsights": "1.0.8", @@ -54,10 +55,10 @@ "https-proxy-agent": "^2.2.3", "iconv-lite-umd": "0.6.8", "jschardet": "2.2.1", - "keytar": "^5.5.0", + "keytar": "7.2.0", "minimist": "^1.2.5", "native-is-elevated": "0.4.1", - "native-keymap": "2.2.0", + "native-keymap": "2.2.1", "native-watchdog": "1.3.0", "node-pty": "0.10.0-beta17", "spdlog": "^0.11.1", @@ -67,7 +68,8 @@ "vscode-nsfw": "1.2.9", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.9.0", + "vscode-regexpp": "^3.1.0", + "vscode-ripgrep": "^1.11.1", "vscode-sqlite3": "4.0.10", "vscode-textmate": "5.2.0", "xterm": "4.10.0-beta.4", @@ -110,7 +112,8 @@ "css-loader": "^3.2.0", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "9.3.3", + "electron": "9.3.5", + "electron-rebuild": "2.0.3", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", "eslint-plugin-mocha": "8.0.0", @@ -157,7 +160,7 @@ "opn": "^6.0.0", "optimist": "0.3.5", "p-all": "^1.0.0", - "playwright": "1.3.0", + "playwright": "1.6.2", "pump": "^1.0.1", "queue": "3.0.6", "rcedit": "^1.1.0", @@ -167,13 +170,13 @@ "style-loader": "^1.0.0", "ts-loader": "^4.4.2", "tsec": "googleinterns/tsec", - "typescript": "^4.1.0-dev.20201018", + "typescript": "^4.2.0-dev.20201119", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", "vsce": "1.48.0", - "vscode-debugprotocol": "1.41.0", + "vscode-debugprotocol": "1.43.0", "vscode-nls-dev": "^3.3.1", "webpack": "^4.43.0", "webpack-cli": "^3.3.12", diff --git a/product.json b/product.json index 7cab6d1b9..207bcf8f5 100644 --- a/product.json +++ b/product.json @@ -25,13 +25,13 @@ "extensionAllowedProposedApi": [ "ms-vscode.vscode-js-profile-flame", "ms-vscode.vscode-js-profile-table", - "ms-vscode.references-view", - "ms-vscode.github-browser" + "ms-vscode.github-browser", + "ms-vscode.github-richnav" ], "builtInExtensions": [ { "name": "ms-vscode.node-debug", - "version": "1.44.14", + "version": "1.44.15", "repo": "https://github.com/microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", @@ -61,7 +61,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.71", + "version": "0.0.74", "repo": "https://github.com/microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", @@ -91,7 +91,7 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.51.0", + "version": "1.52.2", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", @@ -123,7 +123,7 @@ "webBuiltInExtensions": [ { "name": "ms-vscode.github-browser", - "version": "0.0.13", + "version": "0.0.14", "repo": "https://github.com/microsoft/vscode-github-browser", "metadata": { "id": "c1bcff4b-4ecb-466e-b8f6-b02788b5fb5a", diff --git a/remote/package.json b/remote/package.json index d353586d7..f68deac64 100644 --- a/remote/package.json +++ b/remote/package.json @@ -18,8 +18,9 @@ "vscode-nsfw": "1.2.9", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.9.0", + "vscode-ripgrep": "^1.11.1", "vscode-textmate": "5.2.0", + "vscode-regexpp": "^3.1.0", "xterm": "4.10.0-beta.4", "xterm-addon-search": "0.8.0-beta.3", "xterm-addon-unicode11": "0.3.0-beta.3", diff --git a/remote/yarn.lock b/remote/yarn.lock index 5ba96e1c5..22d8ebe3f 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -167,11 +167,16 @@ glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -graceful-fs@4.2.3, graceful-fs@^4.1.2, graceful-fs@^4.1.6: +graceful-fs@4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -279,7 +284,12 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -nan@^2.10.0, nan@^2.14.0: +nan@^2.10.0: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + +nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -410,10 +420,15 @@ vscode-proxy-agent@^0.5.2: https-proxy-agent "^2.2.3" socks-proxy-agent "^4.0.1" -vscode-ripgrep@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.9.0.tgz#d6cdea4d290f3c2919472cdcfe2440d5fb1f99db" - integrity sha512-7jyAC/NNfvMPZgCVkyqIn0STYJ7wIk3PF2qA2cX1sEutx1g/e2VtgKAodXnfpreJq4993JT/BSIigOv/0lBSzg== +vscode-regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/vscode-regexpp/-/vscode-regexpp-3.1.0.tgz#42d059b6fffe99bd42939c0d013f632f0cad823f" + integrity sha512-pqtN65VC1jRLawfluX4Y80MMG0DHJydWhe5ZwMHewZD6sys4LbU6lHwFAHxeuaVE6Y6+xZOtAw+9hvq7/0ejkg== + +vscode-ripgrep@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.11.1.tgz#9fa3c0a96c2939d5a2389f71218bd1bb6eaa8679" + integrity sha512-oHJfpqeXuTQhOO+szqIObYOddwQ9o+lzd4PQLlTQN+sQ7ex8D1qqFip207O2iJyFc5oWE8Bekf4YHTibdbW66w== dependencies: https-proxy-agent "^4.0.0" proxy-from-env "^1.1.0" diff --git a/resources/linux/debian/postinst.template b/resources/linux/debian/postinst.template index 9f26b3509..c72fe5f9f 100755 --- a/resources/linux/debian/postinst.template +++ b/resources/linux/debian/postinst.template @@ -73,6 +73,6 @@ NdCFTW7wY0Fb1fWJ+/KTsC4= if [ "$WRITE_SOURCE" -eq "1" ]; then echo "### THIS FILE IS AUTOMATICALLY CONFIGURED ### # You may comment out this entry, but any other modifications may be lost. -deb [arch=amd64] http://packages.microsoft.com/repos/vscode stable main" > $CODE_SOURCE_PART +deb [arch=amd64,arm64,armhf] http://packages.microsoft.com/repos/@@REPOSITORY_NAME@@ stable main" > $CODE_SOURCE_PART fi fi diff --git a/resources/linux/rpm/dependencies.json b/resources/linux/rpm/dependencies.json index 7f95cd3e5..8a055565b 100644 --- a/resources/linux/rpm/dependencies.json +++ b/resources/linux/rpm/dependencies.json @@ -66,133 +66,120 @@ "libgbm.so.1()(64bit)" ], "aarch64": [ - "libpthread.so.0()(aarch64)", - "libpthread.so.0(GLIBC_2.2.5)(aarch64)", - "libpthread.so.0(GLIBC_2.3.2)(aarch64)", - "libpthread.so.0(GLIBC_2.3.3)(aarch64)", - "libgtk-3.so.0()(aarch64)", - "libgdk-x11-2.0.so.0()(aarch64)", - "libatk-1.0.so.0()(aarch64)", - "libgio-2.0.so.0()(aarch64)", - "libpangocairo-1.0.so.0()(aarch64)", - "libgdk_pixbuf-2.0.so.0()(aarch64)", - "libcairo.so.2()(aarch64)", - "libpango-1.0.so.0()(aarch64)", - "libfreetype.so.6()(aarch64)", - "libfontconfig.so.1()(aarch64)", - "libgobject-2.0.so.0()(aarch64)", - "libdbus-1.so.3()(aarch64)", - "libXi.so.6()(aarch64)", - "libXcursor.so.1()(aarch64)", - "libXdamage.so.1()(aarch64)", - "libXrandr.so.2()(aarch64)", - "libXcomposite.so.1()(aarch64)", - "libXext.so.6()(aarch64)", - "libXfixes.so.3()(aarch64)", - "libXrender.so.1()(aarch64)", - "libX11.so.6()(aarch64)", - "libXss.so.1()(aarch64)", - "libXtst.so.6()(aarch64)", - "libgmodule-2.0.so.0()(aarch64)", - "librt.so.1()(aarch64)", - "libglib-2.0.so.0()(aarch64)", - "libnss3.so()(aarch64)", - "libnssutil3.so()(aarch64)", - "libsmime3.so()(aarch64)", - "libnspr4.so()(aarch64)", - "libasound.so.2()(aarch64)", - "libcups.so.2()(aarch64)", - "libdl.so.2()(aarch64)", - "libexpat.so.1()(aarch64)", - "libstdc++.so.6()(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4)(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4.10)(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4.11)(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4.14)(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4.15)(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4.9)(aarch64)", - "libm.so.6()(aarch64)", - "libm.so.6(GLIBC_2.2.5)(aarch64)", - "libgcc_s.so.1()(aarch64)", - "libgcc_s.so.1(GCC_3.0)(aarch64)", - "libgcc_s.so.1(GCC_4.0.0)(aarch64)", - "libc.so.6()(aarch64)", - "libc.so.6(GLIBC_2.11)(aarch64)", - "libc.so.6(GLIBC_2.2.5)(aarch64)", - "libc.so.6(GLIBC_2.3)(aarch64)", - "libc.so.6(GLIBC_2.3.2)(aarch64)", - "libc.so.6(GLIBC_2.3.4)(aarch64)", - "libc.so.6(GLIBC_2.4)(aarch64)", - "libc.so.6(GLIBC_2.6)(aarch64)", - "libc.so.6(GLIBC_2.7)(aarch64)", - "libc.so.6(GLIBC_2.9)(aarch64)", - "libxcb.so.1()(aarch64)", - "libxkbfile.so.1()(aarch64)", - "libsecret-1.so.0()(aarch64)" + "libpthread.so.0()(64bit)", + "libpthread.so.0(GLIBC_2.17)(64bit)", + "libgtk-3.so.0()(64bit)", + "libgdk-x11-2.0.so.0()(64bit)", + "libatk-1.0.so.0()(64bit)", + "libgio-2.0.so.0()(64bit)", + "libpangocairo-1.0.so.0()(64bit)", + "libgdk_pixbuf-2.0.so.0()(64bit)", + "libcairo.so.2()(64bit)", + "libpango-1.0.so.0()(64bit)", + "libfreetype.so.6()(64bit)", + "libfontconfig.so.1()(64bit)", + "libgobject-2.0.so.0()(64bit)", + "libdbus-1.so.3()(64bit)", + "libXi.so.6()(64bit)", + "libXcursor.so.1()(64bit)", + "libXdamage.so.1()(64bit)", + "libXrandr.so.2()(64bit)", + "libXcomposite.so.1()(64bit)", + "libXext.so.6()(64bit)", + "libXfixes.so.3()(64bit)", + "libXrender.so.1()(64bit)", + "libX11.so.6()(64bit)", + "libXss.so.1()(64bit)", + "libXtst.so.6()(64bit)", + "libgmodule-2.0.so.0()(64bit)", + "librt.so.1()(64bit)", + "libglib-2.0.so.0()(64bit)", + "libnss3.so()(64bit)", + "libnssutil3.so()(64bit)", + "libsmime3.so()(64bit)", + "libnspr4.so()(64bit)", + "libasound.so.2()(64bit)", + "libcups.so.2()(64bit)", + "libdl.so.2()(64bit)", + "libexpat.so.1()(64bit)", + "libstdc++.so.6()(64bit)", + "libstdc++.so.6(GLIBCXX_3.4)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.10)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.11)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.14)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.15)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.9)(64bit)", + "libm.so.6()(64bit)", + "libm.so.6(GLIBC_2.17)(64bit)", + "libgcc_s.so.1()(64bit)", + "libgcc_s.so.1(GCC_3.0)(64bit)", + "libgcc_s.so.1(GCC_4.0.0)(64bit)", + "libc.so.6()(64bit)", + "libc.so.6(GLIBC_2.17)(64bit)", + "libxcb.so.1()(64bit)", + "libxkbfile.so.1()(64bit)", + "libsecret-1.so.0()(64bit)" ], "armv7hl": [ - "libpthread.so.0()(armv7hl)", - "libpthread.so.0(GLIBC_2.2.5)(armv7hl)", - "libpthread.so.0(GLIBC_2.3.2)(armv7hl)", - "libpthread.so.0(GLIBC_2.3.3)(armv7hl)", - "libgtk-3.so.0()(armv7hl)", - "libgdk-x11-2.0.so.0()(armv7hl)", - "libatk-1.0.so.0()(armv7hl)", - "libgio-2.0.so.0()(armv7hl)", - "libpangocairo-1.0.so.0()(armv7hl)", - "libgdk_pixbuf-2.0.so.0()(armv7hl)", - "libcairo.so.2()(armv7hl)", - "libpango-1.0.so.0()(armv7hl)", - "libfreetype.so.6()(armv7hl)", - "libfontconfig.so.1()(armv7hl)", - "libgobject-2.0.so.0()(armv7hl)", - "libdbus-1.so.3()(armv7hl)", - "libXi.so.6()(armv7hl)", - "libXcursor.so.1()(armv7hl)", - "libXdamage.so.1()(armv7hl)", - "libXrandr.so.2()(armv7hl)", - "libXcomposite.so.1()(armv7hl)", - "libXext.so.6()(armv7hl)", - "libXfixes.so.3()(armv7hl)", - "libXrender.so.1()(armv7hl)", - "libX11.so.6()(armv7hl)", - "libXss.so.1()(armv7hl)", - "libXtst.so.6()(armv7hl)", - "libgmodule-2.0.so.0()(armv7hl)", - "librt.so.1()(armv7hl)", - "libglib-2.0.so.0()(armv7hl)", - "libnss3.so()(armv7hl)", - "libnssutil3.so()(armv7hl)", - "libsmime3.so()(armv7hl)", - "libnspr4.so()(armv7hl)", - "libasound.so.2()(armv7hl)", - "libcups.so.2()(armv7hl)", - "libdl.so.2()(armv7hl)", - "libexpat.so.1()(armv7hl)", - "libstdc++.so.6()(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4)(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4.10)(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4.11)(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4.14)(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4.15)(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4.9)(armv7hl)", - "libm.so.6()(armv7hl)", - "libm.so.6(GLIBC_2.2.5)(armv7hl)", - "libgcc_s.so.1()(armv7hl)", - "libgcc_s.so.1(GCC_3.0)(armv7hl)", - "libgcc_s.so.1(GCC_4.0.0)(armv7hl)", - "libc.so.6()(armv7hl)", - "libc.so.6(GLIBC_2.11)(armv7hl)", - "libc.so.6(GLIBC_2.2.5)(armv7hl)", - "libc.so.6(GLIBC_2.3)(armv7hl)", - "libc.so.6(GLIBC_2.3.2)(armv7hl)", - "libc.so.6(GLIBC_2.3.4)(armv7hl)", - "libc.so.6(GLIBC_2.4)(armv7hl)", - "libc.so.6(GLIBC_2.6)(armv7hl)", - "libc.so.6(GLIBC_2.7)(armv7hl)", - "libc.so.6(GLIBC_2.9)(armv7hl)", - "libxcb.so.1()(armv7hl)", - "libxkbfile.so.1()(armv7hl)", - "libsecret-1.so.0()(armv7hl)" + "libpthread.so.0()", + "libpthread.so.0(GLIBC_2.4)", + "libpthread.so.0(GLIBC_2.11)", + "libpthread.so.0(GLIBC_2.12)", + "libgtk-3.so.0()", + "libgdk-x11-2.0.so.0()", + "libatk-1.0.so.0()", + "libgio-2.0.so.0()", + "libpangocairo-1.0.so.0()", + "libgdk_pixbuf-2.0.so.0()", + "libcairo.so.2()", + "libpango-1.0.so.0()", + "libfreetype.so.6()", + "libfontconfig.so.1()", + "libgobject-2.0.so.0()", + "libdbus-1.so.3()", + "libXi.so.6()", + "libXcursor.so.1()", + "libXdamage.so.1()", + "libXrandr.so.2()", + "libXcomposite.so.1()", + "libXext.so.6()", + "libXfixes.so.3()", + "libXrender.so.1()", + "libX11.so.6()", + "libXss.so.1()", + "libXtst.so.6()", + "libgmodule-2.0.so.0()", + "librt.so.1()", + "libglib-2.0.so.0()", + "libnss3.so()", + "libnssutil3.so()", + "libsmime3.so()", + "libnspr4.so()", + "libasound.so.2()", + "libcups.so.2()", + "libdl.so.2()", + "libexpat.so.1()", + "libstdc++.so.6()", + "libstdc++.so.6(GLIBCXX_3.4)", + "libstdc++.so.6(GLIBCXX_3.4.10)", + "libstdc++.so.6(GLIBCXX_3.4.11)", + "libstdc++.so.6(GLIBCXX_3.4.14)", + "libstdc++.so.6(GLIBCXX_3.4.15)", + "libstdc++.so.6(GLIBCXX_3.4.9)", + "libm.so.6()", + "libm.so.6(GLIBC_2.4)", + "libm.so.6(GLIBC_2.15)", + "libgcc_s.so.1()", + "libgcc_s.so.1(GCC_3.0)", + "libgcc_s.so.1(GCC_4.0.0)", + "libc.so.6()", + "libc.so.6(GLIBC_2.11)", + "libc.so.6(GLIBC_2.4)", + "libc.so.6(GLIBC_2.6)", + "libc.so.6(GLIBC_2.7)", + "libc.so.6(GLIBC_2.9)", + "libxcb.so.1()", + "libxkbfile.so.1()", + "libsecret-1.so.0()" ] } diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index 046158e88..c24d0af3e 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -6,6 +6,10 @@ description: | simplicity of a code editor with what developers need for the core edit-build-debug cycle. +architectures: + - build-on: amd64 + run-on: @@ARCHITECTURE@@ + grade: stable confinement: classic diff --git a/resources/web/code-web.js b/resources/web/code-web.js index 8658b737c..9be3bc2d8 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -386,6 +386,10 @@ async function handleRoot(req, res) { if (args['wrap-iframe']) { webConfigJSON._wrapWebWorkerExtHostInIframe = true; } + if (req.headers['x-forwarded-host']) { + // support for running in codespace => no iframe wrapping + delete webConfigJSON.webWorkerExtensionHostIframeSrc; + } const authSessionInfo = args['github-auth'] ? { id: uuid.v4(), diff --git a/scripts/code.bat b/scripts/code.bat index 7ef1fd33f..a05bea92d 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -22,7 +22,6 @@ set VSCODE_DEV=1 set VSCODE_CLI=1 set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 -set VSCODE_LOGS= :: Launch Code diff --git a/scripts/code.sh b/scripts/code.sh index 7ab367c17..3095f3897 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -41,7 +41,6 @@ function code() { export VSCODE_CLI=1 export ELECTRON_ENABLE_STACK_DUMPING=1 export ELECTRON_ENABLE_LOGGING=1 - export VSCODE_LOGS= # Launch Code exec "$CODE" . --no-sandbox "$@" diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index b302ce28d..b82dbec11 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -46,21 +46,46 @@ else echo "Running integration tests with '$INTEGRATION_TEST_ELECTRON_PATH' as build." fi +if [ -z "$INTEGRATION_TEST_APP_NAME" ]; then + after_suite() { true; } +else + after_suite() { killall $INTEGRATION_TEST_APP_NAME || true; } +fi + # Integration tests in AMD ./scripts/test.sh --runGlob **/*.integrationTest.js "$@" +after_suite # Tests in the extension host "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + #"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +# after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite # Tests in commonJS (CSS, HTML) cd $ROOT/extensions/css-language-features/server && $ROOT/scripts/node-electron.sh test/index.js +after_suite + cd $ROOT/extensions/html-language-features/server && $ROOT/scripts/node-electron.sh test/index.js +after_suite rm -rf $VSCODEUSERDATADIR diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index 752e4ab63..58355aebd 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -18,7 +18,9 @@ loader.config({ catchError: true, nodeRequire: require, nodeMain: __filename, - 'vs/nls': nlsConfig + 'vs/nls': nlsConfig, + amdModulesPattern: /^vs\//, + recordStats: true }); // Running in Electron @@ -29,7 +31,7 @@ if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']) { } // Pseudo NLS support -if (nlsConfig.pseudo) { +if (nlsConfig && nlsConfig.pseudo) { loader(['vs/nls'], function (nlsPlugin) { nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); }); diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index 97a4d9934..68e2219ba 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -13,7 +13,7 @@ const bootstrapNode = require('./bootstrap-node'); bootstrapNode.removeGlobalNodeModuleLookupPaths(); // Enable ASAR in our forked processes -bootstrap.enableASARSupport(); +bootstrap.enableASARSupport(undefined); if (process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']) { bootstrapNode.injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']); @@ -81,7 +81,9 @@ function pipeLoggingToParent() { // to start the stacktrace where the console message was being written if (process.env.VSCODE_LOG_STACK === 'true') { const stack = new Error().stack; - argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') }); + if (stack) { + argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') }); + } } try { @@ -114,7 +116,9 @@ function pipeLoggingToParent() { */ function safeSend(arg) { try { - process.send(arg); + if (process.send) { + process.send(arg); + } } catch (error) { // Can happen if the parent channel is closed meanwhile } diff --git a/src/bootstrap-node.js b/src/bootstrap-node.js index 268624997..161d68d59 100644 --- a/src/bootstrap-node.js +++ b/src/bootstrap-node.js @@ -58,3 +58,73 @@ exports.removeGlobalNodeModuleLookupPaths = function () { return paths.slice(0, paths.length - commonSuffixLength); }; }; + +/** + * Helper to enable portable mode. + * + * @param {{ portable?: string; applicationName: string; }} product + * @returns {{ portableDataPath: string; isPortable: boolean; }} + */ +exports.configurePortable = function (product) { + const fs = require('fs'); + const path = require('path'); + + const appRoot = path.dirname(__dirname); + + /** + * @param {import('path')} path + */ + function getApplicationPath(path) { + if (process.env['VSCODE_DEV']) { + return appRoot; + } + + if (process.platform === 'darwin') { + return path.dirname(path.dirname(path.dirname(appRoot))); + } + + return path.dirname(path.dirname(appRoot)); + } + + /** + * @param {import('path')} path + */ + function getPortableDataPath(path) { + if (process.env['VSCODE_PORTABLE']) { + return process.env['VSCODE_PORTABLE']; + } + + if (process.platform === 'win32' || process.platform === 'linux') { + return path.join(getApplicationPath(path), 'data'); + } + + // @ts-ignore + const portableDataName = product.portable || `${product.applicationName}-portable-data`; + return path.join(path.dirname(getApplicationPath(path)), portableDataName); + } + + const portableDataPath = getPortableDataPath(path); + const isPortable = !('target' in product) && fs.existsSync(portableDataPath); + const portableTempPath = path.join(portableDataPath, 'tmp'); + const isTempPortable = isPortable && fs.existsSync(portableTempPath); + + if (isPortable) { + process.env['VSCODE_PORTABLE'] = portableDataPath; + } else { + delete process.env['VSCODE_PORTABLE']; + } + + if (isTempPortable) { + if (process.platform === 'win32') { + process.env['TMP'] = portableTempPath; + process.env['TEMP'] = portableTempPath; + } else { + process.env['TMPDIR'] = portableTempPath; + } + } + + return { + portableDataPath, + isPortable + }; +}; diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 3fc0f419b..d7cc72d3b 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -25,7 +25,12 @@ const preloadGlobals = globals(); const sandbox = preloadGlobals.context.sandbox; const webFrame = preloadGlobals.webFrame; - const safeProcess = sandbox ? preloadGlobals.process : process; + const safeProcess = preloadGlobals.process; + const configuration = parseWindowConfiguration(); + + // Start to resolve process.env before anything gets load + // so that we can run loading and resolving in parallel + const whenEnvResolved = safeProcess.resolveEnv(configuration.userEnv); /** * @param {string[]} modulePaths @@ -33,18 +38,6 @@ * @param {{ forceEnableDeveloperKeybindings?: boolean, disallowReloadKeybinding?: boolean, removeDeveloperKeybindingsAfterLoad?: boolean, canModifyDOM?: (config: object) => void, beforeLoaderConfig?: (config: object, loaderConfig: object) => void, beforeRequire?: () => void }=} options */ function load(modulePaths, resultCallback, options) { - const args = parseURLQueryArgs(); - /** - * // configuration: INativeWindowConfiguration - * @type {{ - * zoomLevel?: number, - * extensionDevelopmentPath?: string[], - * extensionTestsPath?: string, - * userEnv?: { [key: string]: string | undefined }, - * appRoot?: string, - * nodeCachedDataDir?: string - * }} */ - const configuration = JSON.parse(args['config'] || '{}') || {}; // Apply zoom level early to avoid glitches const zoomLevel = configuration.zoomLevel; @@ -64,22 +57,15 @@ developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); } - // Correctly inherit the parent's environment (TODO@sandbox non-sandboxed only) - if (!sandbox) { - Object.assign(safeProcess.env, configuration.userEnv); - } - - // Enable ASAR support (TODO@sandbox non-sandboxed only) - if (!sandbox) { - globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot); - } + // Enable ASAR support + globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot); if (options && typeof options.canModifyDOM === 'function') { options.canModifyDOM(configuration); } - // Get the nls configuration into the process.env as early as possible (TODO@sandbox non-sandboxed only) - const nlsConfig = sandbox ? { availableLanguages: {} } : globalThis.MonacoBootstrap.setupNLS(); + // Get the nls configuration into the process.env as early as possible + const nlsConfig = globalThis.MonacoBootstrap.setupNLS(); let locale = nlsConfig.availableLanguages['*'] || 'en'; if (locale === 'zh-tw') { @@ -102,9 +88,14 @@ window['MonacoEnvironment'] = {}; + const baseUrl = sandbox ? + `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out` : + `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32' })}/out`; + const loaderConfig = { - baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32' })}/out`, - 'vs/nls': nlsConfig + baseUrl, + 'vs/nls': nlsConfig, + preferScriptTags: sandbox }; // Enable loading of node modules: @@ -150,17 +141,23 @@ options.beforeRequire(); } - require(modulePaths, result => { + require(modulePaths, async result => { try { + + // Wait for process environment being fully resolved + const perf = perfLib(); + perf.mark('willWaitForShellEnv'); + await whenEnvResolved; + perf.mark('didWaitForShellEnv'); + + // Callback only after process environment is resolved const callbackResult = resultCallback(result, configuration); - if (callbackResult && typeof callbackResult.then === 'function') { - callbackResult.then(() => { - if (developerToolsUnbind && options && options.removeDeveloperKeybindingsAfterLoad) { - developerToolsUnbind(); - } - }, error => { - onUnexpectedError(error, enableDeveloperTools); - }); + if (callbackResult instanceof Promise) { + await callbackResult; + + if (developerToolsUnbind && options && options.removeDeveloperKeybindingsAfterLoad) { + developerToolsUnbind(); + } } } catch (error) { onUnexpectedError(error, enableDeveloperTools); @@ -169,20 +166,30 @@ } /** - * @returns {{[param: string]: string }} + * Parses the contents of the `INativeWindowConfiguration` that + * is passed into the URL from the `electron-main` side. + * + * @returns {{ + * zoomLevel?: number, + * extensionDevelopmentPath?: string[], + * extensionTestsPath?: string, + * userEnv?: { [key: string]: string | undefined }, + * appRoot: string, + * nodeCachedDataDir?: string + * }} */ - function parseURLQueryArgs() { - const search = window.location.search || ''; - - return search.split(/[?&]/) + function parseWindowConfiguration() { + const rawConfiguration = (window.location.search || '').split(/[?&]/) .filter(function (param) { return !!param; }) .map(function (param) { return param.split('='); }) .filter(function (param) { return param.length === 2; }) .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); + + return JSON.parse(rawConfiguration['config'] || '{}') || {}; } /** - * @param {boolean} disallowReloadKeybinding + * @param {boolean | undefined} disallowReloadKeybinding * @returns {() => void} */ function registerDeveloperKeybindings(disallowReloadKeybinding) { @@ -203,6 +210,7 @@ const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12 const RELOAD_KB = (safeProcess.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R + /** @type {((e: any) => void) | undefined} */ let listener = function (e) { const key = extractKey(e); if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) { @@ -255,8 +263,26 @@ return window.vscode; } + /** + * @return {{ mark: (name: string) => void }} + */ + function perfLib() { + globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; + + return { + /** + * @param {string} name + */ + mark(name) { + globalThis.MonacoPerformanceMarks.push(name, Date.now()); + performance.mark(name); + } + }; + } + return { load, - globals + globals, + perfLib }; })); diff --git a/src/bootstrap.js b/src/bootstrap.js index 0cb6466ae..5392a5602 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -42,11 +42,11 @@ //#region Add support for using node_modules.asar /** - * @param {string} appRoot + * @param {string | undefined} appRoot */ function enableASARSupport(appRoot) { - if (!path || !Module) { - console.warn('enableASARSupport() is only available in node.js environments'); + if (!path || !Module || typeof process === 'undefined') { + console.warn('enableASARSupport() is only available in node.js environments'); // TODO@sandbox ASAR is currently non-sandboxed only return; } @@ -124,17 +124,14 @@ //#region NLS helpers /** - * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean }} + * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean } | undefined} */ function setupNLS() { - if (!path || !fs) { - console.warn('setupNLS() is only available in node.js environments'); - return; - } - // Get the nls configuration into the process.env as early as possible. + // Get the nls configuration as early as possible. + const process = safeProcess(); let nlsConfig = { availableLanguages: {} }; - if (process.env['VSCODE_NLS_CONFIG']) { + if (process && process.env['VSCODE_NLS_CONFIG']) { try { nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']); } catch (e) { @@ -153,8 +150,7 @@ return; } - const bundleFile = path.join(nlsConfig._resolvedLanguagePackCoreLocation, `${bundle.replace(/\//g, '!')}.nls.json`); - fs.promises.readFile(bundleFile, 'utf8').then(function (content) { + safeReadNlsFile(nlsConfig._resolvedLanguagePackCoreLocation, `${bundle.replace(/\//g, '!')}.nls.json`).then(function (content) { const json = JSON.parse(content); bundles[bundle] = json; @@ -162,7 +158,7 @@ }).catch((error) => { try { if (nlsConfig._corruptedFile) { - fs.promises.writeFile(nlsConfig._corruptedFile, 'corrupted', 'utf8').catch(function (error) { console.error(error); }); + safeWriteNlsFile(nlsConfig._corruptedFile, 'corrupted').catch(function (error) { console.error(error); }); } } finally { cb(error, undefined); @@ -174,77 +170,73 @@ return nlsConfig; } - //#endregion + function safeGlobals() { + const globals = (typeof self === 'object' ? self : typeof global === 'object' ? global : {}); - - //#region Portable helpers + return globals.vscode; + } /** - * @param {{ portable: string; applicationName: string; }} product - * @returns {{ portableDataPath: string; isPortable: boolean; }} + * @returns {NodeJS.Process | undefined} */ - function configurePortable(product) { - if (!path || !fs) { - console.warn('configurePortable() is only available in node.js environments'); - return; + function safeProcess() { + if (typeof process !== 'undefined') { + return process; // Native environment (non-sandboxed) } - const appRoot = path.dirname(__dirname); + const globals = safeGlobals(); + if (globals) { + return globals.process; // Native environment (sandboxed) + } + } - function getApplicationPath() { - if (process.env['VSCODE_DEV']) { - return appRoot; - } + /** + * @returns {Electron.IpcRenderer | undefined} + */ + function safeIpcRenderer() { + const globals = safeGlobals(); + if (globals) { + return globals.ipcRenderer; + } + } - if (process.platform === 'darwin') { - return path.dirname(path.dirname(path.dirname(appRoot))); - } - - return path.dirname(path.dirname(appRoot)); + /** + * @param {string[]} pathSegments + * @returns {Promise} + */ + async function safeReadNlsFile(...pathSegments) { + const ipcRenderer = safeIpcRenderer(); + if (ipcRenderer) { + return ipcRenderer.invoke('vscode:readNlsFile', ...pathSegments); } - function getPortableDataPath() { - if (process.env['VSCODE_PORTABLE']) { - return process.env['VSCODE_PORTABLE']; - } - - if (process.platform === 'win32' || process.platform === 'linux') { - return path.join(getApplicationPath(), 'data'); - } - - // @ts-ignore - const portableDataName = product.portable || `${product.applicationName}-portable-data`; - return path.join(path.dirname(getApplicationPath()), portableDataName); + if (fs && path) { + return (await fs.promises.readFile(path.join(...pathSegments))).toString(); } - const portableDataPath = getPortableDataPath(); - const isPortable = !('target' in product) && fs.existsSync(portableDataPath); - const portableTempPath = path.join(portableDataPath, 'tmp'); - const isTempPortable = isPortable && fs.existsSync(portableTempPath); + throw new Error('Unsupported operation (read NLS files)'); + } - if (isPortable) { - process.env['VSCODE_PORTABLE'] = portableDataPath; - } else { - delete process.env['VSCODE_PORTABLE']; + /** + * @param {string} path + * @param {string} content + * @returns {Promise} + */ + function safeWriteNlsFile(path, content) { + const ipcRenderer = safeIpcRenderer(); + if (ipcRenderer) { + return ipcRenderer.invoke('vscode:writeNlsFile', path, content); } - if (isTempPortable) { - if (process.platform === 'win32') { - process.env['TMP'] = portableTempPath; - process.env['TEMP'] = portableTempPath; - } else { - process.env['TMPDIR'] = portableTempPath; - } + if (fs) { + return fs.promises.writeFile(path, content); } - return { - portableDataPath, - isPortable - }; + throw new Error('Unsupported operation (write NLS files)'); } //#endregion - + //#region ApplicationInsights @@ -267,7 +259,6 @@ return { enableASARSupport, avoidMonkeyPatchFromAppInsights, - configurePortable, setupNLS, fileUriFromPath }; diff --git a/src/buildfile.js b/src/buildfile.js index f6d1c647d..c0400de76 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -10,7 +10,7 @@ function entrypoint(name) { exports.base = [{ name: 'vs/base/common/worker/simpleWorker', include: ['vs/editor/common/services/editorSimpleWorker'], - prepend: ['vs/loader.js'], + prepend: ['vs/loader.js', 'vs/nls.js'], append: ['vs/base/worker/workerMain'], dest: 'vs/base/worker/workerMain.js' }]; diff --git a/src/cli.js b/src/cli.js index 872cd6673..5624cb93b 100644 --- a/src/cli.js +++ b/src/cli.js @@ -7,16 +7,17 @@ 'use strict'; const bootstrap = require('./bootstrap'); +const bootstrapNode = require('./bootstrap-node'); const product = require('../product.json'); // Avoid Monkey Patches from Application Insights bootstrap.avoidMonkeyPatchFromAppInsights(); // Enable portable support -bootstrap.configurePortable(product); +bootstrapNode.configurePortable(product); // Enable ASAR support -bootstrap.enableASARSupport(); +bootstrap.enableASARSupport(undefined); // Load CLI through AMD loader require('./bootstrap-amd').load('vs/code/node/cli'); diff --git a/src/main.js b/src/main.js index 2a9756ab2..0bc566099 100644 --- a/src/main.js +++ b/src/main.js @@ -15,6 +15,7 @@ const path = require('path'); const fs = require('fs'); const os = require('os'); const bootstrap = require('./bootstrap'); +const bootstrapNode = require('./bootstrap-node'); const paths = require('./paths'); /** @type {any} */ const product = require('../product.json'); @@ -25,10 +26,10 @@ const { app, protocol, crashReporter } = require('electron'); app.allowRendererProcessReuse = false; // Enable portable support -const portable = bootstrap.configurePortable(product); +const portable = bootstrapNode.configurePortable(product); // Enable ASAR support -bootstrap.enableASARSupport(); +bootstrap.enableASARSupport(undefined); // Set userData path before app 'ready' event const args = parseCLIArgs(); @@ -107,7 +108,7 @@ crashReporter.start({ // to ensure that no 'logs' folder is created on disk at a // location outside of the portable directory // (https://github.com/microsoft/vscode/issues/56651) -if (portable.isPortable) { +if (portable && portable.isPortable) { app.setAppLogsPath(path.join(userDataPath, 'logs')); } @@ -133,6 +134,15 @@ protocol.registerSchemesAsPrivileged([ corsEnabled: true, } }, + { + scheme: 'vscode-file', + privileges: { + secure: true, + standard: true, + supportFetchAPI: true, + corsEnabled: true + } + } ]); // Global app listeners @@ -145,7 +155,7 @@ const nodeCachedDataDir = getNodeCachedDir(); * Support user defined locale: load it early before app('ready') * to have more things running in parallel. * - * @type {Promise} nlsConfig | undefined + * @type {Promise | undefined} */ let nlsConfigurationPromise = undefined; @@ -359,7 +369,7 @@ function getArgvConfigPath() { /** * @param {NativeParsedArgs} cliArgs - * @returns {string} + * @returns {string | null} */ function getJSFlags(cliArgs) { const jsFlags = []; @@ -387,7 +397,7 @@ function getUserDataPath(cliArgs) { return path.join(portable.portableDataPath, 'user-data'); } - return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath(process.platform)); + return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath()); } /** @@ -468,12 +478,14 @@ function getNodeCachedDir() { } async ensureExists() { - try { - await mkdirp(this.value); + if (typeof this.value === 'string') { + try { + await mkdirp(this.value); - return this.value; - } catch (error) { - // ignore + return this.value; + } catch (error) { + // ignore + } } } diff --git a/src/paths.js b/src/paths.js index a88927d20..2042123d7 100644 --- a/src/paths.js +++ b/src/paths.js @@ -11,25 +11,38 @@ const path = require('path'); const os = require('os'); /** - * @param {string} platform * @returns {string} */ -function getAppDataPath(platform) { - switch (platform) { - case 'win32': return process.env['VSCODE_APPDATA'] || process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming'); - case 'darwin': return process.env['VSCODE_APPDATA'] || path.join(os.homedir(), 'Library', 'Application Support'); - case 'linux': return process.env['VSCODE_APPDATA'] || process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); - default: throw new Error('Platform not supported'); +function getDefaultUserDataPath() { + + // Support global VSCODE_APPDATA environment variable + let appDataPath = process.env['VSCODE_APPDATA']; + + // Otherwise check per platform + if (!appDataPath) { + switch (process.platform) { + case 'win32': + appDataPath = process.env['APPDATA']; + if (!appDataPath) { + const userProfile = process.env['USERPROFILE']; + if (typeof userProfile !== 'string') { + throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable'); + } + appDataPath = path.join(userProfile, 'AppData', 'Roaming'); + } + break; + case 'darwin': + appDataPath = path.join(os.homedir(), 'Library', 'Application Support'); + break; + case 'linux': + appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); + break; + default: + throw new Error('Platform not supported'); + } } + + return path.join(appDataPath, pkg.name); } -/** - * @param {string} platform - * @returns {string} - */ -function getDefaultUserDataPath(platform) { - return path.join(getAppDataPath(platform), pkg.name); -} - -exports.getAppDataPath = getAppDataPath; exports.getDefaultUserDataPath = getDefaultUserDataPath; diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index 2119700f8..fc2ee0320 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -5,7 +5,7 @@ import { IAction, IActionRunner, IActionViewItem } from 'vs/base/common/actions'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview'; export interface IContextMenuEvent { readonly shiftKey?: boolean; @@ -26,6 +26,7 @@ export interface IContextMenuDelegate { actionRunner?: IActionRunner; autoSelectFirstItem?: boolean; anchorAlignment?: AnchorAlignment; + anchorAxisAlignment?: AnchorAxisAlignment; domForShadowRoot?: HTMLElement; } diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 430ef8093..b4740dade 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -661,6 +661,48 @@ export function isAncestor(testChild: Node | null, testAncestor: Node | null): b return false; } +const parentFlowToDataKey = 'parentFlowToElementId'; + +/** + * Set an explicit parent to use for nodes that are not part of the + * regular dom structure. + */ +export function setParentFlowTo(fromChildElement: HTMLElement, toParentElement: Element): void { + fromChildElement.dataset[parentFlowToDataKey] = toParentElement.id; +} + +function getParentFlowToElement(node: HTMLElement): HTMLElement | null { + const flowToParentId = node.dataset[parentFlowToDataKey]; + if (typeof flowToParentId === 'string') { + return document.getElementById(flowToParentId); + } + return null; +} + +/** + * Check if `testAncestor` is an ancessor of `testChild`, observing the explicit + * parents set by `setParentFlowTo`. + */ +export function isAncestorUsingFlowTo(testChild: Node, testAncestor: Node): boolean { + let node: Node | null = testChild; + while (node) { + if (node === testAncestor) { + return true; + } + + if (node instanceof HTMLElement) { + const flowToParentElement = getParentFlowToElement(node); + if (flowToParentElement) { + node = flowToParentElement; + continue; + } + } + node = node.parentNode; + } + + return false; +} + export function findParentWithClass(node: HTMLElement, clazz: string, stopAtClazzOrNode?: string | HTMLElement): HTMLElement | null { while (node && node.nodeType === node.ELEMENT_NODE) { if (node.classList.contains(clazz)) { @@ -1331,8 +1373,8 @@ export function safeInnerHtml(node: HTMLElement, value: string): void { const options = _extInsaneOptions({ allowedTags: ['a', 'button', 'blockquote', 'code', 'div', 'h1', 'h2', 'h3', 'input', 'label', 'li', 'p', 'pre', 'select', 'small', 'span', 'strong', 'textarea', 'ul', 'ol'], allowedAttributes: { - 'a': ['href'], - 'button': ['data-href'], + 'a': ['href', 'x-dispatch'], + 'button': ['data-href', 'x-dispatch'], 'input': ['type', 'placeholder', 'checked', 'required'], 'label': ['for'], 'select': ['required'], diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index 328ecaee0..4911b59e0 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -4,11 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import * as platform from 'vs/base/common/platform'; import { IframeUtils } from 'vs/base/browser/iframe'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { BrowserFeatures } from 'vs/base/browser/canIUse'; export interface IStandardMouseMoveEventData { leftButton: boolean; @@ -26,7 +24,7 @@ export interface IMouseMoveCallback { } export interface IOnStopCallback { - (): void; + (browserEvent?: MouseEvent | KeyboardEvent): void; } export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData | null, currentEvent: MouseEvent): IStandardMouseMoveEventData { @@ -52,7 +50,7 @@ export class GlobalMouseMoveMonitor implements I this._hooks.dispose(); } - public stopMonitoring(invokeStopCallback: boolean): void { + public stopMonitoring(invokeStopCallback: boolean, browserEvent?: MouseEvent | KeyboardEvent): void { if (!this.isMonitoring()) { // Not monitoring return; @@ -66,7 +64,7 @@ export class GlobalMouseMoveMonitor implements I this._onStopCallback = null; if (invokeStopCallback && onStopCallback) { - onStopCallback(); + onStopCallback(browserEvent); } } @@ -90,8 +88,8 @@ export class GlobalMouseMoveMonitor implements I this._onStopCallback = onStopCallback; const windowChain = IframeUtils.getSameOriginWindowChain(); - const mouseMove = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointermove' : 'mousemove'; - const mouseUp = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointerup' : 'mouseup'; + const mouseMove = 'mousemove'; + const mouseUp = 'mouseup'; const listenTo: (Document | ShadowRoot)[] = windowChain.map(element => element.window.document); const shadowRoot = dom.getShadowRoot(initialElement); diff --git a/src/vs/base/browser/hash.ts b/src/vs/base/browser/hash.ts new file mode 100644 index 000000000..bf48b614b --- /dev/null +++ b/src/vs/base/browser/hash.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from 'vs/base/common/buffer'; +import { StringSHA1, toHexString } from 'vs/base/common/hash'; + +export async function sha1Hex(str: string): Promise { + + // Prefer to use browser's crypto module + if (globalThis?.crypto?.subtle) { + const hash = await globalThis.crypto.subtle.digest({ name: 'sha-1' }, VSBuffer.fromString(str).buffer); + + return toHexString(hash); + } + + // Otherwise fallback to `StringSHA1` + else { + const computer = new StringSHA1(); + computer.update(str); + + return computer.digest(); + } +} diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 2d601e975..a5cd4c5ec 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -28,7 +28,7 @@ export interface MarkedOptions extends marked.MarkedOptions { export interface MarkdownRenderOptions extends FormattedTextRenderOptions { codeBlockRenderer?: (modeId: string, value: string) => Promise; - codeBlockRenderCallback?: () => void; + asyncRenderCallback?: () => void; baseUrl?: URI; } @@ -177,8 +177,8 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende // ignore }); - if (options.codeBlockRenderCallback) { - promise.then(options.codeBlockRenderCallback); + if (options.asyncRenderCallback) { + promise.then(options.asyncRenderCallback); } return `

${escape(code)}
`; @@ -215,11 +215,15 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende // Use our own sanitizer so that we can let through only spans. // Otherwise, we'd be letting all html be rendered. // If we want to allow markdown permitted tags, then we can delete sanitizer and sanitize. + // We always pass the output through insane after this so that we don't rely on + // marked for sanitization. markedOptions.sanitizer = (html: string): string => { const match = markdown.isTrusted ? html.match(/^()|(<\/\s*span>)$/) : undefined; return match ? html : ''; }; markedOptions.sanitize = true; + markedOptions.silent = true; + markedOptions.renderer = renderer; // values that are too long will freeze the UI @@ -240,6 +244,17 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende // signal that async code blocks can be now be inserted signalInnerHTML!(); + // signal size changes for image tags + if (options.asyncRenderCallback) { + for (const img of element.getElementsByTagName('img')) { + const listener = DOM.addDisposableListener(img, 'load', () => { + listener.dispose(); + options.asyncRenderCallback!(); + }); + } + } + + return element; } @@ -301,3 +316,76 @@ function getInsaneOptions(options: { readonly isTrusted?: boolean }): InsaneOpti }; } +/** + * Strips all markdown from `markdown`. For example `# Header` would be output as `Header`. + */ +export function renderMarkdownAsPlaintext(markdown: IMarkdownString) { + const renderer = new marked.Renderer(); + + renderer.code = (code: string): string => { + return code; + }; + renderer.blockquote = (quote: string): string => { + return quote; + }; + renderer.html = (_html: string): string => { + return ''; + }; + renderer.heading = (text: string, _level: 1 | 2 | 3 | 4 | 5 | 6, _raw: string): string => { + return text + '\n'; + }; + renderer.hr = (): string => { + return ''; + }; + renderer.list = (body: string, _ordered: boolean): string => { + return body; + }; + renderer.listitem = (text: string): string => { + return text + '\n'; + }; + renderer.paragraph = (text: string): string => { + return text + '\n'; + }; + renderer.table = (header: string, body: string): string => { + return header + body + '\n'; + }; + renderer.tablerow = (content: string): string => { + return content; + }; + renderer.tablecell = (content: string, _flags: { + header: boolean; + align: 'center' | 'left' | 'right' | null; + }): string => { + return content + ' '; + }; + renderer.strong = (text: string): string => { + return text; + }; + renderer.em = (text: string): string => { + return text; + }; + renderer.codespan = (code: string): string => { + return code; + }; + renderer.br = (): string => { + return '\n'; + }; + renderer.del = (text: string): string => { + return text; + }; + renderer.image = (_href: string, _title: string, _text: string): string => { + return ''; + }; + renderer.text = (text: string): string => { + return text; + }; + renderer.link = (_href: string, _title: string, text: string): string => { + return text; + }; + // values that are too long will freeze the UI + let value = markdown.value ?? ''; + if (value.length > 100_000) { + value = `${value.substr(0, 100_000)}…`; + } + return sanitizeRenderedMarkdown({ isTrusted: false }, marked.parse(value, { renderer })).toString(); +} diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 9d206dabe..788a83d55 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -60,6 +60,9 @@ export class ActionBar extends Disposable implements IActionRunner { protected focusedItem?: number; private focusTracker: DOM.IFocusTracker; + // Trigger Key Tracking + private triggerKeyDown: boolean = false; + // Elements domNode: HTMLElement; protected actionsList: HTMLElement; @@ -74,8 +77,8 @@ export class ActionBar extends Disposable implements IActionRunner { private _onDidRun = this._register(new Emitter()); readonly onDidRun = this._onDidRun.event; - private _onDidBeforeRun = this._register(new Emitter()); - readonly onDidBeforeRun = this._onDidBeforeRun.event; + private _onBeforeRun = this._register(new Emitter()); + readonly onBeforeRun = this._onBeforeRun.event; constructor(container: HTMLElement, options: IActionBarOptions = {}) { super(); @@ -96,7 +99,7 @@ export class ActionBar extends Disposable implements IActionRunner { } this._register(this._actionRunner.onDidRun(e => this._onDidRun.fire(e))); - this._register(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e))); + this._register(this._actionRunner.onBeforeRun(e => this._onBeforeRun.fire(e))); this._actionIds = []; this.viewItems = []; @@ -148,6 +151,8 @@ export class ActionBar extends Disposable implements IActionRunner { // Staying out of the else branch even if not triggered if (this._triggerKeys.keyDown) { this.doTrigger(event); + } else { + this.triggerKeyDown = true; } } else { eventHandled = false; @@ -164,7 +169,8 @@ export class ActionBar extends Disposable implements IActionRunner { // Run action on Enter/Space if (this.isTriggerKeyEvent(event)) { - if (!this._triggerKeys.keyDown) { + if (!this._triggerKeys.keyDown && this.triggerKeyDown) { + this.triggerKeyDown = false; this.doTrigger(event); } @@ -183,6 +189,7 @@ export class ActionBar extends Disposable implements IActionRunner { if (DOM.getActiveElement() === this.domNode || !DOM.isAncestor(DOM.getActiveElement(), this.domNode)) { this._onDidBlur.fire(); this.focusedItem = undefined; + this.triggerKeyDown = false; } })); diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 27981af27..37b6b03fe 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -11,7 +11,7 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; import 'vs/css!./breadcrumbsWidget'; export abstract class BreadcrumbsItem { @@ -56,7 +56,7 @@ export interface IBreadcrumbsItemEvent { payload: any; } -const breadcrumbSeparatorIcon = registerIcon('breadcrumb-separator', Codicon.chevronRight); +const breadcrumbSeparatorIcon = registerCodicon('breadcrumb-separator', Codicon.chevronRight); export class BreadcrumbsWidget { diff --git a/src/vs/base/browser/ui/button/button.css b/src/vs/base/browser/ui/button/button.css index 6d6823524..217c1656e 100644 --- a/src/vs/base/browser/ui/button/button.css +++ b/src/vs/base/browser/ui/button/button.css @@ -10,7 +10,6 @@ padding: 4px; text-align: center; cursor: pointer; - outline-offset: 2px !important; justify-content: center; align-items: center; } @@ -24,7 +23,15 @@ cursor: default; } -.monaco-button > .codicon { +.monaco-text-button > .codicon { margin: 0 0.2em; color: inherit !important; } + +.monaco-button-dropdown { + display: flex; +} + +.monaco-button-dropdown > .monaco-dropdown-button { + margin-left: 1px; +} diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index a3ca3ac97..b3697f9bf 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -9,10 +9,13 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { Event as BaseEvent, Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch'; import { renderCodicons } from 'vs/base/browser/codicons'; import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; +import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { CSSIcon, Codicon } from 'vs/base/common/codicons'; export interface IButtonOptions extends IButtonStyles { readonly title?: boolean | string; @@ -36,7 +39,18 @@ const defaultOptions: IButtonStyles = { buttonForeground: Color.white }; -export class Button extends Disposable { +export interface IButton extends IDisposable { + readonly element: HTMLElement; + readonly onDidClick: BaseEvent; + label: string; + icon: CSSIcon; + enabled: boolean; + style(styles: IButtonStyles): void; + focus(): void; + hasFocus(): boolean; +} + +export class Button extends Disposable implements IButton { private _element: HTMLElement; private options: IButtonOptions; @@ -188,8 +202,8 @@ export class Button extends Disposable { } } - set icon(iconClassName: string) { - this._element.classList.add(iconClassName); + set icon(icon: CSSIcon) { + this._element.classList.add(...icon.classNames.split(' ')); } set enabled(value: boolean) { @@ -217,47 +231,124 @@ export class Button extends Disposable { } } -export class ButtonGroup extends Disposable { - private _buttons: Button[] = []; +export interface IButtonWithDropdownOptions extends IButtonOptions { + readonly contextMenuProvider: IContextMenuProvider; + readonly actions: IAction[]; + readonly actionRunner?: IActionRunner; +} - constructor(container: HTMLElement, count: number, options?: IButtonOptions) { +export class ButtonWithDropdown extends Disposable implements IButton { + + private readonly button: Button; + private readonly dropdownButton: Button; + + readonly element: HTMLElement; + readonly onDidClick: BaseEvent; + + constructor(container: HTMLElement, options: IButtonWithDropdownOptions) { super(); - this.create(container, count, options); + this.element = document.createElement('div'); + this.element.classList.add('monaco-button-dropdown'); + container.appendChild(this.element); + + this.button = this._register(new Button(this.element, options)); + this.onDidClick = this.button.onDidClick; + + this.dropdownButton = this._register(new Button(this.element, { ...options, title: false, supportCodicons: true })); + this.dropdownButton.element.classList.add('monaco-dropdown-button'); + this.dropdownButton.icon = Codicon.dropDownButton; + this._register(this.dropdownButton.onDidClick(() => { + options.contextMenuProvider.showContextMenu({ + getAnchor: () => this.dropdownButton.element, + getActions: () => options.actions, + actionRunner: options.actionRunner, + onHide: () => this.dropdownButton.element.setAttribute('aria-expanded', 'false') + }); + this.dropdownButton.element.setAttribute('aria-expanded', 'true'); + })); } - get buttons(): Button[] { + set label(value: string) { + this.button.label = value; + } + + set icon(icon: CSSIcon) { + this.button.icon = icon; + } + + set enabled(enabled: boolean) { + this.button.enabled = enabled; + this.dropdownButton.enabled = enabled; + } + + get enabled(): boolean { + return this.button.enabled; + } + + style(styles: IButtonStyles): void { + this.button.style(styles); + this.dropdownButton.style(styles); + } + + focus(): void { + this.button.focus(); + } + + hasFocus(): boolean { + return this.button.hasFocus() || this.dropdownButton.hasFocus(); + } +} + +export class ButtonBar extends Disposable { + + private _buttons: IButton[] = []; + + constructor(private readonly container: HTMLElement) { + super(); + } + + get buttons(): IButton[] { return this._buttons; } - private create(container: HTMLElement, count: number, options?: IButtonOptions): void { - for (let index = 0; index < count; index++) { - const button = this._register(new Button(container, options)); - this._buttons.push(button); - - // Implement keyboard access in buttons if there are multiple - if (count > 1) { - this._register(addDisposableListener(button.element, EventType.KEY_DOWN, e => { - const event = new StandardKeyboardEvent(e); - let eventHandled = true; - - // Next / Previous Button - let buttonIndexToFocus: number | undefined; - if (event.equals(KeyCode.LeftArrow)) { - buttonIndexToFocus = index > 0 ? index - 1 : this._buttons.length - 1; - } else if (event.equals(KeyCode.RightArrow)) { - buttonIndexToFocus = index === this._buttons.length - 1 ? 0 : index + 1; - } else { - eventHandled = false; - } - - if (eventHandled && typeof buttonIndexToFocus === 'number') { - this._buttons[buttonIndexToFocus].focus(); - EventHelper.stop(e, true); - } - - })); - } - } + addButton(options?: IButtonOptions): IButton { + const button = this._register(new Button(this.container, options)); + this.pushButton(button); + return button; } + + addButtonWithDropdown(options: IButtonWithDropdownOptions): IButton { + const button = this._register(new ButtonWithDropdown(this.container, options)); + this.pushButton(button); + return button; + } + + private pushButton(button: IButton): void { + this._buttons.push(button); + + const index = this._buttons.length - 1; + this._register(addDisposableListener(button.element, EventType.KEY_DOWN, e => { + const event = new StandardKeyboardEvent(e); + let eventHandled = true; + + // Next / Previous Button + let buttonIndexToFocus: number | undefined; + if (event.equals(KeyCode.LeftArrow)) { + buttonIndexToFocus = index > 0 ? index - 1 : this._buttons.length - 1; + } else if (event.equals(KeyCode.RightArrow)) { + buttonIndexToFocus = index === this._buttons.length - 1 ? 0 : index + 1; + } else { + eventHandled = false; + } + + if (eventHandled && typeof buttonIndexToFocus === 'number') { + this._buttons[buttonIndexToFocus].focus(); + EventHelper.stop(e, true); + } + + })); + + } + } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index c3b0e243f..932a28602 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -11,12 +11,12 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { Codicon } from 'vs/base/common/codicons'; +import { Codicon, CSSIcon } from 'vs/base/common/codicons'; import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export interface ICheckboxOpts extends ICheckboxStyles { readonly actionClassName?: string; - readonly icon?: Codicon; + readonly icon?: CSSIcon; readonly title: string; readonly isChecked: boolean; } diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 4c16209d6a9203bb2b9f9b2e160fc20e947e7f36..7fdf6ed7b9db8580b4a762998936d06b55c37b51 100644 GIT binary patch delta 5614 zcmZA53tX1<*$437^?=|25#iw|M^R29$U)8`qJjqmJRzQtgD4sT9#S*&F*7T3WYS#o zE$6J6(Ytb^+PmIz&ACo9=bW`N*UB8T|9>@RX3mwpzuT*KecqSP=X+oG!}IX)+}zjy zx~@N0`n|3guRV#LZGh|sP}j1$sq5-n!5M&WJP>kzMf+{bd)M#W3&cDEoG5E+ZCVy} z_T3$P?FW?C#&7ukaZnD=U!;+3tJiOeILb4~MPN`#duL12%Irr=fT%A4*TL0Io4Q=9 z@Cu)g;EUornpU?aJQ+3dPT<4ye1TtA=eqS9dp}+Td~^vIw(G$c6DQO_oHqpC`Rt3n zr9(?Thrbt}1m3$iz2V#V^>wdLygugR!Eia>liwE!^_TnMz!}swpet`eLBX|@C@IxHL zk8lW&;m3FaNAMJ$#xppIpW<0Ohv)Hg{1^TkFX0$|iI?$foL~yPiZ}3E{0@J>S-gd} z@eba_IlPDS_!B zLMM8pgsC+YQ}H-n#}|@@-{XGf(Ra`hQ>lBv|%z7pv z58!Hoxm9sp!E8_%%eYZ-t-)+kTy-$FDXwFf&5A1#=61z33A06Ub;8`CxK3fVD!k_5 z$v3!oVYVq8XS`E!LBniUT-q>qDK2uD9SSV0=58gKj60Q60L?uL&Y$18RV1m5yU4?X z4CBfDO5z!vHzSEI^Gn6L2J^CF zg@gH(Vy%O5{@lc>2lH#i`Uk_+;b0|%`Hf;tgn31=I>NjPRt_FmC}Cb#ESWGT6^kd# zn~G%==9FSVh54OgX@xnhSY%;-uUKwj&L|dMm_I0%V3@Ot#h6+A79SAnGR)hG6&l7# zd19@Gc}KBo!@R3lzhTZPR&tp46l*%ndBy4u^C!hR5A(ia#fSN`!^2p& z%-%Yo`~NjxbRXEj z6dNh5D7I8smtu2;^-^rJu-=Ld7j}?h>xK1EY{IaZXSnQ@fj^a7itILE*yD`%*+DT=cVZ0b!OaLj>CQyh3; zM=Op#u<43J5bPMmaR@d;aWI0-R2-3D$0`m>uvv;@6KuBP00o<)I7-3h^7q?ZUj2E- zafW%KTdX*n!ImgaX|UrJ=QY?;_0 zGd3%UVQf(n$GA+%2*y?=BN>+~Nn%`~#7W#XB~Ie5RN^G7XB7`5PPX2n#L3onB~G@k zR>DNJ9ZH-8?Ns6Y6a)S~lDK{!{l5&$0 zCn;}J;w0r}B~BvVuEa^iElQk(yF&?c&2ClV5i63HE@(7mPnt+(W@0RNPI${z$>Orb9}=_?Y5e3)XpG!kdggR@{HV z9(GCt58R5uKB2fNgFT|)q>XbIh#NIn=PnSpY_Lx$Zr)&>J3`#X!8&(@xS@l6T5)R! z`;6iy5B8|S2k3bwDs0qgK6G9}-21^ktGEkkX0dDLym=9a=YDi?)FeuXhG<*(Bq*O!ve!{!F79kV{>NX#3t z^|4!G55^vki-?;YcW=Ba{%C@CLUqEq5o;4;6PrgyjeK_0=A?k6*-6Wjo=-+{N^(c? zJ1J2qRVn*Zk(!swi&b1uU>!?QM{JLBohhRn^G zhceHNtr&YPYi`!AtV`MM?4s;t*$?Lg6w8^6K&p8hpOOHVbr8h11vZra*(ra8WEST z|F7bktLy6?d;5}>Z!gFV_Rdk+{DA!MNy!<)tMYMkxrBuzCg+Fq-*{IxE^jF+DJg1M z{wYt2i+Pft-krWleA6d54{m-}{{6hT^tryl-Q(v+iW(>HuGzkZMK;iW>Z=Dg~F2Ht~Q02(D#&YoNr*71BG5ODcG3`-MQ)MVZADgED@HzxO5g9$)M} z|JDEF&FkJjdH=x2P2lBz%71)~#!E<)&KQe8 z>5uuSz(sz%H*io=PzMiIOT2_(s>Dj5L`iE2l4$8DG13Zaq`9=KyW!i$zb`V8g>3Xk zZe47Xww})Df;eQ9R!f1>^F>c2=j7JG3 zpcE4^31yg!DVT;CxD&H58&#NtyD%5?a5wHjH5OnY7NG|BVlis51WWmx`>+hlJy?lV zcmNM#9oFL^Y{bLZgh%iXY{5UV6_4UEJdSO60#D&-?7%KOhv)GEUc?^MV=w-NSMYEA zhZ*rY4&Y53!eP9PBX|$TZ~`CUV|;>7aSET|G(N`}oW zD<&#Tg<{IWOi}oPajIe(!%S0npK-ckYQx;&aW{PD{fsjd^Bv|+g*3)W#WMgiQ{gYh zSxVd=I$QCCz*H%o6_`1Srv~OO#q$F*SHa^x)jY*R1#`FJ@q(GJkjQwC;*o=?Ry=$# z3lvyu%tFNj3A0G?Xu{Me9#RW-;J#H?A-l_e~JbFifq&LB=JD=Ne|I;t7Yj zPw}k7EK^v`xLip*;|j$}0JBnID&s08v3#|0FCuZD-~lD9#Ac0>){N_v1Tn5x63w_l zNk_(ql*BN)Kaf~%U^Xha-}kT*R(Z3@@!^x&)$MEgM&G`C_>5v*g4wB9ph!Qmle7hZXA~ z%-f2U5yo9LVvU4(*Ii29uw25tr&u^)jwzN<7@j0H2SnXlXD%O3NuN@CxP|7>!6q^CeHwu#&&nq?vm~RzZ1G$_9tR zfMFXewqjUc#ik6~M6o@?-lp&fufO$E?AfsXirpJFK(UX*HdXBCu&g-_dpm59VwZ;v zR_yn%%@npVhA7Seu%U|60Bm#e@P>l{Y?$JR02{72EWox<92;O;Dh?2^trSNI*a*d; z0=Bi{cmW%!IB38|DUKYl(Tc+dJhlxV5a$rs7{!SMwyol90^3fxxz)B;oL68wC{8Z0 z9TjI7*jUAB2DX#pTm#!#al(P^atpuzBfJx*IQGDHRUCj|;}u6C*aXF)2sTl1Jc8|} zI4Hp;DUM9A$%?}hY>MI-1>0S5pn^^1``c80`p+4AC{9_hX^Qg}Y){3>3pQQh9AhuV zX$-cv;#>yXM{z=f%}|`xVEZagZLpcj&2O+-ijy2{w&F|&o1@&(u>BP0JlOt<6CZ4@ z;_L@IKyeC$%~PBQVFxNshOmPaXGGY+?)vkF<09-3#laDFsNx6-o3A)b!VXg$D`5*1 z2Ta&P#ZeP>gu+e6kxKX$c9ar6utiFIc>e8ZJ|JntI7W#t<5(q47>kwM#(29DcT~qI z31A$rB$%;8Ni)U?N~h5g1a^huDgwJw zaVddarMRBJ-mfssUH8?BI}7XsirWk98U^>~>96`Pgxs@vA8y~ll9TNQUD*hdw&CfLUmt};HZxJkio zQ{1UwpHSScVBPD9dlu|diW?W~c7>yiPb+R=usd97;0+ftSoducS2I}mZ4+Fo>{61= z=)QU4;s)!!dEyEO`<&u32m8F@S_k`r;(`bJqT;Ft`;y|)SNCQ_xc3J@-HnLRod%_p|zohLQglJ)qH#N6Jd#Ag<%WB zwuK!JyAmEAUJ$-Ce0TVn7Je<#TkLF^(z4Ria#hRYtx{Umwz?8g7_m0uQtR^8dmC#5>&p_I$rqq|ph-`)Mk z)Y#OL)U~OHdj#~z=uwmAmsZ|$YR?@#&!>+}f2~(cuj*dcdgu4v+xtlGzxxd8v#QVb zJ}3J$WYlCF>|50LU}jwA=FDSR*;%z&JF}Z)$7QGI1mvvBd8uEzr{9|Xjr-5*e>^uX zw={Q4?%xBZ4>*zMmzSD1CT~;Tg@J(s^9C*(czICDpz6VXgBK2N7?L~W=+J8jK5pK) z72`IK_ZmNB{FCEPmBg3qozQqf#)R4l=St&C7ng1?JuOWYQQ6%~Geit@-)$hFciqx;XG{?8z=gRCx{{sO&gkb;x diff --git a/src/vs/base/browser/ui/codicons/codiconStyles.ts b/src/vs/base/browser/ui/codicons/codiconStyles.ts index 8229f2f1c..442811967 100644 --- a/src/vs/base/browser/ui/codicons/codiconStyles.ts +++ b/src/vs/base/browser/ui/codicons/codiconStyles.ts @@ -7,18 +7,7 @@ import 'vs/css!./codicon/codicon'; import 'vs/css!./codicon/codicon-modifications'; import 'vs/css!./codicon/codicon-animations'; -import { Codicon, iconRegistry } from 'vs/base/common/codicons'; - -export const CodiconStyles = new class { - onDidChange = iconRegistry.onDidRegister; - public getCSS(): string { - const rules = []; - for (let c of iconRegistry.all) { - rules.push(formatRule(c)); - } - return rules.join('\n'); - } -}; +import { Codicon } from 'vs/base/common/codicons'; export function formatRule(c: Codicon) { let def = c.definition; diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 97e78cefe..ef695b6a5 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -31,6 +31,10 @@ export const enum AnchorPosition { BELOW, ABOVE } +export const enum AnchorAxisAlignment { + VERTICAL, HORIZONTAL +} + export interface IDelegate { getAnchor(): HTMLElement | IAnchor; render(container: HTMLElement): IDisposable | null; @@ -38,6 +42,7 @@ export interface IDelegate { layout?(): void; anchorAlignment?: AnchorAlignment; // default: left anchorPosition?: AnchorPosition; // default: below + anchorAxisAlignment?: AnchorAxisAlignment; // default: vertical canRelayout?: boolean; // default: true onDOMEvent?(e: Event, activeElement: HTMLElement): void; onHide?(data?: any): void; @@ -66,9 +71,15 @@ export const enum LayoutAnchorPosition { After } +export enum LayoutAnchorMode { + AVOID, + ALIGN +} + export interface ILayoutAnchor { offset: number; size: number; + mode?: LayoutAnchorMode; // default: AVOID position: LayoutAnchorPosition; } @@ -78,25 +89,26 @@ export interface ILayoutAnchor { * @returns The view offset within the viewport. */ export function layout(viewportSize: number, viewSize: number, anchor: ILayoutAnchor): number { - const anchorEnd = anchor.offset + anchor.size; + const layoutAfterAnchorBoundary = anchor.mode === LayoutAnchorMode.ALIGN ? anchor.offset : anchor.offset + anchor.size; + const layoutBeforeAnchorBoundary = anchor.mode === LayoutAnchorMode.ALIGN ? anchor.offset + anchor.size : anchor.offset; if (anchor.position === LayoutAnchorPosition.Before) { - if (viewSize <= viewportSize - anchorEnd) { - return anchorEnd; // happy case, lay it out after the anchor + if (viewSize <= viewportSize - layoutAfterAnchorBoundary) { + return layoutAfterAnchorBoundary; // happy case, lay it out after the anchor } - if (viewSize <= anchor.offset) { - return anchor.offset - viewSize; // ok case, lay it out before the anchor + if (viewSize <= layoutBeforeAnchorBoundary) { + return layoutBeforeAnchorBoundary - viewSize; // ok case, lay it out before the anchor } return Math.max(viewportSize - viewSize, 0); // sad case, lay it over the anchor } else { - if (viewSize <= anchor.offset) { - return anchor.offset - viewSize; // happy case, lay it out before the anchor + if (viewSize <= layoutBeforeAnchorBoundary) { + return layoutBeforeAnchorBoundary - viewSize; // happy case, lay it out before the anchor } - if (viewSize <= viewportSize - anchorEnd) { - return anchorEnd; // ok case, lay it out after the anchor + if (viewSize <= viewportSize - layoutAfterAnchorBoundary) { + return layoutAfterAnchorBoundary; // ok case, lay it out after the anchor } return 0; // sad case, lay it over the anchor @@ -270,28 +282,36 @@ export class ContextView extends Disposable { const anchorPosition = this.delegate!.anchorPosition || AnchorPosition.BELOW; const anchorAlignment = this.delegate!.anchorAlignment || AnchorAlignment.LEFT; + const anchorAxisAlignment = this.delegate!.anchorAxisAlignment || AnchorAxisAlignment.VERTICAL; - const verticalAnchor: ILayoutAnchor = { offset: around.top - window.pageYOffset, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; + let top: number; + let left: number; - let horizontalAnchor: ILayoutAnchor; + if (anchorAxisAlignment === AnchorAxisAlignment.VERTICAL) { + const verticalAnchor: ILayoutAnchor = { offset: around.top - window.pageYOffset, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; + const horizontalAnchor: ILayoutAnchor = { offset: around.left, size: around.width, position: anchorAlignment === AnchorAlignment.LEFT ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, mode: LayoutAnchorMode.ALIGN }; - if (anchorAlignment === AnchorAlignment.LEFT) { - horizontalAnchor = { offset: around.left, size: 0, position: LayoutAnchorPosition.Before }; - } else { - horizontalAnchor = { offset: around.left + around.width, size: 0, position: LayoutAnchorPosition.After }; - } + top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) + window.pageYOffset; - const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) + window.pageYOffset; - - // if view intersects vertically with anchor, shift it horizontally - if (Range.intersects({ start: top, end: top + viewSizeHeight }, { start: verticalAnchor.offset, end: verticalAnchor.offset + verticalAnchor.size })) { - horizontalAnchor.size = around.width; - if (anchorAlignment === AnchorAlignment.RIGHT) { - horizontalAnchor.offset = around.left; + // if view intersects vertically with anchor, we must avoid the anchor + if (Range.intersects({ start: top, end: top + viewSizeHeight }, { start: verticalAnchor.offset, end: verticalAnchor.offset + verticalAnchor.size })) { + horizontalAnchor.mode = LayoutAnchorMode.AVOID; } - } - const left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor); + left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor); + } else { + const horizontalAnchor: ILayoutAnchor = { offset: around.left, size: around.width, position: anchorAlignment === AnchorAlignment.LEFT ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; + const verticalAnchor: ILayoutAnchor = { offset: around.top, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, mode: LayoutAnchorMode.ALIGN }; + + left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor); + + // if view intersects horizontally with anchor, we must avoid the anchor + if (Range.intersects({ start: left, end: left + viewSizeWidth }, { start: horizontalAnchor.offset, end: horizontalAnchor.offset + horizontalAnchor.size })) { + verticalAnchor.mode = LayoutAnchorMode.AVOID; + } + + top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) + window.pageYOffset; + } this.view.classList.remove('top', 'bottom', 'left', 'right'); this.view.classList.add(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top'); diff --git a/src/vs/base/browser/ui/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css index 1f1180b8f..7c5ffc1c3 100644 --- a/src/vs/base/browser/ui/dialog/dialog.css +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -148,7 +148,8 @@ width: fit-content; width: -moz-fit-content; padding: 5px 10px; - margin: 4px 5px; /* allows button focus outline to be visible */ overflow: hidden; text-overflow: ellipsis; + margin: 4px 5px; /* allows button focus outline to be visible */ + outline-offset: 2px !important; } diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 097e879e3..4d8a075df 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -11,13 +11,13 @@ import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Color } from 'vs/base/common/color'; -import { ButtonGroup, IButtonStyles } from 'vs/base/browser/ui/button/button'; +import { ButtonBar, IButtonStyles } from 'vs/base/browser/ui/button/button'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { isMacintosh, isLinux } from 'vs/base/common/platform'; import { SimpleCheckbox, ISimpleCheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; export interface IDialogInputOptions { @@ -60,10 +60,10 @@ interface ButtonMapEntry { readonly index: number; } -const dialogErrorIcon = registerIcon('dialog-error', Codicon.error); -const dialogWarningIcon = registerIcon('dialog-warning', Codicon.warning); -const dialogInfoIcon = registerIcon('dialog-info', Codicon.info); -const dialogCloseIcon = registerIcon('dialog-close', Codicon.close); +const dialogErrorIcon = registerCodicon('dialog-error', Codicon.error); +const dialogWarningIcon = registerCodicon('dialog-warning', Codicon.warning); +const dialogInfoIcon = registerCodicon('dialog-info', Codicon.info); +const dialogCloseIcon = registerCodicon('dialog-close', Codicon.close); export class Dialog extends Disposable { private readonly element: HTMLElement; @@ -74,7 +74,7 @@ export class Dialog extends Disposable { private readonly iconElement: HTMLElement; private readonly checkbox: SimpleCheckbox | undefined; private readonly toolbarContainer: HTMLElement; - private buttonGroup: ButtonGroup | undefined; + private buttonBar: ButtonBar | undefined; private styles: IDialogStyles | undefined; private focusToReturn: HTMLElement | undefined; private readonly inputs: InputBox[]; @@ -173,11 +173,12 @@ export class Dialog extends Disposable { return new Promise((resolve) => { clearNode(this.buttonsContainer); - const buttonGroup = this.buttonGroup = this._register(new ButtonGroup(this.buttonsContainer, this.buttons.length, { title: true })); + const buttonBar = this.buttonBar = this._register(new ButtonBar(this.buttonsContainer)); const buttonMap = this.rearrangeButtons(this.buttons, this.options.cancelId); // Handle button clicks - buttonGroup.buttons.forEach((button, index) => { + buttonMap.forEach((entry, index) => { + const button = this._register(buttonBar.addButton({ title: true })); button.label = mnemonicButtonLabel(buttonMap[index].label, true); this._register(button.onDidClick(e => { @@ -237,8 +238,8 @@ export class Dialog extends Disposable { } } - if (this.buttonGroup) { - for (const button of this.buttonGroup.buttons) { + if (this.buttonBar) { + for (const button of this.buttonBar.buttons) { focusableElements.push(button); if (button.hasFocus()) { focusedIndex = focusableElements.length - 1; @@ -349,7 +350,7 @@ export class Dialog extends Disposable { } else { buttonMap.forEach((value, index) => { if (value.index === 0) { - buttonGroup.buttons[index].focus(); + buttonBar.buttons[index].focus(); } }); } @@ -371,8 +372,8 @@ export class Dialog extends Disposable { this.element.style.backgroundColor = bgColor?.toString() ?? ''; this.element.style.border = border; - if (this.buttonGroup) { - this.buttonGroup.buttons.forEach(button => button.style(style)); + if (this.buttonBar) { + this.buttonBar.buttons.forEach(button => button.style(style)); } if (this.checkbox) { diff --git a/src/vs/base/browser/ui/dropdown/dropdown.css b/src/vs/base/browser/ui/dropdown/dropdown.css index fb87b8c5d..77060ee20 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.css +++ b/src/vs/base/browser/ui/dropdown/dropdown.css @@ -12,3 +12,7 @@ cursor: pointer; height: 100%; } + +.monaco-dropdown > .dropdown-label > .action-label.disabled { + cursor: default; +} diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 5713a8a93..ea32bdc07 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -13,6 +13,7 @@ import { Emitter } from 'vs/base/common/event'; import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IActionProvider, DropdownMenu, IDropdownMenuOptions, ILabelRenderer } from 'vs/base/browser/ui/dropdown/dropdown'; import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; +import { Codicon } from 'vs/base/common/codicons'; export interface IKeybindingProvider { (action: IAction): ResolvedKeybinding | undefined; @@ -35,6 +36,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { private menuActionsOrProvider: readonly IAction[] | IActionProvider; private dropdownMenu: DropdownMenu | undefined; private contextMenuProvider: IContextMenuProvider; + private actionItem: HTMLElement | null = null; private _onDidChangeVisibility = this._register(new Emitter()); readonly onDidChangeVisibility = this._onDidChangeVisibility.event; @@ -56,6 +58,8 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { } render(container: HTMLElement): void { + this.actionItem = container; + const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => { this.element = append(el, $('a.action-label')); @@ -115,6 +119,8 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { } }; } + + this.updateEnabled(); } setActionContext(newContext: unknown): void { @@ -134,6 +140,12 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { this.dropdownMenu.show(); } } + + protected updateEnabled(): void { + const disabled = !this.getAction().enabled; + this.actionItem?.classList.toggle('disabled', disabled); + this.element?.classList.toggle('disabled', disabled); + } } export interface IActionWithDropdownActionViewItemOptions extends IActionViewItemOptions { @@ -158,7 +170,7 @@ export class ActionWithDropdownActionViewItem extends ActionViewItem { super.render(container); if (this.element) { this.element.classList.add('action-dropdown-item'); - this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(new Action('dropdownAction', undefined), (this.options).menuActionsOrProvider, this.contextMenuProvider, { classNames: ['dropdown', 'codicon-chevron-down', ...(this.options).menuActionClassNames || []] }); + this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(new Action('dropdownAction', undefined), (this.options).menuActionsOrProvider, this.contextMenuProvider, { classNames: ['dropdown', ...Codicon.dropDownButton.classNamesArray, ...(this.options).menuActionClassNames || []] }); this.dropdownMenuActionViewItem.render(this.element); } } diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index db736d5da..22d8a75e9 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -175,7 +175,7 @@ function getGridLocation(element: HTMLElement): number[] { } const index = indexInParent(parentElement); - const ancestor = parentElement.parentElement!.parentElement!.parentElement!; + const ancestor = parentElement.parentElement!.parentElement!.parentElement!.parentElement!; return [...getGridLocation(ancestor), index]; } @@ -215,6 +215,8 @@ export class Grid extends Disposable { get boundarySashes(): IBoundarySashes { return this.gridview.boundarySashes; } set boundarySashes(boundarySashes: IBoundarySashes) { this.gridview.boundarySashes = boundarySashes; } + set edgeSnapping(edgeSnapping: boolean) { this.gridview.edgeSnapping = edgeSnapping; } + get element(): HTMLElement { return this.gridview.element; } private didLayout = false; diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index b08b72a94..323f71fd6 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -170,6 +170,7 @@ class BranchNode implements ISplitView, IDisposable { private absoluteOffset: number = 0; private absoluteOrthogonalOffset: number = 0; + private absoluteOrthogonalSize: number = 0; private _styles: IGridViewStyles; get styles(): IGridViewStyles { return this._styles; } @@ -270,6 +271,24 @@ class BranchNode implements ISplitView, IDisposable { } } + private _edgeSnapping = false; + get edgeSnapping(): boolean { return this._edgeSnapping; } + set edgeSnapping(edgeSnapping: boolean) { + if (this._edgeSnapping === edgeSnapping) { + return; + } + + this._edgeSnapping = edgeSnapping; + + for (const child of this.children) { + if (child instanceof BranchNode) { + child.edgeSnapping = edgeSnapping; + } + } + + this.updateSplitviewEdgeSnappingEnablement(); + } + constructor( readonly orientation: Orientation, readonly layoutController: ILayoutController, @@ -277,6 +296,7 @@ class BranchNode implements ISplitView, IDisposable { readonly proportionalLayout: boolean, size: number = 0, orthogonalSize: number = 0, + edgeSnapping: boolean = false, childDescriptors?: INodeDescriptor[] ) { this._styles = styles; @@ -355,6 +375,7 @@ class BranchNode implements ISplitView, IDisposable { this._orthogonalSize = size; this.absoluteOffset = ctx.absoluteOffset + offset; this.absoluteOrthogonalOffset = ctx.absoluteOrthogonalOffset; + this.absoluteOrthogonalSize = ctx.absoluteOrthogonalSize; this.splitview.layout(ctx.orthogonalSize, { orthogonalSize: size, @@ -364,9 +385,7 @@ class BranchNode implements ISplitView, IDisposable { absoluteOrthogonalSize: ctx.absoluteSize }); - // Disable snapping on views which sit on the edges of the grid - this.splitview.startSnappingEnabled = this.absoluteOrthogonalOffset > 0; - this.splitview.endSnappingEnabled = this.absoluteOrthogonalOffset + ctx.orthogonalSize < ctx.absoluteOrthogonalSize; + this.updateSplitviewEdgeSnappingEnablement(); } setVisible(visible: boolean): void { @@ -607,6 +626,11 @@ class BranchNode implements ISplitView, IDisposable { }); } + private updateSplitviewEdgeSnappingEnablement(): void { + this.splitview.startSnappingEnabled = this._edgeSnapping || this.absoluteOrthogonalOffset > 0; + this.splitview.endSnappingEnabled = this._edgeSnapping || this.absoluteOrthogonalOffset + this._size < this.absoluteOrthogonalSize; + } + dispose(): void { for (const child of this.children) { child.dispose(); @@ -775,7 +799,7 @@ export interface INodeDescriptor { function flipNode(node: T, size: number, orthogonalSize: number): T { if (node instanceof BranchNode) { - const result = new BranchNode(orthogonal(node.orientation), node.layoutController, node.styles, node.proportionalLayout, size, orthogonalSize); + const result = new BranchNode(orthogonal(node.orientation), node.layoutController, node.styles, node.proportionalLayout, size, orthogonalSize, node.edgeSnapping); let totalSize = 0; @@ -863,6 +887,10 @@ export class GridView implements IDisposable { this.root.boundarySashes = fromAbsoluteBoundarySashes(boundarySashes, this.orientation); } + set edgeSnapping(edgeSnapping: boolean) { + this.root.edgeSnapping = edgeSnapping; + } + /** * The first layout controller makes sure layout only propagates * to the views after the very first call to gridview.layout() @@ -932,7 +960,7 @@ export class GridView implements IDisposable { grandParent.removeChild(parentIndex); - const newParent = new BranchNode(parent.orientation, parent.layoutController, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize); + const newParent = new BranchNode(parent.orientation, parent.layoutController, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize, grandParent.edgeSnapping); grandParent.addChild(newParent, parent.size, parentIndex); const newSibling = new LeafNode(parent.view, grandParent.orientation, this.layoutController, parent.size); @@ -1205,7 +1233,7 @@ export class GridView implements IDisposable { } as INodeDescriptor; }); - result = new BranchNode(orientation, this.layoutController, this.styles, this.proportionalLayout, node.size, orthogonalSize, children); + result = new BranchNode(orientation, this.layoutController, this.styles, this.proportionalLayout, node.size, orthogonalSize, undefined, children); } else { result = new LeafNode(deserializer.fromJSON(node.data), orientation, this.layoutController, orthogonalSize, node.size); } diff --git a/src/vs/base/browser/ui/hover/hover.css b/src/vs/base/browser/ui/hover/hover.css index a5390be41..c2e11fa89 100644 --- a/src/vs/base/browser/ui/hover/hover.css +++ b/src/vs/base/browser/ui/hover/hover.css @@ -87,7 +87,6 @@ .monaco-hover .monaco-tokenized-source { white-space: pre-wrap; - word-break: break-all; } .monaco-hover .hover-row.status-bar { diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 18a32bf99..44c56834f 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -14,7 +14,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { isString } from 'vs/base/common/types'; +import { isFunction, isString } from 'vs/base/common/types'; import { domEvent } from 'vs/base/browser/event'; export interface IIconLabelCreationOptions { @@ -24,8 +24,13 @@ export interface IIconLabelCreationOptions { hoverDelegate?: IHoverDelegate; } +export interface IIconLabelMarkdownString { + markdown: IMarkdownString | string | undefined | (() => Promise); + markdownNotSupportedFallback: string | undefined; +} + export interface IIconLabelValueOptions { - title?: string | IMarkdownString | Promise; + title?: string | IIconLabelMarkdownString; descriptionTitle?: string; hideIcon?: boolean; extraClasses?: string[]; @@ -93,6 +98,8 @@ export class IconLabel extends Disposable { private descriptionNode: FastLabelNode | HighlightedLabel | undefined; private descriptionNodeFactory: () => FastLabelNode | HighlightedLabel; + private labelContainer: HTMLElement; + private hoverDelegate: IHoverDelegate | undefined = undefined; private readonly customHovers: Map = new Map(); @@ -101,10 +108,10 @@ export class IconLabel extends Disposable { this.domNode = this._register(new FastLabelNode(dom.append(container, dom.$('.monaco-icon-label')))); - const labelContainer = dom.append(this.domNode.element, dom.$('.monaco-icon-label-container')); + this.labelContainer = dom.append(this.domNode.element, dom.$('.monaco-icon-label-container')); - const nameContainer = dom.append(labelContainer, dom.$('span.monaco-icon-name-container')); - this.descriptionContainer = this._register(new FastLabelNode(dom.append(labelContainer, dom.$('span.monaco-icon-description-container')))); + const nameContainer = dom.append(this.labelContainer, dom.$('span.monaco-icon-name-container')); + this.descriptionContainer = this._register(new FastLabelNode(dom.append(this.labelContainer, dom.$('span.monaco-icon-description-container')))); if (options?.supportHighlights) { this.nameNode = new LabelWithHighlights(nameContainer, !!options.supportCodicons); @@ -144,7 +151,7 @@ export class IconLabel extends Disposable { } this.domNode.className = classes.join(' '); - this.setupHover(this.domNode.element, options?.title); + this.setupHover(this.labelContainer, options?.title); this.nameNode.setLabel(label, options); @@ -164,7 +171,7 @@ export class IconLabel extends Disposable { } } - private setupHover(htmlElement: HTMLElement, tooltip: string | IMarkdownString | Promise | undefined): void { + private setupHover(htmlElement: HTMLElement, tooltip: string | IIconLabelMarkdownString | undefined): void { const previousCustomHover = this.customHovers.get(htmlElement); if (previousCustomHover) { previousCustomHover.dispose(); @@ -183,22 +190,39 @@ export class IconLabel extends Disposable { } } - private setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, tooltip: string | IMarkdownString | Promise | undefined): void { + private setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, markdownTooltip: string | IIconLabelMarkdownString): void { htmlElement.removeAttribute('title'); + let tooltip: () => Promise; + if (isString(markdownTooltip)) { + tooltip = async () => markdownTooltip; + } else if (isFunction(markdownTooltip.markdown)) { + tooltip = markdownTooltip.markdown; + } else { + const markdown = markdownTooltip.markdown; + tooltip = async () => markdown; + } // Testing has indicated that on Windows and Linux 500 ms matches the native hovers most closely. // On Mac, the delay is 1500. const hoverDelay = isMacintosh ? 1500 : 500; let hoverOptions: IHoverDelegateOptions | undefined; let mouseX: number | undefined; + let isHovering = false; function mouseOver(this: HTMLElement, e: MouseEvent): any { - let isHovering = true; + if (isHovering) { + return; + } + function mouseLeaveOrDown(this: HTMLElement, e: MouseEvent): any { + isHovering = false; + mouseLeaveDisposable.dispose(); + mouseDownDisposable.dispose(); + } + const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeaveOrDown.bind(htmlElement)); + const mouseDownDisposable = domEvent(htmlElement, dom.EventType.MOUSE_DOWN, true)(mouseLeaveOrDown.bind(htmlElement)); + isHovering = true; + function mouseMove(this: HTMLElement, e: MouseEvent): any { mouseX = e.x; } - function mouseLeave(this: HTMLElement, e: MouseEvent): any { - isHovering = false; - } - const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeave.bind(htmlElement)); const mouseMoveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_MOVE, true)(mouseMove.bind(htmlElement)); setTimeout(async () => { if (isHovering && tooltip) { @@ -208,7 +232,7 @@ export class IconLabel extends Disposable { targetElements: [this], dispose: () => { } }; - const resolvedTooltip = await tooltip; + const resolvedTooltip = await tooltip(); if (resolvedTooltip) { hoverOptions = { text: resolvedTooltip, @@ -217,7 +241,8 @@ export class IconLabel extends Disposable { }; } } - if (hoverOptions) { + // awaiting the tooltip could take a while. Make sure we're still hovering. + if (hoverOptions && isHovering) { if (mouseX !== undefined) { (hoverOptions.target).x = mouseX + 10; } @@ -225,15 +250,20 @@ export class IconLabel extends Disposable { } } mouseMoveDisposable.dispose(); - mouseLeaveDisposable.dispose(); }, hoverDelay); } const mouseOverDisposable = this._register(domEvent(htmlElement, dom.EventType.MOUSE_OVER, true)(mouseOver.bind(htmlElement))); this.customHovers.set(htmlElement, mouseOverDisposable); } - private setupNativeHover(htmlElement: HTMLElement, tooltip: string | IMarkdownString | Promise | undefined): void { - htmlElement.title = isString(tooltip) ? tooltip : ''; + private setupNativeHover(htmlElement: HTMLElement, tooltip: string | IIconLabelMarkdownString | undefined): void { + let stringTooltip: string = ''; + if (isString(tooltip)) { + stringTooltip = tooltip; + } else if (tooltip?.markdownNotSupportedFallback) { + stringTooltip = tooltip.markdownNotSupportedFallback; + } + htmlElement.title = stringTooltip; } } diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css index 9341febad..06224874c 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -60,12 +60,12 @@ } .monaco-icon-label.italic > .monaco-icon-label-container > .monaco-icon-name-container > .label-name, -.monaco-icon-label.italic > .monaco-icon-description-container > .label-description { +.monaco-icon-label.italic > .monaco-icon-label-container > .monaco-icon-description-container > .label-description { font-style: italic; } .monaco-icon-label.strikethrough > .monaco-icon-label-container > .monaco-icon-name-container > .label-name, -.monaco-icon-label.strikethrough > .monaco-icon-description-container > .label-description { +.monaco-icon-label.strikethrough > .monaco-icon-label-container > .monaco-icon-description-container > .label-description { text-decoration: line-through; } diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 03db9d2a6..e2e8e849a 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -258,6 +258,10 @@ export class PagedList implements IThemable, IDisposable { return this.list.getSelection(); } + getSelectedElements(): T[] { + return this.getSelection().map(i => this.model.get(i)); + } + layout(height?: number, width?: number): void { this.list.layout(height, width); } diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index c527ca29f..26b198431 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -327,7 +327,6 @@ export class ListView implements ISpliceable, IDisposable { this.scrollable = new Scrollable(getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, cb => scheduleAtNextAnimationFrame(cb)); this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { - alwaysConsumeMouseWheel: true, horizontal: ScrollbarVisibility.Auto, vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows), @@ -1323,6 +1322,9 @@ export class ListView implements ISpliceable, IDisposable { if (item.row) { const renderer = this.renderers.get(item.row.templateId); if (renderer) { + if (renderer.disposeElement) { + renderer.disposeElement(item.element, -1, item.row.templateData, undefined); + } renderer.disposeTemplate(item.row.templateData); } } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 39d1bbed0..e530b43d3 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -318,10 +318,12 @@ class KeyboardController implements IDisposable { } private onEscape(e: StandardKeyboardEvent): void { - e.preventDefault(); - e.stopPropagation(); - this.list.setSelection([], e.browserEvent); - this.view.domNode.focus(); + if (this.list.getSelection().length) { + e.preventDefault(); + e.stopPropagation(); + this.list.setSelection([], e.browserEvent); + this.view.domNode.focus(); + } } dispose() { @@ -1460,6 +1462,8 @@ export class List implements ISpliceable, IThemable, IDisposable { this.view.setScrollTop(previousScrollTop + this.view.renderHeight - this.view.elementHeight(lastPageIndex)); if (this.view.getScrollTop() !== previousScrollTop) { + this.setFocus([]); + // Let the scroll event listener run setTimeout(() => this.focusNextPage(browserEvent, filter), 0); } @@ -1492,6 +1496,8 @@ export class List implements ISpliceable, IThemable, IDisposable { this.view.setScrollTop(scrollTop - this.view.renderHeight); if (this.view.getScrollTop() !== previousScrollTop) { + this.setFocus([]); + // Let the scroll event listener run setTimeout(() => this.focusPreviousPage(browserEvent, filter), 0); } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 8b3da5df8..fc129fef5 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -18,7 +18,7 @@ import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { Event } from 'vs/base/common/event'; import { AnchorAlignment, layout, LayoutAnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; -import { Codicon, registerIcon, stripCodicons } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon, stripCodicons } from 'vs/base/common/codicons'; import { BaseActionViewItem, ActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { formatRule } from 'vs/base/browser/ui/codicons/codiconStyles'; import { isFirefox } from 'vs/base/browser/browser'; @@ -27,8 +27,8 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; export const MENU_MNEMONIC_REGEX = /\(&([^\s&])\)|(^|[^&])&([^\s&])/; export const MENU_ESCAPED_MNEMONIC_REGEX = /(&)?(&)([^\s&])/g; -const menuSelectionIcon = registerIcon('menu-selection', Codicon.check); -const menuSubmenuIcon = registerIcon('menu-submenu', Codicon.chevronRight); +const menuSelectionIcon = registerCodicon('menu-selection', Codicon.check); +const menuSubmenuIcon = registerCodicon('menu-submenu', Codicon.chevronRight); export enum Direction { Right, @@ -728,6 +728,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { if (this.item) { this.item.classList.add('monaco-submenu-item'); + this.item.tabIndex = 0; this.item.setAttribute('aria-haspopup', 'true'); this.updateAriaExpanded('false'); this.submenuIndicator = append(this.item, $('span.submenu-indicator' + menuSubmenuIcon.cssSelector)); @@ -777,6 +778,12 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { })); } + updateEnabled(): void { + // override on submenu entry + // native menus do not observe enablement on sumbenus + // we mimic that behavior + } + open(selectFirst?: boolean): void { this.cleanupExistingSubmenu(false); this.createSubmenu(selectFirst); @@ -1189,6 +1196,7 @@ ${formatRule(menuSubmenuIcon)} outline: 0; border: none; animation: fadeIn 0.083s linear; + -webkit-app-region: no-drag; } .context-view.monaco-menu-container :focus, diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index cb2b3fc0c..55858a589 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -21,11 +21,11 @@ import { asArray } from 'vs/base/common/arrays'; import { ScanCodeUtils, ScanCode } from 'vs/base/common/scanCode'; import { isMacintosh } from 'vs/base/common/platform'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; const $ = DOM.$; -const menuBarMoreIcon = registerIcon('menubar-more', Codicon.more); +const menuBarMoreIcon = registerCodicon('menubar-more', Codicon.more); export interface IMenuBarOptions { enableMnemonics?: boolean; @@ -115,7 +115,7 @@ export class MenuBar extends Disposable { this.menuUpdater = this._register(new RunOnceScheduler(() => this.update(), 200)); this.actionRunner = this._register(new ActionRunner()); - this._register(this.actionRunner.onDidBeforeRun(() => { + this._register(this.actionRunner.onBeforeRun(() => { this.setUnfocusedState(); })); diff --git a/src/vs/base/browser/ui/progressbar/progressbar.css b/src/vs/base/browser/ui/progressbar/progressbar.css index 64368d699..55e6a1a69 100644 --- a/src/vs/base/browser/ui/progressbar/progressbar.css +++ b/src/vs/base/browser/ui/progressbar/progressbar.css @@ -42,7 +42,10 @@ * The progress bit has a width: 2% (1/50) of the parent container. The animation moves it from 0% to 100% of * that container. Since translateX is relative to the progress bit size, we have to multiple it with * its relative size to the parent container: - * 50%: 50 * 50 = 2500% - * 100%: 50 * 100 - 50 (do not overflow): 4950% + * parent width: 5000% + * bit width: 100% + * translateX should be as follow: + * 50%: 5000% * 50% - 50% (set to center) = 2450% + * 100%: 5000% * 100% - 100% (do not overflow) = 4900% */ -@keyframes progress { from { transform: translateX(0%) scaleX(1) } 50% { transform: translateX(2500%) scaleX(3) } to { transform: translateX(4950%) scaleX(1) } } +@keyframes progress { from { transform: translateX(0%) scaleX(1) } 50% { transform: translateX(2500%) scaleX(3) } to { transform: translateX(4900%) scaleX(1) } } diff --git a/src/vs/base/browser/ui/progressbar/progressbar.ts b/src/vs/base/browser/ui/progressbar/progressbar.ts index 5e47bcb13..a8aa8b61e 100644 --- a/src/vs/base/browser/ui/progressbar/progressbar.ts +++ b/src/vs/base/browser/ui/progressbar/progressbar.ts @@ -58,6 +58,7 @@ export class ProgressBar extends Disposable { this.element = document.createElement('div'); this.element.classList.add('monaco-progress-container'); this.element.setAttribute('role', 'progressbar'); + this.element.setAttribute('aria-valuemin', '0'); container.appendChild(this.element); this.bit = document.createElement('div'); diff --git a/src/vs/base/browser/ui/sash/sash.css b/src/vs/base/browser/ui/sash/sash.css index ac9e628c9..db8977d69 100644 --- a/src/vs/base/browser/ui/sash/sash.css +++ b/src/vs/base/browser/ui/sash/sash.css @@ -60,19 +60,48 @@ height: var(--sash-size); } -.monaco-sash:not(.disabled).orthogonal-start::before, .monaco-sash:not(.disabled).orthogonal-end::after { - content: ' '; +.monaco-sash:not(.disabled).orthogonal-start::before, +.monaco-sash:not(.disabled).orthogonal-end::after { + content: " "; height: calc(var(--sash-size) * 2); width: calc(var(--sash-size) * 2); z-index: 100; display: block; - cursor: all-scroll; position: absolute; + cursor: all-scroll; + position: absolute; } -.monaco-sash.orthogonal-start.vertical::before { left: -calc(var(--sash-size) / 2); top: calc(var(--sash-size) * -1); } -.monaco-sash.orthogonal-end.vertical::after { left: -calc(var(--sash-size) / 2); bottom: calc(var(--sash-size) * -1); } -.monaco-sash.orthogonal-start.horizontal::before { top: -calc(var(--sash-size) / 2); left: calc(var(--sash-size) * -1); } -.monaco-sash.orthogonal-end.horizontal::after { top: -calc(var(--sash-size) / 2); right: calc(var(--sash-size) * -1); } +.monaco-sash.horizontal.orthogonal-edge-north:not(.disabled).orthogonal-start::before, +.monaco-sash.horizontal.orthogonal-edge-south:not(.disabled).orthogonal-end::after { + cursor: nwse-resize; +} + +.monaco-sash.horizontal.orthogonal-edge-north:not(.disabled).orthogonal-end::after, +.monaco-sash.horizontal.orthogonal-edge-south:not(.disabled).orthogonal-start::before { + cursor: nesw-resize; +} + +.monaco-sash.orthogonal-start.vertical::before { + left: -calc(var(--sash-size) / 2); + top: calc(var(--sash-size) * -1); +} +.monaco-sash.orthogonal-end.vertical::after { + left: -calc(var(--sash-size) / 2); + bottom: calc(var(--sash-size) * -1); +} +.monaco-sash.orthogonal-start.horizontal::before { + top: -calc(var(--sash-size) / 2); + left: calc(var(--sash-size) * -1); +} +.monaco-sash.orthogonal-end.horizontal::after { + top: -calc(var(--sash-size) / 2); + right: calc(var(--sash-size) * -1); +} + +.monaco-sash { + transition: background-color 0.1s ease-out; + background: transparent; +} /** Debug **/ diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index 3f7801ddb..fa0aa709d 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -37,11 +37,19 @@ export interface ISashEvent { altKey: boolean; } +export enum OrthogonalEdge { + North = 'north', + South = 'south', + East = 'east', + West = 'west' +} + export interface ISashOptions { readonly orientation: Orientation; readonly orthogonalStartSash?: Sash; readonly orthogonalEndSash?: Sash; readonly size?: number; + readonly orthogonalEdge?: OrthogonalEdge; } export interface IVerticalSashOptions extends ISashOptions { @@ -150,6 +158,10 @@ export class Sash extends Disposable { this.el = append(container, $('.monaco-sash')); + if (options.orthogonalEdge) { + this.el.classList.add(`orthogonal-edge-${options.orthogonalEdge}`); + } + if (isMacintosh) { this.el.classList.add('mac'); } diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index 23bfdb2c7..133ec9350 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -38,12 +38,14 @@ export interface AbstractScrollbarOptions { visibility: ScrollbarVisibility; extraScrollbarClassName: string; scrollable: Scrollable; + scrollByPage: boolean; } export abstract class AbstractScrollbar extends Widget { protected _host: ScrollbarHost; protected _scrollable: Scrollable; + protected _scrollByPage: boolean; private _lazyRender: boolean; protected _scrollbarState: ScrollbarState; private _visibilityController: ScrollbarVisibilityController; @@ -59,6 +61,7 @@ export abstract class AbstractScrollbar extends Widget { this._lazyRender = opts.lazyRender; this._host = opts.host; this._scrollable = opts.scrollable; + this._scrollByPage = opts.scrollByPage; this._scrollbarState = opts.scrollbarState; this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName)); this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()); @@ -210,7 +213,14 @@ export abstract class AbstractScrollbar extends Widget { offsetX = e.posx - domNodePosition.left; offsetY = e.posy - domNodePosition.top; } - this._setDesiredScrollPositionNow(this._scrollbarState.getDesiredScrollPositionFromOffset(this._mouseDownRelativePosition(offsetX, offsetY))); + + const offset = this._mouseDownRelativePosition(offsetX, offsetY); + this._setDesiredScrollPositionNow( + this._scrollByPage + ? this._scrollbarState.getDesiredScrollPositionFromOffsetPaged(offset) + : this._scrollbarState.getDesiredScrollPositionFromOffset(offset) + ); + if (e.leftButton) { e.preventDefault(); this._sliderMouseDown(e, () => { /*nothing to do*/ }); diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index 6e7f132e9..9d9a22683 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -9,11 +9,11 @@ import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/s import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; -const scrollbarButtonLeftIcon = registerIcon('scrollbar-button-left', Codicon.triangleLeft); -const scrollbarButtonRightIcon = registerIcon('scrollbar-button-right', Codicon.triangleRight); +const scrollbarButtonLeftIcon = registerCodicon('scrollbar-button-left', Codicon.triangleLeft); +const scrollbarButtonRightIcon = registerCodicon('scrollbar-button-right', Codicon.triangleRight); export class HorizontalScrollbar extends AbstractScrollbar { @@ -33,7 +33,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { ), visibility: options.horizontal, extraScrollbarClassName: 'horizontal', - scrollable: scrollable + scrollable: scrollable, + scrollByPage: options.scrollByPage }); if (options.horizontalHasArrows) { diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 00bb9830d..115864f3d 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -361,6 +361,8 @@ export abstract class AbstractScrollableElement extends Widget { // console.log(`${Date.now()}, ${e.deltaY}, ${e.deltaX}`); + let didScroll = false; + if (e.deltaY || e.deltaX) { let deltaY = e.deltaY * this._options.mouseWheelScrollSensitivity; let deltaX = e.deltaX * this._options.mouseWheelScrollSensitivity; @@ -419,11 +421,12 @@ export abstract class AbstractScrollableElement extends Widget { } else { this._scrollable.setScrollPositionNow(desiredScrollPosition); } - this._shouldRender = true; + + didScroll = true; } } - if (this._options.alwaysConsumeMouseWheel || this._shouldRender) { + if (this._options.alwaysConsumeMouseWheel || didScroll) { e.preventDefault(); e.stopPropagation(); } @@ -614,7 +617,9 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : ScrollbarVisibility.Auto), verticalScrollbarSize: (typeof opts.verticalScrollbarSize !== 'undefined' ? opts.verticalScrollbarSize : 10), verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false), - verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0) + verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0), + + scrollByPage: (typeof opts.scrollByPage !== 'undefined' ? opts.scrollByPage : false) }; result.horizontalSliderSize = (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : result.horizontalScrollbarSize); diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts index afb227be7..556d20839 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts @@ -114,6 +114,11 @@ export interface ScrollableElementCreationOptions { * Defaults to false. */ verticalHasArrows?: boolean; + /** + * Scroll gutter clicks move by page vs. jump to position. + * Defaults to false. + */ + scrollByPage?: boolean; } export interface ScrollableElementChangeOptions { @@ -146,4 +151,5 @@ export interface ScrollableElementResolvedOptions { verticalScrollbarSize: number; verticalSliderSize: number; verticalHasArrows: boolean; + scrollByPage: boolean; } diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts index 48e20a5a0..d0d98b9a8 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts @@ -202,6 +202,28 @@ export class ScrollbarState { return Math.round(desiredSliderPosition / this._computedSliderRatio); } + /** + * Compute a desired `scrollPosition` from if offset is before or after the slider position. + * If offset is before slider, treat as a page up (or left). If after, page down (or right). + * `offset` and `_computedSliderPosition` are based on the same coordinate system. + * `_visibleSize` corresponds to a "page" of lines in the returned coordinate system. + */ + public getDesiredScrollPositionFromOffsetPaged(offset: number): number { + if (!this._computedIsNeeded) { + // no need for a slider + return 0; + } + + let correctedOffset = offset - this._arrowSize; // compensate if has arrows + let desiredScrollPosition = this._scrollPosition; + if (correctedOffset < this._computedSliderPosition) { + desiredScrollPosition -= this._visibleSize; // page up/left + } else { + desiredScrollPosition += this._visibleSize; // page down/right + } + return desiredScrollPosition; + } + /** * Compute a desired `scrollPosition` such that the slider moves by `delta`. */ diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index 296913a3f..12d2887b6 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -9,10 +9,10 @@ import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/s import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; -const scrollbarButtonUpIcon = registerIcon('scrollbar-button-up', Codicon.triangleUp); -const scrollbarButtonDownIcon = registerIcon('scrollbar-button-down', Codicon.triangleDown); +const scrollbarButtonUpIcon = registerCodicon('scrollbar-button-up', Codicon.triangleUp); +const scrollbarButtonDownIcon = registerCodicon('scrollbar-button-down', Codicon.triangleDown); export class VerticalScrollbar extends AbstractScrollbar { @@ -33,7 +33,8 @@ export class VerticalScrollbar extends AbstractScrollbar { ), visibility: options.vertical, extraScrollbarClassName: 'vertical', - scrollable: scrollable + scrollable: scrollable, + scrollByPage: options.scrollByPage }); if (options.verticalHasArrows) { diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index 79f0da0e6..4315b8e8f 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -46,6 +46,7 @@ export interface ISelectBoxOptions { // Utilize optionItem interface to capture all option parameters export interface ISelectOptionItem { text: string; + detail?: string; decoratorRight?: string; description?: string; descriptionIsMarkdown?: boolean; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css index 536a94223..f385bf29d 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css @@ -75,6 +75,15 @@ float: left; } +.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row > .option-detail { + text-overflow: ellipsis; + overflow: hidden; + padding-left: 3.5px; + white-space: nowrap; + float: left; + opacity: 0.7; +} + .monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row > .option-decorator-right { text-overflow: ellipsis; overflow: hidden; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 62438e1b3..841f2ee44 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -29,6 +29,7 @@ const SELECT_OPTION_ENTRY_TEMPLATE_ID = 'selectOption.entry.template'; interface ISelectListTemplateData { root: HTMLElement; text: HTMLElement; + detail: HTMLElement; decoratorRight: HTMLElement; disposables: IDisposable[]; } @@ -42,6 +43,7 @@ class SelectListRenderer implements IListRenderer { - const len = option.text.length + (!!option.decoratorRight ? option.decoratorRight.length : 0); + const detailLength = !!option.detail ? option.detail.length : 0; + const rightDecoratorLength = !!option.decoratorRight ? option.decoratorRight.length : 0; + + const len = option.text.length + detailLength + rightDecoratorLength; if (len > longestLength) { longest = index; longestLength = len; @@ -697,6 +706,10 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi accessibilityProvider: { getAriaLabel: element => { let label = element.text; + if (element.detail) { + label += `. ${element.detail}`; + } + if (element.decoratorRight) { label += `. ${element.decoratorRight}`; } diff --git a/src/vs/base/browser/ui/splitview/paneview.css b/src/vs/base/browser/ui/splitview/paneview.css index 06743287c..3ffad073b 100644 --- a/src/vs/base/browser/ui/splitview/paneview.css +++ b/src/vs/base/browser/ui/splitview/paneview.css @@ -38,23 +38,12 @@ width: 22px; } -.monaco-pane-view .pane > .pane-header > .twisties { - width: 20px; - display: flex; - align-items: center; - justify-content: center; - transform-origin: center; - color: inherit; - flex-shrink: 0; +.monaco-pane-view .pane > .pane-header > .codicon:first-of-type { + margin: 0 2px; } -.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header > .twisties { - margin-top: 2px; - margin-bottom: 2px; -} - -.monaco-pane-view .pane > .pane-header.expanded > .twisties::before { - transform: rotate(90deg); +.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header > .codicon:first-of-type { + margin: 2px; } /* TODO: actions should be part of the pane, but they aren't yet */ diff --git a/src/vs/base/browser/ui/splitview/splitview.css b/src/vs/base/browser/ui/splitview/splitview.css index e5baba1f3..3af3e9062 100644 --- a/src/vs/base/browser/ui/splitview/splitview.css +++ b/src/vs/base/browser/ui/splitview/splitview.css @@ -20,31 +20,36 @@ pointer-events: initial; } -.monaco-split-view2 > .split-view-container { +.monaco-split-view2 > .monaco-scrollable-element { + width: 100%; + height: 100%; +} + +.monaco-split-view2 > .monaco-scrollable-element > .split-view-container { width: 100%; height: 100%; white-space: nowrap; position: relative; } -.monaco-split-view2 > .split-view-container > .split-view-view { +.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view { white-space: initial; position: absolute; } -.monaco-split-view2 > .split-view-container > .split-view-view:not(.visible) { +.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view:not(.visible) { display: none; } -.monaco-split-view2.vertical > .split-view-container > .split-view-view { +.monaco-split-view2.vertical > .monaco-scrollable-element > .split-view-container > .split-view-view { width: 100%; } -.monaco-split-view2.horizontal > .split-view-container > .split-view-view { +.monaco-split-view2.horizontal > .monaco-scrollable-element > .split-view-container > .split-view-view { height: 100%; } -.monaco-split-view2.separator-border > .split-view-container > .split-view-view:not(:first-child)::before { +.monaco-split-view2.separator-border > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before { content: ' '; position: absolute; top: 0; @@ -54,12 +59,12 @@ background-color: var(--separator-border); } -.monaco-split-view2.separator-border.horizontal > .split-view-container > .split-view-view:not(:first-child)::before { +.monaco-split-view2.separator-border.horizontal > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before { height: 100%; width: 1px; } -.monaco-split-view2.separator-border.vertical > .split-view-container > .split-view-view:not(:first-child)::before { +.monaco-split-view2.separator-border.vertical > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before { height: 1px; width: 100%; } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 10c15797c..8bb893912 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -12,7 +12,9 @@ import { range, pushToStart, pushToEnd } from 'vs/base/common/arrays'; import { Sash, Orientation, ISashEvent as IBaseSashEvent, SashState } from 'vs/base/browser/ui/sash/sash'; import { Color } from 'vs/base/common/color'; import { domEvent } from 'vs/base/browser/event'; -import { $, append } from 'vs/base/browser/dom'; +import { $, append, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; export { Orientation } from 'vs/base/browser/ui/sash/sash'; export interface ISplitViewStyles { @@ -213,6 +215,8 @@ export class SplitView extends Disposable { readonly el: HTMLElement; private sashContainer: HTMLElement; private viewContainer: HTMLElement; + private scrollable: Scrollable; + private scrollableElement: SmoothScrollableElement; private size = 0; private layoutContext: TLayoutContext | undefined; private contentSize = 0; @@ -301,7 +305,20 @@ export class SplitView extends Disposable { container.appendChild(this.el); this.sashContainer = append(this.el, $('.sash-container')); - this.viewContainer = append(this.el, $('.split-view-container')); + this.viewContainer = $('.split-view-container'); + + this.scrollable = new Scrollable(125, scheduleAtNextAnimationFrame); + this.scrollableElement = this._register(new SmoothScrollableElement(this.viewContainer, { + vertical: this.orientation === Orientation.VERTICAL ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden, + horizontal: this.orientation === Orientation.HORIZONTAL ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden + }, this.scrollable)); + + this._register(this.scrollableElement.onScroll(e => { + this.viewContainer.scrollTop = e.scrollTop; + this.viewContainer.scrollLeft = e.scrollLeft; + })); + + append(this.el, this.scrollableElement.getDomNode()); this.style(options.styles || defaultStyles); @@ -897,6 +914,21 @@ export class SplitView extends Disposable { // Layout sashes this.sashItems.forEach(item => item.sash.layout()); this.updateSashEnablement(); + this.updateScrollableElement(); + } + + private updateScrollableElement(): void { + if (this.orientation === Orientation.VERTICAL) { + this.scrollableElement.setScrollDimensions({ + height: this.size, + scrollHeight: this.contentSize + }); + } else { + this.scrollableElement.setScrollDimensions({ + width: this.size, + scrollWidth: this.contentSize + }); + } } private updateSashEnablement(): void { diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index d2b361446..d782e1bcb 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -11,12 +11,12 @@ import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, CSSIcon, registerCodicon } from 'vs/base/common/codicons'; import { EventMultiplexer } from 'vs/base/common/event'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; -const toolBarMoreIcon = registerIcon('toolbar-more', Codicon.more); +const toolBarMoreIcon = registerCodicon('toolbar-more', Codicon.more); export interface IToolBarOptions { orientation?: ActionsOrientation; @@ -27,6 +27,7 @@ export interface IToolBarOptions { toggleMenuTitle?: string; anchorAlignmentProvider?: () => AnchorAlignment; renderDropdownAsChildElement?: boolean; + moreIcon?: CSSIcon; } /** @@ -72,7 +73,7 @@ export class ToolBar extends Disposable { actionViewItemProvider: this.options.actionViewItemProvider, actionRunner: this.actionRunner, keybindingProvider: this.options.getKeyBinding, - classNames: toolBarMoreIcon.classNamesArray, + classNames: (options.moreIcon ?? toolBarMoreIcon).classNames, anchorAlignmentProvider: this.options.anchorAlignmentProvider, menuAsChild: !!this.options.renderDropdownAsChildElement } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index fa8ac3e84..4df19745c 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -400,15 +400,23 @@ class TreeRenderer implements IListRenderer } private renderTwistie(node: ITreeNode, templateData: ITreeListTemplateData) { + templateData.twistie.classList.remove(...treeItemExpandedIcon.classNamesArray); + + let twistieRendered = false; + if (this.renderer.renderTwistie) { - this.renderer.renderTwistie(node.element, templateData.twistie); + twistieRendered = this.renderer.renderTwistie(node.element, templateData.twistie); } if (node.collapsible && (!this.hideTwistiesOfChildlessElements || node.visibleChildrenCount > 0)) { - templateData.twistie.classList.add(...treeItemExpandedIcon.classNamesArray, 'collapsible'); + if (!twistieRendered) { + templateData.twistie.classList.add(...treeItemExpandedIcon.classNamesArray); + } + + templateData.twistie.classList.add('collapsible'); templateData.twistie.classList.toggle('collapsed', node.collapsed); } else { - templateData.twistie.classList.remove(...treeItemExpandedIcon.classNamesArray, 'collapsible', 'collapsed'); + templateData.twistie.classList.remove('collapsible', 'collapsed'); } if (node.collapsible) { @@ -811,7 +819,7 @@ class TypeFilterController implements IDisposable { const onDragOver = (event: DragEvent) => { event.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) - const x = event.screenX - left; + const x = event.clientX - left; if (event.dataTransfer) { event.dataTransfer.dropEffect = 'none'; } @@ -954,6 +962,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { readonly smoothScrolling?: boolean; readonly horizontalScrolling?: boolean; readonly expandOnlyOnDoubleClick?: boolean; + readonly expandOnlyOnTwistieClick?: boolean | ((e: any) => boolean); // e is T } export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions { @@ -961,7 +970,6 @@ export interface IAbstractTreeOptions extends IAbstractTr readonly filter?: ITreeFilter; readonly dnd?: ITreeDragAndDrop; readonly keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; - readonly expandOnlyOnTwistieClick?: boolean | ((e: T) => boolean); readonly additionalScrollHeight?: number; } @@ -1109,7 +1117,7 @@ class TreeNodeListMouseController extends MouseController< expandOnlyOnTwistieClick = !!this.tree.expandOnlyOnTwistieClick; } - if (expandOnlyOnTwistieClick && !onTwistie) { + if (expandOnlyOnTwistieClick && !onTwistie && e.browserEvent.detail !== 2) { return super.onViewPointer(e); } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 970eddf5c..e4a93aef0 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -110,10 +110,11 @@ class AsyncDataTreeRenderer implements IT renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { if (element.slow) { twistieElement.classList.add(...treeItemLoadingIcon.classNamesArray); + return true; } else { twistieElement.classList.remove(...treeItemLoadingIcon.classNamesArray); + return false; } - return false; } disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { @@ -1053,10 +1054,11 @@ class CompressibleAsyncDataTreeRenderer i renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { if (element.slow) { twistieElement.classList.add(...treeItemLoadingIcon.classNamesArray); + return true; } else { twistieElement.classList.remove(...treeItemLoadingIcon.classNamesArray); + return false; } - return false; } disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { diff --git a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts index 150f49ce5..3dc8ab961 100644 --- a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts @@ -38,8 +38,8 @@ export function compress(element: ICompressedTreeElement): ITreeElement>; - let children: ITreeElement[]; + let childrenIterator: Iterable>; + let children: ICompressedTreeElement[]; while (true) { [children, childrenIterator] = Iterable.consume(Iterable.from(element.children), 2); @@ -48,12 +48,11 @@ export function compress(element: ICompressedTreeElement): ITreeElement, TFilterData, TTemplateDat this.renderer.disposeTemplate(templateData.data); } - renderTwistie?(element: T, twistieElement: HTMLElement): void { + renderTwistie?(element: T, twistieElement: HTMLElement): boolean { if (this.renderer.renderTwistie) { - this.renderer.renderTwistie(element, twistieElement); + return this.renderer.renderTwistie(element, twistieElement); } + return false; } } diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 085bb3d90..c17b41fbb 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -129,7 +129,7 @@ export interface ITreeModel { } export interface ITreeRenderer extends IListRenderer, TTemplateData> { - renderTwistie?(element: T, twistieElement: HTMLElement): void; + renderTwistie?(element: T, twistieElement: HTMLElement): boolean; onDidChangeTwistieState?: Event; } diff --git a/src/vs/base/browser/ui/tree/treeIcons.ts b/src/vs/base/browser/ui/tree/treeIcons.ts index bccd51889..9755ae59e 100644 --- a/src/vs/base/browser/ui/tree/treeIcons.ts +++ b/src/vs/base/browser/ui/tree/treeIcons.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; -export const treeItemExpandedIcon = registerIcon('tree-item-expanded', Codicon.chevronDown); // collapsed is done with rotation +export const treeItemExpandedIcon = registerCodicon('tree-item-expanded', Codicon.chevronDown); // collapsed is done with rotation -export const treeFilterOnTypeOnIcon = registerIcon('tree-filter-on-type-on', Codicon.listFilter); -export const treeFilterOnTypeOffIcon = registerIcon('tree-filter-on-type-off', Codicon.listSelection); -export const treeFilterClearIcon = registerIcon('tree-filter-clear', Codicon.close); +export const treeFilterOnTypeOnIcon = registerCodicon('tree-filter-on-type-on', Codicon.listFilter); +export const treeFilterOnTypeOffIcon = registerCodicon('tree-filter-on-type-off', Codicon.listSelection); +export const treeFilterClearIcon = registerCodicon('tree-filter-clear', Codicon.close); -export const treeItemLoadingIcon = registerIcon('tree-item-loading', Codicon.loading); +export const treeItemLoadingIcon = registerCodicon('tree-item-loading', Codicon.loading); diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index cb4b12d5c..8be6f19af 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -36,7 +36,7 @@ export interface IAction extends IDisposable { export interface IActionRunner extends IDisposable { run(action: IAction, context?: any): Promise; readonly onDidRun: Event; - readonly onDidBeforeRun: Event; + readonly onBeforeRun: Event; } export interface IActionViewItem extends IDisposable { @@ -178,8 +178,8 @@ export interface IRunEvent { export class ActionRunner extends Disposable implements IActionRunner { - private _onDidBeforeRun = this._register(new Emitter()); - readonly onDidBeforeRun: Event = this._onDidBeforeRun.event; + private _onBeforeRun = this._register(new Emitter()); + readonly onBeforeRun: Event = this._onBeforeRun.event; private _onDidRun = this._register(new Emitter()); readonly onDidRun: Event = this._onDidRun.event; @@ -189,7 +189,7 @@ export class ActionRunner extends Disposable implements IActionRunner { return Promise.resolve(null); } - this._onDidBeforeRun.fire({ action: action }); + this._onBeforeRun.fire({ action: action }); try { const result = await this.runAction(action, context); @@ -235,6 +235,17 @@ export class Separator extends Action { } } +export class ActionWithMenuAction extends Action { + + get actions(): IAction[] { + return this._actions; + } + + constructor(id: string, private _actions: IAction[], label?: string, cssClass?: string, enabled?: boolean, actionCallback?: (event?: any) => Promise) { + super(id, label, cssClass, enabled, actionCallback); + } +} + export class SubmenuAction extends Action { get actions(): IAction[] { @@ -242,7 +253,7 @@ export class SubmenuAction extends Action { } constructor(id: string, label: string, private _actions: IAction[], cssClass?: string) { - super(id, label, cssClass, true); + super(id, label, cssClass, !!_actions?.length); } } diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index d8ce68b55..a0ee6f54d 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { mergeSort } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; /** @@ -18,3 +19,133 @@ export function getPathFromAmdModule(requirefn: typeof require, relativePath: st export function getUriFromAmdModule(requirefn: typeof require, relativePath: string): URI { return URI.parse(requirefn.toUrl(relativePath)); } + +export abstract class LoaderStats { + abstract get amdLoad(): [string, number][]; + abstract get amdInvoke(): [string, number][]; + abstract get nodeRequire(): [string, number][]; + abstract get nodeEval(): [string, number][]; + abstract get nodeRequireTotal(): number; + + + static get(): LoaderStats { + + + const amdLoadScript = new Map(); + const amdInvokeFactory = new Map(); + const nodeRequire = new Map(); + const nodeEval = new Map(); + + function mark(map: Map, stat: LoaderEvent) { + if (map.has(stat.detail)) { + // console.warn('BAD events, DOUBLE start', stat); + // map.delete(stat.detail); + return; + } + map.set(stat.detail, -stat.timestamp); + } + + function diff(map: Map, stat: LoaderEvent) { + let duration = map.get(stat.detail); + if (!duration) { + // console.warn('BAD events, end WITHOUT start', stat); + // map.delete(stat.detail); + return; + } + if (duration >= 0) { + // console.warn('BAD events, DOUBLE end', stat); + // map.delete(stat.detail); + return; + } + map.set(stat.detail, duration + stat.timestamp); + } + + const stats = mergeSort(require.getStats().slice(0), (a, b) => a.timestamp - b.timestamp); + + for (const stat of stats) { + switch (stat.type) { + case LoaderEventType.BeginLoadingScript: + mark(amdLoadScript, stat); + break; + case LoaderEventType.EndLoadingScriptOK: + case LoaderEventType.EndLoadingScriptError: + diff(amdLoadScript, stat); + break; + + case LoaderEventType.BeginInvokeFactory: + mark(amdInvokeFactory, stat); + break; + case LoaderEventType.EndInvokeFactory: + diff(amdInvokeFactory, stat); + break; + + case LoaderEventType.NodeBeginNativeRequire: + mark(nodeRequire, stat); + break; + case LoaderEventType.NodeEndNativeRequire: + diff(nodeRequire, stat); + break; + + case LoaderEventType.NodeBeginEvaluatingScript: + mark(nodeEval, stat); + break; + case LoaderEventType.NodeEndEvaluatingScript: + diff(nodeEval, stat); + break; + } + } + + let nodeRequireTotal = 0; + nodeRequire.forEach(value => nodeRequireTotal += value); + + function to2dArray(map: Map): [string, number][] { + let res: [string, number][] = []; + map.forEach((value, index) => res.push([index, value])); + return res; + } + + return { + amdLoad: to2dArray(amdLoadScript), + amdInvoke: to2dArray(amdInvokeFactory), + nodeRequire: to2dArray(nodeRequire), + nodeEval: to2dArray(nodeEval), + nodeRequireTotal + }; + } + + static toMarkdownTable(header: string[], rows: Array>): string { + let result = ''; + + let lengths: number[] = []; + header.forEach((cell, ci) => { + lengths[ci] = cell.length; + }); + rows.forEach(row => { + row.forEach((cell, ci) => { + if (typeof cell === 'undefined') { + cell = row[ci] = '-'; + } + const len = cell.toString().length; + lengths[ci] = Math.max(len, lengths[ci]); + }); + }); + + // header + header.forEach((cell, ci) => { result += `| ${cell + ' '.repeat(lengths[ci] - cell.toString().length)} `; }); + result += '|\n'; + header.forEach((_cell, ci) => { result += `| ${'-'.repeat(lengths[ci])} `; }); + result += '|\n'; + + // cells + rows.forEach(row => { + row.forEach((cell, ci) => { + if (typeof cell !== 'undefined') { + result += `| ${cell + ' '.repeat(lengths[ci] - cell.toString().length)} `; + } + }); + result += '|\n'; + }); + + return result; + } +} diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index bc0aa40a6..c4820d9cf 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -588,3 +588,17 @@ export function asArray(x: T | T[]): T[] { export function getRandomElement(arr: T[]): T | undefined { return arr[Math.floor(Math.random() * arr.length)]; } + +/** + * Returns the first mapped value of the array which is not undefined. + */ +export function mapFind(array: Iterable, mapFn: (value: T) => R | undefined): R | undefined { + for (const value of array) { + const mapped = mapFn(value); + if (mapped !== undefined) { + return mapped; + } + } + + return undefined; +} diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 632c1989f..167a47d15 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -445,6 +445,45 @@ export function first(promiseFactories: ITask>[], shouldStop: (t: return loop(); } +/** + * Returns the result of the first promise that matches the "shouldStop", + * running all promises in parallel. Supports cancelable promises. + */ +export function firstParallel(promiseList: Promise[], shouldStop?: (t: T) => boolean, defaultValue?: T | null): Promise; +export function firstParallel(promiseList: Promise[], shouldStop: (t: T) => t is R, defaultValue?: R | null): Promise; +export function firstParallel(promiseList: Promise[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null) { + if (promiseList.length === 0) { + return Promise.resolve(defaultValue); + } + + let todo = promiseList.length; + const finish = () => { + todo = -1; + for (const promise of promiseList) { + (promise as Partial>).cancel?.(); + } + }; + + return new Promise((resolve, reject) => { + for (const promise of promiseList) { + promise.then(result => { + if (--todo >= 0 && shouldStop(result)) { + finish(); + resolve(result); + } else if (todo === 0) { + resolve(defaultValue); + } + }) + .catch(err => { + if (--todo >= 0) { + finish(); + reject(err); + } + }); + } + }); +} + interface ILimitedTaskFactory { factory: ITask>; c: (value: T | Promise) => void; diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index 3c4fffd3b..8681df323 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -5,6 +5,7 @@ import { codiconStartMarker } from 'vs/base/common/codicon'; import { Emitter, Event } from 'vs/base/common/event'; +import { localize } from 'vs/nls'; export interface IIconRegistry { readonly all: IterableIterator; @@ -18,9 +19,12 @@ class Registry implements IIconRegistry { private readonly _onDidRegister = new Emitter(); public add(icon: Codicon) { - if (!this._icons.has(icon.id)) { + const existing = this._icons.get(icon.id); + if (!existing) { this._icons.set(icon.id, icon); this._onDidRegister.fire(icon); + } else if (icon.description) { + existing.description = icon.description; } else { console.error(`Duplicate registration of codicon ${icon.id}`); } @@ -43,11 +47,11 @@ const _registry = new Registry(); export const iconRegistry: IIconRegistry = _registry; -export function registerIcon(id: string, def: Codicon, description?: string) { - return new Codicon(id, def); +export function registerCodicon(id: string, def: Codicon, description?: string): Codicon { + return new Codicon(id, def, description); } -export class Codicon { +export class Codicon implements CSSIcon { constructor(public readonly id: string, public readonly definition: Codicon | IconDefinition, public description?: string) { _registry.add(this); } @@ -57,6 +61,12 @@ export class Codicon { public get cssSelector() { return '.codicon.codicon-' + this.id; } } +export interface CSSIcon { + readonly classNames: string; +} + + + interface IconDefinition { character: string; } @@ -484,8 +494,15 @@ export namespace Codicon { export const redo = new Codicon('redo', { character: '\\ebb0' }); export const checkAll = new Codicon('check-all', { character: '\\ebb1' }); export const pinnedDirty = new Codicon('pinned-dirty', { character: '\\ebb2' }); + export const passFilled = new Codicon('pass-filled', { character: '\\ebb3' }); + export const circleLargeFilled = new Codicon('circle-large-filled', { character: '\\ebb4' }); + export const circleLargeOutline = new Codicon('circle-large-outline', { character: '\\ebb5' }); + + export const dropDownButton = new Codicon('drop-down-button', Codicon.chevronDown.definition, localize('dropDownButton', 'Icon for drop down buttons.')); } +// common icons + diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index 8729b7303..f97dc23f6 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -76,7 +76,37 @@ export function fromMap(original: Map): IStringDictionary { return result; } +export function diffSets(before: Set, after: Set): { removed: T[], added: T[] } { + const removed: T[] = []; + const added: T[] = []; + for (let element of before) { + if (!after.has(element)) { + removed.push(element); + } + } + for (let element of after) { + if (!before.has(element)) { + added.push(element); + } + } + return { removed, added }; +} +export function diffMaps(before: Map, after: Map): { removed: V[], added: V[] } { + const removed: V[] = []; + const added: V[] = []; + for (let [index, value] of before) { + if (!after.has(index)) { + removed.push(value); + } + } + for (let [index, value] of after) { + if (!before.has(index)) { + added.push(value); + } + } + return { removed, added }; +} export class SetMap { private map = new Map>(); diff --git a/src/vs/base/common/diff/diff.ts b/src/vs/base/common/diff/diff.ts index d81c79eaa..97fdcbc00 100644 --- a/src/vs/base/common/diff/diff.ts +++ b/src/vs/base/common/diff/diff.ts @@ -880,9 +880,81 @@ export class LcsDiff { change.modifiedStart -= bestDelta; } + // There could be multiple longest common substrings. + // Give preference to the ones containing longer lines + if (this._hasStrings) { + for (let i = 1, len = changes.length; i < len; i++) { + const aChange = changes[i - 1]; + const bChange = changes[i]; + const matchedLength = bChange.originalStart - aChange.originalStart - aChange.originalLength; + const aOriginalStart = aChange.originalStart; + const bOriginalEnd = bChange.originalStart + bChange.originalLength; + const abOriginalLength = bOriginalEnd - aOriginalStart; + const aModifiedStart = aChange.modifiedStart; + const bModifiedEnd = bChange.modifiedStart + bChange.modifiedLength; + const abModifiedLength = bModifiedEnd - aModifiedStart; + // Avoid wasting a lot of time with these searches + if (matchedLength < 5 && abOriginalLength < 20 && abModifiedLength < 20) { + const t = this._findBetterContiguousSequence( + aOriginalStart, abOriginalLength, + aModifiedStart, abModifiedLength, + matchedLength + ); + if (t) { + const [originalMatchStart, modifiedMatchStart] = t; + if (originalMatchStart !== aChange.originalStart + aChange.originalLength || modifiedMatchStart !== aChange.modifiedStart + aChange.modifiedLength) { + // switch to another sequence that has a better score + aChange.originalLength = originalMatchStart - aChange.originalStart; + aChange.modifiedLength = modifiedMatchStart - aChange.modifiedStart; + bChange.originalStart = originalMatchStart + matchedLength; + bChange.modifiedStart = modifiedMatchStart + matchedLength; + bChange.originalLength = bOriginalEnd - bChange.originalStart; + bChange.modifiedLength = bModifiedEnd - bChange.modifiedStart; + } + } + } + } + } + return changes; } + private _findBetterContiguousSequence(originalStart: number, originalLength: number, modifiedStart: number, modifiedLength: number, desiredLength: number): [number, number] | null { + if (originalLength < desiredLength || modifiedLength < desiredLength) { + return null; + } + const originalMax = originalStart + originalLength - desiredLength + 1; + const modifiedMax = modifiedStart + modifiedLength - desiredLength + 1; + let bestScore = 0; + let bestOriginalStart = 0; + let bestModifiedStart = 0; + for (let i = originalStart; i < originalMax; i++) { + for (let j = modifiedStart; j < modifiedMax; j++) { + const score = this._contiguousSequenceScore(i, j, desiredLength); + if (score > 0 && score > bestScore) { + bestScore = score; + bestOriginalStart = i; + bestModifiedStart = j; + } + } + } + if (bestScore > 0) { + return [bestOriginalStart, bestModifiedStart]; + } + return null; + } + + private _contiguousSequenceScore(originalStart: number, modifiedStart: number, length: number): number { + let score = 0; + for (let l = 0; l < length; l++) { + if (!this.ElementsAreEqual(originalStart + l, modifiedStart + l)) { + return 0; + } + score += this._originalStringElements[originalStart + l].length; + } + return score; + } + private _OriginalIsBoundary(index: number): boolean { if (index <= 0 || index >= this._originalElementsOrHash.length - 1) { return true; diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index f70aa9d8d..6a69c0e6a 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -629,7 +629,7 @@ export class PauseableEmitter extends Emitter { if (this._mergeFn) { // use the merge function to create a single composite // event. make a copy in case firing pauses this emitter - const events = this._eventQueue.toArray(); + const events = Array.from(this._eventQueue); this._eventQueue.clear(); super.fire(this._mergeFn(events)); diff --git a/src/vs/base/common/hash.ts b/src/vs/base/common/hash.ts index 13a1847e0..b39581a84 100644 --- a/src/vs/base/common/hash.ts +++ b/src/vs/base/common/hash.ts @@ -106,8 +106,14 @@ function leftPad(value: string, length: number, char: string = '0'): string { return value; } -function toHexString(value: number, bitsize: number = 32): string { - return leftPad((value >>> 0).toString(16), bitsize / 4); +export function toHexString(buffer: ArrayBuffer): string; +export function toHexString(value: number, bitsize?: number): string; +export function toHexString(bufferOrValue: ArrayBuffer | number, bitsize: number = 32): string { + if (bufferOrValue instanceof ArrayBuffer) { + return Array.from(new Uint8Array(bufferOrValue)).map(b => b.toString(16).padStart(2, '0')).join(''); + } + + return leftPad((bufferOrValue >>> 0).toString(16), bitsize / 4); } /** diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index 32f463ff8..829a34800 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -21,52 +21,52 @@ export const enum MarkdownStringTextNewlineStyle { } export class MarkdownString implements IMarkdownString { - private readonly _isTrusted: boolean; - private readonly _supportThemeIcons: boolean; + + public value: string; + public isTrusted?: boolean; + public supportThemeIcons?: boolean; constructor( - private _value: string = '', + value: string = '', isTrustedOrOptions: boolean | { isTrusted?: boolean, supportThemeIcons?: boolean } = false, ) { - if (typeof this._value !== 'string') { + this.value = value; + if (typeof this.value !== 'string') { throw illegalArgument('value'); } if (typeof isTrustedOrOptions === 'boolean') { - this._isTrusted = isTrustedOrOptions; - this._supportThemeIcons = false; + this.isTrusted = isTrustedOrOptions; + this.supportThemeIcons = false; } else { - this._isTrusted = isTrustedOrOptions.isTrusted ?? false; - this._supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; + this.isTrusted = isTrustedOrOptions.isTrusted ?? undefined; + this.supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; } } - get value() { return this._value; } - get isTrusted() { return this._isTrusted; } - get supportThemeIcons() { return this._supportThemeIcons; } - appendText(value: string, newlineStyle: MarkdownStringTextNewlineStyle = MarkdownStringTextNewlineStyle.Paragraph): MarkdownString { // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this._value += (this._supportThemeIcons ? escapeCodicons(value) : value) + this.value += (this.supportThemeIcons ? escapeCodicons(value) : value) .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') + .replace(/([ \t]+)/g, (_match, g1) => ' '.repeat(g1.length)) + .replace(/^>/gm, '\\>') .replace(/\n/g, newlineStyle === MarkdownStringTextNewlineStyle.Break ? '\\\n' : '\n\n'); return this; } appendMarkdown(value: string): MarkdownString { - this._value += value; - + this.value += value; return this; } appendCodeblock(langId: string, code: string): MarkdownString { - this._value += '\n```'; - this._value += langId; - this._value += '\n'; - this._value += code; - this._value += '\n```\n'; + this.value += '\n```'; + this.value += langId; + this.value += '\n'; + this.value += code; + this.value += '\n```\n'; return this; } } diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index 933e370f9..8b464edbf 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -126,7 +126,7 @@ export interface Location { /** * Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices). * '*' will match a single segment, of any property name or index. - * '**' will match a sequece of segments or no segment, of any property name or index. + * '**' will match a sequence of segments or no segment, of any property name or index. */ matches: (patterns: JSONPath) => boolean; /** diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 5f3fab864..5c1aac3bc 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -95,6 +95,10 @@ function hasDriveLetter(path: string): boolean { return !!(isWindows && path && path[1] === ':'); } +export function extractDriveLetter(path: string): string | undefined { + return hasDriveLetter(path) ? path[0] : undefined; +} + export function normalizeDriveLetter(path: string): string { if (hasDriveLetter(path)) { return path.charAt(0).toUpperCase() + path.slice(1); diff --git a/src/vs/base/common/linkedList.ts b/src/vs/base/common/linkedList.ts index 8ca17bc73..ddeb494cc 100644 --- a/src/vs/base/common/linkedList.ts +++ b/src/vs/base/common/linkedList.ts @@ -131,12 +131,4 @@ export class LinkedList { node = node.next; } } - - toArray(): E[] { - const result: E[] = []; - for (let node = this._first; node !== Node.Undefined; node = node.next) { - result.push(node.element); - } - return result; - } } diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index deaf56955..0e08876e5 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -6,8 +6,6 @@ import { URI } from 'vs/base/common/uri'; import { CharCode } from 'vs/base/common/charCode'; import { compareSubstringIgnoreCase, compare, compareSubstring, compareIgnoreCase } from 'vs/base/common/strings'; -import { isLinux } from 'vs/base/common/platform'; -import { Schemas } from 'vs/base/common/network'; export function getOrSet(map: Map, key: K, value: V): V { let result = map.get(key); @@ -77,6 +75,57 @@ export class StringIterator implements IKeyIterator { } } +export class ConfigKeysIterator implements IKeyIterator { + + private _value!: string; + private _from!: number; + private _to!: number; + + constructor( + private readonly _caseSensitive: boolean = true + ) { } + + reset(key: string): this { + this._value = key; + this._from = 0; + this._to = 0; + return this.next(); + } + + hasNext(): boolean { + return this._to < this._value.length; + } + + next(): this { + // this._data = key.split(/[\\/]/).filter(s => !!s); + this._from = this._to; + let justSeps = true; + for (; this._to < this._value.length; this._to++) { + const ch = this._value.charCodeAt(this._to); + if (ch === CharCode.Period) { + if (justSeps) { + this._from++; + } else { + break; + } + } else { + justSeps = false; + } + } + return this; + } + + cmp(a: string): number { + return this._caseSensitive + ? compareSubstring(a, this._value, 0, a.length, this._from, this._to) + : compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to); + } + + value(): string { + return this._value.substring(this._from, this._to); + } +} + export class PathIterator implements IKeyIterator { private _value!: string; @@ -140,7 +189,7 @@ export class UriIterator implements IKeyIterator { private _states: UriIteratorState[] = []; private _stateIdx: number = 0; - constructor(private readonly _ignorePathCasing: boolean | undefined) { } + constructor(private readonly _ignorePathCasing: (uri: URI) => boolean) { } reset(key: URI): this { this._value = key; @@ -152,10 +201,7 @@ export class UriIterator implements IKeyIterator { this._states.push(UriIteratorState.Authority); } if (this._value.path) { - this._pathIterator = new PathIterator(false, this._ignorePathCasing === undefined - ? key.scheme === Schemas.file && isLinux - : !this._ignorePathCasing - ); + this._pathIterator = new PathIterator(false, !this._ignorePathCasing(key)); this._pathIterator.reset(key.path); if (this._pathIterator.value()) { this._states.push(UriIteratorState.Path); @@ -231,7 +277,7 @@ class TernarySearchTreeNode { export class TernarySearchTree { - static forUris(ignorePathCasing?: boolean): TernarySearchTree { + static forUris(ignorePathCasing: (key: URI) => boolean = () => false): TernarySearchTree { return new TernarySearchTree(new UriIterator(ignorePathCasing)); } @@ -243,6 +289,10 @@ export class TernarySearchTree { return new TernarySearchTree(new StringIterator()); } + static forConfigKeys(): TernarySearchTree { + return new TernarySearchTree(new ConfigKeysIterator()); + } + private _iter: IKeyIterator; private _root: TernarySearchTreeNode | undefined; @@ -301,6 +351,10 @@ export class TernarySearchTree { } get(key: K): V | undefined { + return this._getNode(key)?.value; + } + + private _getNode(key: K) { const iter = this._iter.reset(key); let node = this._root; while (node) { @@ -319,7 +373,12 @@ export class TernarySearchTree { break; } } - return node ? node.value : undefined; + return node; + } + + has(key: K): boolean { + const node = this._getNode(key); + return !(node?.value === undefined && node?.mid === undefined); } delete(key: K): void { @@ -352,11 +411,18 @@ export class TernarySearchTree { stack.push([0, node]); node = node.mid; } else { - // remove element - node.value = undefined; + if (superStr) { + // remove children + node.left = undefined; + node.mid = undefined; + node.right = undefined; + } else { + // remove element + node.value = undefined; + } // clean up empty nodes - while (stack.length > 0 && (node.isEmpty() || superStr)) { + while (stack.length > 0 && node.isEmpty()) { let [dir, parent] = stack.pop()!; switch (dir) { case 1: parent.left = undefined; break; @@ -394,7 +460,7 @@ export class TernarySearchTree { return node && node.value || candidate; } - findSuperstr(key: K): Iterator | undefined { + findSuperstr(key: K): IterableIterator<[K, V]> | undefined { const iter = this._iter.reset(key); let node = this._root; while (node) { @@ -414,7 +480,7 @@ export class TernarySearchTree { if (!node.mid) { return undefined; } else { - return this._values(node.mid); + return this._entries(node.mid); } } } @@ -431,12 +497,6 @@ export class TernarySearchTree { yield* this._entries(this._root); } - private *_values(node: TernarySearchTreeNode): IterableIterator { - for (const [, value] of this._entries(node)) { - yield value; - } - } - private *_entries(node: TernarySearchTreeNode | undefined): IterableIterator<[K, V]> { if (node) { // left diff --git a/src/vs/base/common/marked/cgmanifest.json b/src/vs/base/common/marked/cgmanifest.json index f3477931d..15e641cb5 100644 --- a/src/vs/base/common/marked/cgmanifest.json +++ b/src/vs/base/common/marked/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "marked", "repositoryUrl": "https://github.com/markedjs/marked", - "commitHash": "529a8d4e185a8aa561e4d8d2891f8556b5717cd4" + "commitHash": "8cfa29ccd2a2759e8e60fe0d8d6df8c022beda4e" } }, "license": "MIT", - "version": "0.6.2" + "version": "1.1.0" } ], "version": 1 diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index fb133b90f..ce8d16410 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -12,7 +12,7 @@ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : - (global = global || self, global.marked = factory()); + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.marked = factory()); }(this, (function () { 'use strict'; function _defineProperties(target, props) { @@ -368,11 +368,28 @@ function checkSanitizeDeprecation(opt) { if (opt && opt.sanitize && !opt.silent) { - // VS CODE CHANGE - // Disable logging about sanitize options. We already use insane after running the sanitizer - - // console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); + console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); } + } // copied from https://stackoverflow.com/a/5450113/806777 + + + function repeatString(pattern, count) { + if (count < 1) { + return ''; + } + + var result = ''; + + while (count > 1) { + if (count & 1) { + result += pattern; + } + + count >>= 1; + pattern += pattern; + } + + return result + pattern; } var helpers = { @@ -386,7 +403,8 @@ splitCells: splitCells, rtrim: rtrim, findClosingBracket: findClosingBracket, - checkSanitizeDeprecation: checkSanitizeDeprecation + checkSanitizeDeprecation: checkSanitizeDeprecation, + repeatString: repeatString }; var defaults$1 = defaults.defaults; @@ -620,7 +638,7 @@ // so it is seen as the next token. space = item.length; - item = item.replace(/^ *([*+-]|\d+[.)]) */, ''); // Outdent whatever the + item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the // list item contains. Hacky. if (~item.indexOf('\n ')) { @@ -1138,11 +1156,11 @@ block.gfm = merge$1({}, block.normal, { nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header - + ' *([-:]+ *\\|[-| :]*)' // Align + + ' {0,3}([-:]+ *\\|[-| :]*)' // Align + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells table: '^ *\\|(.+)\\n' // Header - + ' *\\|?( *[-:]+[-| :]*)' // Align + + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); @@ -1187,24 +1205,24 @@ start: /^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/, // (1) returns if starts w/ punctuation middle: /^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/, - endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation\s]|$))/, + endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/, // last char can't be punct, or final * must also be followed by punct (or endline) - endUnd: /[^\s]__(?!_)(?:(?=[punctuation\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) + endUnd: /[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) }, em: { start: /^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/, // (1) returns if starts w/ punctuation middle: /^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/, - endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation\s]|$))/, + endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/, // last char can't be punct, or final * must also be followed by punct (or endline) - endUnd: /[^\s]_(?!_)(?:(?=[punctuation\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) + endUnd: /[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) }, code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, br: /^( {2,}|\\)\n(?!\s*$)/, del: noopTest$1, - text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\ 0) { while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); } } } @@ -1660,7 +1688,7 @@ while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); } while (src) { @@ -2073,6 +2101,15 @@ var parser = new Parser(options); return parser.parse(tokens); } + /** + * Static Parse Inline Method + */ + ; + + Parser.parseInline = function parseInline(tokens, options) { + var parser = new Parser(options); + return parser.parseInline(tokens); + } /** * Parse Loop */ @@ -2606,6 +2643,42 @@ } } }; + /** + * Parse Inline + */ + + + marked.parseInline = function (src, opt) { + // throw error in case of non string input + if (typeof src === 'undefined' || src === null) { + throw new Error('marked.parseInline(): input parameter is undefined or null'); + } + + if (typeof src !== 'string') { + throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'); + } + + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); + + try { + var tokens = Lexer_1.lexInline(src, opt); + + if (opt.walkTokens) { + marked.walkTokens(tokens, opt.walkTokens); + } + + return Parser_1.parseInline(tokens, opt); + } catch (e) { + e.message += '\nPlease report this to https://github.com/markedjs/marked.'; + + if (opt.silent) { + return '

An error occurred:

' + escape$2(e.message + '', true) + '
'; + } + + throw e; + } + }; /** * Expose */ diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index f475b10e5..4e12b577e 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -78,6 +78,12 @@ export namespace Schemas { * Scheme used for extension pages */ export const extension = 'extension'; + + /** + * Scheme used as a replacement of `file` scheme to load + * files with our custom protocol handler (desktop only). + */ + export const vscodeFileResource = 'vscode-file'; } class RemoteAuthoritiesImpl { @@ -132,6 +138,8 @@ export const RemoteAuthorities = new RemoteAuthoritiesImpl(); class FileAccessImpl { + private readonly FALLBACK_AUTHORITY = 'vscode-app'; + /** * Returns a URI to use in contexts where the browser is responsible * for loading (e.g. fetch()) or when used within the DOM. @@ -143,13 +151,43 @@ class FileAccessImpl { asBrowserUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { const uri = this.toUri(uriOrModule, moduleIdToUrl); + // Handle remote URIs via `RemoteAuthorities` if (uri.scheme === Schemas.vscodeRemote) { return RemoteAuthorities.rewrite(uri); } + // Only convert the URI if we are in a native context and it has `file:` scheme + if (platform.isElectronSandboxed && platform.isNative && uri.scheme === Schemas.file) { + return this.toCodeFileUri(uri); + } + return uri; } + /** + * TODO@bpasero remove me eventually when vscode-file is adopted everywhere + */ + _asCodeFileUri(uri: URI): URI; + _asCodeFileUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }): URI; + _asCodeFileUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { + const uri = this.toUri(uriOrModule, moduleIdToUrl); + + return this.toCodeFileUri(uri); + } + + private toCodeFileUri(uri: URI): URI { + return uri.with({ + scheme: Schemas.vscodeFileResource, + // We need to provide an authority here so that it can serve + // as origin for network and loading matters in chromium. + // If the URI is not coming with an authority already, we + // add our own + authority: uri.authority || this.FALLBACK_AUTHORITY, + query: null, + fragment: null + }); + } + /** * Returns the `file` URI to use in contexts where node.js * is responsible for loading. @@ -159,6 +197,19 @@ class FileAccessImpl { asFileUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { const uri = this.toUri(uriOrModule, moduleIdToUrl); + // Only convert the URI if it is `vscode-file:` scheme + if (uri.scheme === Schemas.vscodeFileResource) { + return uri.with({ + scheme: Schemas.file, + // Only preserve the `authority` if it is different from + // our fallback authority. This ensures we properly preserve + // Windows UNC paths that come with their own authority. + authority: uri.authority !== this.FALLBACK_AUTHORITY ? uri.authority : null, + query: null, + fragment: null + }); + } + return uri; } diff --git a/src/vs/base/common/performance.js b/src/vs/base/common/performance.js index 5893db444..b10f9b1d6 100644 --- a/src/vs/base/common/performance.js +++ b/src/vs/base/common/performance.js @@ -12,7 +12,7 @@ function _factory(sharedObj) { sharedObj.MonacoPerformanceMarks = sharedObj.MonacoPerformanceMarks || []; const _dataLen = 2; - const _timeStamp = typeof console.timeStamp === 'function' ? console.timeStamp.bind(console) : () => { }; + const _nativeMark = typeof performance === 'object' && typeof performance.mark === 'function' ? performance.mark.bind(performance) : () => { }; function importEntries(entries) { sharedObj.MonacoPerformanceMarks.splice(0, 0, ...entries); @@ -55,7 +55,7 @@ function _factory(sharedObj) { function mark(name) { sharedObj.MonacoPerformanceMarks.push(name, Date.now()); - _timeStamp(name); + _nativeMark(name); } const exports = { diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 3361d83be..61fa4d71f 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -33,8 +33,8 @@ export interface INodeProcess { versions?: { electron?: string; }; + sandboxed?: boolean; // Electron type?: string; - getuid(): number; cwd(): string; } declare const process: INodeProcess; @@ -55,11 +55,12 @@ if (typeof process !== 'undefined') { // Native environment (non-sandboxed) nodeProcess = process; } else if (typeof _globals.vscode !== 'undefined') { - // Native envionment (sandboxed) + // Native environment (sandboxed) nodeProcess = _globals.vscode.process; } const isElectronRenderer = typeof nodeProcess?.versions?.electron === 'string' && nodeProcess.type === 'renderer'; +export const isElectronSandboxed = isElectronRenderer && nodeProcess?.sandboxed; // Web environment if (typeof navigator === 'object' && !isElectronRenderer) { @@ -133,10 +134,6 @@ export const isIOS = _isIOS; export const platform = _platform; export const userAgent = _userAgent; -export function isRootUser(): boolean { - return !!nodeProcess && !_isWindows && (nodeProcess.getuid() === 0); -} - /** * The language used for the user interface. The format of * the string is all lower case (e.g. zh-tw for Traditional diff --git a/src/vs/base/common/process.ts b/src/vs/base/common/process.ts index 5e85d6721..4b9dde872 100644 --- a/src/vs/base/common/process.ts +++ b/src/vs/base/common/process.ts @@ -16,7 +16,16 @@ if (typeof process !== 'undefined') { // Native sandbox environment else if (typeof globals.vscode !== 'undefined') { - safeProcess = globals.vscode.process; + safeProcess = { + + // Supported + get platform(): 'win32' | 'linux' | 'darwin' { return globals.vscode.process.platform; }, + get env() { return globals.vscode.process.env; }, + nextTick(callback: (...args: any[]) => void): void { return setImmediate(callback); }, + + // Unsupported + cwd(): string { return globals.vscode.process.env['VSCODE_CWD'] || globals.vscode.process.execPath.substr(0, globals.vscode.process.execPath.lastIndexOf(globals.vscode.process.platform === 'win32' ? '\\' : '/')); } + }; } // Web environment @@ -29,8 +38,7 @@ else { // Unsupported get env() { return Object.create(null); }, - cwd(): string { return '/'; }, - getuid(): number { return -1; } + cwd(): string { return '/'; } }; } diff --git a/src/vs/base/common/resourceTree.ts b/src/vs/base/common/resourceTree.ts index 23f661e1b..96cdee54c 100644 --- a/src/vs/base/common/resourceTree.ts +++ b/src/vs/base/common/resourceTree.ts @@ -5,7 +5,7 @@ import { memoize } from 'vs/base/common/decorators'; import * as paths from 'vs/base/common/path'; -import { relativePath, joinPath } from 'vs/base/common/resources'; +import { IExtUri, extUri as defaultExtUri } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { PathIterator } from 'vs/base/common/map'; @@ -95,12 +95,12 @@ export class ResourceTree, C> { return obj instanceof Node; } - constructor(context: C, rootURI: URI = URI.file('/')) { + constructor(context: C, rootURI: URI = URI.file('/'), private extUri: IExtUri = defaultExtUri) { this.root = new Node(rootURI, '', context); } add(uri: URI, element: T): void { - const key = relativePath(this.root.uri, uri) || uri.fsPath; + const key = this.extUri.relativePath(this.root.uri, uri) || uri.path; const iterator = new PathIterator(false).reset(key); let node = this.root; let path = ''; @@ -113,7 +113,7 @@ export class ResourceTree, C> { if (!child) { child = new Node( - joinPath(this.root.uri, path), + this.extUri.joinPath(this.root.uri, path), path, this.root.context, iterator.hasNext() ? undefined : element, @@ -136,7 +136,7 @@ export class ResourceTree, C> { } delete(uri: URI): T | undefined { - const key = relativePath(this.root.uri, uri) || uri.fsPath; + const key = this.extUri.relativePath(this.root.uri, uri) || uri.path; const iterator = new PathIterator(false).reset(key); return this._delete(this.root, iterator); } @@ -168,7 +168,7 @@ export class ResourceTree, C> { } getNode(uri: URI): IResourceNode | undefined { - const key = relativePath(this.root.uri, uri) || uri.fsPath; + const key = this.extUri.relativePath(this.root.uri, uri) || uri.path; const iterator = new PathIterator(false).reset(key); let node = this.root; diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 621c97368..82619c4b0 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -10,8 +10,6 @@ import { equalsIgnoreCase, compare as strCompare } from 'vs/base/common/strings' import { Schemas } from 'vs/base/common/network'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { CharCode } from 'vs/base/common/charCode'; -import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; -import { TernarySearchTree } from 'vs/base/common/map'; export function originalFSPath(uri: URI): string { return uriToFsPath(uri, true); @@ -58,6 +56,11 @@ export interface IExtUri { */ getComparisonKey(uri: URI, ignoreFragment?: boolean): string; + /** + * Whether the casing of the path-component of the uri should be ignored. + */ + ignorePathCasing(uri: URI): boolean; + // --- path math basenameOrAuthority(resource: URI): string; @@ -161,6 +164,10 @@ export class ExtUri implements IExtUri { }).toString(); } + ignorePathCasing(uri: URI): boolean { + return this._ignorePathCasing(uri); + } + isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment: boolean = false): boolean { if (base.scheme === parentCandidate.scheme) { if (base.scheme === Schemas.file) { @@ -427,33 +434,6 @@ export namespace DataUri { } } -export class ResourceGlobMatcher { - - private readonly globalExpression: ParsedExpression; - private readonly expressionsByRoot: TernarySearchTree = TernarySearchTree.forUris<{ root: URI, expression: ParsedExpression }>(); - - constructor( - globalExpression: IExpression, - rootExpressions: { root: URI, expression: IExpression }[] - ) { - this.globalExpression = parse(globalExpression); - for (const expression of rootExpressions) { - this.expressionsByRoot.set(expression.root, { root: expression.root, expression: parse(expression.expression) }); - } - } - - matches(resource: URI): boolean { - const rootExpression = this.expressionsByRoot.findSubstr(resource); - if (rootExpression) { - const path = relativePath(rootExpression.root, resource); - if (path && !!rootExpression.expression(path)) { - return true; - } - } - return !!this.globalExpression(resource.path); - } -} - export function toLocalResource(resource: URI, authority: string | undefined, localScheme: string): URI { if (authority) { let path = resource.path; diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 9e35fd6cb..84e9c52f2 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -69,6 +69,14 @@ export function count(value: string, character: string): number { return result; } +export function truncate(value: string, maxLength: number, suffix = '…'): string { + if (value.length <= maxLength) { + return value; + } + + return `${value.substr(0, maxLength)}${suffix}`; +} + /** * Removes all occurrences of needle from the beginning and end of haystack. * @param haystack string to trim @@ -208,6 +216,10 @@ export function regExpFlags(regexp: RegExp): string { + ((regexp as any /* standalone editor compilation */).unicode ? 'u' : ''); } +export function splitLines(str: string): string[] { + return str.split(/\r\n|\r|\n/); +} + /** * Returns first index of the string that is not whitespace. * If string is empty or contains only whitespaces, returns -1 @@ -879,9 +891,15 @@ export function getNLines(str: string, n = 1): string { n--; } while (n > 0 && idx >= 0); - return idx >= 0 ? - str.substr(0, idx) : - str; + if (idx === -1) { + return str; + } + + if (str[idx - 1] === '\r') { + idx--; + } + + return str.substr(0, idx); } /** diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 5ac44dc84..8111602ef 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -345,7 +345,7 @@ export class URI implements UriComponents { */ static joinPath(uri: URI, ...pathFragment: string[]): URI { if (!uri.path) { - throw new Error(`[UriError]: cannot call joinPaths on URI without path`); + throw new Error(`[UriError]: cannot call joinPath on URI without path`); } let newPath: string; if (isWindows && uri.scheme === 'file') { diff --git a/src/vs/base/common/uuid.ts b/src/vs/base/common/uuid.ts index 57d9db69d..141fd83ce 100644 --- a/src/vs/base/common/uuid.ts +++ b/src/vs/base/common/uuid.ts @@ -17,8 +17,9 @@ for (let i = 0; i < 256; i++) { _hex.push(i.toString(16).padStart(2, '0')); } -// todo@joh node nodejs use `crypto#randomBytes`, see: https://nodejs.org/docs/latest/api/crypto.html#crypto_crypto_randombytes_size_callback -// todo@joh use browser-crypto +// todo@jrieken +// 1. node nodejs use`crypto#randomBytes`, see: https://nodejs.org/docs/latest/api/crypto.html#crypto_crypto_randombytes_size_callback +// 2. use browser-crypto const _fillRandomValues = function (bucket: Uint8Array): Uint8Array { for (let i = 0; i < bucket.length; i++) { bucket[i] = Math.floor(Math.random() * 256); diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index 19f154a79..1b81394ae 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -358,6 +358,10 @@ export class SimpleWorkerServer { delete loaderConfig.paths['vs']; } } + if (typeof loaderConfig.trustedTypesPolicy !== undefined) { + // don't use, it has been destroyed during serialize + delete loaderConfig['trustedTypesPolicy']; + } // Since this is in a web worker, enable catching errors loaderConfig.catchError = true; diff --git a/src/vs/base/node/paths.ts b/src/vs/base/node/paths.ts index 977eaf880..eaf03e6e4 100644 --- a/src/vs/base/node/paths.ts +++ b/src/vs/base/node/paths.ts @@ -5,12 +5,7 @@ import { FileAccess } from 'vs/base/common/network'; -interface IPaths { - getAppDataPath(platform: string): string; - getDefaultUserDataPath(platform: string): string; -} - const pathsPath = FileAccess.asFileUri('paths', require).fsPath; -const paths = require.__$__nodeRequire(pathsPath); -export const getAppDataPath = paths.getAppDataPath; +const paths = require.__$__nodeRequire<{ getDefaultUserDataPath(): string }>(pathsPath); + export const getDefaultUserDataPath = paths.getDefaultUserDataPath; diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index bc8e97c26..b27c8734f 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -126,7 +126,8 @@ const enum ProtocolMessageType { Control = 2, Ack = 3, KeepAlive = 4, - Disconnect = 5 + Disconnect = 5, + ReplayRequest = 6 } export const enum ProtocolConstants { @@ -274,7 +275,11 @@ class ProtocolWriter { } public dispose(): void { - this.flush(); + try { + this.flush(); + } catch (err) { + // ignore error, since the socket could be already closed + } this._isDisposed = true; } @@ -601,6 +606,8 @@ export class PersistentProtocol implements IMessagePassingProtocol { private _outgoingKeepAliveTimeout: any | null; private _incomingKeepAliveTimeout: any | null; + private _lastReplayRequestTime: number; + private _socket: ISocket; private _socketWriter: ProtocolWriter; private _socketReader: ProtocolReader; @@ -642,6 +649,8 @@ export class PersistentProtocol implements IMessagePassingProtocol { this._outgoingKeepAliveTimeout = null; this._incomingKeepAliveTimeout = null; + this._lastReplayRequestTime = 0; + this._socketDisposables = []; this._socket = socket; this._socketWriter = new ProtocolWriter(this._socket); @@ -747,6 +756,8 @@ export class PersistentProtocol implements IMessagePassingProtocol { this._onSocketTimeout.flushBuffer(); this._socket.dispose(); + this._lastReplayRequestTime = 0; + this._socket = socket; this._socketWriter = new ProtocolWriter(this._socket); this._socketDisposables.push(this._socketWriter); @@ -792,17 +803,31 @@ export class PersistentProtocol implements IMessagePassingProtocol { if (msg.type === ProtocolMessageType.Regular) { if (msg.id > this._incomingMsgId) { if (msg.id !== this._incomingMsgId + 1) { - console.error(`PROTOCOL CORRUPTION, LAST SAW MSG ${this._incomingMsgId} AND HAVE NOW RECEIVED MSG ${msg.id}`); + // in case we missed some messages we ask the other party to resend them + const now = Date.now(); + if (now - this._lastReplayRequestTime > 10000) { + // send a replay request at most once every 10s + this._lastReplayRequestTime = now; + this._socketWriter.write(new ProtocolMessage(ProtocolMessageType.ReplayRequest, 0, 0, getEmptyBuffer())); + } + } else { + this._incomingMsgId = msg.id; + this._incomingMsgLastTime = Date.now(); + this._sendAckCheck(); + this._onMessage.fire(msg.data); } - this._incomingMsgId = msg.id; - this._incomingMsgLastTime = Date.now(); - this._sendAckCheck(); - this._onMessage.fire(msg.data); } } else if (msg.type === ProtocolMessageType.Control) { this._onControlMessage.fire(msg.data); } else if (msg.type === ProtocolMessageType.Disconnect) { this._onClose.fire(); + } else if (msg.type === ProtocolMessageType.ReplayRequest) { + // Send again all unacknowledged messages + const toSend = this._outgoingUnackMsg.toArray(); + for (let i = 0, len = toSend.length; i < len; i++) { + this._socketWriter.write(toSend[i]); + } + this._recvAckCheck(); } } diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css index 29a3f02bc..4489a73ac 100644 --- a/src/vs/base/parts/quickinput/browser/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -130,6 +130,11 @@ padding: 5px 5px 2px 5px; } +.quick-input-message > .codicon { + margin: 0 0.2em; + vertical-align: text-bottom; +} + .quick-input-progress.monaco-progress-container { position: relative; } diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 0a4c9f1ba..bddbd8a6e 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -27,8 +27,10 @@ import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/lis import { List, IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox'; import { Color } from 'vs/base/common/color'; -import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { registerCodicon, Codicon } from 'vs/base/common/codicons'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { escape } from 'vs/base/common/strings'; +import { renderCodicons } from 'vs/base/browser/codicons'; export interface IQuickInputOptions { idPrefix: string; @@ -70,7 +72,7 @@ const $ = dom.$; type Writeable = { -readonly [P in keyof T]: T[P] }; -const backButtonIcon = registerIcon('quick-input-back', Codicon.arrowLeft); +const backButtonIcon = registerCodicon('quick-input-back', Codicon.arrowLeft, localize('backButtonIcon', 'Icon for the back button in the quick input dialog.')); const backButton = { iconClass: backButtonIcon.classNames, @@ -413,6 +415,7 @@ class QuickPick extends QuickInput implements IQuickPi private _valueSelection: Readonly<[number, number]> | undefined; private valueSelectionUpdated = true; private _validationMessage: string | undefined; + private _lastValidationMessage: string | undefined; private _ok: boolean | 'default' = 'default'; private _customButton = false; private _customButtonLabel: string | undefined; @@ -961,12 +964,11 @@ class QuickPick extends QuickInput implements IQuickPi this.selectedItemsToConfirm = null; } } - if (this.validationMessage) { - this.ui.message.textContent = this.validationMessage; - this.showMessageDecoration(Severity.Error); - } else { - this.ui.message.textContent = null; - this.showMessageDecoration(Severity.Ignore); + const validationMessage = this.validationMessage || ''; + if (this._lastValidationMessage !== validationMessage) { + this._lastValidationMessage = validationMessage; + dom.reset(this.ui.message, ...renderCodicons(escape(validationMessage))); + this.showMessageDecoration(this.validationMessage ? Severity.Error : Severity.Ignore); } this.ui.customButton.label = this.customLabel || ''; this.ui.customButton.element.title = this.customHover || ''; @@ -996,6 +998,7 @@ class InputBox extends QuickInput implements IInputBox { private _prompt: string | undefined; private noValidationMessage = InputBox.noPromptMessage; private _validationMessage: string | undefined; + private _lastValidationMessage: string | undefined; private readonly onDidValueChangeEmitter = this._register(new Emitter()); private readonly onDidAcceptEmitter = this._register(new Emitter()); @@ -1097,13 +1100,11 @@ class InputBox extends QuickInput implements IInputBox { if (this.ui.inputBox.password !== this.password) { this.ui.inputBox.password = this.password; } - if (!this.validationMessage && this.ui.message.textContent !== this.noValidationMessage) { - this.ui.message.textContent = this.noValidationMessage; - this.showMessageDecoration(Severity.Ignore); - } - if (this.validationMessage && this.ui.message.textContent !== this.validationMessage) { - this.ui.message.textContent = this.validationMessage; - this.showMessageDecoration(Severity.Error); + const validationMessage = this.validationMessage || this.noValidationMessage; + if (this._lastValidationMessage !== validationMessage) { + this._lastValidationMessage = validationMessage; + dom.reset(this.ui.message, ...renderCodicons(validationMessage)); + this.showMessageDecoration(this.validationMessage ? Severity.Error : Severity.Ignore); } } } @@ -1528,7 +1529,7 @@ export class QuickInputController extends Disposable { ui.inputBox.showDecoration(Severity.Ignore); ui.visibleCount.setCount(0); ui.count.setCount(0); - ui.message.textContent = ''; + dom.reset(ui.message); ui.progressBar.stop(); ui.list.setElements([]); ui.list.matchOnDescription = false; @@ -1696,7 +1697,7 @@ export class QuickInputController extends Disposable { this.ui.container.style.backgroundColor = quickInputBackground ? quickInputBackground.toString() : ''; this.ui.container.style.color = quickInputForeground ? quickInputForeground.toString() : ''; this.ui.container.style.border = contrastBorder ? `1px solid ${contrastBorder}` : ''; - this.ui.container.style.boxShadow = widgetShadow ? `0 5px 8px ${widgetShadow}` : ''; + this.ui.container.style.boxShadow = widgetShadow ? `0 0 8px 2px ${widgetShadow}` : ''; this.ui.inputBox.style(this.styles.inputBox); this.ui.count.style(this.styles.countBadge); this.ui.ok.style(this.styles.button); diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js index d0cb6d428..a1aa638f8 100644 --- a/src/vs/base/parts/sandbox/electron-browser/preload.js +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -35,6 +35,17 @@ } }, + /** + * @param {string} channel + * @param {any[]} args + * @returns {Promise | undefined} + */ + invoke(channel, ...args) { + if (validateIPC(channel)) { + return ipcRenderer.invoke(channel, ...args); + } + }, + /** * @param {string} channel * @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener @@ -97,80 +108,48 @@ /** * Support for a subset of access to node.js global `process`. + * + * Note: when `sandbox` is enabled, the only properties available + * are https://github.com/electron/electron/blob/master/docs/api/process.md#sandbox */ process: { get platform() { return process.platform; }, get env() { return process.env; }, get versions() { return process.versions; }, get type() { return 'renderer'; }, + get execPath() { return process.execPath; }, - _whenEnvResolved: undefined, - whenEnvResolved: - /** - * @returns when the shell environment has been resolved. - */ - function () { - if (!this._whenEnvResolved) { - this._whenEnvResolved = resolveEnv(); - } + /** + * @param {{[key: string]: string}} userEnv + * @returns {Promise} + */ + resolveEnv(userEnv) { + return resolveEnv(userEnv); + }, - return this._whenEnvResolved; - }, + /** + * @returns {Promise} + */ + getProcessMemoryInfo() { + return process.getProcessMemoryInfo(); + }, - nextTick: - /** - * Adds callback to the "next tick queue". This queue is fully drained - * after the current operation on the JavaScript stack runs to completion - * and before the event loop is allowed to continue. - * - * @param {Function} callback - * @param {any[]} args - */ - function nextTick(callback, ...args) { - return process.nextTick(callback, ...args); - }, - - cwd: - /** - * @returns the current working directory. - */ - function () { - return process.cwd(); - }, - - getuid: - /** - * @returns the numeric user identity of the process - */ - function () { - return process.getuid(); - }, - - getProcessMemoryInfo: - /** - * @returns {Promise} - */ - function () { - return process.getProcessMemoryInfo(); - }, - - on: - /** - * @param {string} type - * @param {() => void} callback - */ - function (type, callback) { - if (validateProcessEventType(type)) { - process.on(type, callback); - } + /** + * @param {string} type + * @param {() => void} callback + */ + on(type, callback) { + if (validateProcessEventType(type)) { + process.on(type, callback); } + } }, /** * Some information about the context we are running in. */ context: { - get sandbox() { return process.argv.includes('--enable-sandbox'); } + get sandbox() { return process.sandboxed; } } }; @@ -197,6 +176,7 @@ /** * @param {string} channel + * @returns {true | never} */ function validateIPC(channel) { if (!channel || !channel.startsWith('vscode:')) { @@ -218,32 +198,40 @@ return true; } + /** @type {Promise | undefined} */ + let resolvedEnv = undefined; + /** * If VSCode is not run from a terminal, we should resolve additional * shell specific environment from the OS shell to ensure we are seeing * all development related environment variables. We do this from the * main process because it may involve spawning a shell. + * + * @param {{[key: string]: string}} userEnv + * @returns {Promise} */ - function resolveEnv() { - return new Promise(function (resolve) { - const handle = setTimeout(function () { - console.warn('Preload: Unable to resolve shell environment in a reasonable time'); + function resolveEnv(userEnv) { + if (!resolvedEnv) { - // It took too long to fetch the shell environment, return - resolve(); - }, 3000); + // Apply `userEnv` directly + Object.assign(process.env, userEnv); - ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) { - clearTimeout(handle); + // Resolve `shellEnv` from the main side + resolvedEnv = new Promise(function (resolve) { + ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) { - // Assign all keys of the shell environment to our process environment - Object.assign(process.env, shellEnv); + // Assign all keys of the shell environment to our process environment + // But make sure that the user environment wins in the end + Object.assign(process.env, shellEnv, userEnv); - resolve(); + resolve(); + }); + + ipcRenderer.send('vscode:fetchShellEnv'); }); + } - ipcRenderer.send('vscode:fetchShellEnv'); - }); + return resolvedEnv; } //#endregion diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts index 923383b48..69f3a2a86 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -12,45 +12,45 @@ export interface ISandboxNodeProcess extends INodeProcess { * The process.platform property returns a string identifying the operating system platform * on which the Node.js process is running. */ - platform: 'win32' | 'linux' | 'darwin'; + readonly platform: 'win32' | 'linux' | 'darwin'; /** * The type will always be Electron renderer. */ - type: 'renderer'; + readonly type: 'renderer'; /** * A list of versions for the current node.js/electron configuration. */ - versions: { [key: string]: string | undefined }; + readonly versions: { [key: string]: string | undefined }; /** * The process.env property returns an object containing the user environment. */ - env: IProcessEnvironment; + readonly env: IProcessEnvironment; /** - * The current working directory. + * The `execPath` will be the location of the executable of this application. */ - cwd(): string; + readonly execPath: string; /** - * Returns the numeric user identity of the process. + * Resolve the true process environment to use and apply it to `process.env`. + * + * There are different layers of environment that will apply: + * - `process.env`: this is the actual environment of the process before this method + * - `shellEnv` : if the program was not started from a terminal, we resolve all shell + * variables to get the same experience as if the program was started from + * a terminal (Linux, macOS) + * - `userEnv` : this is instance specific environment, e.g. if the user started the program + * from a terminal and changed certain variables + * + * The order of overwrites is `process.env` < `shellEnv` < `userEnv`. + * + * It is critical that every process awaits this method early on startup to get the right + * set of environment in `process.env`. */ - getuid(): number; - - /** - * Allows to await resolving the full process environment by checking for the shell environment - * of the OS in certain cases (e.g. when the app is started from the Dock on macOS). - */ - whenEnvResolved(): Promise; - - /** - * Adds callback to the "next tick queue". This queue is fully drained - * after the current operation on the JavaScript stack runs to completion - * and before the event loop is allowed to continue. - */ - nextTick(callback: (...args: any[]) => void, ...args: any[]): void; + resolveEnv(userEnv: IProcessEnvironment): Promise; /** * A listener on the process. Only a small subset of listener types are allowed. diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index b6602b6a6..8b55819cc 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -43,9 +43,10 @@ export interface IStorageDatabase { export interface IStorage extends IDisposable { + readonly onDidChangeStorage: Event; + readonly items: Map; readonly size: number; - readonly onDidChangeStorage: Event; init(): Promise; @@ -61,6 +62,8 @@ export interface IStorage extends IDisposable { set(key: string, value: string | boolean | number | undefined | null): Promise; delete(key: string): Promise; + whenFlushed(): Promise; + close(): Promise; } @@ -86,6 +89,8 @@ export class Storage extends Disposable implements IStorage { private pendingDeletes = new Set(); private pendingInserts = new Map(); + private readonly whenFlushedCallbacks: Function[] = []; + constructor( protected readonly database: IStorageDatabase, private readonly options: IStorageOptions = Object.create(null) @@ -273,8 +278,12 @@ export class Storage extends Disposable implements IStorage { await this.database.close(() => this.cache); } + private get hasPending() { + return this.pendingInserts.size > 0 || this.pendingDeletes.size > 0; + } + private flushPending(): Promise { - if (this.pendingInserts.size === 0 && this.pendingDeletes.size === 0) { + if (!this.hasPending) { return Promise.resolve(); // return early if nothing to do } @@ -285,8 +294,23 @@ export class Storage extends Disposable implements IStorage { this.pendingDeletes = new Set(); this.pendingInserts = new Map(); - // Update in storage - return this.database.updateItems(updateRequest); + // Update in storage and release any + // waiters we have once done + return this.database.updateItems(updateRequest).finally(() => { + if (!this.hasPending) { + while (this.whenFlushedCallbacks.length) { + this.whenFlushedCallbacks.pop()?.(); + } + } + }); + } + + whenFlushed(): Promise { + if (!this.hasPending) { + return Promise.resolve(); // return early if nothing to do + } + + return new Promise(resolve => this.whenFlushedCallbacks.push(resolve)); } } diff --git a/src/vs/base/parts/storage/test/node/storage.test.ts b/src/vs/base/parts/storage/test/node/storage.test.ts index 3c3510390..e735634c2 100644 --- a/src/vs/base/parts/storage/test/node/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -45,11 +45,16 @@ suite('Storage Library', function () { changes.add(key); }); + await storage.whenFlushed(); // returns immediately when no pending updates + // Simple updates const set1Promise = storage.set('bar', 'foo'); const set2Promise = storage.set('barNumber', 55); const set3Promise = storage.set('barBoolean', true); + let flushPromiseResolved = false; + storage.whenFlushed().then(() => flushPromiseResolved = true); + equal(storage.get('bar'), 'foo'); equal(storage.getNumber('barNumber'), 55); equal(storage.getBoolean('barBoolean'), true); @@ -62,6 +67,7 @@ suite('Storage Library', function () { let setPromiseResolved = false; await Promise.all([set1Promise, set2Promise, set3Promise]).then(() => setPromiseResolved = true); equal(setPromiseResolved, true); + equal(flushPromiseResolved, true); changes = new Set(); @@ -166,6 +172,9 @@ suite('Storage Library', function () { const set1Promise = storage.set('foo', 'bar'); const set2Promise = storage.set('bar', 'foo'); + let flushPromiseResolved = false; + storage.whenFlushed().then(() => flushPromiseResolved = true); + equal(storage.get('foo'), 'bar'); equal(storage.get('bar'), 'foo'); @@ -175,6 +184,7 @@ suite('Storage Library', function () { await storage.close(); equal(setPromiseResolved, true); + equal(flushPromiseResolved, true); storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); await storage.init(); @@ -226,6 +236,9 @@ suite('Storage Library', function () { const set2Promise = storage.set('foo', 'bar2'); const set3Promise = storage.set('foo', 'bar3'); + let flushPromiseResolved = false; + storage.whenFlushed().then(() => flushPromiseResolved = true); + equal(storage.get('foo'), 'bar3'); equal(changes.size, 1); ok(changes.has('foo')); @@ -233,6 +246,7 @@ suite('Storage Library', function () { let setPromiseResolved = false; await Promise.all([set1Promise, set2Promise, set3Promise]).then(() => setPromiseResolved = true); ok(setPromiseResolved); + ok(flushPromiseResolved); changes = new Set(); diff --git a/src/vs/base/test/browser/comparers.test.ts b/src/vs/base/test/browser/comparers.test.ts index 4c40ea2a0..77ec3adf6 100644 --- a/src/vs/base/test/browser/comparers.test.ts +++ b/src/vs/base/test/browser/comparers.test.ts @@ -197,7 +197,7 @@ suite('Comparers', () => { // name-only comparisons assert(compareFileNamesDefault('a', 'A') === compareLocale('a', 'A'), 'the same letter sorts by locale'); assert(compareFileNamesDefault('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter sorts by locale'); - assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNamesDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); + // assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNamesDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNamesDefault), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents sort in locale order'); // numeric comparisons @@ -259,7 +259,7 @@ suite('Comparers', () => { // name-only comparisons assert(compareFileExtensionsDefault('a', 'A') === compareLocale('a', 'A'), 'the same letter of different case sorts by locale'); assert(compareFileExtensionsDefault('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter of different case sorts by locale'); - assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensionsDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); + // assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensionsDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileExtensionsDefault), ['email', 'Email', 'émail', 'Émail'].sort((a, b) => a.localeCompare(b)), 'the same base characters with different case or accents sort in locale order'); // name plus extension comparisons diff --git a/src/vs/base/test/common/hash.test.ts b/src/vs/base/test/browser/hash.test.ts similarity index 85% rename from src/vs/base/test/common/hash.test.ts rename to src/vs/base/test/browser/hash.test.ts index b5074f4ff..68ee17eeb 100644 --- a/src/vs/base/test/common/hash.test.ts +++ b/src/vs/base/test/browser/hash.test.ts @@ -2,8 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { hash, StringSHA1 } from 'vs/base/common/hash'; +import { sha1Hex } from 'vs/base/browser/hash'; suite('Hash', () => { test('string', () => { @@ -71,28 +73,32 @@ suite('Hash', () => { }); - function checkSHA1(strings: string[], expected: string) { + async function checkSHA1(str: string, expected: string) { + + // Test with StringSHA1 const hash = new StringSHA1(); - for (const str of strings) { - hash.update(str); - } - const actual = hash.digest(); + hash.update(str); + let actual = hash.digest(); + assert.equal(actual, expected); + + // Test with crypto.subtle + actual = await sha1Hex(str); assert.equal(actual, expected); } test('sha1-1', () => { - checkSHA1(['\udd56'], '9bdb77276c1852e1fb067820472812fcf6084024'); + return checkSHA1('\udd56', '9bdb77276c1852e1fb067820472812fcf6084024'); }); test('sha1-2', () => { - checkSHA1(['\udb52'], '9bdb77276c1852e1fb067820472812fcf6084024'); + return checkSHA1('\udb52', '9bdb77276c1852e1fb067820472812fcf6084024'); }); test('sha1-3', () => { - checkSHA1(['\uda02ꑍ'], '9b483a471f22fe7e09d83f221871a987244bbd3f'); + return checkSHA1('\uda02ꑍ', '9b483a471f22fe7e09d83f221871a987244bbd3f'); }); test('sha1-4', () => { - checkSHA1(['hello'], 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d'); + return checkSHA1('hello', 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d'); }); }); diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 78f82030b..f4bfa18d1 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as marked from 'vs/base/common/marked/marked'; -import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; +import { renderMarkdown, renderMarkdownAsPlaintext } from 'vs/base/browser/markdownRenderer'; import { MarkdownString, IMarkdownString } from 'vs/base/common/htmlContent'; import { URI } from 'vs/base/common/uri'; import { parse } from 'vs/base/common/marshalling'; @@ -57,7 +57,7 @@ suite('MarkdownRenderer', () => { mds.appendText('$(zap) $(not a theme icon) $(add)'); let result: HTMLElement = renderMarkdown(mds); - assert.strictEqual(result.innerHTML, `

$(zap) $(not a theme icon) $(add)

`); + assert.strictEqual(result.innerHTML, `

$(zap) $(not a theme icon) $(add)

`); }); test('render appendMarkdown', () => { @@ -85,7 +85,7 @@ suite('MarkdownRenderer', () => { mds.appendText('$(zap) $(not a theme icon) $(add)'); let result: HTMLElement = renderMarkdown(mds); - assert.strictEqual(result.innerHTML, `

$(zap) $(not a theme icon) $(add)

`); + assert.strictEqual(result.innerHTML, `

$(zap) $(not a theme icon) $(add)

`); }); test('render appendMarkdown with escaped icon', () => { @@ -115,4 +115,20 @@ suite('MarkdownRenderer', () => { assert.ok(data.documentUri.toString().startsWith('file:///c%3A/')); }); + suite('PlaintextMarkdownRender', () => { + + test('test code, blockquote, heading, list, listitem, paragraph, table, tablerow, tablecell, strong, em, br, del, text are rendered plaintext', () => { + const markdown = { value: '`code`\n>quote\n# heading\n- list\n\n\ntable | table2\n--- | --- \none | two\n\n\nbo**ld**\n_italic_\n~~del~~\nsome text' }; + const expected = 'code\nquote\nheading\nlist\ntable table2 one two \nbold\nitalic\ndel\nsome text\n'; + const result: string = renderMarkdownAsPlaintext(markdown); + assert.strictEqual(result, expected); + }); + + test('test html, hr, image, link are rendered plaintext', () => { + const markdown = { value: '
html
\n\n---\n![image](imageLink)\n[text](textLink)' }; + const expected = '\ntext\n'; + const result: string = renderMarkdownAsPlaintext(markdown); + assert.strictEqual(result, expected); + }); + }); }); diff --git a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts index 5ab03d9cd..4d3c0944a 100644 --- a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts +++ b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts @@ -18,8 +18,11 @@ suite('ScrollbarState', () => { assert.equal(actual.getSliderSize(), 20); assert.equal(actual.getSliderPosition(), 249); - assert.equal(actual.getDesiredScrollPositionFromOffset(259), 32849); + + // 259 is greater than 230 so page down, 32787 + 339 = 33126 + assert.equal(actual.getDesiredScrollPositionFromOffsetPaged(259), 33126); + actual.setScrollPosition(32849); assert.equal(actual.getArrowSize(), 0); assert.equal(actual.getScrollPosition(), 32849); @@ -41,8 +44,11 @@ suite('ScrollbarState', () => { assert.equal(actual.getSliderSize(), 20); assert.equal(actual.getSliderPosition(), 230); - assert.equal(actual.getDesiredScrollPositionFromOffset(240 + 12), 32811); + + // 240 + 12 = 252; greater than 230 so page down, 32787 + 339 = 33126 + assert.equal(actual.getDesiredScrollPositionFromOffsetPaged(240 + 12), 33126); + actual.setScrollPosition(32811); assert.equal(actual.getArrowSize(), 12); assert.equal(actual.getScrollPosition(), 32811); diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 2620db0b7..c36a51fd2 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -91,7 +91,7 @@ suite('Splitview', () => { splitview.addView(view2, 20); splitview.addView(view3, 20); - let viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + let viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 3, 'split view should have 3 views'); let sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); @@ -99,7 +99,7 @@ suite('Splitview', () => { splitview.removeView(2); - viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 2, 'split view should have 2 views'); sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); @@ -107,7 +107,7 @@ suite('Splitview', () => { splitview.removeView(0); - viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 1, 'split view should have 1 view'); sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); @@ -115,7 +115,7 @@ suite('Splitview', () => { splitview.removeView(0); - viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 0, 'split view should have no views'); sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index a7d505b21..d1baf9b5b 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import * as async from 'vs/base/common/async'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; suite('Async', () => { @@ -651,41 +651,39 @@ suite('Async', () => { test('raceCancellation', async () => { const cts = new CancellationTokenSource(); - const now = Date.now(); - - const p = async.raceCancellation(async.timeout(100), cts.token); + let triggered = false; + const p = async.raceCancellation(async.timeout(100).then(() => triggered = true), cts.token); cts.cancel(); await p; - assert.ok(Date.now() - now < 100); + assert.ok(!triggered); }); test('raceTimeout', async () => { const cts = new CancellationTokenSource(); // timeout wins - let now = Date.now(); let timedout = false; + let triggered = false; - const p1 = async.raceTimeout(async.timeout(100), 1, () => timedout = true); + const p1 = async.raceTimeout(async.timeout(100).then(() => triggered = true), 1, () => timedout = true); cts.cancel(); await p1; - assert.ok(Date.now() - now < 100); + assert.ok(!triggered); assert.equal(timedout, true); // promise wins - now = Date.now(); timedout = false; - const p2 = async.raceTimeout(async.timeout(1), 100, () => timedout = true); + const p2 = async.raceTimeout(async.timeout(1).then(() => triggered = true), 100, () => timedout = true); cts.cancel(); await p2; - assert.ok(Date.now() - now < 100); + assert.ok(triggered); assert.equal(timedout, false); }); @@ -719,4 +717,63 @@ suite('Async', () => { assert.equal(counter.increment(), 2); assert.equal(counter.increment(), 3); }); + + test('firstParallel - simple', async () => { + const a = await async.firstParallel([ + Promise.resolve(1), + Promise.resolve(2), + Promise.resolve(3), + ], v => v === 2); + assert.equal(a, 2); + }); + + test('firstParallel - uses null default', async () => { + assert.equal(await async.firstParallel([Promise.resolve(1)], v => v === 2), null); + }); + + test('firstParallel - uses value default', async () => { + assert.equal(await async.firstParallel([Promise.resolve(1)], v => v === 2, 4), 4); + }); + + test('firstParallel - empty', async () => { + assert.equal(await async.firstParallel([], v => v === 2, 4), 4); + }); + + test('firstParallel - cancels', async () => { + let ct1: CancellationToken; + const p1 = async.createCancelablePromise(async (ct) => { + ct1 = ct; + await async.timeout(200, ct); + return 1; + }); + let ct2: CancellationToken; + const p2 = async.createCancelablePromise(async (ct) => { + ct2 = ct; + await async.timeout(2, ct); + return 2; + }); + + assert.equal(await async.firstParallel([p1, p2], v => v === 2, 4), 2); + assert.equal(ct1!.isCancellationRequested, true, 'should cancel a'); + assert.equal(ct2!.isCancellationRequested, true, 'should cancel b'); + }); + + test('firstParallel - rejection handling', async () => { + let ct1: CancellationToken; + const p1 = async.createCancelablePromise(async (ct) => { + ct1 = ct; + await async.timeout(200, ct); + return 1; + }); + let ct2: CancellationToken; + const p2 = async.createCancelablePromise(async (ct) => { + ct2 = ct; + await async.timeout(2, ct); + throw new Error('oh no'); + }); + + assert.equal(await async.firstParallel([p1, p2], v => v === 2, 4).catch(() => 'ok'), 'ok'); + assert.equal(ct1!.isCancellationRequested, true, 'should cancel a'); + assert.equal(ct2!.isCancellationRequested, true, 'should cancel b'); + }); }); diff --git a/src/vs/base/test/common/linkedList.test.ts b/src/vs/base/test/common/linkedList.test.ts index e63fda850..dbb057a21 100644 --- a/src/vs/base/test/common/linkedList.test.ts +++ b/src/vs/base/test/common/linkedList.test.ts @@ -14,7 +14,7 @@ suite('LinkedList', function () { assert.equal(list.size, elements.length); // assert toArray - assert.deepEqual(list.toArray(), elements); + assert.deepEqual(Array.from(list), elements); // assert Symbol.iterator (1) assert.deepEqual([...list], elements); diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 2615f098e..5781de52a 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, UriIterator } from 'vs/base/common/map'; +import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, UriIterator, ConfigKeysIterator } from 'vs/base/common/map'; import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { extUriIgnorePathCase } from 'vs/base/common/resources'; @@ -368,7 +368,7 @@ suite('Map', () => { }); test('URIIterator', function () { - const iter = new UriIterator(false); + const iter = new UriIterator(() => false); iter.reset(URI.parse('file:///usr/bin/file.txt')); assert.equal(iter.value(), 'file'); @@ -549,13 +549,15 @@ suite('Map', () => { trie.set('foo', 1); trie.set('foobar', 2); trie.set('bar', 3); + trie.set('foobarbaz', 4); trie.deleteSuperstr('foo'); - assertTernarySearchTree(trie, ['bar', 3]); + assertTernarySearchTree(trie, ['foo', 1], ['bar', 3]); trie = new TernarySearchTree(new StringIterator()); trie.set('foo', 1); trie.set('foobar', 2); trie.set('bar', 3); + trie.set('foobarbaz', 4); trie.deleteSuperstr('fo'); assertTernarySearchTree(trie, ['bar', 3]); @@ -612,17 +614,17 @@ suite('Map', () => { map.set('/user/foo/flip/flop', 3); map.set('/usr/foo', 4); - let item: IteratorResult; + let item: IteratorResult<[string, number]>; let iter = map.findSuperstr('/user'); item = iter!.next(); - assert.equal(item.value, 2); + assert.equal(item.value[1], 2); assert.equal(item.done, false); item = iter!.next(); - assert.equal(item.value, 1); + assert.equal(item.value[1], 1); assert.equal(item.done, false); item = iter!.next(); - assert.equal(item.value, 3); + assert.equal(item.value[1], 3); assert.equal(item.done, false); item = iter!.next(); assert.equal(item.value, undefined); @@ -630,7 +632,7 @@ suite('Map', () => { iter = map.findSuperstr('/usr'); item = iter!.next(); - assert.equal(item.value, 4); + assert.equal(item.value[1], 4); assert.equal(item.done, false); item = iter!.next(); @@ -675,12 +677,12 @@ suite('Map', () => { map.set('/usr/foo', 4); map.deleteSuperstr('/user/foo'); assertTernarySearchTree(map, - ['/usr/foo', 4], + ['/user/foo', 2], ['/usr/foo', 4], ); }); test('TernarySearchTree (URI) - basics', function () { - let trie = new TernarySearchTree(new UriIterator(false)); + let trie = new TernarySearchTree(new UriIterator(() => false)); trie.set(URI.file('/user/foo/bar'), 1); trie.set(URI.file('/user/foo'), 2); @@ -700,7 +702,7 @@ suite('Map', () => { test('TernarySearchTree (URI) - lookup', function () { - const map = new TernarySearchTree(new UriIterator(false)); + const map = new TernarySearchTree(new UriIterator(() => false)); map.set(URI.parse('http://foo.bar/user/foo/bar'), 1); map.set(URI.parse('http://foo.bar/user/foo?query'), 2); map.set(URI.parse('http://foo.bar/user/foo?QUERY'), 3); @@ -715,25 +717,35 @@ suite('Map', () => { assert.equal(map.get(URI.parse('http://foo.bar/user/foo/bar/boo')), undefined); }); - test('TernarySearchTree (PathSegments) - superstr', function () { + test('TernarySearchTree (URI) - lookup, casing', function () { - const map = new TernarySearchTree(new UriIterator(false)); + const map = new TernarySearchTree(new UriIterator(uri => /^https?$/.test(uri.scheme))); + map.set(URI.parse('http://foo.bar/user/foo/bar'), 1); + assert.equal(map.get(URI.parse('http://foo.bar/USER/foo/bar')), 1); + + map.set(URI.parse('foo://foo.bar/user/foo/bar'), 1); + assert.equal(map.get(URI.parse('foo://foo.bar/USER/foo/bar')), undefined); + }); + + test('TernarySearchTree (URI) - superstr', function () { + + const map = new TernarySearchTree(new UriIterator(() => false)); map.set(URI.file('/user/foo/bar'), 1); map.set(URI.file('/user/foo'), 2); map.set(URI.file('/user/foo/flip/flop'), 3); map.set(URI.file('/usr/foo'), 4); - let item: IteratorResult; + let item: IteratorResult<[URI, number]>; let iter = map.findSuperstr(URI.file('/user'))!; item = iter.next(); - assert.equal(item.value, 2); + assert.equal(item.value[1], 2); assert.equal(item.done, false); item = iter.next(); - assert.equal(item.value, 1); + assert.equal(item.value[1], 1); assert.equal(item.done, false); item = iter.next(); - assert.equal(item.value, 3); + assert.equal(item.value[1], 3); assert.equal(item.done, false); item = iter.next(); assert.equal(item.value, undefined); @@ -741,7 +753,7 @@ suite('Map', () => { iter = map.findSuperstr(URI.file('/usr'))!; item = iter.next(); - assert.equal(item.value, 4); + assert.equal(item.value[1], 4); assert.equal(item.done, false); item = iter.next(); @@ -750,16 +762,16 @@ suite('Map', () => { iter = map.findSuperstr(URI.file('/'))!; item = iter.next(); - assert.equal(item.value, 2); + assert.equal(item.value[1], 2); assert.equal(item.done, false); item = iter.next(); - assert.equal(item.value, 1); + assert.equal(item.value[1], 1); assert.equal(item.done, false); item = iter.next(); - assert.equal(item.value, 3); + assert.equal(item.value[1], 3); assert.equal(item.done, false); item = iter.next(); - assert.equal(item.value, 4); + assert.equal(item.value[1], 4); assert.equal(item.done, false); item = iter.next(); assert.equal(item.value, undefined); @@ -771,6 +783,103 @@ suite('Map', () => { assert.equal(map.findSuperstr(URI.file('/userr')), undefined); }); + test('TernarySearchTree (ConfigKeySegments) - basics', function () { + let trie = new TernarySearchTree(new ConfigKeysIterator()); + + trie.set('config.foo.bar', 1); + trie.set('config.foo', 2); + trie.set('config.foo.flip.flop', 3); + + assert.equal(trie.get('config.foo.bar'), 1); + assert.equal(trie.get('config.foo'), 2); + assert.equal(trie.get('config.foo.flip.flop'), 3); + + assert.equal(trie.findSubstr('config.bar'), undefined); + assert.equal(trie.findSubstr('config.foo'), 2); + assert.equal(trie.findSubstr('config.foo.ba'), 2); + assert.equal(trie.findSubstr('config.foo.far.boo'), 2); + assert.equal(trie.findSubstr('config.foo.bar'), 1); + assert.equal(trie.findSubstr('config.foo.bar.far.boo'), 1); + }); + + test('TernarySearchTree (ConfigKeySegments) - lookup', function () { + + const map = new TernarySearchTree(new ConfigKeysIterator()); + map.set('config.foo.bar', 1); + map.set('config.foo', 2); + map.set('config.foo.flip.flop', 3); + + assert.equal(map.get('foo'), undefined); + assert.equal(map.get('config'), undefined); + assert.equal(map.get('config.foo'), 2); + assert.equal(map.get('config.foo.bar'), 1); + assert.equal(map.get('config.foo.bar.boo'), undefined); + }); + + test('TernarySearchTree (ConfigKeySegments) - superstr', function () { + + const map = new TernarySearchTree(new ConfigKeysIterator()); + map.set('config.foo.bar', 1); + map.set('config.foo', 2); + map.set('config.foo.flip.flop', 3); + map.set('boo', 4); + + let item: IteratorResult<[string, number]>; + let iter = map.findSuperstr('config'); + + item = iter!.next(); + assert.equal(item.value[1], 2); + assert.equal(item.done, false); + item = iter!.next(); + assert.equal(item.value[1], 1); + assert.equal(item.done, false); + item = iter!.next(); + assert.equal(item.value[1], 3); + assert.equal(item.done, false); + item = iter!.next(); + assert.equal(item.value, undefined); + assert.equal(item.done, true); + + assert.equal(map.findSuperstr('foo'), undefined); + assert.equal(map.findSuperstr('config.foo.no'), undefined); + assert.equal(map.findSuperstr('config.foop'), undefined); + }); + + + test('TernarySearchTree (ConfigKeySegments) - delete_superstr', function () { + + const map = new TernarySearchTree(new ConfigKeysIterator()); + map.set('config.foo.bar', 1); + map.set('config.foo', 2); + map.set('config.foo.flip.flop', 3); + map.set('boo', 4); + + assertTernarySearchTree(map, + ['config.foo.bar', 1], + ['config.foo', 2], + ['config.foo.flip.flop', 3], + ['boo', 4], + ); + + // not a segment + map.deleteSuperstr('config.fo'); + assertTernarySearchTree(map, + ['config.foo.bar', 1], + ['config.foo', 2], + ['config.foo.flip.flop', 3], + ['boo', 4], + ); + + // delete a segment + map.set('config.foo.bar', 1); + map.set('config.foo', 2); + map.set('config.foo.flip.flop', 3); + map.set('config.boo', 4); + map.deleteSuperstr('config.foo'); + assertTernarySearchTree(map, + ['config.foo', 2], ['boo', 4], + ); + }); test('ResourceMap - basics', function () { const map = new ResourceMap(); diff --git a/src/vs/base/test/common/markdownString.test.ts b/src/vs/base/test/common/markdownString.test.ts index f33f741f6..424c2bd46 100644 --- a/src/vs/base/test/common/markdownString.test.ts +++ b/src/vs/base/test/common/markdownString.test.ts @@ -8,12 +8,24 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; suite('MarkdownString', () => { + test('Escape leading whitespace', function () { + const mds = new MarkdownString(); + mds.appendText('Hello\n Not a code block'); + assert.equal(mds.value, 'Hello\n\n    Not a code block'); + }); + + test('MarkdownString.appendText doesn\'t escape quote #109040', function () { + const mds = new MarkdownString(); + mds.appendText('> Text\n>More'); + assert.equal(mds.value, '\\> Text\n\n\\>More'); + }); + test('appendText', () => { const mds = new MarkdownString(); mds.appendText('# foo\n*bar*'); - assert.equal(mds.value, '\\# foo\n\n\\*bar\\*'); + assert.equal(mds.value, '\\# foo\n\n\\*bar\\*'); }); suite('ThemeIcons', () => { @@ -24,7 +36,7 @@ suite('MarkdownString', () => { const mds = new MarkdownString(undefined, { supportThemeIcons: true }); mds.appendText('$(zap) $(not a theme icon) $(add)'); - assert.equal(mds.value, '\\\\$\\(zap\\) $\\(not a theme icon\\) \\\\$\\(add\\)'); + assert.equal(mds.value, '\\\\$\\(zap\\) $\\(not a theme icon\\) \\\\$\\(add\\)'); }); test('appendMarkdown', () => { @@ -49,7 +61,7 @@ suite('MarkdownString', () => { const mds = new MarkdownString(undefined, { supportThemeIcons: false }); mds.appendText('$(zap) $(not a theme icon) $(add)'); - assert.equal(mds.value, '$\\(zap\\) $\\(not a theme icon\\) $\\(add\\)'); + assert.equal(mds.value, '$\\(zap\\) $\\(not a theme icon\\) $\\(add\\)'); }); test('appendMarkdown', () => { diff --git a/src/vs/base/test/common/network.test.ts b/src/vs/base/test/common/network.test.ts new file mode 100644 index 000000000..1729d4b1c --- /dev/null +++ b/src/vs/base/test/common/network.test.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { URI } from 'vs/base/common/uri'; +import { FileAccess, Schemas } from 'vs/base/common/network'; +import { isEqual } from 'vs/base/common/resources'; +import { isElectronSandboxed } from 'vs/base/common/platform'; + +suite('network', () => { + const enableTest = isElectronSandboxed; + + (!enableTest ? test.skip : test)('FileAccess: URI (native)', () => { + + // asCodeUri() & asFileUri(): simple, without authority + let originalFileUri = URI.file('network.test.ts'); + let browserUri = FileAccess.asBrowserUri(originalFileUri); + assert.ok(browserUri.authority.length > 0); + let fileUri = FileAccess.asFileUri(browserUri); + assert.equal(fileUri.authority.length, 0); + assert(isEqual(originalFileUri, fileUri)); + + // asCodeUri() & asFileUri(): with authority + originalFileUri = URI.file('network.test.ts').with({ authority: 'test-authority' }); + browserUri = FileAccess.asBrowserUri(originalFileUri); + assert.equal(browserUri.authority, originalFileUri.authority); + fileUri = FileAccess.asFileUri(browserUri); + assert(isEqual(originalFileUri, fileUri)); + }); + + (!enableTest ? test.skip : test)('FileAccess: moduleId (native)', () => { + const browserUri = FileAccess.asBrowserUri('vs/base/test/node/network.test', require); + assert.equal(browserUri.scheme, Schemas.vscodeFileResource); + + const fileUri = FileAccess.asFileUri('vs/base/test/node/network.test', require); + assert.equal(fileUri.scheme, Schemas.file); + }); + + (!enableTest ? test.skip : test)('FileAccess: query and fragment is dropped (native)', () => { + let originalFileUri = URI.file('network.test.ts').with({ query: 'foo=bar', fragment: 'something' }); + let browserUri = FileAccess.asBrowserUri(originalFileUri); + assert.equal(browserUri.query, ''); + assert.equal(browserUri.fragment, ''); + }); + + (!enableTest ? test.skip : test)('FileAccess: query and fragment is kept if URI is already of same scheme (native)', () => { + let originalFileUri = URI.file('network.test.ts').with({ query: 'foo=bar', fragment: 'something' }); + let browserUri = FileAccess.asBrowserUri(originalFileUri.with({ scheme: Schemas.vscodeFileResource })); + assert.equal(browserUri.query, 'foo=bar'); + assert.equal(browserUri.fragment, 'something'); + + let fileUri = FileAccess.asFileUri(originalFileUri); + assert.equal(fileUri.query, 'foo=bar'); + assert.equal(fileUri.fragment, 'something'); + }); + + (!enableTest ? test.skip : test)('FileAccess: web', () => { + const originalHttpsUri = URI.file('network.test.ts').with({ scheme: 'https' }); + const browserUri = FileAccess.asBrowserUri(originalHttpsUri); + assert.equal(originalHttpsUri.toString(), browserUri.toString()); + }); + + test('FileAccess: remote URIs', () => { + const originalRemoteUri = URI.file('network.test.ts').with({ scheme: Schemas.vscodeRemote }); + const browserUri = FileAccess.asBrowserUri(originalRemoteUri); + assert.notEqual(originalRemoteUri.scheme, browserUri.scheme); + }); +}); diff --git a/src/vs/base/test/common/processes.test.ts b/src/vs/base/test/common/processes.test.ts index 4b49a3811..ee0d25c88 100644 --- a/src/vs/base/test/common/processes.test.ts +++ b/src/vs/base/test/common/processes.test.ts @@ -19,7 +19,6 @@ suite('Processes', () => { VSCODE_CLI: 'x', VSCODE_DEV: 'x', VSCODE_IPC_HOOK: 'x', - VSCODE_LOGS: 'x', VSCODE_NLS_CONFIG: 'x', VSCODE_PORTABLE: 'x', VSCODE_PID: 'x', diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 543cb460b..d3366a1e5 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -417,4 +417,9 @@ suite('Strings', () => { test('getGraphemeBreakType', () => { assert.equal(strings.getGraphemeBreakType(0xBC1), strings.GraphemeBreakType.SpacingMark); }); + + test('truncate', () => { + assert.equal('hello world', strings.truncate('hello world', 100)); + assert.equal('hello…', strings.truncate('hello world', 5)); + }); }); diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts index 71c6724e9..c39dba9a3 100644 --- a/src/vs/base/worker/workerMain.ts +++ b/src/vs/base/worker/workerMain.ts @@ -8,14 +8,20 @@ let MonacoEnvironment = (self).MonacoEnvironment; let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../'; + const trustedTypesPolicy = self.trustedTypes?.createPolicy('amdLoader', { createScriptURL: value => value }); + if (typeof (self).define !== 'function' || !(self).define.amd) { - importScripts(monacoBaseUrl + 'vs/loader.js'); + let loaderSrc: string | TrustedScriptURL = monacoBaseUrl + 'vs/loader.js'; + if (trustedTypesPolicy) { + loaderSrc = trustedTypesPolicy.createScriptURL(loaderSrc); + } + importScripts(loaderSrc as string); } require.config({ baseUrl: monacoBaseUrl, catchError: true, - createTrustedScriptURL: (value: string) => value, + trustedTypesPolicy, }); let loadCode = function (moduleId: string) { diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index c3c259cfa..8b1869294 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -33,7 +33,14 @@ self.require = { baseUrl: `${window.location.origin}/static/out`, recordStats: true, - createTrustedScriptURL: value => value, + trustedTypesPolicy: window.trustedTypes?.createPolicy('amdLoader', { + createScriptURL(value) { + if(value.startsWith(window.location.origin)) { + return value; + } + throw new Error(`Invalid script url: ${value}`) + } + }), paths: { 'vscode-textmate': `${window.location.origin}/static/remote/web/node_modules/vscode-textmate/release/main`, 'vscode-oniguruma': `${window.location.origin}/static/remote/web/node_modules/vscode-oniguruma/release/main`, diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 300ceb2fb..1dbdce5d6 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -32,7 +32,14 @@ self.require = { baseUrl: `${window.location.origin}/static/out`, recordStats: true, - createTrustedScriptURL: value => value, + trustedTypesPolicy: window.trustedTypes?.createPolicy('amdLoader', { + createScriptURL(value) { + if(value.startsWith(window.location.origin)) { + return value; + } + throw new Error(`Invalid script url: ${value}`) + } + }), paths: { 'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`, 'vscode-oniguruma': `${window.location.origin}/static/node_modules/vscode-oniguruma/release/main`, diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 0ef8b9dc8..d1dba0721 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -17,6 +17,7 @@ import { isStandalone } from 'vs/base/browser/browser'; import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; import product from 'vs/platform/product/common/product'; +import { parseLogLevel } from 'vs/platform/log/common/log'; function doCreateUri(path: string, queryValues: Map): URI { let query: string | undefined = undefined; @@ -417,6 +418,7 @@ class WindowIndicator implements IWindowIndicator { let foundWorkspace = false; let workspace: IWorkspace; let payload = Object.create(null); + let logLevel: string | undefined = undefined; const query = new URL(document.location.href).searchParams; query.forEach((value, key) => { @@ -448,6 +450,11 @@ class WindowIndicator implements IWindowIndicator { console.error(error); // possible invalid JSON } break; + + // Log level + case 'logLevel': + logLevel = value; + break; } }); @@ -514,6 +521,7 @@ class WindowIndicator implements IWindowIndicator { // Finally create workbench create(document.body, { ...config, + logLevel: logLevel ? parseLogLevel(logLevel) : undefined, settingsSyncOptions, homeIndicator, windowIndicator, diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 88f3869f8..3dcc360b5 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -51,7 +51,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; -import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel, StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; +import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; @@ -63,7 +63,6 @@ import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagemen import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService'; import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; @@ -72,6 +71,7 @@ import { ActiveWindowManager } from 'vs/platform/windows/common/windowTracker'; import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender'; import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; +import { ExtensionsStorageSyncService, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -151,7 +151,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IStorageService, storageService); disposables.add(toDisposable(() => storageService.flush())); - services.set(IStorageKeysSyncRegistryService, new SyncDescriptor(StorageKeysSyncRegistryService)); services.set(IEnvironmentService, environmentService); services.set(INativeEnvironmentService, environmentService); @@ -188,7 +187,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat appender: telemetryAppender, commonProperties: resolveCommonProperties(product.commit, product.version, configuration.machineId, product.msftInternalDomains, installSourcePath), sendErrorTelemetry: true, - piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot] + piiPaths: [appRoot, extensionsPath] }; telemetryService = new TelemetryService(config, configurationService); @@ -199,10 +198,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat } server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(telemetryAppender)); - const storageKeysSyncRegistryService = accessor.get(IStorageKeysSyncRegistryService); - const storageKeysSyncChannel = new StorageKeysSyncRegistryChannel(storageKeysSyncRegistryService); - server.registerChannel('storageKeysSyncRegistryService', storageKeysSyncChannel); - services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); @@ -214,6 +209,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main'))); services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService)); services.set(IIgnoredExtensionsManagementService, new SyncDescriptor(IgnoredExtensionsManagementService)); + services.set(IExtensionsStorageSyncService, new SyncDescriptor(ExtensionsStorageSyncService)); services.set(IUserDataSyncStoreManagementService, new SyncDescriptor(UserDataSyncStoreManagementService)); services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); services.set(IUserDataSyncMachinesService, new SyncDescriptor(UserDataSyncMachinesService)); diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 3dd6691d3..14c699461 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -9,15 +9,12 @@ 'use strict'; (function () { + const bootstrapWindow = bootstrapWindowLib(); // Add a perf entry right from the top - const perf = perfLib(); + const perf = bootstrapWindow.perfLib(); perf.mark('renderer/started'); - // Load environment in parallel to workbench loading to avoid waterfall - const bootstrapWindow = bootstrapWindowLib(); - const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved(); - // Load workbench main JS, CSS and NLS all in parallel. This is an // optimization to prevent a waterfall of loading to happen, because // we know for a fact that workbench.desktop.main will depend on @@ -31,12 +28,6 @@ // Mark start of workbench perf.mark('didLoadWorkbenchMain'); - performance.mark('workbench-start'); - - // Wait for process environment being fully resolved - await whenEnvResolved; - - perf.mark('main/startup'); // @ts-ignore return require('vs/workbench/electron-browser/desktop.main').main(configuration); @@ -58,23 +49,11 @@ //region Helpers - function perfLib() { - globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; - - return { - /** - * @param {string} name - */ - mark(name) { - globalThis.MonacoPerformanceMarks.push(name, Date.now()); - } - }; - } - /** * @returns {{ * load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown, - * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals') + * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals'), + * perfLib: () => { mark: (name: string) => void } * }} */ function bootstrapWindowLib() { @@ -187,5 +166,5 @@ } //#endregion - + }()); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index cf79f58d5..759c85bc7 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, ipcMain as ipc, systemPreferences, contentTracing, protocol, IpcMainEvent, BrowserWindow, dialog, session } from 'electron'; -import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform'; +import { app, ipcMain, systemPreferences, contentTracing, protocol, BrowserWindow, dialog, session } from 'electron'; +import { IProcessEnvironment, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; import { WindowsMainService } from 'vs/platform/windows/electron-main/windowsMainService'; import { IWindowOpenable } from 'vs/platform/windows/common/windows'; import { OpenContext } from 'vs/platform/windows/node/window'; import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { getShellEnvironment } from 'vs/code/node/shellEnv'; +import { resolveShellEnv } from 'vs/code/node/shellEnv'; import { IUpdateService } from 'vs/platform/update/common/update'; import { UpdateChannel } from 'vs/platform/update/electron-main/updateIpc'; import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main'; @@ -24,7 +24,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IStateService } from 'vs/platform/state/node/state'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IURLService } from 'vs/platform/url/common/url'; +import { IOpenURLOptions, IURLService } from 'vs/platform/url/common/url'; import { URLHandlerChannelClient, URLHandlerRouter } from 'vs/platform/url/common/urlIpc'; import { ITelemetryService, machineIdKey } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -35,6 +35,7 @@ import { getDelayedChannel, StaticRouter, createChannelReceiver, createChannelSe import product from 'vs/platform/product/common/product'; import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; import { ProxyAuthHandler2 } from 'vs/code/electron-main/auth2'; +import { FileProtocolHandler } from 'vs/code/electron-main/protocol'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { URI } from 'vs/base/common/uri'; @@ -52,7 +53,7 @@ import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService'; import { RunOnceScheduler } from 'vs/base/common/async'; import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu'; -import { sep, posix } from 'vs/base/common/path'; +import { sep, posix, join, isAbsolute } from 'vs/base/common/path'; import { joinPath } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; @@ -72,7 +73,6 @@ import { INativeHostMainService, NativeHostMainService } from 'vs/platform/nativ import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService'; import { IDialogMainService, DialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { coalesce } from 'vs/base/common/arrays'; import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels'; import { WebviewMainService } from 'vs/platform/webview/electron-main/webviewMainService'; import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; @@ -82,6 +82,14 @@ import { generateUuid } from 'vs/base/common/uuid'; import { VSBuffer } from 'vs/base/common/buffer'; import { EncryptionMainService, IEncryptionMainService } from 'vs/platform/encryption/electron-main/encryptionMainService'; import { ActiveWindowManager } from 'vs/platform/windows/common/windowTracker'; +import { IKeyboardLayoutMainService, KeyboardLayoutMainService } from 'vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { DisplayMainService, IDisplayMainService } from 'vs/platform/display/electron-main/displayMainService'; +import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; +import { isEqualOrParent } from 'vs/base/common/extpath'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; +import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService'; export class CodeApplication extends Disposable { private windowsMainService: IWindowsMainService | undefined; @@ -96,7 +104,8 @@ export class CodeApplication extends Disposable { @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IStateService private readonly stateService: IStateService + @IStateService private readonly stateService: IStateService, + @IFileService private readonly fileService: IFileService ) { super(); @@ -118,9 +127,7 @@ export class CodeApplication extends Disposable { // Accessibility change event app.on('accessibility-support-changed', (event, accessibilitySupportEnabled) => { - if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled); - } + this.windowsMainService?.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled); }); // macOS dock activate @@ -128,8 +135,8 @@ export class CodeApplication extends Disposable { this.logService.trace('app#activate'); // Mac only event: open new window when we get activated - if (!hasVisibleWindows && this.windowsMainService) { - this.windowsMainService.openEmptyWindow({ context: OpenContext.DOCK }); + if (!hasVisibleWindows) { + this.windowsMainService?.openEmptyWindow({ context: OpenContext.DOCK }); } }); @@ -214,9 +221,7 @@ export class CodeApplication extends Disposable { contents.on('new-window', (event, url) => { event.preventDefault(); // prevent code that wants to open links - if (this.nativeHostMainService) { - this.nativeHostMainService.openExternal(undefined, url); - } + this.nativeHostMainService?.openExternal(undefined, url); }); session.defaultSession.setPermissionRequestHandler((webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback) => { @@ -247,62 +252,119 @@ export class CodeApplication extends Disposable { // Handle paths delayed in case more are coming! runningTimeout = setTimeout(() => { - if (this.windowsMainService) { - this.windowsMainService.open({ - context: OpenContext.DOCK /* can also be opening from finder while app is running */, - cli: this.environmentService.args, - urisToOpen: macOpenFileURIs, - gotoLineMode: false, - preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ - }); + this.windowsMainService?.open({ + context: OpenContext.DOCK /* can also be opening from finder while app is running */, + cli: this.environmentService.args, + urisToOpen: macOpenFileURIs, + gotoLineMode: false, + preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ + }); - macOpenFileURIs = []; - runningTimeout = null; - } + macOpenFileURIs = []; + runningTimeout = null; }, 100); }); app.on('new-window-for-tab', () => { - if (this.windowsMainService) { - this.windowsMainService.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button - } + this.windowsMainService?.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button }); - ipc.on('vscode:fetchShellEnv', async (event: IpcMainEvent) => { + //#region Bootstrap IPC Handlers + + ipcMain.on('vscode:fetchShellEnv', async event => { const webContents = event.sender; + const window = this.windowsMainService?.getWindowByWebContents(event.sender); - try { - const shellEnv = await getShellEnvironment(this.logService, this.environmentService); + let replied = false; - if (!webContents.isDestroyed()) { - webContents.send('vscode:acceptShellEnv', shellEnv); + function acceptShellEnv(env: NodeJS.ProcessEnv): void { + clearTimeout(shellEnvSlowWarningHandle); + clearTimeout(shellEnvTimeoutErrorHandle); + + if (!replied) { + replied = true; + + if (!webContents.isDestroyed()) { + webContents.send('vscode:acceptShellEnv', env); + } } - } catch (error) { - if (!webContents.isDestroyed()) { - webContents.send('vscode:acceptShellEnv', {}); - } - - this.logService.error('Error fetching shell env', error); } + + // Handle slow shell environment resolve calls: + // - a warning after 3s but continue to resolve + // - an error after 10s and stop trying to resolve + const cts = new CancellationTokenSource(); + const shellEnvSlowWarningHandle = setTimeout(() => window?.sendWhenReady('vscode:showShellEnvSlowWarning', cts.token), 3000); + const shellEnvTimeoutErrorHandle = setTimeout(function () { + cts.dispose(true); + window?.sendWhenReady('vscode:showShellEnvTimeoutError', CancellationToken.None); + acceptShellEnv({}); + }, 10000); + + // Prefer to use the args and env from the target window + // when resolving the shell env. It is possible that + // a first window was opened from the UI but a second + // from the CLI and that has implications for wether to + // resolve the shell environment or not. + let args: NativeParsedArgs; + let env: NodeJS.ProcessEnv; + if (window?.config) { + args = window.config; + env = { ...process.env, ...window.config.userEnv }; + } else { + args = this.environmentService.args; + env = process.env; + } + + // Resolve shell env + const shellEnv = await resolveShellEnv(this.logService, args, env); + acceptShellEnv(shellEnv); }); - ipc.on('vscode:toggleDevTools', (event: IpcMainEvent) => event.sender.toggleDevTools()); - ipc.on('vscode:openDevTools', (event: IpcMainEvent) => event.sender.openDevTools()); + ipcMain.handle('vscode:writeNlsFile', (event, path: unknown, data: unknown) => { + const uri = this.validateNlsPath([path]); + if (!uri || typeof data !== 'string') { + return Promise.reject('Invalid operation (vscode:writeNlsFile)'); + } - ipc.on('vscode:reloadWindow', (event: IpcMainEvent) => event.sender.reload()); + return this.fileService.writeFile(uri, VSBuffer.fromString(data)); + }); - // Some listeners after window opened - (async () => { - await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); + ipcMain.handle('vscode:readNlsFile', async (event, ...paths: unknown[]) => { + const uri = this.validateNlsPath(paths); + if (!uri) { + return Promise.reject('Invalid operation (vscode:readNlsFile)'); + } - // Keyboard layout changes (after window opened) - const nativeKeymap = await import('native-keymap'); - nativeKeymap.onDidChangeKeyboardLayout(() => { - if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged'); + return (await this.fileService.readFile(uri)).value.toString(); + }); + + ipcMain.on('vscode:toggleDevTools', event => event.sender.toggleDevTools()); + ipcMain.on('vscode:openDevTools', event => event.sender.openDevTools()); + + ipcMain.on('vscode:reloadWindow', event => event.sender.reload()); + + //#endregion + } + + private validateNlsPath(pathSegments: unknown[]): URI | undefined { + let path: string | undefined = undefined; + + for (const pathSegment of pathSegments) { + if (typeof pathSegment === 'string') { + if (typeof path !== 'string') { + path = pathSegment; + } else { + path = join(path, pathSegment); } - }); - })(); + } + } + + if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentService.cachedLanguagesPath, !isLinux)) { + return undefined; + } + + return URI.file(path); } private onUnexpectedError(err: Error): void { @@ -315,9 +377,7 @@ export class CodeApplication extends Disposable { }; // handle on client side - if (this.windowsMainService) { - this.windowsMainService.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); - } + this.windowsMainService?.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); } this.logService.error(`[uncaught exception in main]: ${err}`); @@ -354,6 +414,9 @@ export class CodeApplication extends Disposable { this.logService.error(error); } + // Setup Protocol Handler + const fileProtocolHandler = this._register(this.instantiationService.createInstance(FileProtocolHandler)); + // Create Electron IPC Server const electronIpcServer = new ElectronIPCServer(); @@ -376,7 +439,7 @@ export class CodeApplication extends Disposable { }); this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => { this._register(new RunOnceScheduler(async () => { - sharedProcess.spawn(await getShellEnvironment(this.logService, this.environmentService)); + sharedProcess.spawn(await resolveShellEnv(this.logService, this.environmentService.args, process.env)); }, 3000)).schedule(); }); @@ -391,15 +454,15 @@ export class CodeApplication extends Disposable { this._register(server); } - // Setup Auth Handler - if (this.configurationService.getValue('window.enableExperimentalProxyLoginDialog') !== true) { + // Setup Auth Handler (TODO@ben remove old auth handler eventually) + if (this.configurationService.getValue('window.enableExperimentalProxyLoginDialog') === false) { this._register(new ProxyAuthHandler()); } else { this._register(appInstantiationService.createInstance(ProxyAuthHandler2)); } // Open Windows - const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient)); + const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient, fileProtocolHandler)); // Post Open Windows Tasks appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor)); @@ -453,10 +516,13 @@ export class CodeApplication extends Disposable { services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv])); services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService, [machineId])); + services.set(IKeyboardLayoutMainService, new SyncDescriptor(KeyboardLayoutMainService)); + services.set(IDisplayMainService, new SyncDescriptor(DisplayMainService)); services.set(INativeHostMainService, new SyncDescriptor(NativeHostMainService)); services.set(IWebviewManagerService, new SyncDescriptor(WebviewMainService)); services.set(IWorkspacesService, new SyncDescriptor(WorkspacesService)); services.set(IMenubarMainService, new SyncDescriptor(MenubarMainService)); + services.set(IExtensionUrlTrustService, new SyncDescriptor(ExtensionUrlTrustService)); const storageMainService = new StorageMainService(this.logService, this.environmentService); services.set(IStorageMainService, storageMainService); @@ -474,7 +540,7 @@ export class CodeApplication extends Disposable { const channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender'))); const appender = new TelemetryAppenderClient(channel); const commonProperties = resolveCommonProperties(product.commit, product.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath); - const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot]; + const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, sendErrorTelemetry: true }; services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); @@ -502,14 +568,12 @@ export class CodeApplication extends Disposable { const path = await contentTracing.stopRecording(joinPath(this.environmentService.userHome, `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath); if (!timeout) { - if (this.dialogMainService) { - this.dialogMainService.showMessageBox({ - type: 'info', - message: localize('trace.message', "Successfully created trace."), - detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), - buttons: [localize('trace.ok', "OK")] - }, withNullAsUndefined(BrowserWindow.getFocusedWindow())); - } + this.dialogMainService?.showMessageBox({ + type: 'info', + message: localize('trace.message', "Successfully created trace."), + detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), + buttons: [localize('trace.ok', "OK")] + }, withNullAsUndefined(BrowserWindow.getFocusedWindow())); } else { this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`); } @@ -525,7 +589,7 @@ export class CodeApplication extends Disposable { }); } - private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise>): ICodeWindow[] { + private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise>, fileProtocolHandler: FileProtocolHandler): ICodeWindow[] { // Register more Main IPC services const launchMainService = accessor.get(ILaunchMainService); @@ -545,6 +609,14 @@ export class CodeApplication extends Disposable { const encryptionChannel = createChannelReceiver(encryptionMainService); electronIpcServer.registerChannel('encryption', encryptionChannel); + const keyboardLayoutMainService = accessor.get(IKeyboardLayoutMainService); + const keyboardLayoutChannel = createChannelReceiver(keyboardLayoutMainService); + electronIpcServer.registerChannel('keyboardLayout', keyboardLayoutChannel); + + const displayMainService = accessor.get(IDisplayMainService); + const displayChannel = createChannelReceiver(displayMainService); + electronIpcServer.registerChannel('display', displayChannel); + const nativeHostMainService = this.nativeHostMainService = accessor.get(INativeHostMainService); const nativeHostChannel = createChannelReceiver(this.nativeHostMainService); electronIpcServer.registerChannel('nativeHost', nativeHostChannel); @@ -566,6 +638,10 @@ export class CodeApplication extends Disposable { const urlChannel = createChannelReceiver(urlService); electronIpcServer.registerChannel('url', urlChannel); + const extensionUrlTrustService = accessor.get(IExtensionUrlTrustService); + const extensionUrlTrustChannel = createChannelReceiver(extensionUrlTrustService); + electronIpcServer.registerChannel('extensionUrlTrust', extensionUrlTrustChannel); + const webviewManagerService = accessor.get(IWebviewManagerService); const webviewChannel = createChannelReceiver(webviewManagerService); electronIpcServer.registerChannel('webview', webviewChannel); @@ -579,8 +655,10 @@ export class CodeApplication extends Disposable { electronIpcServer.registerChannel('logger', loggerChannel); sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel)); - // ExtensionHost Debug broadcast service const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); + fileProtocolHandler.injectWindowsMainService(windowsMainService); + + // ExtensionHost Debug broadcast service electronIpcServer.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ElectronExtensionHostDebugBroadcastChannel(windowsMainService)); // Signal phase: ready (services set) @@ -591,29 +669,32 @@ export class CodeApplication extends Disposable { // Check for initial URLs to handle from protocol link invocations const pendingWindowOpenablesFromProtocolLinks: IWindowOpenable[] = []; - const pendingProtocolLinksToHandle = coalesce([ - + const pendingProtocolLinksToHandle = [ // Windows/Linux: protocol handler invokes CLI with --open-url ...this.environmentService.args['open-url'] ? this.environmentService.args._urls || [] : [], // macOS: open-url events ...((global).getOpenUrls() || []) as string[] - ].map(pendingUrlToHandle => { + ].map(url => { try { - return URI.parse(pendingUrlToHandle); - } catch (error) { - return undefined; + return { uri: URI.parse(url), url }; + } catch { + return null; } - })).filter(pendingUriToHandle => { - // if URI should be blocked, filter it out - if (this.shouldBlockURI(pendingUriToHandle)) { + }).filter((obj): obj is { uri: URI, url: string } => { + if (!obj) { return false; } - // filter out any protocol link that wants to open as window so that + // If URI should be blocked, filter it out + if (this.shouldBlockURI(obj.uri)) { + return false; + } + + // Filter out any protocol link that wants to open as window so that // we open the right set of windows on startup and not restore the // previous workspace too. - const windowOpenable = this.getWindowOpenableFromProtocolLink(pendingUriToHandle); + const windowOpenable = this.getWindowOpenableFromProtocolLink(obj.uri); if (windowOpenable) { pendingWindowOpenablesFromProtocolLinks.push(windowOpenable); @@ -627,8 +708,9 @@ export class CodeApplication extends Disposable { const app = this; const environmentService = this.environmentService; urlService.registerHandler({ - async handleURL(uri: URI): Promise { - // if URI should be blocked, behave as if it's handled + async handleURL(uri: URI, options?: IOpenURLOptions): Promise { + + // If URI should be blocked, behave as if it's handled if (app.shouldBlockURI(uri)) { return true; } @@ -658,7 +740,7 @@ export class CodeApplication extends Disposable { await window.ready(); - return urlService.open(uri); + return urlService.open(uri, options); } return false; @@ -666,11 +748,11 @@ export class CodeApplication extends Disposable { }); // Create a URL handler which forwards to the last active window - const activeWindowManager = new ActiveWindowManager({ + const activeWindowManager = this._register(new ActiveWindowManager({ onDidOpenWindow: nativeHostMainService.onDidOpenWindow, onDidFocusWindow: nativeHostMainService.onDidFocusWindow, getActiveWindowId: () => nativeHostMainService.getActiveWindowId(-1) - }); + })); const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); const urlHandlerRouter = new URLHandlerRouter(activeWindowRouter); const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', urlHandlerRouter); @@ -682,7 +764,7 @@ export class CodeApplication extends Disposable { // Open our first window const args = this.environmentService.args; const macOpenFiles: string[] = (global).macOpenFiles; - const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; + const context = isLaunchedFromCli(process.env) ? OpenContext.CLI : OpenContext.DESKTOP; const hasCliArgs = args._.length; const hasFolderURIs = !!args['folder-uri']; const hasFileURIs = !!args['file-uri']; @@ -828,12 +910,14 @@ export class CodeApplication extends Disposable { updateService.initialize(); } + // Start to fetch shell environment (if needed) after window has opened + resolveShellEnv(this.logService, this.environmentService.args, process.env); + // If enable-crash-reporter argv is undefined then this is a fresh start, // based on telemetry.enableCrashreporter settings, generate a UUID which // will be used as crash reporter id and also update the json file. try { - const fileService = accessor.get(IFileService); - const argvContent = await fileService.readFile(this.environmentService.argvResource); + const argvContent = await this.fileService.readFile(this.environmentService.argvResource); const argvString = argvContent.value.toString(); const argvJSON = JSON.parse(stripComments(argvString)); if (argvJSON['enable-crash-reporter'] === undefined) { @@ -850,14 +934,11 @@ export class CodeApplication extends Disposable { '}' ]; const newArgvString = argvString.substring(0, argvString.length - 2).concat(',\n', additionalArgvContent.join('\n')); - await fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString)); + await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString)); } } catch (error) { this.logService.error(error); } - - // Start to fetch shell environment after window has opened - getShellEnvironment(this.logService, this.environmentService); } private handleRemoteAuthorities(): void { diff --git a/src/vs/code/electron-main/auth2.ts b/src/vs/code/electron-main/auth2.ts index 1bce04457..1b84d4bc4 100644 --- a/src/vs/code/electron-main/auth2.ts +++ b/src/vs/code/electron-main/auth2.ts @@ -13,6 +13,7 @@ import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeH import { IEncryptionMainService } from 'vs/platform/encryption/electron-main/encryptionMainService'; import { generateUuid } from 'vs/base/common/uuid'; import product from 'vs/platform/product/common/product'; +import { CancellationToken } from 'vs/base/common/cancellation'; interface ElectronAuthenticationResponseDetails extends AuthenticationResponseDetails { firstAuthAttempt?: boolean; // https://github.com/electron/electron/blob/84a42a050e7d45225e69df5bd2d2bf9f1037ea41/shell/browser/login_handler.cc#L70 @@ -192,7 +193,7 @@ export class ProxyAuthHandler2 extends Disposable { password: this.sessionCredentials?.password ?? storedPassword, // prefer to show already used password (if any) over stored replyChannel: `vscode:proxyAuthResponse:${generateUuid()}` }; - window.sendWhenReady('vscode:openProxyAuthenticationDialog', payload); + window.sendWhenReady('vscode:openProxyAuthenticationDialog', CancellationToken.None, payload); this.state = ProxyAuthState.LoginDialogShown; // Handle reply diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 10f4a19f1..783ac6fb5 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -200,7 +200,7 @@ class CodeMain { VSCODE_IPC_HOOK: environmentService.mainIPCHandle }; - ['VSCODE_NLS_CONFIG', 'VSCODE_LOGS', 'VSCODE_PORTABLE'].forEach(key => { + ['VSCODE_NLS_CONFIG', 'VSCODE_PORTABLE'].forEach(key => { const value = process.env[key]; if (typeof value === 'string') { instanceEnvironment[key] = value; @@ -343,15 +343,7 @@ class CodeMain { private handleStartupDataDirError(environmentService: IEnvironmentMainService, error: NodeJS.ErrnoException): void { if (error.code === 'EACCES' || error.code === 'EPERM') { - const directories = [environmentService.userDataPath]; - - if (environmentService.extensionsPath) { - directories.push(environmentService.extensionsPath); - } - - if (XDG_RUNTIME_DIR) { - directories.push(XDG_RUNTIME_DIR); - } + const directories = coalesce([environmentService.userDataPath, environmentService.extensionsPath, XDG_RUNTIME_DIR]); this.showStartupWarningDialog( localize('startupDataDirError', "Unable to write program user data."), diff --git a/src/vs/code/electron-main/protocol.ts b/src/vs/code/electron-main/protocol.ts new file mode 100644 index 000000000..36d093199 --- /dev/null +++ b/src/vs/code/electron-main/protocol.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { FileAccess, Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { session } from 'electron'; +import { ILogService } from 'vs/platform/log/common/log'; +import { TernarySearchTree } from 'vs/base/common/map'; +import { isLinux } from 'vs/base/common/platform'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; + +type ProtocolCallback = { (result: string | Electron.FilePathWithHeaders | { error: number }): void }; + +export class FileProtocolHandler extends Disposable { + + private readonly validRoots = TernarySearchTree.forUris(() => !isLinux); + + constructor( + @INativeEnvironmentService environmentService: INativeEnvironmentService, + @ILogService private readonly logService: ILogService + ) { + super(); + + const { defaultSession } = session; + + // Define an initial set of roots we allow loading from + // - appRoot : all files installed as part of the app + // - extensions : all files shipped from extensions + this.validRoots.set(URI.file(environmentService.appRoot), true); + this.validRoots.set(URI.file(environmentService.extensionsPath), true); + + // Register vscode-file:// handler + defaultSession.protocol.registerFileProtocol(Schemas.vscodeFileResource, (request, callback) => this.handleResourceRequest(request, callback as unknown as ProtocolCallback)); + + // Block any file:// access (sandbox only) + if (environmentService.args.__sandbox) { + defaultSession.protocol.interceptFileProtocol(Schemas.file, (request, callback) => this.handleFileRequest(request, callback as unknown as ProtocolCallback)); + } + + // Cleanup + this._register(toDisposable(() => { + defaultSession.protocol.unregisterProtocol(Schemas.vscodeFileResource); + if (environmentService.args.__sandbox) { + defaultSession.protocol.uninterceptProtocol(Schemas.file); + } + })); + } + + injectWindowsMainService(windowsMainService: IWindowsMainService): void { + this._register(windowsMainService.onWindowReady(window => { + if (window.config?.extensionDevelopmentPath || window.config?.extensionTestsPath) { + const disposables = new DisposableStore(); + disposables.add(Event.any(window.onClose, window.onDestroy)(() => disposables.dispose())); + + // Allow access to extension development path + if (window.config.extensionDevelopmentPath) { + for (const extensionDevelopmentPath of window.config.extensionDevelopmentPath) { + disposables.add(this.addValidRoot(URI.file(extensionDevelopmentPath))); + } + } + + // Allow access to extension tests path + if (window.config.extensionTestsPath) { + disposables.add(this.addValidRoot(URI.file(window.config.extensionTestsPath))); + } + } + })); + } + + private addValidRoot(root: URI): IDisposable { + if (!this.validRoots.get(root)) { + this.validRoots.set(root, true); + + return toDisposable(() => this.validRoots.delete(root)); + } + + return Disposable.None; + } + + private async handleFileRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback) { + const uri = URI.parse(request.url); + + this.logService.error(`Refused to load resource ${uri.fsPath} from ${Schemas.file}: protocol`); + callback({ error: -3 /* ABORTED */ }); + } + + private async handleResourceRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback) { + const uri = URI.parse(request.url); + + // Restore the `vscode-file` URI to a `file` URI so that we can + // ensure the root is valid and properly tell Chrome where the + // resource is at. + const fileUri = FileAccess.asFileUri(uri); + if (this.validRoots.findSubstr(fileUri)) { + return callback({ + path: fileUri.fsPath + }); + } + + this.logService.error(`${Schemas.vscodeFileResource}: Refused to load resource ${fileUri.fsPath}}`); + callback({ error: -3 /* ABORTED */ }); + } +} diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index c513c3c6c..dd721db92 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -60,8 +60,9 @@ export class SharedProcess implements ISharedProcess { windowId: this.window.id }; - const windowUrl = FileAccess - .asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require) + const windowUrl = (this.environmentService.sandbox ? + FileAccess._asCodeFileUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require) : + FileAccess.asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require)) .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }); this.window.loadURL(windowUrl.toString(true)); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 79e1658c6..a9f47be81 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as os from 'os'; import * as path from 'vs/base/common/path'; -import * as objects from 'vs/base/common/objects'; import * as nls from 'vs/nls'; +import * as perf from 'vs/base/common/performance'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event, Details } from 'electron'; @@ -23,7 +24,6 @@ import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; -import * as perf from 'vs/base/common/performance'; import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -33,8 +33,10 @@ import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; -import { IFileService } from 'vs/platform/files/common/files'; +import { ByteSize, IFileService } from 'vs/platform/files/common/files'; import { FileAccess, Schemas } from 'vs/base/common/network'; +import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface IWindowCreationOptions { state: IWindowState; @@ -84,7 +86,7 @@ const enum ReadyState { export class CodeWindow extends Disposable implements ICodeWindow { - private static readonly MAX_URL_LENGTH = 2 * 1024 * 1024; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 + private static readonly MAX_URL_LENGTH = 2 * ByteSize.MB; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 private readonly _onLoad = this._register(new Emitter()); readonly onLoad = this._onLoad.event; @@ -110,8 +112,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[]; - private pendingLoadConfig?: INativeWindowConfiguration; - private marketplaceHeadersPromise: Promise; private readonly touchBarGroups: TouchBarSegmentedControl[]; @@ -150,7 +150,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below) const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen); - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); const options: BrowserWindowConstructorOptions = { width: this.windowState.width, @@ -211,7 +211,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs } - const useCustomTitleStyle = getTitleBarStyle(this.configurationService, this.environmentService, !!config.extensionDevelopmentPath) === 'custom'; + const useCustomTitleStyle = getTitleBarStyle(this.configurationService) === 'custom'; if (useCustomTitleStyle) { options.titleBarStyle = 'hidden'; this.hiddenTitleBarStyle = true; @@ -285,6 +285,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.registerListeners(); } + private pendingLoadConfig: INativeWindowConfiguration | undefined; + private currentConfig: INativeWindowConfiguration | undefined; get config(): INativeWindowConfiguration | undefined { return this.currentConfig; } @@ -296,11 +298,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { get hasHiddenTitleBarStyle(): boolean { return !!this.hiddenTitleBarStyle; } - get isExtensionDevelopmentHost(): boolean { return !!(this.config && this.config.extensionDevelopmentPath); } + get isExtensionDevelopmentHost(): boolean { return !!(this.currentConfig?.extensionDevelopmentPath); } - get isExtensionTestHost(): boolean { return !!(this.config && this.config.extensionTestsPath); } + get isExtensionTestHost(): boolean { return !!(this.currentConfig?.extensionTestsPath); } - get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.config?.debugId; } + get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.currentConfig?.debugId; } setRepresentedFilename(filename: string): void { if (isMacintosh) { @@ -468,9 +470,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { return; // disposed } - // Notify renderers about displays changed - this.sendWhenReady('vscode:displayChanged'); - // Simple fullscreen doesn't resize automatically when the resolution changes so as a workaround // we need to detect when display metrics change or displays are added/removed and toggle the // fullscreen manually. @@ -520,11 +519,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Window Fullscreen this._win.on('enter-full-screen', () => { - this.sendWhenReady('vscode:enterFullScreen'); + this.sendWhenReady('vscode:enterFullScreen', CancellationToken.None); }); this._win.on('leave-full-screen', () => { - this.sendWhenReady('vscode:leaveFullScreen'); + this.sendWhenReady('vscode:leaveFullScreen', CancellationToken.None); }); // Window Failed to load @@ -680,6 +679,16 @@ export class CodeWindow extends Disposable implements ICodeWindow { load(config: INativeWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void { + // If this window was loaded before from the command line + // (as indicated by VSCODE_CLI environment), make sure to + // preserve that user environment in subsequent loads, + // unless the new configuration context was also a CLI + // (for https://github.com/microsoft/vscode/issues/108571) + const currentUserEnv = (this.currentConfig ?? this.pendingLoadConfig)?.userEnv; + if (currentUserEnv && isLaunchedFromCli(currentUserEnv) && !isLaunchedFromCli(config.userEnv)) { + config.userEnv = { ...currentUserEnv, ...config.userEnv }; // still allow to override certain environment as passed in + } + // If this is the first time the window is loaded, we associate the paths // directly with the window because we assume the loading will just work if (this._readyState === ReadyState.NONE) { @@ -738,10 +747,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._onLoad.fire(); } - reload(configurationIn?: INativeWindowConfiguration, cli?: NativeParsedArgs): void { + reload(cli?: NativeParsedArgs): void { - // If config is not provided, copy our current one - const configuration = configurationIn ? configurationIn : objects.mixin({}, this.currentConfig); + // Copy our current config for reuse + const configuration = Object.assign({}, this.currentConfig); // Delete some properties we do not want during reload delete configuration.filesToOpenOrCreate; @@ -773,7 +782,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { windowConfiguration.logLevel = this.logService.getLevel(); // Set zoomlevel - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); const zoomLevel = windowConfig?.zoomLevel; if (typeof zoomLevel === 'number') { windowConfiguration.zoomLevel = zoomLevel; @@ -799,6 +808,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Parts splash windowConfiguration.partsSplashPath = path.join(this.environmentService.userDataPath, 'rapid_render.json'); + // OS Info + windowConfiguration.os = { + release: os.release() + }; + // Config (combination of process.argv and window configuration) const environment = parseArgs(process.argv, OPTIONS); const config = Object.assign(environment, windowConfiguration) as unknown as { [key: string]: unknown }; @@ -814,10 +828,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { // large depending on user configuration, so we can only remove it in that case. let configUrl = this.doGetUrl(config); if (configUrl.length > CodeWindow.MAX_URL_LENGTH) { - delete config.userEnv; this.logService.warn('Application URL exceeds maximum of 2MB and was shortened.'); - configUrl = this.doGetUrl(config); + configUrl = this.doGetUrl({ ...config, userEnv: undefined }); if (configUrl.length > CodeWindow.MAX_URL_LENGTH) { this.logService.error('Application URL exceeds maximum of 2MB and cannot be loaded.'); @@ -835,10 +848,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { workbench = 'vs/code/electron-browser/workbench/workbench.html'; } - return FileAccess - .asBrowserUri(workbench, require) - .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }) - .toString(true); + return (this.environmentService.sandbox ? + FileAccess._asCodeFileUri(workbench, require) : + FileAccess.asBrowserUri(workbench, require)) + .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }).toString(true); } serializeWindowState(): IWindowState { @@ -1088,7 +1101,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Events - this.sendWhenReady(fullscreen ? 'vscode:enterFullScreen' : 'vscode:leaveFullScreen'); + this.sendWhenReady(fullscreen ? 'vscode:enterFullScreen' : 'vscode:leaveFullScreen', CancellationToken.None); // Respect configured menu bar visibility or default to toggle if not set if (this.currentMenuBarVisibility) { @@ -1116,7 +1129,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private useNativeFullScreen(): boolean { - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); if (!windowConfig || typeof windowConfig.nativeFullScreen !== 'boolean') { return true; // default } @@ -1133,7 +1146,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private getMenuBarVisibility(): MenuBarVisibility { - let menuBarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService, !!this.config?.extensionDevelopmentPath); + let menuBarVisibility = getMenuBarVisibility(this.configurationService); if (['visible', 'toggle', 'hidden'].indexOf(menuBarVisibility) < 0) { menuBarVisibility = 'default'; } @@ -1229,11 +1242,15 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - sendWhenReady(channel: string, ...args: any[]): void { + sendWhenReady(channel: string, token: CancellationToken, ...args: any[]): void { if (this.isReady) { this.send(channel, ...args); } else { - this.ready().then(() => this.send(channel, ...args)); + this.ready().then(() => { + if (!token.isCancellationRequested) { + this.send(channel, ...args); + } + }); } } @@ -1283,7 +1300,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { mode: 'buttons', segmentStyle: 'automatic', change: (selectedIndex) => { - this.sendWhenReady('vscode:runAction', { id: (control.segments[selectedIndex] as ITouchBarSegment).id, from: 'touchbar' }); + this.sendWhenReady('vscode:runAction', CancellationToken.None, { id: (control.segments[selectedIndex] as ITouchBarSegment).id, from: 'touchbar' }); } }); diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index f48450b25..252260a57 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -150,6 +150,7 @@ export class IssueReporter extends Disposable { applyZoom(configuration.data.zoomLevel); this.applyStyles(configuration.data.styles); this.handleExtensionData(configuration.data.enabledExtensions); + this.updateExperimentsInfo(configuration.data.experiments); } render(): void { @@ -285,7 +286,7 @@ export class IssueReporter extends Disposable { this.render(); }); - (['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeSearchedExtensions', 'includeSettingsSearchDetails'] as const).forEach(elementId => { + (['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeExperiments'] as const).forEach(elementId => { this.addEventListener(elementId, 'click', (event: Event) => { event.stopPropagation(); this.issueReporterModel.update({ [elementId]: !this.issueReporterModel.getData()[elementId] }); @@ -693,8 +694,7 @@ export class IssueReporter extends Disposable { const processBlock = document.querySelector('.block-process'); const workspaceBlock = document.querySelector('.block-workspace'); const extensionsBlock = document.querySelector('.block-extensions'); - const searchedExtensionsBlock = document.querySelector('.block-searchedExtensions'); - const settingsSearchResultsBlock = document.querySelector('.block-settingsSearchResults'); + const experimentsBlock = document.querySelector('.block-experiments'); const problemSource = this.getElementById('problem-source')!; const descriptionTitle = this.getElementById('issue-description-label')!; @@ -707,8 +707,7 @@ export class IssueReporter extends Disposable { hide(processBlock); hide(workspaceBlock); hide(extensionsBlock); - hide(searchedExtensionsBlock); - hide(settingsSearchResultsBlock); + hide(experimentsBlock); hide(problemSource); hide(extensionSelector); @@ -716,6 +715,7 @@ export class IssueReporter extends Disposable { show(blockContainer); show(systemBlock); show(problemSource); + show(experimentsBlock); if (fileOnExtension) { show(extensionSelector); @@ -730,6 +730,7 @@ export class IssueReporter extends Disposable { show(processBlock); show(workspaceBlock); show(problemSource); + show(experimentsBlock); if (fileOnExtension) { show(extensionSelector); @@ -1084,6 +1085,14 @@ export class IssueReporter extends Disposable { } } + private updateExperimentsInfo(experimentInfo: string | undefined) { + this.issueReporterModel.update({ experimentInfo }); + const target = document.querySelector('.block-experiments .block-info'); + if (target) { + target.textContent = experimentInfo ? experimentInfo : localize('noCurrentExperiments', "No current experiments."); + } + } + private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): HTMLTableElement { return $('table', undefined, $('tr', undefined, diff --git a/src/vs/code/electron-sandbox/issue/issueReporterModel.ts b/src/vs/code/electron-sandbox/issue/issueReporterModel.ts index b529e48cd..f850e4d80 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterModel.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterModel.ts @@ -19,8 +19,7 @@ export interface IssueReporterData { includeWorkspaceInfo: boolean; includeProcessInfo: boolean; includeExtensions: boolean; - includeSearchedExtensions: boolean; - includeSettingsSearchDetails: boolean; + includeExperiments: boolean; numberOfThemeExtesions?: number; allExtensions: IssueReporterExtensionData[]; @@ -31,6 +30,7 @@ export interface IssueReporterData { actualSearchResults?: ISettingSearchResult[]; query?: string; filterResultCount?: number; + experimentInfo?: string; } export class IssueReporterModel { @@ -43,8 +43,7 @@ export class IssueReporterModel { includeWorkspaceInfo: true, includeProcessInfo: true, includeExtensions: true, - includeSearchedExtensions: true, - includeSettingsSearchDetails: true, + includeExperiments: true, allExtensions: [] }; @@ -133,6 +132,12 @@ ${this.getInfos()} } } + if (this._data.issueType === IssueType.Bug || this._data.issueType === IssueType.PerformanceIssue) { + if (this._data.includeExperiments && this._data.experimentInfo) { + info += this.generateExperimentsInfoMd(); + } + } + return info; } @@ -150,7 +155,7 @@ ${this.getInfos()} |GPU Status|${Object.keys(this._data.systemInfo.gpuStatus).map(key => `${key}: ${this._data.systemInfo!.gpuStatus[key]}`).join('
')}| |Load (avg)|${this._data.systemInfo.load}| |Memory (System)|${this._data.systemInfo.memory}| -|Process Argv|${this._data.systemInfo.processArgs}| +|Process Argv|${this._data.systemInfo.processArgs.replace(/\\/g, '\\\\')}| |Screen Reader|${this._data.systemInfo.screenReader}| |VM|${this._data.systemInfo.vmHint}|`; @@ -203,6 +208,18 @@ ${this._data.processInfo} ${this._data.workspaceInfo}; \`\`\` + +`; + } + + private generateExperimentsInfoMd(): string { + return `
+A/B Experiments + +\`\`\` +${this._data.experimentInfo} +\`\`\` +
`; } diff --git a/src/vs/code/electron-sandbox/issue/issueReporterPage.ts b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts index d7be46aeb..bf3c1353b 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterPage.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts @@ -111,25 +111,15 @@ export default (): string => ` -
- - - -
- - - +
`; diff --git a/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts b/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts index b09f4fb76..215f66498 100644 --- a/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts +++ b/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts @@ -18,8 +18,7 @@ suite('IssueReporter', () => { includeWorkspaceInfo: true, includeProcessInfo: true, includeExtensions: true, - includeSearchedExtensions: true, - includeSettingsSearchDetails: true, + includeExperiments: true, issueType: 0 }); }); @@ -78,6 +77,58 @@ OS version: undefined |Screen Reader|no| |VM|0%| Extensions: none +`); + }); + + test('serializes experiment info when data is provided', () => { + const issueReporterModel = new IssueReporterModel({ + issueType: 0, + systemInfo: { + os: 'Darwin', + cpus: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)', + memory: '16.00GB', + vmHint: '0%', + processArgs: '', + screenReader: 'no', + remoteData: [], + gpuStatus: { + '2d_canvas': 'enabled', + 'checker_imaging': 'disabled_off' + } + }, + experimentInfo: 'vsliv695:30137379\nvsins829:30139715' + }); + assert.equal(issueReporterModel.serialize(), + ` +Issue Type: Bug + +undefined + +VS Code version: undefined +OS version: undefined + +
+System Info + +|Item|Value| +|---|---| +|CPUs|Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)| +|GPU Status|2d_canvas: enabled
checker_imaging: disabled_off| +|Load (avg)|undefined| +|Memory (System)|16.00GB| +|Process Argv|| +|Screen Reader|no| +|VM|0%| +
Extensions: none
+A/B Experiments + +\`\`\` +vsliv695:30137379 +vsins829:30139715 +\`\`\` + +
+ `); }); @@ -191,6 +242,45 @@ Remote OS version: Linux x64 4.18.0 `); }); + test('escapes backslashes in processArgs', () => { + const issueReporterModel = new IssueReporterModel({ + issueType: 0, + systemInfo: { + os: 'Darwin', + cpus: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)', + memory: '16.00GB', + vmHint: '0%', + processArgs: '\\\\HOST\\path', + screenReader: 'no', + remoteData: [], + gpuStatus: {} + } + }); + assert.equal(issueReporterModel.serialize(), + ` +Issue Type: Bug + +undefined + +VS Code version: undefined +OS version: undefined + +
+System Info + +|Item|Value| +|---|---| +|CPUs|Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)| +|GPU Status|| +|Load (avg)|undefined| +|Memory (System)|16.00GB| +|Process Argv|\\\\\\\\HOST\\\\path| +|Screen Reader|no| +|VM|0%| +
Extensions: none +`); + }); + test('should normalize GitHub urls', () => { [ 'https://github.com/repo', diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts index 947a6e9f3..ef93a1c52 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -19,6 +19,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; +import { ByteSize } from 'vs/platform/files/common/files'; const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/; const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/; @@ -67,18 +68,19 @@ class ProcessExplorer { private getProcessList(rootProcess: ProcessItem, isLocal: boolean, totalMem: number): FormattedProcessItem[] { const processes: FormattedProcessItem[] = []; + const handledProcesses = new Set(); if (rootProcess) { - this.getProcessItem(processes, rootProcess, 0, isLocal, totalMem); + this.getProcessItem(processes, rootProcess, 0, isLocal, totalMem, handledProcesses); } return processes; } - private getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean, totalMem: number): void { + private getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean, totalMem: number, handledProcesses: Set): void { const isRoot = (indent === 0); - const MB = 1024 * 1024; + handledProcesses.add(item.pid); let name = item.name; if (isRoot) { @@ -95,7 +97,7 @@ class ProcessExplorer { const memory = this.data.platform === 'win32' ? item.mem : (totalMem * (item.mem / 100)); processes.push({ cpu: item.load, - memory: (memory / MB), + memory: (memory / ByteSize.MB), pid: item.pid.toFixed(0), name, formattedName, @@ -105,9 +107,11 @@ class ProcessExplorer { // Recurse into children if any if (Array.isArray(item.children)) { item.children.forEach(child => { - if (child) { - this.getProcessItem(processes, child, indent + 1, isLocal, totalMem); + if (!child || handledProcesses.has(child.pid)) { + return; // prevent loops } + + this.getProcessItem(processes, child, indent + 1, isLocal, totalMem, handledProcesses); }); } } diff --git a/src/vs/code/electron-sandbox/workbench/workbench.js b/src/vs/code/electron-sandbox/workbench/workbench.js index 3966a9d0e..6a46ef2b0 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.js +++ b/src/vs/code/electron-sandbox/workbench/workbench.js @@ -9,15 +9,12 @@ 'use strict'; (function () { + const bootstrapWindow = bootstrapWindowLib(); // Add a perf entry right from the top - const perf = perfLib(); + const perf = bootstrapWindow.perfLib(); perf.mark('renderer/started'); - // Load environment in parallel to workbench loading to avoid waterfall - const bootstrapWindow = bootstrapWindowLib(); - const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved(); - // Load workbench main JS, CSS and NLS all in parallel. This is an // optimization to prevent a waterfall of loading to happen, because // we know for a fact that workbench.desktop.sandbox.main will depend on @@ -31,12 +28,6 @@ // Mark start of workbench perf.mark('didLoadWorkbenchMain'); - performance.mark('workbench-start'); - - // Wait for process environment being fully resolved - await whenEnvResolved; - - perf.mark('main/startup'); // @ts-ignore return require('vs/workbench/electron-sandbox/desktop.main').main(configuration); @@ -58,23 +49,11 @@ //region Helpers - function perfLib() { - globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; - - return { - /** - * @param {string} name - */ - mark(name) { - globalThis.MonacoPerformanceMarks.push(name, Date.now()); - } - }; - } - /** * @returns {{ * load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown, - * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals') + * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals'), + * perfLib: () => { mark: (name: string) => void } * }} */ function bootstrapWindowLib() { diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 040144f4c..c6415a551 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -123,10 +123,6 @@ export async function main(argv: string[]): Promise { 'ELECTRON_NO_ATTACH_CONSOLE': '1' }; - if (args['force-user-env']) { - env['VSCODE_FORCE_USER_ENV'] = '1'; - } - delete env['ELECTRON_RUN_AS_NODE']; const processCallbacks: ((child: ChildProcess) => Promise)[] = []; @@ -157,41 +153,39 @@ export async function main(argv: string[]): Promise { // stdin. We do this because there is no reliable way to find out if data is piped to stdin. Just // checking for stdin being connected to a TTY is not enough (https://github.com/microsoft/vscode/issues/40351) - if (args._.length === 0) { - if (hasReadStdinArg) { - stdinFilePath = getStdinFilePath(); + if (hasReadStdinArg) { + stdinFilePath = getStdinFilePath(); - // returns a file path where stdin input is written into (write in progress). - try { - readFromStdin(stdinFilePath, !!verbose); // throws error if file can not be written + // returns a file path where stdin input is written into (write in progress). + try { + readFromStdin(stdinFilePath, !!verbose); // throws error if file can not be written - // Make sure to open tmp file - addArg(argv, stdinFilePath); + // Make sure to open tmp file + addArg(argv, stdinFilePath); - // Enable --wait to get all data and ignore adding this to history - addArg(argv, '--wait'); - addArg(argv, '--skip-add-to-recently-opened'); - args.wait = true; + // Enable --wait to get all data and ignore adding this to history + addArg(argv, '--wait'); + addArg(argv, '--skip-add-to-recently-opened'); + args.wait = true; - console.log(`Reading from stdin via: ${stdinFilePath}`); - } catch (e) { - console.log(`Failed to create file to read via stdin: ${e.toString()}`); - stdinFilePath = undefined; - } - } else { - - // If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message - // if we detect that data flows into via stdin after a certain timeout. - processCallbacks.push(_ => stdinDataListener(1000).then(dataReceived => { - if (dataReceived) { - if (isWindows) { - console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`); - } else { - console.log(`Run with '${product.applicationName} -' to read from stdin (e.g. 'ps aux | grep code | ${product.applicationName} -').`); - } - } - })); + console.log(`Reading from stdin via: ${stdinFilePath}`); + } catch (e) { + console.log(`Failed to create file to read via stdin: ${e.toString()}`); + stdinFilePath = undefined; } + } else { + + // If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message + // if we detect that data flows into via stdin after a certain timeout. + processCallbacks.push(_ => stdinDataListener(1000).then(dataReceived => { + if (dataReceived) { + if (isWindows) { + console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`); + } else { + console.log(`Run with '${product.applicationName} -' to read from stdin (e.g. 'ps aux | grep code | ${product.applicationName} -').`); + } + } + })); } } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 426cde72b..84fa06d76 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -31,7 +31,7 @@ import { mkdirp, writeFile } from 'vs/base/node/pfs'; import { getBaseLabel } from 'vs/base/common/labels'; import { IStateService } from 'vs/platform/state/node/state'; import { StateService } from 'vs/platform/state/node/stateService'; -import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; +import { ILogService, getLogLevel, LogLevel, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { URI } from 'vs/base/common/uri'; @@ -78,7 +78,7 @@ export class Main { @IInstantiationService private readonly instantiationService: IInstantiationService, @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, ) { } async run(argv: NativeParsedArgs): Promise { @@ -93,7 +93,7 @@ export class Main { } else if (argv['locate-extension']) { await this.locateExtension(argv['locate-extension']); } else if (argv['telemetry']) { - console.log(buildTelemetryMessage(this.environmentService.appRoot, this.environmentService.extensionsPath ? this.environmentService.extensionsPath : undefined)); + console.log(buildTelemetryMessage(this.environmentService.appRoot, this.environmentService.extensionsPath)); } } @@ -126,13 +126,28 @@ export class Main { extensions.forEach(e => console.log(getId(e.manifest, showVersions))); } - private async installExtensions(extensions: string[], builtinExtensionIds: string[], isMachineScoped: boolean, force: boolean): Promise { + async installExtensions(extensions: string[], builtinExtensionIds: string[], isMachineScoped: boolean, force: boolean): Promise { const failed: string[] = []; const installedExtensionsManifests: IExtensionManifest[] = []; if (extensions.length) { console.log(localize('installingExtensions', "Installing extensions...")); } + const installed = await this.extensionManagementService.getInstalled(ExtensionType.User); + const checkIfNotInstalled = (id: string, version?: string): boolean => { + const installedExtension = installed.find(i => areSameExtensions(i.identifier, { id })); + if (installedExtension) { + if (!version && !force) { + console.log(localize('alreadyInstalled-checkAndUpdate', "Extension '{0}' v{1} is already installed. Use '--force' option to update to latest version or provide '@' to install a specific version, for example: '{2}@1.2.3'.", id, installedExtension.manifest.version, id)); + return false; + } + if (version && installedExtension.manifest.version === version) { + console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", `${id}@${version}`)); + return false; + } + } + return true; + }; const vsixs: string[] = []; const installExtensionInfos: InstallExtensionInfo[] = []; for (const extension of extensions) { @@ -140,12 +155,16 @@ export class Main { vsixs.push(extension); } else { const [id, version] = getIdAndVersion(extension); - installExtensionInfos.push({ id, version, installOptions: { isBuiltin: false, isMachineScoped } }); + if (checkIfNotInstalled(id, version)) { + installExtensionInfos.push({ id, version, installOptions: { isBuiltin: false, isMachineScoped } }); + } } } for (const extension of builtinExtensionIds) { const [id, version] = getIdAndVersion(extension); - installExtensionInfos.push({ id, version, installOptions: { isBuiltin: true, isMachineScoped: false } }); + if (checkIfNotInstalled(id, version)) { + installExtensionInfos.push({ id, version, installOptions: { isBuiltin: true, isMachineScoped: false } }); + } } if (vsixs.length) { @@ -162,28 +181,29 @@ export class Main { })); } - const [galleryExtensions, installed] = await Promise.all([ - this.getGalleryExtensions(installExtensionInfos), - this.extensionManagementService.getInstalled(ExtensionType.User) - ]); + if (installExtensionInfos.length) { - await Promise.all(installExtensionInfos.map(async extensionInfo => { - const gallery = galleryExtensions.get(extensionInfo.id.toLowerCase()); - if (gallery) { - try { - const manifest = await this.installFromGallery(extensionInfo, gallery, installed, force); - if (manifest) { - installedExtensionsManifests.push(manifest); + const galleryExtensions = await this.getGalleryExtensions(installExtensionInfos); + + await Promise.all(installExtensionInfos.map(async extensionInfo => { + const gallery = galleryExtensions.get(extensionInfo.id.toLowerCase()); + if (gallery) { + try { + const manifest = await this.installFromGallery(extensionInfo, gallery, installed, force); + if (manifest) { + installedExtensionsManifests.push(manifest); + } + } catch (err) { + console.error(err.message || err.stack || err); + failed.push(extensionInfo.id); } - } catch (err) { - console.error(err.message || err.stack || err); + } else { + console.error(`${notFound(extensionInfo.version ? `${extensionInfo.id}@${extensionInfo.version}` : extensionInfo.id)}\n${useId}`); failed.push(extensionInfo.id); } - } else { - console.error(`${notFound(extensionInfo.version ? `${extensionInfo.id}@${extensionInfo.version}` : extensionInfo.id)}\n${useId}`); - failed.push(extensionInfo.id); - } - })); + })); + + } if (installedExtensionsManifests.some(manifest => isLanguagePackExtension(manifest))) { await this.updateLocalizationsCache(); @@ -244,10 +264,6 @@ export class Main { console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", version ? `${id}@${version}` : id)); return null; } - if (!version && !force) { - console.log(localize('forceUpdate', "Extension '{0}' v{1} is already installed, but a newer version {2} is available in the marketplace. Use '--force' option to update to newer version.", id, installedExtension.manifest.version, galleryExtension.version)); - return null; - } console.log(localize('updateMessage', "Updating the extension '{0}' to the version {1}", id, galleryExtension.version)); } @@ -315,7 +331,7 @@ export class Main { return; } console.log(localize('uninstalling', "Uninstalling {0}...", id)); - await this.extensionManagementService.uninstall(extensionToUninstall, true); + await this.extensionManagementService.uninstall(extensionToUninstall); uninstalledExtensions.push(extensionToUninstall); console.log(localize('successUninstall', "Extension '{0}' was successfully uninstalled!", id)); } @@ -353,7 +369,13 @@ export async function main(argv: NativeParsedArgs): Promise { const disposables = new DisposableStore(); const environmentService = new NativeEnvironmentService(argv); - const logService: ILogService = new SpdLogService('cli', environmentService.logsPath, getLogLevel(environmentService)); + const logLevel = getLogLevel(environmentService); + const loggers: ILogService[] = []; + loggers.push(new SpdLogService('cli', environmentService.logsPath, logLevel)); + if (logLevel === LogLevel.Trace) { + loggers.push(new ConsoleLogService(logLevel)); + } + const logService = new MultiplexLogService(loggers); process.once('exit', () => logService.dispose()); logService.info('main', argv); @@ -403,7 +425,7 @@ export async function main(argv: NativeParsedArgs): Promise { appender: combinedAppender(...appenders), sendErrorTelemetry: false, commonProperties: resolveCommonProperties(product.commit, product.version, stateService.getItem('telemetry.machineId'), product.msftInternalDomains, installSourcePath), - piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot] + piiPaths: [appRoot, extensionsPath] }; services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); diff --git a/src/vs/code/node/shellEnv.ts b/src/vs/code/node/shellEnv.ts index 619886d0f..29ef8c143 100644 --- a/src/vs/code/node/shellEnv.ts +++ b/src/vs/code/node/shellEnv.ts @@ -7,9 +7,57 @@ import { spawn } from 'child_process'; import { generateUuid } from 'vs/base/common/uuid'; import { isWindows } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; -import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; -function getUnixShellEnvironment(logService: ILogService): Promise { +/** + * We need to get the environment from a user's shell. + * This should only be done when Code itself is not launched + * from within a shell. + */ +export async function resolveShellEnv(logService: ILogService, args: NativeParsedArgs, env: NodeJS.ProcessEnv): Promise { + + // Skip if --force-disable-user-env + if (args['force-disable-user-env']) { + logService.trace('resolveShellEnv(): skipped (--force-disable-user-env)'); + + return {}; + } + + // Skip on windows + else if (isWindows) { + logService.trace('resolveShellEnv(): skipped (Windows)'); + + return {}; + } + + // Skip if running from CLI already + else if (isLaunchedFromCli(env) && !args['force-user-env']) { + logService.trace('resolveShellEnv(): skipped (VSCODE_CLI is set)'); + + return {}; + } + + // Otherwise resolve (macOS, Linux) + else { + if (isLaunchedFromCli(env)) { + logService.trace('resolveShellEnv(): running (--force-user-env)'); + } else { + logService.trace('resolveShellEnv(): running (macOS/Linux)'); + } + + if (!unixShellEnvPromise) { + unixShellEnvPromise = doResolveUnixShellEnv(logService); + } + + return unixShellEnvPromise; + } +} + +let unixShellEnvPromise: Promise | undefined = undefined; + +async function doResolveUnixShellEnv(logService: ILogService): Promise { const promise = new Promise((resolve, reject) => { const runAsNode = process.env['ELECTRON_RUN_AS_NODE']; logService.trace('getUnixShellEnvironment#runAsNode', runAsNode); @@ -78,33 +126,11 @@ function getUnixShellEnvironment(logService: ILogService): Promise ({})); -} + try { + return await promise; + } catch (error) { + logService.error('getUnixShellEnvironment#error', toErrorMessage(error)); -let shellEnvPromise: Promise | undefined = undefined; - -/** - * We need to get the environment from a user's shell. - * This should only be done when Code itself is not launched - * from within a shell. - */ -export function getShellEnvironment(logService: ILogService, environmentService: INativeEnvironmentService): Promise { - if (!shellEnvPromise) { - if (environmentService.args['disable-user-env-probe']) { - logService.trace('getShellEnvironment: disable-user-env-probe set, skipping'); - shellEnvPromise = Promise.resolve({}); - } else if (isWindows) { - logService.trace('getShellEnvironment: running on Windows, skipping'); - shellEnvPromise = Promise.resolve({}); - } else if (process.env['VSCODE_CLI'] === '1' && process.env['VSCODE_FORCE_USER_ENV'] !== '1') { - logService.trace('getShellEnvironment: running on CLI, skipping'); - shellEnvPromise = Promise.resolve({}); - } else { - logService.trace('getShellEnvironment: running on Unix'); - shellEnvPromise = getUnixShellEnvironment(logService); - } + return {}; // ignore any errors } - - return shellEnvPromise; } diff --git a/src/vs/code/test/electron-main/nativeHelpers.test.ts b/src/vs/code/test/electron-main/nativeHelpers.test.ts deleted file mode 100644 index 1ce464480..000000000 --- a/src/vs/code/test/electron-main/nativeHelpers.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { isWindows } from 'vs/base/common/platform'; - -suite('Windows Native Helpers', () => { - if (!isWindows) { - return; - } - - test('windows-mutex', async () => { - const mutex = await import('windows-mutex'); - assert.ok(mutex && typeof mutex.isActive === 'function', 'Unable to load windows-mutex dependency.'); - assert.ok(typeof mutex.isActive === 'function', 'Unable to load windows-mutex dependency.'); - }); - - test('windows-foreground-love', async () => { - const foregroundLove = await import('windows-foreground-love'); - assert.ok(foregroundLove && typeof foregroundLove.allowSetForegroundWindow === 'function', 'Unable to load windows-foreground-love dependency.'); - }); - - test('windows-process-tree', async () => { - const processTree = await import('windows-process-tree'); - assert.ok(processTree && typeof processTree.getProcessTree === 'function', 'Unable to load windows-process-tree dependency.'); - }); - - test('vscode-windows-ca-certs', async () => { - // @ts-ignore Windows only - const windowsCerts = await import('vscode-windows-ca-certs'); - assert.ok(windowsCerts, 'Unable to load vscode-windows-ca-certs dependency.'); - }); - - test('vscode-windows-registry', async () => { - const windowsRegistry = await import('vscode-windows-registry'); - assert.ok(windowsRegistry && typeof windowsRegistry.GetStringRegKey === 'function', 'Unable to load vscode-windows-registry dependency.'); - }); -}); diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 1e7b0929d..06eecaa6a 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -355,13 +355,18 @@ class MouseDownOperation extends Disposable { e.buttons, createMouseMoveEventMerger(null), (e) => this._onMouseDownThenMove(e), - () => { + (browserEvent?: MouseEvent | KeyboardEvent) => { const position = this._findMousePosition(this._lastMouseEvent!, true); - this._viewController.emitMouseDrop({ - event: this._lastMouseEvent!, - target: (position ? this._createMouseTarget(this._lastMouseEvent!, true) : null) // Ignoring because position is unknown, e.g., Content View Zone - }); + if (browserEvent && browserEvent instanceof KeyboardEvent) { + // cancel + this._viewController.emitMouseDropCanceled(); + } else { + this._viewController.emitMouseDrop({ + event: this._lastMouseEvent!, + target: (position ? this._createMouseTarget(this._lastMouseEvent!, true) : null) // Ignoring because position is unknown, e.g., Content View Zone + }); + } this._stop(); } diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index f2be04e3a..37c1d8b05 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -18,6 +18,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; import * as dom from 'vs/base/browser/dom'; +import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations'; export interface IViewZoneData { viewZoneId: string; @@ -239,6 +240,7 @@ export class HitTestContext { public readonly layoutInfo: EditorLayoutInfo; public readonly viewDomNode: HTMLElement; public readonly lineHeight: number; + public readonly stickyTabStops: boolean; public readonly typicalHalfwidthCharacterWidth: number; public readonly lastRenderData: PointerHandlerLastRenderData; @@ -251,6 +253,7 @@ export class HitTestContext { this.layoutInfo = options.get(EditorOption.layoutInfo); this.viewDomNode = viewHelper.viewDomNode; this.lineHeight = options.get(EditorOption.lineHeight); + this.stickyTabStops = options.get(EditorOption.stickyTabStops); this.typicalHalfwidthCharacterWidth = options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth; this.lastRenderData = lastRenderData; this._context = context; @@ -329,6 +332,14 @@ export class HitTestContext { return this._context.viewLayout.isAfterLines(mouseVerticalOffset); } + public isInTopPadding(mouseVerticalOffset: number): boolean { + return this._context.viewLayout.isInTopPadding(mouseVerticalOffset); + } + + public isInBottomPadding(mouseVerticalOffset: number): boolean { + return this._context.viewLayout.isInBottomPadding(mouseVerticalOffset); + } + public getVerticalOffsetForLineNumber(lineNumber: number): number { return this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber); } @@ -656,8 +667,12 @@ export class MouseTargetFactory { return null; } + if (ctx.isInTopPadding(request.mouseVerticalOffset)) { + return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(1, 1), undefined, EMPTY_CONTENT_AFTER_LINES); + } + // Check if it is below any lines and any view zones - if (ctx.isAfterLines(request.mouseVerticalOffset)) { + if (ctx.isAfterLines(request.mouseVerticalOffset) || ctx.isInBottomPadding(request.mouseVerticalOffset)) { // This most likely indicates it happened after the last view-line const lineCount = ctx.model.getLineCount(); const maxLineColumn = ctx.model.getLineMaxColumn(lineCount); @@ -998,6 +1013,17 @@ export class MouseTargetFactory { }; } + private static _snapToSoftTabBoundary(position: Position, viewModel: IViewModel): Position { + const minColumn = viewModel.getLineMinColumn(position.lineNumber); + const lineContent = viewModel.getLineContent(position.lineNumber); + const { tabSize } = viewModel.getTextModelOptions(); + const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, position.column - minColumn, tabSize, Direction.Nearest); + if (newPosition !== -1) { + return new Position(position.lineNumber, newPosition + minColumn); + } + return position; + } + private static _doHitTest(ctx: HitTestContext, request: BareHitTestRequest): IHitTestResult { // State of the art (18.10.2012): // The spec says browsers should support document.caretPositionFromPoint, but nobody implemented it (http://dev.w3.org/csswg/cssom-view/) @@ -1016,24 +1042,24 @@ export class MouseTargetFactory { // Thank you browsers for making this so 'easy' :) + let result: IHitTestResult; if (typeof document.caretRangeFromPoint === 'function') { - - return this._doHitTestWithCaretRangeFromPoint(ctx, request); - + result = this._doHitTestWithCaretRangeFromPoint(ctx, request); } else if ((document).caretPositionFromPoint) { - - return this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates()); - + result = this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates()); } else if ((document.body).createTextRange) { - - return this._doHitTestWithMoveToPoint(ctx, request.pos.toClientCoordinates()); - + result = this._doHitTestWithMoveToPoint(ctx, request.pos.toClientCoordinates()); + } else { + result = { + position: null, + hitTarget: null + }; } - - return { - position: null, - hitTarget: null - }; + // Snap to the nearest soft tab boundary if atomic soft tabs are enabled. + if (result.position && ctx.stickyTabStops) { + result.position = this._snapToSoftTabBoundary(result.position, ctx.model); + } + return result; } } @@ -1047,7 +1073,7 @@ export function shadowCaretRangeFromPoint(shadowRoot: ShadowRoot, x: number, y: // Get the last child of the element until its firstChild is a text node // This assumes that the pointer is on the right of the line, out of the tokens // and that we want to get the offset of the last token of the line - while (el && el.firstChild && el.firstChild.nodeType !== el.firstChild.TEXT_NODE) { + while (el && el.firstChild && el.firstChild.nodeType !== el.firstChild.TEXT_NODE && el.lastChild && el.lastChild.firstChild) { el = el.lastChild; } diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 83fd67d6f..b86fd84fd 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -182,7 +182,7 @@ export class PointerEventHandler extends MouseHandler { } public _onMouseDown(e: EditorMouseEvent): void { - if (e.target && this.viewHelper.linesContentDomNode.contains(e.target) && this._lastPointerType === 'touch') { + if ((e.browserEvent as any).pointerType === 'touch') { return; } diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 07eadee84..2f26618aa 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -276,6 +276,7 @@ export class TextAreaHandler extends ViewPart { this.textArea.setClassName(`inputarea ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME} ime-input`); this._viewController.compositionStart(); + this._context.model.onCompositionStart(); })); this._register(this._textAreaInput.onCompositionUpdate((e: ICompositionData) => { @@ -297,6 +298,7 @@ export class TextAreaHandler extends ViewPart { this.textArea.setClassName(`inputarea ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`); this._viewController.compositionEnd(); + this._context.model.onCompositionEnd(); })); this._register(this._textAreaInput.onFocus(() => { diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index e2d42baf4..bc1e26c1c 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -295,9 +295,12 @@ export class TextAreaInput extends Disposable { this._onType.fire(typeInput); } - // Due to isEdgeOrIE (where the textarea was not cleared initially) and isChrome (the textarea is not updated correctly when composition ends) + // Due to + // isEdgeOrIE (where the textarea was not cleared initially) + // and isChrome (the textarea is not updated correctly when composition ends) + // and isFirefox (the textare ais not updated correctly after inserting emojis) // we cannot assume the text at the end consists only of the composited text - if (browser.isEdge || browser.isChrome) { + if (browser.isEdge || browser.isChrome || browser.isFirefox) { this._textAreaState = TextAreaState.readFromTextArea(this._textArea); } diff --git a/src/vs/editor/browser/core/markdownRenderer.ts b/src/vs/editor/browser/core/markdownRenderer.ts index 375518d2c..e1eb76c8a 100644 --- a/src/vs/editor/browser/core/markdownRenderer.ts +++ b/src/vs/editor/browser/core/markdownRenderer.ts @@ -38,8 +38,8 @@ export class MarkdownRenderer { } }); - private readonly _onDidRenderCodeBlock = new Emitter(); - readonly onDidRenderCodeBlock = this._onDidRenderCodeBlock.event; + private readonly _onDidRenderAsync = new Emitter(); + readonly onDidRenderAsync = this._onDidRenderAsync.event; constructor( private readonly _options: IMarkdownRendererOptions, @@ -48,7 +48,7 @@ export class MarkdownRenderer { ) { } dispose(): void { - this._onDidRenderCodeBlock.dispose(); + this._onDidRenderAsync.dispose(); } render(markdown: IMarkdownString | undefined, options?: MarkdownRenderOptions, markedOptions?: MarkedOptions): IMarkdownRenderResult { @@ -103,7 +103,7 @@ export class MarkdownRenderer { return element; }, - codeBlockRenderCallback: () => this._onDidRenderCodeBlock.fire(), + asyncRenderCallback: () => this._onDidRenderAsync.fire(), actionHandler: { callback: (content) => this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError), disposeables diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 815fdf6b5..43305c283 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -494,6 +494,12 @@ export interface ICodeEditor extends editorCommon.IEditor { * @event */ onMouseDrop(listener: (e: IPartialEditorMouseEvent) => void): IDisposable; + /** + * An event emitted on a "mousedropcanceled". + * @internal + * @event + */ + onMouseDropCanceled(listener: () => void): IDisposable; /** * An event emitted on a "contextmenu". * @event @@ -678,10 +684,15 @@ export interface ICodeEditor extends editorCommon.IEditor { executeCommand(source: string | null | undefined, command: editorCommon.ICommand): void; /** - * Push an "undo stop" in the undo-redo stack. + * Create an "undo stop" in the undo-redo stack. */ pushUndoStop(): boolean; + /** + * Remove the "undo stop" in the undo-redo stack. + */ + popUndoStop(): boolean; + /** * Execute edits on the editor. * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 4042ae826..3d9ffe64f 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -187,7 +187,7 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { initialButtons: number, merger: EditorMouseEventMerger, mouseMoveCallback: (e: EditorMouseEvent) => void, - onStopCallback: () => void + onStopCallback: (browserEvent?: MouseEvent | KeyboardEvent) => void ): void { // Add a <> keydown event listener that will cancel the monitoring @@ -198,16 +198,16 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { // Allow modifier keys return; } - this._globalMouseMoveMonitor.stopMonitoring(true); + this._globalMouseMoveMonitor.stopMonitoring(true, e.browserEvent); }, true); const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode)); }; - this._globalMouseMoveMonitor.startMonitoring(initialElement, initialButtons, myMerger, mouseMoveCallback, () => { + this._globalMouseMoveMonitor.startMonitoring(initialElement, initialButtons, myMerger, mouseMoveCallback, (e) => { this._keydownListener!.dispose(); - onStopCallback(); + onStopCallback(e); }); } } diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index aaf9feaf3..893c2159a 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -4,8 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IPosition } from 'vs/base/browser/ui/contextview/contextview'; -import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -409,47 +407,6 @@ export abstract class EditorAction2 extends Action2 { // --- Registration of commands and actions -export function registerLanguageCommand(id: string, handler: (accessor: ServicesAccessor, args: Args) => any) { - CommandsRegistry.registerCommand(id, (accessor, args) => handler(accessor, args || {})); -} - -interface IDefaultArgs { - resource: URI; - position: IPosition; - [name: string]: any; -} - -export function registerDefaultLanguageCommand(id: string, handler: (model: ITextModel, position: Position, args: IDefaultArgs) => any) { - registerLanguageCommand(id, function (accessor, args: IDefaultArgs) { - - const { resource, position } = args; - if (!(resource instanceof URI)) { - throw illegalArgument('resource'); - } - if (!Position.isIPosition(position)) { - throw illegalArgument('position'); - } - - const model = accessor.get(IModelService).getModel(resource); - if (model) { - const editorPosition = Position.lift(position); - return handler(model, editorPosition, args); - } - - return accessor.get(ITextModelService).createModelReference(resource).then(reference => { - return new Promise((resolve, reject) => { - try { - const result = handler(reference.object.textEditorModel, Position.lift(position), args); - resolve(result); - } catch (err) { - reject(err); - } - }).finally(() => { - reference.dispose(); - }); - }); - }); -} export function registerModelAndPositionCommand(id: string, handler: (model: ITextModel, position: Position, ...args: any[]) => any) { CommandsRegistry.registerCommand(id, function (accessor, ...args) { diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index f48a9cff8..3ebb6454b 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -31,6 +31,8 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC private readonly _onDidChangeTransientModelProperty: Emitter = this._register(new Emitter()); public readonly onDidChangeTransientModelProperty: Event = this._onDidChangeTransientModelProperty.event; + protected readonly _onDecorationTypeRegistered: Emitter = this._register(new Emitter()); + public onDecorationTypeRegistered: Event = this._onDecorationTypeRegistered.event; private readonly _codeEditors: { [editorId: string]: ICodeEditor; }; private readonly _diffEditors: { [editorId: string]: IDiffEditor; }; @@ -93,6 +95,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void; abstract removeDecorationType(key: string): void; abstract resolveDecorationOptions(decorationTypeKey: string | undefined, writable: boolean): IModelDecorationOptions; + abstract resolveDecorationCSSRules(decorationTypeKey: string): CSSRuleList | null; private readonly _transientWatchers: { [uri: string]: ModelTransientSettingWatcher; } = {}; private readonly _modelProperties = new Map>(); diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 7a1f3346e..a9e8b99dc 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -10,6 +10,8 @@ import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { isObject } from 'vs/base/common/types'; +import { UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const IBulkEditService = createDecorator('IWorkspaceEditService'); @@ -65,9 +67,13 @@ export class ResourceFileEdit extends ResourceEdit { export interface IBulkEditOptions { editor?: ICodeEditor; progress?: IProgress; + token?: CancellationToken; showPreview?: boolean; + suppressPreview?: boolean; label?: string; quotableLabel?: string; + undoRedoSource?: UndoRedoSource; + undoRedoGroupId?: number; } export interface IBulkEditResult { diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index 0174886d2..8665de002 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -23,6 +23,7 @@ export interface ICodeEditorService { readonly onDiffEditorRemove: Event; readonly onDidChangeTransientModelProperty: Event; + readonly onDecorationTypeRegistered: Event; addCodeEditor(editor: ICodeEditor): void; @@ -41,6 +42,7 @@ export interface ICodeEditorService { registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void; removeDecorationType(key: string): void; resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions; + resolveDecorationCSSRules(decorationTypeKey: string): CSSRuleList | null; setModelProperty(resource: URI, key: string, value: any): void; getModelProperty(resource: URI, key: string): any; diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 464c1a33d..c961bfd79 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -21,6 +21,10 @@ export class RefCountedStyleSheet { private readonly _styleSheet: HTMLStyleElement; private _refCount: number; + public get sheet() { + return this._styleSheet.sheet as CSSStyleSheet; + } + constructor(parent: CodeEditorServiceImpl, editorId: string, styleSheet: HTMLStyleElement) { this._parent = parent; this._editorId = editorId; @@ -53,6 +57,10 @@ export class RefCountedStyleSheet { export class GlobalStyleSheet { private readonly _styleSheet: HTMLStyleElement; + public get sheet() { + return this._styleSheet.sheet as CSSStyleSheet; + } + constructor(styleSheet: HTMLStyleElement) { this._styleSheet = styleSheet; } @@ -129,6 +137,7 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { provider = new DecorationSubTypeOptionsProvider(this._themeService, styleSheet, providerArgs); } this._decorationOptionProviders.set(key, provider); + this._onDecorationTypeRegistered.fire(key); } provider.refCount++; } @@ -153,6 +162,14 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { return provider.getOptions(this, writable); } + public resolveDecorationCSSRules(decorationTypeKey: string) { + const provider = this._decorationOptionProviders.get(decorationTypeKey); + if (!provider) { + return null; + } + return provider.resolveDecorationCSSRules(); + } + abstract getActiveCodeEditor(): ICodeEditor | null; abstract openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; } @@ -160,9 +177,10 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { interface IModelDecorationOptionsProvider extends IDisposable { refCount: number; getOptions(codeEditorService: AbstractCodeEditorService, writable: boolean): IModelDecorationOptions; + resolveDecorationCSSRules(): CSSRuleList; } -class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvider { +export class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvider { private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet; public refCount: number; @@ -192,6 +210,10 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide return options; } + public resolveDecorationCSSRules(): CSSRuleList { + return this._styleSheet.sheet.cssRules; + } + public dispose(): void { if (this._beforeContentRules) { this._beforeContentRules.dispose(); @@ -213,7 +235,7 @@ interface ProviderArguments { } -class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { +export class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { private readonly _disposables = new DisposableStore(); private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet; @@ -295,6 +317,10 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { }; } + public resolveDecorationCSSRules(): CSSRuleList { + return this._styleSheet.sheet.rules; + } + public dispose(): void { this._disposables.dispose(); this._styleSheet.unref(); diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index d898abd30..1360871f7 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -14,6 +14,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { ICommandService } from 'vs/platform/commands/common/commands'; import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener, matchesScheme } from 'vs/platform/opener/common/opener'; import { EditorOpenContext } from 'vs/platform/editor/common/editor'; +import { ResourceMap } from 'vs/base/common/map'; class CommandOpener implements IOpener { @@ -74,7 +75,14 @@ class EditorOpener implements IOpener { } await this._editorService.openCodeEditor( - { resource: target, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } }, + { + resource: target, + options: { + selection, + context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API, + ...options?.editorOptions + } + }, this._editorService.getFocusedCodeEditor(), options?.openToSide ); @@ -90,6 +98,7 @@ export class OpenerService implements IOpenerService { private readonly _openers = new LinkedList(); private readonly _validators = new LinkedList(); private readonly _resolvers = new LinkedList(); + private readonly _resolvedUriTargets = new ResourceMap(uri => uri.with({ path: null, fragment: null, query: null }).toString()); private _externalOpener: IExternalOpener; @@ -148,16 +157,18 @@ export class OpenerService implements IOpenerService { } async open(target: URI | string, options?: OpenOptions): Promise { - // check with contributed validators - for (const validator of this._validators.toArray()) { - if (!(await validator.shouldOpen(target))) { + const targetURI = typeof target === 'string' ? URI.parse(target) : target; + // validate against the original URI that this URI resolves to, if one exists + const validationTarget = this._resolvedUriTargets.get(targetURI) ?? target; + for (const validator of this._validators) { + if (!(await validator.shouldOpen(validationTarget))) { return false; } } // check with contributed openers - for (const opener of this._openers.toArray()) { + for (const opener of this._openers) { const handled = await opener.open(target, options); if (handled) { return true; @@ -168,9 +179,10 @@ export class OpenerService implements IOpenerService { } async resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise { - for (const resolver of this._resolvers.toArray()) { + for (const resolver of this._resolvers) { const result = await resolver.resolveExternalUri(resource, options); if (result) { + this._resolvedUriTargets.set(result.resolved, resource); return result; } } @@ -180,7 +192,7 @@ export class OpenerService implements IOpenerService { private async _doOpenExternal(resource: URI | string, options: OpenOptions | undefined): Promise { - //todo@joh IExternalUriResolver should support `uri: URI | string` + //todo@jrieken IExternalUriResolver should support `uri: URI | string` const uri = typeof resource === 'string' ? URI.parse(resource) : resource; const { resolved } = await this.resolveExternalUri(uri, options); diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index 45c568d07..87c6461d5 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -3,13 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { createStringBuilder, IStringBuilder } from 'vs/editor/common/core/stringBuilder'; import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { Configuration } from 'vs/editor/browser/config/configuration'; +import { ILineBreaksComputer, LineBreakData } from 'vs/editor/common/viewModel/viewModel'; + +const ttPolicy = window.trustedTypes?.createPolicy('domLineBreaksComputer', { createHTML: value => value }); export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory { @@ -107,7 +110,9 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe allCharOffsets[i] = tmp[0]; allVisibleColumns[i] = tmp[1]; } - containerDomNode.innerHTML = sb.build(); + const html = sb.build(); + const trustedhtml = ttPolicy ? ttPolicy.createHTML(html) : html; + containerDomNode.innerHTML = trustedhtml as unknown as string; containerDomNode.style.position = 'absolute'; containerDomNode.style.top = '10000'; diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index 519a26bb5..a218b7071 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -322,6 +322,10 @@ export class ViewController { this.userInputEvents.emitMouseDrop(e); } + public emitMouseDropCanceled(): void { + this.userInputEvents.emitMouseDropCanceled(); + } + public emitMouseWheel(e: IMouseWheelEvent): void { this.userInputEvents.emitMouseWheel(e); } diff --git a/src/vs/editor/browser/view/viewUserInputEvents.ts b/src/vs/editor/browser/view/viewUserInputEvents.ts index 22906bda6..2a58f89d5 100644 --- a/src/vs/editor/browser/view/viewUserInputEvents.ts +++ b/src/vs/editor/browser/view/viewUserInputEvents.ts @@ -26,6 +26,7 @@ export class ViewUserInputEvents { public onMouseUp: EventCallback | null = null; public onMouseDrag: EventCallback | null = null; public onMouseDrop: EventCallback | null = null; + public onMouseDropCanceled: EventCallback | null = null; public onMouseWheel: EventCallback | null = null; private readonly _coordinatesConverter: ICoordinatesConverter; @@ -88,6 +89,12 @@ export class ViewUserInputEvents { } } + public emitMouseDropCanceled(): void { + if (this.onMouseDropCanceled) { + this.onMouseDropCanceled(); + } + } + public emitMouseWheel(e: IMouseWheelEvent): void { if (this.onMouseWheel) { this.onMouseWheel(e); diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts index 098bc6c76..6208c89fb 100644 --- a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts @@ -23,7 +23,7 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { protected _contentLeft: number; protected _contentWidth: number; protected _selectionIsEmpty: boolean; - protected _renderLineHightlightOnlyWhenFocus: boolean; + protected _renderLineHighlightOnlyWhenFocus: boolean; protected _focused: boolean; private _cursorLineNumbers: number[]; private _selections: Selection[]; @@ -37,7 +37,7 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { const layoutInfo = options.get(EditorOption.layoutInfo); this._lineHeight = options.get(EditorOption.lineHeight); this._renderLineHighlight = options.get(EditorOption.renderLineHighlight); - this._renderLineHightlightOnlyWhenFocus = options.get(EditorOption.renderLineHighlightOnlyWhenFocus); + this._renderLineHighlightOnlyWhenFocus = options.get(EditorOption.renderLineHighlightOnlyWhenFocus); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; this._selectionIsEmpty = true; @@ -85,7 +85,7 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { const layoutInfo = options.get(EditorOption.layoutInfo); this._lineHeight = options.get(EditorOption.lineHeight); this._renderLineHighlight = options.get(EditorOption.renderLineHighlight); - this._renderLineHightlightOnlyWhenFocus = options.get(EditorOption.renderLineHighlightOnlyWhenFocus); + this._renderLineHighlightOnlyWhenFocus = options.get(EditorOption.renderLineHighlightOnlyWhenFocus); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; return true; @@ -110,7 +110,7 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { return true; } public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { - if (!this._renderLineHightlightOnlyWhenFocus) { + if (!this._renderLineHighlightOnlyWhenFocus) { return false; } @@ -170,33 +170,36 @@ export class CurrentLineHighlightOverlay extends AbstractLineHighlightOverlay { return ( (this._renderLineHighlight === 'line' || this._renderLineHighlight === 'all') && this._selectionIsEmpty - && (!this._renderLineHightlightOnlyWhenFocus || this._focused) + && (!this._renderLineHighlightOnlyWhenFocus || this._focused) ); } protected _shouldRenderOther(): boolean { return ( (this._renderLineHighlight === 'gutter' || this._renderLineHighlight === 'all') - && (!this._renderLineHightlightOnlyWhenFocus || this._focused) + && (!this._renderLineHighlightOnlyWhenFocus || this._focused) ); } } export class CurrentLineMarginHighlightOverlay extends AbstractLineHighlightOverlay { protected _renderOne(ctx: RenderingContext): string { - const className = 'current-line current-line-margin' + (this._shouldRenderOther() ? ' current-line-margin-both' : ''); + const className = 'current-line' + (this._shouldRenderMargin() ? ' current-line-margin' : '') + (this._shouldRenderOther() ? ' current-line-margin-both' : ''); return `
`; } - protected _shouldRenderThis(): boolean { + protected _shouldRenderMargin(): boolean { return ( (this._renderLineHighlight === 'gutter' || this._renderLineHighlight === 'all') - && (!this._renderLineHightlightOnlyWhenFocus || this._focused) + && (!this._renderLineHighlightOnlyWhenFocus || this._focused) ); } + protected _shouldRenderThis(): boolean { + return true; + } protected _shouldRenderOther(): boolean { return ( (this._renderLineHighlight === 'line' || this._renderLineHighlight === 'all') && this._selectionIsEmpty - && (!this._renderLineHightlightOnlyWhenFocus || this._focused) + && (!this._renderLineHighlightOnlyWhenFocus || this._focused) ); } } diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index 21193bac1..5a6df759b 100644 --- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -56,6 +56,7 @@ export class EditorScrollbar extends ViewPart { mouseWheelScrollSensitivity: mouseWheelScrollSensitivity, fastScrollSensitivity: fastScrollSensitivity, scrollPredominantAxis: scrollPredominantAxis, + scrollByPage: scrollbar.scrollByPage, }; this.scrollbar = this._register(new SmoothScrollableElement(linesContent.domNode, scrollbarOptions, this._context.viewLayout.getScrollable())); diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 1be53aad7..7ca33211a 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -226,8 +226,7 @@ class MinimapLayout { * Compute a desired `scrollPosition` such that the slider moves by `delta`. */ public getDesiredScrollTopFromDelta(delta: number): number { - const desiredSliderPosition = this.sliderTop + delta; - return Math.round(desiredSliderPosition / this._computedSliderRatio); + return Math.round(this.scrollTop + delta / this._computedSliderRatio); } public getDesiredScrollTopFromTouchLocation(pageY: number): number { @@ -238,6 +237,7 @@ class MinimapLayout { options: MinimapOptions, viewportStartLineNumber: number, viewportEndLineNumber: number, + viewportStartLineNumberVerticalOffset: number, viewportHeight: number, viewportContainsWhitespaceGaps: boolean, lineCount: number, @@ -332,8 +332,10 @@ class MinimapLayout { } const endLineNumber = Math.min(lineCount, startLineNumber + minimapLinesFitting - 1); + const partialLine = (scrollTop - viewportStartLineNumberVerticalOffset) / lineHeight; + const sliderTopAligned = (viewportStartLineNumber - startLineNumber + partialLine) * minimapLineHeight / pixelRatio; - return new MinimapLayout(scrollTop, scrollHeight, true, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); + return new MinimapLayout(scrollTop, scrollHeight, true, computedSliderRatio, sliderTopAligned, sliderHeight, startLineNumber, endLineNumber); } } } @@ -505,6 +507,7 @@ interface IMinimapRenderingContext { readonly viewportStartLineNumber: number; readonly viewportEndLineNumber: number; + readonly viewportStartLineNumberVerticalOffset: number; readonly scrollTop: number; readonly scrollLeft: number; @@ -891,6 +894,7 @@ export class Minimap extends ViewPart implements IMinimapModel { viewportStartLineNumber: viewportStartLineNumber, viewportEndLineNumber: viewportEndLineNumber, + viewportStartLineNumberVerticalOffset: ctx.getVerticalOffsetForLineNumber(viewportStartLineNumber), scrollTop: ctx.scrollTop, scrollLeft: ctx.scrollLeft, @@ -1344,6 +1348,7 @@ class InnerMinimap extends Disposable { this._model.options, renderingCtx.viewportStartLineNumber, renderingCtx.viewportEndLineNumber, + renderingCtx.viewportStartLineNumberVerticalOffset, renderingCtx.viewportHeight, renderingCtx.viewportContainsWhitespaceGaps, this._model.getLineCount(), diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index ef3f6e301..ec5077ea0 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -25,6 +25,7 @@ export class ViewCursors extends ViewPart { private _cursorStyle: TextEditorCursorStyle; private _cursorSmoothCaretAnimation: boolean; private _selectionIsEmpty: boolean; + private _isComposingInput: boolean; private _isVisible: boolean; @@ -49,6 +50,7 @@ export class ViewCursors extends ViewPart { this._cursorStyle = options.get(EditorOption.cursorStyle); this._cursorSmoothCaretAnimation = options.get(EditorOption.cursorSmoothCaretAnimation); this._selectionIsEmpty = true; + this._isComposingInput = false; this._isVisible = false; @@ -83,7 +85,16 @@ export class ViewCursors extends ViewPart { } // --- begin event handlers - + public onCompositionStart(e: viewEvents.ViewCompositionStartEvent): boolean { + this._isComposingInput = true; + this._updateBlinking(); + return true; + } + public onCompositionEnd(e: viewEvents.ViewCompositionEndEvent): boolean { + this._isComposingInput = false; + this._updateBlinking(); + return true; + } public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { const options = this._context.configuration.options; @@ -195,6 +206,10 @@ export class ViewCursors extends ViewPart { // ---- blinking logic private _getCursorBlinking(): TextEditorCursorBlinkingStyle { + if (this._isComposingInput) { + // avoid double cursors + return TextEditorCursorBlinkingStyle.Hidden; + } if (!this._editorHasFocus) { return TextEditorCursorBlinkingStyle.Hidden; } diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 17539558c..4c87ce2bb 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -37,7 +37,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import * as modes from 'vs/editor/common/modes'; import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/view/editorColorRegistry'; -import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground, editorErrorBackground, editorInfoBackground, editorWarningBackground } from 'vs/platform/theme/common/colorRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; @@ -176,6 +176,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _onMouseDrop: Emitter = this._register(new Emitter()); public readonly onMouseDrop: Event = this._onMouseDrop.event; + private readonly _onMouseDropCanceled: Emitter = this._register(new Emitter()); + public readonly onMouseDropCanceled: Event = this._onMouseDropCanceled.event; + private readonly _onContextMenu: Emitter = this._register(new Emitter()); public readonly onContextMenu: Event = this._onContextMenu.event; @@ -1024,6 +1027,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (this._triggerEditorCommand(source, handlerId, payload)) { return; } + + this._commandService.executeCommand(handlerId, payload); } private _startComposition(): void { @@ -1117,6 +1122,18 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return true; } + public popUndoStop(): boolean { + if (!this._modelData) { + return false; + } + if (this._configuration.options.get(EditorOption.readOnly)) { + // read only editor => sorry! + return false; + } + this._modelData.model.popStackElement(); + return true; + } + public executeEdits(source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean { if (!this._modelData) { return false; @@ -1614,6 +1631,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE viewUserInputEvents.onMouseUp = (e) => this._onMouseUp.fire(e); viewUserInputEvents.onMouseDrag = (e) => this._onMouseDrag.fire(e); viewUserInputEvents.onMouseDrop = (e) => this._onMouseDrop.fire(e); + viewUserInputEvents.onMouseDropCanceled = (e) => this._onMouseDropCanceled.fire(e); viewUserInputEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e); const view = new View( @@ -1716,6 +1734,7 @@ class EditorContextKeysManager extends Disposable { private readonly _editorTextFocus: IContextKey; private readonly _editorTabMovesFocus: IContextKey; private readonly _editorReadonly: IContextKey; + private readonly _inDiffEditor: IContextKey; private readonly _editorColumnSelection: IContextKey; private readonly _hasMultipleSelections: IContextKey; private readonly _hasNonEmptySelection: IContextKey; @@ -1738,6 +1757,7 @@ class EditorContextKeysManager extends Disposable { this._editorTextFocus = EditorContextKeys.editorTextFocus.bindTo(contextKeyService); this._editorTabMovesFocus = EditorContextKeys.tabMovesFocus.bindTo(contextKeyService); this._editorReadonly = EditorContextKeys.readOnly.bindTo(contextKeyService); + this._inDiffEditor = EditorContextKeys.inDiffEditor.bindTo(contextKeyService); this._editorColumnSelection = EditorContextKeys.columnSelection.bindTo(contextKeyService); this._hasMultipleSelections = EditorContextKeys.hasMultipleSelections.bindTo(contextKeyService); this._hasNonEmptySelection = EditorContextKeys.hasNonEmptySelection.bindTo(contextKeyService); @@ -1766,6 +1786,7 @@ class EditorContextKeysManager extends Disposable { this._editorTabMovesFocus.set(options.get(EditorOption.tabFocusMode)); this._editorReadonly.set(options.get(EditorOption.readOnly)); + this._inDiffEditor.set(options.get(EditorOption.inDiffEditor)); this._editorColumnSelection.set(options.get(EditorOption.columnSelection)); } @@ -1981,6 +2002,10 @@ registerThemingParticipant((theme, collector) => { if (errorForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorErrorDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(errorForeground)}") repeat-x bottom left; }`); } + const errorBackground = theme.getColor(editorErrorBackground); + if (errorBackground) { + collector.addRule(`.monaco-editor .${ClassName.EditorErrorDecoration}::before { display: block; content: ''; width: 100%; height: 100%; background: ${errorBackground}; }`); + } const warningBorderColor = theme.getColor(editorWarningBorder); if (warningBorderColor) { @@ -1990,6 +2015,10 @@ registerThemingParticipant((theme, collector) => { if (warningForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorWarningDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(warningForeground)}") repeat-x bottom left; }`); } + const warningBackground = theme.getColor(editorWarningBackground); + if (warningBackground) { + collector.addRule(`.monaco-editor .${ClassName.EditorWarningDecoration}::before { display: block; content: ''; width: 100%; height: 100%; background: ${warningBackground}; }`); + } const infoBorderColor = theme.getColor(editorInfoBorder); if (infoBorderColor) { @@ -1999,6 +2028,10 @@ registerThemingParticipant((theme, collector) => { if (infoForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorInfoDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(infoForeground)}") repeat-x bottom left; }`); } + const infoBackground = theme.getColor(editorInfoBackground); + if (infoBackground) { + collector.addRule(`.monaco-editor .${ClassName.EditorInfoDecoration}::before { display: block; content: ''; width: 100%; height: 100%; background: ${infoBackground}; }`); + } const hintBorderColor = theme.getColor(editorHintBorder); if (hintBorderColor) { diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index ada211784..0730e4f00 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -20,7 +20,7 @@ import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffReview } from 'vs/editor/browser/widget/diffReview'; -import { IDiffEditorOptions, IEditorOptions, EditorLayoutInfo, IComputedEditorOptions, EditorOption, EditorOptions, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; +import { IDiffEditorOptions, IEditorOptions, EditorLayoutInfo, EditorOption, EditorOptions, EditorFontLigatures, stringSet as validateStringSetOption, boolean as validateBooleanOption } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; @@ -33,13 +33,13 @@ import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; -import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; +import { ILineBreaksComputer, InlineDecoration, InlineDecorationType, IViewModel, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground, diffDiagonalFill } from 'vs/platform/theme/common/colorRegistry'; -import { IColorTheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, IThemeService, getThemeTypeSelector, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -48,8 +48,11 @@ import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs import { onUnexpectedError } from 'vs/base/common/errors'; import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; +import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; +import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; interface IEditorDiffDecorations { decorations: IModelDeltaDecoration[]; @@ -70,17 +73,9 @@ interface IEditorsZones { modified: IMyViewZone[]; } -export interface IDiffEditorWidgetStyle { - getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones; - setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; - applyColors(theme: IColorTheme): boolean; - layout(): number; - dispose(): void; -} - class VisualEditorState { private _zones: string[]; - private inlineDiffMargins: InlineDiffMargin[]; + private _inlineDiffMargins: InlineDiffMargin[]; private _zonesMap: { [zoneId: string]: boolean; }; private _decorations: string[]; @@ -89,7 +84,7 @@ class VisualEditorState { private _clipboardService: IClipboardService ) { this._zones = []; - this.inlineDiffMargins = []; + this._inlineDiffMargins = []; this._zonesMap = {}; this._decorations = []; } @@ -102,8 +97,8 @@ class VisualEditorState { // (1) View zones if (this._zones.length > 0) { editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => { - for (let i = 0, length = this._zones.length; i < length; i++) { - viewChangeAccessor.removeZone(this._zones[i]); + for (const zoneId of this._zones) { + viewChangeAccessor.removeZone(zoneId); } }); } @@ -120,25 +115,25 @@ class VisualEditorState { // view zones editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => { - for (let i = 0, length = this._zones.length; i < length; i++) { - viewChangeAccessor.removeZone(this._zones[i]); + for (const zoneId of this._zones) { + viewChangeAccessor.removeZone(zoneId); } - for (let i = 0, length = this.inlineDiffMargins.length; i < length; i++) { - this.inlineDiffMargins[i].dispose(); + for (const inlineDiffMargin of this._inlineDiffMargins) { + inlineDiffMargin.dispose(); } this._zones = []; this._zonesMap = {}; - this.inlineDiffMargins = []; + this._inlineDiffMargins = []; for (let i = 0, length = newDecorations.zones.length; i < length; i++) { const viewZone = newDecorations.zones[i]; viewZone.suppressMouseDown = true; - let zoneId = viewChangeAccessor.addZone(viewZone); + const zoneId = viewChangeAccessor.addZone(viewZone); this._zones.push(zoneId); this._zonesMap[String(zoneId)] = true; if (newDecorations.zones[i].diff && viewZone.marginDomNode) { viewZone.suppressMouseDown = false; - this.inlineDiffMargins.push(new InlineDiffMargin(zoneId, viewZone.marginDomNode, editor, newDecorations.zones[i].diff!, this._contextMenuService, this._clipboardService)); + this._inlineDiffMargins.push(new InlineDiffMargin(zoneId, viewZone.marginDomNode, editor, newDecorations.zones[i].diff!, this._contextMenuService, this._clipboardService)); } } }); @@ -160,8 +155,9 @@ class VisualEditorState { let DIFF_EDITOR_ID = 0; -const diffInsertIcon = registerIcon('diff-insert', Codicon.add); -const diffRemoveIcon = registerIcon('diff-remove', Codicon.remove); +const diffInsertIcon = registerIcon('diff-insert', Codicon.add, nls.localize('diffInsertIcon', 'Line decoration for inserts in the diff editor.')); +const diffRemoveIcon = registerIcon('diff-remove', Codicon.remove, nls.localize('diffRemoveIcon', 'Line decoration for removals in the diff editor.')); +const ttPolicy = window.trustedTypes?.createPolicy('diffEditorWidget', { createHTML: value => value }); export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffEditor { @@ -178,7 +174,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private readonly _onDidContentSizeChange: Emitter = this._register(new Emitter()); public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; - private readonly id: number; + private readonly _id: number; private _state: editorBrowser.DiffEditorState; private _updatingDiffProgress: IProgressRunner | null; @@ -189,12 +185,12 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private readonly _elementSizeObserver: ElementSizeObserver; - private readonly originalEditor: CodeEditorWidget; + private readonly _originalEditor: CodeEditorWidget; private readonly _originalDomNode: HTMLElement; private readonly _originalEditorState: VisualEditorState; private _originalOverviewRuler: editorBrowser.IOverviewRuler | null; - private readonly modifiedEditor: CodeEditorWidget; + private readonly _modifiedEditor: CodeEditorWidget; private readonly _modifiedDomNode: HTMLElement; private readonly _modifiedEditorState: VisualEditorState; private _modifiedOverviewRuler: editorBrowser.IOverviewRuler | null; @@ -209,14 +205,14 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private _ignoreTrimWhitespace: boolean; private _originalIsEditable: boolean; - private _originalCodeLens: boolean; - private _modifiedCodeLens: boolean; + private _diffCodeLens: boolean; + private _diffWordWrap: 'off' | 'on' | 'inherit'; private _renderSideBySide: boolean; private _maxComputationTime: number; private _renderIndicators: boolean; private _enableSplitViewResizing: boolean; - private _strategy!: IDiffEditorWidgetStyle; + private _strategy!: DiffEditorWidgetStyle; private readonly _updateDecorationsRunner: RunOnceScheduler; @@ -250,7 +246,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._themeService = themeService; this._notificationService = notificationService; - this.id = (++DIFF_EDITOR_ID); + this._id = (++DIFF_EDITOR_ID); this._state = editorBrowser.DiffEditorState.Idle; this._updatingDiffProgress = null; @@ -281,19 +277,14 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._renderIndicators = options.renderIndicators; } - this._originalIsEditable = false; - if (typeof options.originalEditable !== 'undefined') { - this._originalIsEditable = Boolean(options.originalEditable); - } + this._originalIsEditable = validateBooleanOption(options.originalEditable, false); + this._diffCodeLens = validateBooleanOption(options.diffCodeLens, false); + this._diffWordWrap = validateDiffWordWrap(options.diffWordWrap, 'inherit'); - this._originalCodeLens = false; - if (typeof options.originalCodeLens !== 'undefined') { - this._originalCodeLens = Boolean(options.originalCodeLens); - } - - this._modifiedCodeLens = false; - if (typeof options.modifiedCodeLens !== 'undefined') { - this._modifiedCodeLens = Boolean(options.modifiedCodeLens); + if (typeof options.isInEmbeddedEditor !== 'undefined') { + this._contextKeyService.createKey('isInEmbeddedDiffEditor', options.isInEmbeddedEditor); + } else { + this._contextKeyService.createKey('isInEmbeddedDiffEditor', false); } this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0)); @@ -315,7 +306,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._overviewDomElement.appendChild(this._overviewViewportDomElement.domNode); this._register(dom.addStandardDisposableListener(this._overviewDomElement, 'mousedown', (e) => { - this.modifiedEditor.delegateVerticalScrollbarMouseDown(e); + this._modifiedEditor.delegateVerticalScrollbarMouseDown(e); })); this._containerDomElement.appendChild(this._overviewDomElement); @@ -362,8 +353,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE rightServices.set(IContextKeyService, rightContextKeyService); const rightScopedInstantiationService = instantiationService.createChild(rightServices); - this.originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService, leftContextKeyService); - this.modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService, rightContextKeyService); + this._originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService, leftContextKeyService); + this._modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService, rightContextKeyService); this._originalOverviewRuler = null; this._modifiedOverviewRuler = null; @@ -421,7 +412,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public getContentHeight(): number { - return this.modifiedEditor.getContentHeight(); + return this._modifiedEditor.getContentHeight(); } private _setState(newState: editorBrowser.DiffEditorState): void { @@ -466,8 +457,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); this._originalOverviewRuler.dispose(); } - if (this.originalEditor.hasModel()) { - this._originalOverviewRuler = this.originalEditor.createOverviewRuler('original diffOverviewRuler')!; + if (this._originalEditor.hasModel()) { + this._originalOverviewRuler = this._originalEditor.createOverviewRuler('original diffOverviewRuler')!; this._overviewDomElement.appendChild(this._originalOverviewRuler.getDomNode()); } @@ -475,8 +466,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); this._modifiedOverviewRuler.dispose(); } - if (this.modifiedEditor.hasModel()) { - this._modifiedOverviewRuler = this.modifiedEditor.createOverviewRuler('modified diffOverviewRuler')!; + if (this._modifiedEditor.hasModel()) { + this._modifiedOverviewRuler = this._modifiedEditor.createOverviewRuler('modified diffOverviewRuler')!; this._overviewDomElement.appendChild(this._modifiedOverviewRuler.getDomNode()); } @@ -484,7 +475,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _createLeftHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget { - const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable, this._originalCodeLens)); + const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options)); this._register(editor.onDidScrollChange((e) => { if (this._isHandlingScrollEvent) { @@ -494,56 +485,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return; } this._isHandlingScrollEvent = true; - this.modifiedEditor.setScrollPosition({ - scrollLeft: e.scrollLeft, - scrollTop: e.scrollTop - }); - this._isHandlingScrollEvent = false; - - this._layoutOverviewViewport(); - })); - - this._register(editor.onDidChangeViewZones(() => { - this._onViewZonesChanged(); - })); - - this._register(editor.onDidChangeModelContent(() => { - if (this._isVisible) { - this._beginUpdateDecorationsSoon(); - } - })); - - const isInDiffLeftEditorKey = contextKeyService.createKey('isInDiffLeftEditor', undefined); - this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); - this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); - - this._register(editor.onDidContentSizeChange(e => { - const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; - const height = Math.max(this.modifiedEditor.getContentHeight(), this.originalEditor.getContentHeight()); - - this._onDidContentSizeChange.fire({ - contentHeight: height, - contentWidth: width, - contentHeightChanged: e.contentHeightChanged, - contentWidthChanged: e.contentWidthChanged - }); - })); - - return editor; - } - - private _createRightHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget { - const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options, this._modifiedCodeLens)); - - this._register(editor.onDidScrollChange((e) => { - if (this._isHandlingScrollEvent) { - return; - } - if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) { - return; - } - this._isHandlingScrollEvent = true; - this.originalEditor.setScrollPosition({ + this._modifiedEditor.setScrollPosition({ scrollLeft: e.scrollLeft, scrollTop: e.scrollTop }); @@ -557,8 +499,77 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE })); this._register(editor.onDidChangeConfiguration((e) => { - if (e.hasChanged(EditorOption.fontInfo) && editor.getModel()) { - this._onViewZonesChanged(); + if (!editor.getModel()) { + return; + } + if (e.hasChanged(EditorOption.fontInfo)) { + this._updateDecorationsRunner.schedule(); + } + if (e.hasChanged(EditorOption.wrappingInfo)) { + this._updateDecorationsRunner.cancel(); + this._updateDecorations(); + } + })); + + this._register(editor.onDidChangeModelContent(() => { + if (this._isVisible) { + this._beginUpdateDecorationsSoon(); + } + })); + + const isInDiffLeftEditorKey = contextKeyService.createKey('isInDiffLeftEditor', undefined); + this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); + this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); + + this._register(editor.onDidContentSizeChange(e => { + const width = this._originalEditor.getContentWidth() + this._modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; + const height = Math.max(this._modifiedEditor.getContentHeight(), this._originalEditor.getContentHeight()); + + this._onDidContentSizeChange.fire({ + contentHeight: height, + contentWidth: width, + contentHeightChanged: e.contentHeightChanged, + contentWidthChanged: e.contentWidthChanged + }); + })); + + return editor; + } + + private _createRightHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget { + const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options)); + + this._register(editor.onDidScrollChange((e) => { + if (this._isHandlingScrollEvent) { + return; + } + if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) { + return; + } + this._isHandlingScrollEvent = true; + this._originalEditor.setScrollPosition({ + scrollLeft: e.scrollLeft, + scrollTop: e.scrollTop + }); + this._isHandlingScrollEvent = false; + + this._layoutOverviewViewport(); + })); + + this._register(editor.onDidChangeViewZones(() => { + this._onViewZonesChanged(); + })); + + this._register(editor.onDidChangeConfiguration((e) => { + if (!editor.getModel()) { + return; + } + if (e.hasChanged(EditorOption.fontInfo)) { + this._updateDecorationsRunner.schedule(); + } + if (e.hasChanged(EditorOption.wrappingInfo)) { + this._updateDecorationsRunner.cancel(); + this._updateDecorations(); } })); @@ -579,8 +590,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false))); this._register(editor.onDidContentSizeChange(e => { - const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; - const height = Math.max(this.modifiedEditor.getContentHeight(), this.originalEditor.getContentHeight()); + const width = this._originalEditor.getContentWidth() + this._modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; + const height = Math.max(this._modifiedEditor.getContentHeight(), this._originalEditor.getContentHeight()); this._onDidContentSizeChange.fire({ contentHeight: height, @@ -619,10 +630,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._containerDomElement.removeChild(this._overviewDomElement); this._containerDomElement.removeChild(this._originalDomNode); - this.originalEditor.dispose(); + this._originalEditor.dispose(); this._containerDomElement.removeChild(this._modifiedDomNode); - this.modifiedEditor.dispose(); + this._modifiedEditor.dispose(); this._strategy.dispose(); @@ -641,7 +652,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE //------------ begin IDiffEditor methods public getId(): string { - return this.getEditorType() + ':' + this.id; + return this.getEditorType() + ':' + this._id; } public getEditorType(): string { @@ -660,11 +671,11 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public getOriginalEditor(): editorBrowser.ICodeEditor { - return this.originalEditor; + return this._originalEditor; } public getModifiedEditor(): editorBrowser.ICodeEditor { - return this.modifiedEditor; + return this._modifiedEditor; } public updateOptions(newOptions: IDiffEditorOptions): void { @@ -706,18 +717,12 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._beginUpdateDecorations(); } - if (typeof newOptions.originalEditable !== 'undefined') { - this._originalIsEditable = Boolean(newOptions.originalEditable); - } - if (typeof newOptions.originalCodeLens !== 'undefined') { - this._originalCodeLens = Boolean(newOptions.originalCodeLens); - } - if (typeof newOptions.modifiedCodeLens !== 'undefined') { - this._modifiedCodeLens = Boolean(newOptions.modifiedCodeLens); - } + this._originalIsEditable = validateBooleanOption(newOptions.originalEditable, this._originalIsEditable); + this._diffCodeLens = validateBooleanOption(newOptions.diffCodeLens, this._diffCodeLens); + this._diffWordWrap = validateDiffWordWrap(newOptions.diffWordWrap, this._diffWordWrap); - this.modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(newOptions, this._modifiedCodeLens)); - this.originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(newOptions, this._originalIsEditable, this._originalCodeLens)); + this._modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(newOptions)); + this._originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(newOptions)); // enableSplitViewResizing if (typeof newOptions.enableSplitViewResizing !== 'undefined') { @@ -739,8 +744,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE public getModel(): editorCommon.IDiffEditorModel { return { - original: this.originalEditor.getModel()!, - modified: this.modifiedEditor.getModel()! + original: this._originalEditor.getModel()!, + modified: this._modifiedEditor.getModel()! }; } @@ -754,15 +759,15 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._cleanViewZonesAndDecorations(); // Update code editor models - this.originalEditor.setModel(model ? model.original : null); - this.modifiedEditor.setModel(model ? model.modified : null); + this._originalEditor.setModel(model ? model.original : null); + this._modifiedEditor.setModel(model ? model.modified : null); this._updateDecorationsRunner.cancel(); // this.originalEditor.onDidChangeModelOptions if (model) { - this.originalEditor.setScrollTop(0); - this.modifiedEditor.setScrollTop(0); + this._originalEditor.setScrollTop(0); + this._modifiedEditor.setScrollTop(0); } // Disable any diff computations that will come in @@ -785,59 +790,59 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public getVisibleColumnFromPosition(position: IPosition): number { - return this.modifiedEditor.getVisibleColumnFromPosition(position); + return this._modifiedEditor.getVisibleColumnFromPosition(position); } public getStatusbarColumn(position: IPosition): number { - return this.modifiedEditor.getStatusbarColumn(position); + return this._modifiedEditor.getStatusbarColumn(position); } public getPosition(): Position | null { - return this.modifiedEditor.getPosition(); + return this._modifiedEditor.getPosition(); } public setPosition(position: IPosition): void { - this.modifiedEditor.setPosition(position); + this._modifiedEditor.setPosition(position); } public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLine(lineNumber, scrollType); + this._modifiedEditor.revealLine(lineNumber, scrollType); } public revealLineInCenter(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLineInCenter(lineNumber, scrollType); + this._modifiedEditor.revealLineInCenter(lineNumber, scrollType); } public revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType); + this._modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType); } public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLineNearTop(lineNumber, scrollType); + this._modifiedEditor.revealLineNearTop(lineNumber, scrollType); } public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealPosition(position, scrollType); + this._modifiedEditor.revealPosition(position, scrollType); } public revealPositionInCenter(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealPositionInCenter(position, scrollType); + this._modifiedEditor.revealPositionInCenter(position, scrollType); } public revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType); + this._modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType); } public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealPositionNearTop(position, scrollType); + this._modifiedEditor.revealPositionNearTop(position, scrollType); } public getSelection(): Selection | null { - return this.modifiedEditor.getSelection(); + return this._modifiedEditor.getSelection(); } public getSelections(): Selection[] | null { - return this.modifiedEditor.getSelections(); + return this._modifiedEditor.getSelections(); } public setSelection(range: IRange): void; @@ -845,60 +850,60 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE public setSelection(selection: ISelection): void; public setSelection(editorSelection: Selection): void; public setSelection(something: any): void { - this.modifiedEditor.setSelection(something); + this._modifiedEditor.setSelection(something); } public setSelections(ranges: readonly ISelection[]): void { - this.modifiedEditor.setSelections(ranges); + this._modifiedEditor.setSelections(ranges); } public revealLines(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLines(startLineNumber, endLineNumber, scrollType); + this._modifiedEditor.revealLines(startLineNumber, endLineNumber, scrollType); } public revealLinesInCenter(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLinesInCenter(startLineNumber, endLineNumber, scrollType); + this._modifiedEditor.revealLinesInCenter(startLineNumber, endLineNumber, scrollType); } public revealLinesInCenterIfOutsideViewport(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType); + this._modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType); } public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLinesNearTop(startLineNumber, endLineNumber, scrollType); + this._modifiedEditor.revealLinesNearTop(startLineNumber, endLineNumber, scrollType); } public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void { - this.modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal); + this._modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal); } public revealRangeInCenter(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealRangeInCenter(range, scrollType); + this._modifiedEditor.revealRangeInCenter(range, scrollType); } public revealRangeInCenterIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType); + this._modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType); } public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealRangeNearTop(range, scrollType); + this._modifiedEditor.revealRangeNearTop(range, scrollType); } public revealRangeNearTopIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealRangeNearTopIfOutsideViewport(range, scrollType); + this._modifiedEditor.revealRangeNearTopIfOutsideViewport(range, scrollType); } public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealRangeAtTop(range, scrollType); + this._modifiedEditor.revealRangeAtTop(range, scrollType); } public getSupportedActions(): editorCommon.IEditorAction[] { - return this.modifiedEditor.getSupportedActions(); + return this._modifiedEditor.getSupportedActions(); } public saveViewState(): editorCommon.IDiffEditorViewState { - let originalViewState = this.originalEditor.saveViewState(); - let modifiedViewState = this.modifiedEditor.saveViewState(); + const originalViewState = this._originalEditor.saveViewState(); + const modifiedViewState = this._modifiedEditor.saveViewState(); return { original: originalViewState, modified: modifiedViewState @@ -907,9 +912,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE public restoreViewState(s: editorCommon.IDiffEditorViewState): void { if (s.original && s.modified) { - let diffEditorState = s; - this.originalEditor.restoreViewState(diffEditorState.original); - this.modifiedEditor.restoreViewState(diffEditorState.modified); + const diffEditorState = s; + this._originalEditor.restoreViewState(diffEditorState.original); + this._modifiedEditor.restoreViewState(diffEditorState.modified); } } @@ -918,35 +923,35 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public focus(): void { - this.modifiedEditor.focus(); + this._modifiedEditor.focus(); } public hasTextFocus(): boolean { - return this.originalEditor.hasTextFocus() || this.modifiedEditor.hasTextFocus(); + return this._originalEditor.hasTextFocus() || this._modifiedEditor.hasTextFocus(); } public onVisible(): void { this._isVisible = true; - this.originalEditor.onVisible(); - this.modifiedEditor.onVisible(); + this._originalEditor.onVisible(); + this._modifiedEditor.onVisible(); // Begin comparing this._beginUpdateDecorations(); } public onHide(): void { this._isVisible = false; - this.originalEditor.onHide(); - this.modifiedEditor.onHide(); + this._originalEditor.onHide(); + this._modifiedEditor.onHide(); // Remove all view zones & decorations this._cleanViewZonesAndDecorations(); } public trigger(source: string | null | undefined, handlerId: string, payload: any): void { - this.modifiedEditor.trigger(source, handlerId, payload); + this._modifiedEditor.trigger(source, handlerId, payload); } public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any { - return this.modifiedEditor.changeDecorations(callback); + return this._modifiedEditor.changeDecorations(callback); } //------------ end IDiffEditor methods @@ -970,8 +975,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE const height = this._elementSizeObserver.getHeight(); const reviewHeight = this._getReviewHeight(); - let freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH; - let layoutInfo = this.modifiedEditor.getLayoutInfo(); + const freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH; + const layoutInfo = this._modifiedEditor.getLayoutInfo(); if (layoutInfo) { this._originalOverviewRuler.setLayout({ top: 0, @@ -1021,8 +1026,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private _beginUpdateDecorations(): void { this._beginUpdateDecorationsTimeout = -1; - const currentOriginalModel = this.originalEditor.getModel(); - const currentModifiedModel = this.modifiedEditor.getModel(); + const currentOriginalModel = this._originalEditor.getModel(); + const currentModifiedModel = this._modifiedEditor.getModel(); if (!currentOriginalModel || !currentModifiedModel) { return; } @@ -1031,7 +1036,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE // The best method would be to call cancel on the Promise, but this is not // yet supported, so using tokens for now. this._diffComputationToken++; - let currentToken = this._diffComputationToken; + const currentToken = this._diffComputationToken; this._setState(editorBrowser.DiffEditorState.ComputingDiff); if (!this._editorWorkerService.canComputeDiff(currentOriginalModel.uri, currentModifiedModel.uri)) { @@ -1048,8 +1053,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._editorWorkerService.computeDiff(currentOriginalModel.uri, currentModifiedModel.uri, this._ignoreTrimWhitespace, this._maxComputationTime).then((result) => { if (currentToken === this._diffComputationToken - && currentOriginalModel === this.originalEditor.getModel() - && currentModifiedModel === this.modifiedEditor.getModel() + && currentOriginalModel === this._originalEditor.getModel() + && currentModifiedModel === this._modifiedEditor.getModel() ) { this._setState(editorBrowser.DiffEditorState.DiffComputed); this._diffComputationResult = result; @@ -1058,8 +1063,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } }, (error) => { if (currentToken === this._diffComputationToken - && currentOriginalModel === this.originalEditor.getModel() - && currentModifiedModel === this.modifiedEditor.getModel() + && currentOriginalModel === this._originalEditor.getModel() + && currentModifiedModel === this._modifiedEditor.getModel() ) { this._setState(editorBrowser.DiffEditorState.DiffComputed); this._diffComputationResult = null; @@ -1069,40 +1074,38 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _cleanViewZonesAndDecorations(): void { - this._originalEditorState.clean(this.originalEditor); - this._modifiedEditorState.clean(this.modifiedEditor); + this._originalEditorState.clean(this._originalEditor); + this._modifiedEditorState.clean(this._modifiedEditor); } private _updateDecorations(): void { - if (!this.originalEditor.getModel() || !this.modifiedEditor.getModel() || !this._originalOverviewRuler || !this._modifiedOverviewRuler) { + if (!this._originalEditor.getModel() || !this._modifiedEditor.getModel() || !this._originalOverviewRuler || !this._modifiedOverviewRuler) { return; } const lineChanges = (this._diffComputationResult ? this._diffComputationResult.changes : []); - let foreignOriginal = this._originalEditorState.getForeignViewZones(this.originalEditor.getWhitespaces()); - let foreignModified = this._modifiedEditorState.getForeignViewZones(this.modifiedEditor.getWhitespaces()); + const foreignOriginal = this._originalEditorState.getForeignViewZones(this._originalEditor.getWhitespaces()); + const foreignModified = this._modifiedEditorState.getForeignViewZones(this._modifiedEditor.getWhitespaces()); - let diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._ignoreTrimWhitespace, this._renderIndicators, foreignOriginal, foreignModified, this.originalEditor, this.modifiedEditor); + const diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._ignoreTrimWhitespace, this._renderIndicators, foreignOriginal, foreignModified); try { this._currentlyChangingViewZones = true; - this._originalEditorState.apply(this.originalEditor, this._originalOverviewRuler, diffDecorations.original, false); - this._modifiedEditorState.apply(this.modifiedEditor, this._modifiedOverviewRuler, diffDecorations.modified, true); + this._originalEditorState.apply(this._originalEditor, this._originalOverviewRuler, diffDecorations.original, false); + this._modifiedEditorState.apply(this._modifiedEditor, this._modifiedOverviewRuler, diffDecorations.modified, true); } finally { this._currentlyChangingViewZones = false; } } private _adjustOptionsForSubEditor(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IDiffEditorConstructionOptions { - let clonedOptions: editorBrowser.IDiffEditorConstructionOptions = objects.deepClone(options || {}); + const clonedOptions: editorBrowser.IDiffEditorConstructionOptions = objects.deepClone(options || {}); clonedOptions.inDiffEditor = true; - clonedOptions.wordWrap = 'off'; - clonedOptions.wordWrapMinified = false; clonedOptions.automaticLayout = false; clonedOptions.scrollbar = clonedOptions.scrollbar || {}; clonedOptions.scrollbar.vertical = 'visible'; clonedOptions.folding = false; - clonedOptions.codeLens = false; + clonedOptions.codeLens = this._diffCodeLens; clonedOptions.fixedOverflowWidgets = true; clonedOptions.overflowWidgetsDomNode = options.overflowWidgetsDomNode; // clonedOptions.lineDecorationsWidth = '2ch'; @@ -1113,21 +1116,22 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return clonedOptions; } - private _adjustOptionsForLeftHandSide(options: editorBrowser.IDiffEditorConstructionOptions, isEditable: boolean, isCodeLensEnabled: boolean): editorBrowser.IEditorConstructionOptions { - let result = this._adjustOptionsForSubEditor(options); - if (isCodeLensEnabled) { - result.codeLens = true; + private _adjustOptionsForLeftHandSide(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IEditorConstructionOptions { + const result = this._adjustOptionsForSubEditor(options); + if (!this._renderSideBySide) { + // never wrap hidden editor + result.wordWrapOverride1 = 'off'; + } else { + result.wordWrapOverride1 = this._diffWordWrap; } - result.readOnly = !isEditable; + result.readOnly = !this._originalIsEditable; result.extraEditorClassName = 'original-in-monaco-diff-editor'; return result; } - private _adjustOptionsForRightHandSide(options: editorBrowser.IDiffEditorConstructionOptions, isCodeLensEnabled: boolean): editorBrowser.IEditorConstructionOptions { - let result = this._adjustOptionsForSubEditor(options); - if (isCodeLensEnabled) { - result.codeLens = true; - } + private _adjustOptionsForRightHandSide(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IEditorConstructionOptions { + const result = this._adjustOptionsForSubEditor(options); + result.wordWrapOverride1 = this._diffWordWrap; result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; result.scrollbar!.verticalHasArrows = false; result.extraEditorClassName = 'modified-in-monaco-diff-editor'; @@ -1144,7 +1148,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE const height = this._elementSizeObserver.getHeight(); const reviewHeight = this._getReviewHeight(); - let splitPoint = this._strategy.layout(); + const splitPoint = this._strategy.layout(); this._originalDomNode.style.width = splitPoint + 'px'; this._originalDomNode.style.left = '0px'; @@ -1159,8 +1163,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._overviewViewportDomElement.setWidth(DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH); this._overviewViewportDomElement.setHeight(30); - this.originalEditor.layout({ width: splitPoint, height: (height - reviewHeight) }); - this.modifiedEditor.layout({ width: width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (height - reviewHeight) }); + this._originalEditor.layout({ width: splitPoint, height: (height - reviewHeight) }); + this._modifiedEditor.layout({ width: width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (height - reviewHeight) }); if (this._originalOverviewRuler || this._modifiedOverviewRuler) { this._layoutOverviewRulers(); @@ -1172,7 +1176,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _layoutOverviewViewport(): void { - let layout = this._computeOverviewViewport(); + const layout = this._computeOverviewViewport(); if (!layout) { this._overviewViewportDomElement.setTop(0); this._overviewViewportDomElement.setHeight(0); @@ -1183,20 +1187,20 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _computeOverviewViewport(): { height: number; top: number; } | null { - let layoutInfo = this.modifiedEditor.getLayoutInfo(); + const layoutInfo = this._modifiedEditor.getLayoutInfo(); if (!layoutInfo) { return null; } - let scrollTop = this.modifiedEditor.getScrollTop(); - let scrollHeight = this.modifiedEditor.getScrollHeight(); + const scrollTop = this._modifiedEditor.getScrollTop(); + const scrollHeight = this._modifiedEditor.getScrollHeight(); - let computedAvailableSize = Math.max(0, layoutInfo.height); - let computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); - let computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; + const computedAvailableSize = Math.max(0, layoutInfo.height); + const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); + const computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; - let computedSliderSize = Math.max(0, Math.floor(layoutInfo.height * computedRatio)); - let computedSliderPosition = Math.floor(scrollTop * computedRatio); + const computedSliderSize = Math.max(0, Math.floor(layoutInfo.height * computedRatio)); + const computedSliderPosition = Math.floor(scrollTop * computedRatio); return { height: computedSliderSize, @@ -1223,16 +1227,16 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE }, getOriginalEditor: () => { - return this.originalEditor; + return this._originalEditor; }, getModifiedEditor: () => { - return this.modifiedEditor; + return this._modifiedEditor; } }; } - private _setStrategy(newStrategy: IDiffEditorWidgetStyle): void { + private _setStrategy(newStrategy: DiffEditorWidgetStyle): void { if (this._strategy) { this._strategy.dispose(); } @@ -1255,11 +1259,12 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return null; } - let min = 0, max = lineChanges.length - 1; + let min = 0; + let max = lineChanges.length - 1; while (min < max) { - let mid = Math.floor((min + max) / 2); - let midStart = startLineNumberExtractor(lineChanges[mid]); - let midEnd = (mid + 1 <= max ? startLineNumberExtractor(lineChanges[mid + 1]) : Constants.MAX_SAFE_SMALL_INTEGER); + const mid = Math.floor((min + max) / 2); + const midStart = startLineNumberExtractor(lineChanges[mid]); + const midEnd = (mid + 1 <= max ? startLineNumberExtractor(lineChanges[mid + 1]) : Constants.MAX_SAFE_SMALL_INTEGER); if (lineNumber < midStart) { max = mid - 1; @@ -1275,19 +1280,19 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _getEquivalentLineForOriginalLineNumber(lineNumber: number): number { - let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber); + const lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber); if (!lineChange) { return lineNumber; } - let originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); - let modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); - let lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); - let lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + const originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); + const modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); + const lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); + const lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); - let delta = lineNumber - originalEquivalentLineNumber; + const delta = lineNumber - originalEquivalentLineNumber; if (delta <= lineChangeOriginalLength) { return modifiedEquivalentLineNumber + Math.min(delta, lineChangeModifiedLength); @@ -1297,19 +1302,19 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _getEquivalentLineForModifiedLineNumber(lineNumber: number): number { - let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber); + const lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber); if (!lineChange) { return lineNumber; } - let originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); - let modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); - let lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); - let lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + const originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); + const modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); + const lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); + const lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); - let delta = lineNumber - modifiedEquivalentLineNumber; + const delta = lineNumber - modifiedEquivalentLineNumber; if (delta <= lineChangeModifiedLength) { return originalEquivalentLineNumber + Math.min(delta, lineChangeOriginalLength); @@ -1345,15 +1350,15 @@ interface IDataSource { getContainerDomNode(): HTMLElement; relayoutEditors(): void; - getOriginalEditor(): editorBrowser.ICodeEditor; - getModifiedEditor(): editorBrowser.ICodeEditor; + getOriginalEditor(): CodeEditorWidget; + getModifiedEditor(): CodeEditorWidget; } -abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWidgetStyle { +abstract class DiffEditorWidgetStyle extends Disposable { - _dataSource: IDataSource; - _insertColor: Color | null; - _removeColor: Color | null; + protected _dataSource: IDataSource; + protected _insertColor: Color | null; + protected _removeColor: Color | null; constructor(dataSource: IDataSource) { super(); @@ -1363,15 +1368,15 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi } public applyColors(theme: IColorTheme): boolean { - let newInsertColor = (theme.getColor(diffInserted) || defaultInsertColor).transparent(2); - let newRemoveColor = (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2); - let hasChanges = !newInsertColor.equals(this._insertColor) || !newRemoveColor.equals(this._removeColor); + const newInsertColor = (theme.getColor(diffInserted) || defaultInsertColor).transparent(2); + const newRemoveColor = (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2); + const hasChanges = !newInsertColor.equals(this._insertColor) || !newRemoveColor.equals(this._removeColor); this._insertColor = newInsertColor; this._removeColor = newRemoveColor; return hasChanges; } - public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones { + public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[]): IEditorsDiffDecorationsWithZones { // Get view zones modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => { return a.afterLineNumber - b.afterLineNumber; @@ -1379,11 +1384,11 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi originalWhitespaces = originalWhitespaces.sort((a, b) => { return a.afterLineNumber - b.afterLineNumber; }); - let zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, originalEditor, modifiedEditor, renderIndicators); + const zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, renderIndicators); // Get decorations & overview ruler zones - let originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor); - let modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor); + const originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators); + const modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators); return { original: { @@ -1399,9 +1404,9 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi }; } - protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones; - protected abstract _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; - protected abstract _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; + protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], renderIndicators: boolean): IEditorsZones; + protected abstract _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations; + protected abstract _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations; public abstract setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; public abstract layout(): number; @@ -1410,6 +1415,7 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi interface IMyViewZone { shouldNotShrink?: boolean; afterLineNumber: number; + afterColumn?: number; heightInLines: number; minWidthInPx?: number; domNode: HTMLElement | null; @@ -1442,22 +1448,37 @@ class ForeignViewZonesIterator { abstract class ViewZonesComputer { - private readonly lineChanges: editorCommon.ILineChange[]; - private readonly originalForeignVZ: IEditorWhitespace[]; - private readonly originalLineHeight: number; - private readonly modifiedForeignVZ: IEditorWhitespace[]; - private readonly modifiedLineHeight: number; + constructor( + private readonly _lineChanges: editorCommon.ILineChange[], + private readonly _originalForeignVZ: IEditorWhitespace[], + private readonly _modifiedForeignVZ: IEditorWhitespace[], + protected readonly _originalEditor: CodeEditorWidget, + protected readonly _modifiedEditor: CodeEditorWidget + ) { + } - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], originalLineHeight: number, modifiedForeignVZ: IEditorWhitespace[], modifiedLineHeight: number) { - this.lineChanges = lineChanges; - this.originalForeignVZ = originalForeignVZ; - this.originalLineHeight = originalLineHeight; - this.modifiedForeignVZ = modifiedForeignVZ; - this.modifiedLineHeight = modifiedLineHeight; + private static _getViewLineCount(editor: CodeEditorWidget, startLineNumber: number, endLineNumber: number): number { + const model = editor.getModel(); + const viewModel = editor._getViewModel(); + if (model && viewModel) { + const viewRange = getViewRange(model, viewModel, startLineNumber, endLineNumber); + return (viewRange.endLineNumber - viewRange.startLineNumber + 1); + } + + return (endLineNumber - startLineNumber + 1); } public getViewZones(): IEditorsZones { - let result: { original: IMyViewZone[]; modified: IMyViewZone[]; } = { + const originalLineHeight = this._originalEditor.getOption(EditorOption.lineHeight); + const modifiedLineHeight = this._modifiedEditor.getOption(EditorOption.lineHeight); + const originalHasWrapping = (this._originalEditor.getOption(EditorOption.wrappingInfo).wrappingColumn !== -1); + const modifiedHasWrapping = (this._modifiedEditor.getOption(EditorOption.wrappingInfo).wrappingColumn !== -1); + const hasWrapping = (originalHasWrapping || modifiedHasWrapping); + const originalModel = this._originalEditor.getModel()!; + const originalCoordinatesConverter = this._originalEditor._getViewModel()!.coordinatesConverter; + const modifiedCoordinatesConverter = this._modifiedEditor._getViewModel()!.coordinatesConverter; + + const result: { original: IMyViewZone[]; modified: IMyViewZone[]; } = { original: [], modified: [] }; @@ -1469,13 +1490,13 @@ abstract class ViewZonesComputer { let originalEndEquivalentLineNumber: number = 0; let modifiedEndEquivalentLineNumber: number = 0; - let sortMyViewZones = (a: IMyViewZone, b: IMyViewZone) => { + const sortMyViewZones = (a: IMyViewZone, b: IMyViewZone) => { return a.afterLineNumber - b.afterLineNumber; }; - let addAndCombineIfPossible = (destination: IMyViewZone[], item: IMyViewZone) => { + const addAndCombineIfPossible = (destination: IMyViewZone[], item: IMyViewZone) => { if (item.domNode === null && destination.length > 0) { - let lastItem = destination[destination.length - 1]; + const lastItem = destination[destination.length - 1]; if (lastItem.afterLineNumber === item.afterLineNumber && lastItem.domNode === null) { lastItem.heightInLines += item.heightInLines; return; @@ -1484,18 +1505,21 @@ abstract class ViewZonesComputer { destination.push(item); }; - let modifiedForeignVZ = new ForeignViewZonesIterator(this.modifiedForeignVZ); - let originalForeignVZ = new ForeignViewZonesIterator(this.originalForeignVZ); + const modifiedForeignVZ = new ForeignViewZonesIterator(this._modifiedForeignVZ); + const originalForeignVZ = new ForeignViewZonesIterator(this._originalForeignVZ); + + let lastOriginalLineNumber = 1; + let lastModifiedLineNumber = 1; // In order to include foreign view zones after the last line change, the for loop will iterate once more after the end of the `lineChanges` array - for (let i = 0, length = this.lineChanges.length; i <= length; i++) { - let lineChange = (i < length ? this.lineChanges[i] : null); + for (let i = 0, length = this._lineChanges.length; i <= length; i++) { + const lineChange = (i < length ? this._lineChanges[i] : null); if (lineChange !== null) { originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); - lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); - lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? ViewZonesComputer._getViewLineCount(this._originalEditor, lineChange.originalStartLineNumber, lineChange.originalEndLineNumber) : 0); + lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? ViewZonesComputer._getViewLineCount(this._modifiedEditor, lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber) : 0); originalEndEquivalentLineNumber = Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber); modifiedEndEquivalentLineNumber = Math.max(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber); } else { @@ -1512,6 +1536,48 @@ abstract class ViewZonesComputer { // ---------------------------- PRODUCE VIEW ZONES + // [PRODUCE] View zones due to line mapping differences (equal lines but wrapped differently) + if (hasWrapping) { + let count: number; + if (lineChange) { + if (lineChange.originalEndLineNumber > 0) { + count = lineChange.originalStartLineNumber - lastOriginalLineNumber; + } else { + count = lineChange.modifiedStartLineNumber - lastModifiedLineNumber; + } + } else { + count = originalModel.getLineCount() - lastOriginalLineNumber; + } + + for (let i = 0; i < count; i++) { + const originalLineNumber = lastOriginalLineNumber + i; + const modifiedLineNumber = lastModifiedLineNumber + i; + + const originalViewLineCount = originalCoordinatesConverter.getModelLineViewLineCount(originalLineNumber); + const modifiedViewLineCount = modifiedCoordinatesConverter.getModelLineViewLineCount(modifiedLineNumber); + + if (originalViewLineCount < modifiedViewLineCount) { + stepOriginal.push({ + afterLineNumber: originalLineNumber, + heightInLines: modifiedViewLineCount - originalViewLineCount, + domNode: null, + marginDomNode: null + }); + } else if (originalViewLineCount > modifiedViewLineCount) { + stepModified.push({ + afterLineNumber: modifiedLineNumber, + heightInLines: originalViewLineCount - modifiedViewLineCount, + domNode: null, + marginDomNode: null + }); + } + } + if (lineChange) { + lastOriginalLineNumber = (lineChange.originalEndLineNumber > 0 ? lineChange.originalEndLineNumber : lineChange.originalStartLineNumber) + 1; + lastModifiedLineNumber = (lineChange.modifiedEndLineNumber > 0 ? lineChange.modifiedEndLineNumber : lineChange.modifiedStartLineNumber) + 1; + } + } + // [PRODUCE] View zone(s) in original-side due to foreign view zone(s) in modified-side while (modifiedForeignVZ.current && modifiedForeignVZ.current.afterLineNumber <= modifiedEndEquivalentLineNumber) { let viewZoneLineNumber: number; @@ -1528,7 +1594,7 @@ abstract class ViewZonesComputer { stepOriginal.push({ afterLineNumber: viewZoneLineNumber, - heightInLines: modifiedForeignVZ.current.height / this.modifiedLineHeight, + heightInLines: modifiedForeignVZ.current.height / modifiedLineHeight, domNode: null, marginDomNode: marginDomNode }); @@ -1545,21 +1611,21 @@ abstract class ViewZonesComputer { } stepModified.push({ afterLineNumber: viewZoneLineNumber, - heightInLines: originalForeignVZ.current.height / this.originalLineHeight, + heightInLines: originalForeignVZ.current.height / originalLineHeight, domNode: null }); originalForeignVZ.advance(); } if (lineChange !== null && isChangeOrInsert(lineChange)) { - let r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); + const r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); if (r) { stepOriginal.push(r); } } if (lineChange !== null && isChangeOrDelete(lineChange)) { - let r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); + const r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); if (r) { stepModified.push(r); } @@ -1578,11 +1644,11 @@ abstract class ViewZonesComputer { stepModified = stepModified.sort(sortMyViewZones); while (stepOriginalIndex < stepOriginal.length && stepModifiedIndex < stepModified.length) { - let original = stepOriginal[stepOriginalIndex]; - let modified = stepModified[stepModifiedIndex]; + const original = stepOriginal[stepOriginalIndex]; + const modified = stepModified[stepModifiedIndex]; - let originalDelta = original.afterLineNumber - originalEquivalentLineNumber; - let modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber; + const originalDelta = original.afterLineNumber - originalEquivalentLineNumber; + const modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber; if (originalDelta < modifiedDelta) { addAndCombineIfPossible(result.original, original); @@ -1646,14 +1712,14 @@ abstract class ViewZonesComputer { protected abstract _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null; } -export function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) { +function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) { return { range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), options: options }; } -export const DECORATIONS = { +const DECORATIONS = { charDelete: ModelDecorationOptions.register({ className: 'char-delete' @@ -1678,7 +1744,7 @@ export const DECORATIONS = { }), lineInsertWithSign: ModelDecorationOptions.register({ className: 'line-insert', - linesDecorationsClassName: 'insert-sign ' + diffInsertIcon.classNames, + linesDecorationsClassName: 'insert-sign ' + ThemeIcon.asClassName(diffInsertIcon), marginClassName: 'line-insert', isWholeLine: true }), @@ -1690,7 +1756,7 @@ export const DECORATIONS = { }), lineDeleteWithSign: ModelDecorationOptions.register({ className: 'line-delete', - linesDecorationsClassName: 'delete-sign ' + diffRemoveIcon.classNames, + linesDecorationsClassName: 'delete-sign ' + ThemeIcon.asClassName(diffRemoveIcon), marginClassName: 'line-delete', isWholeLine: true @@ -1701,7 +1767,7 @@ export const DECORATIONS = { }; -export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider { +class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerticalSashLayoutProvider { static readonly MINIMUM_EDITOR_WIDTH = 100; @@ -1724,14 +1790,14 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements this._sash.state = SashState.Disabled; } - this._sash.onDidStart(() => this.onSashDragStart()); - this._sash.onDidChange((e: ISashEvent) => this.onSashDrag(e)); - this._sash.onDidEnd(() => this.onSashDragEnd()); - this._sash.onDidReset(() => this.onSashReset()); + this._sash.onDidStart(() => this._onSashDragStart()); + this._sash.onDidChange((e: ISashEvent) => this._onSashDrag(e)); + this._sash.onDidEnd(() => this._onSashDragEnd()); + this._sash.onDidReset(() => this._onSashReset()); } public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void { - let newDisableSash = (enableSplitViewResizing === false); + const newDisableSash = (enableSplitViewResizing === false); if (this._disableSash !== newDisableSash) { this._disableSash = newDisableSash; this._sash.state = this._disableSash ? SashState.Disabled : SashState.Enabled; @@ -1739,11 +1805,11 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements } public layout(sashRatio: number | null = this._sashRatio): number { - let w = this._dataSource.getWidth(); - let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; + const w = this._dataSource.getWidth(); + const contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth); - let midPoint = Math.floor(0.5 * contentWidth); + const midPoint = Math.floor(0.5 * contentWidth); sashPosition = this._disableSash ? midPoint : sashPosition || midPoint; @@ -1767,25 +1833,25 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements return this._sashPosition; } - private onSashDragStart(): void { + private _onSashDragStart(): void { this._startSashPosition = this._sashPosition!; } - private onSashDrag(e: ISashEvent): void { - let w = this._dataSource.getWidth(); - let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; - let sashPosition = this.layout((this._startSashPosition! + (e.currentX - e.startX)) / contentWidth); + private _onSashDrag(e: ISashEvent): void { + const w = this._dataSource.getWidth(); + const contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; + const sashPosition = this.layout((this._startSashPosition! + (e.currentX - e.startX)) / contentWidth); this._sashRatio = sashPosition / contentWidth; this._dataSource.relayoutEditors(); } - private onSashDragEnd(): void { + private _onSashDragEnd(): void { this._sash.layout(); } - private onSashReset(): void { + private _onSashReset(): void { this._sashRatio = 0.5; this._dataSource.relayoutEditors(); this._sash.layout(); @@ -1803,23 +1869,26 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements return this._dataSource.getHeight(); } - protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones { - let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, originalEditor.getOption(EditorOption.lineHeight), modifiedForeignVZ, modifiedEditor.getOption(EditorOption.lineHeight)); + protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]): IEditorsZones { + const originalEditor = this._dataSource.getOriginalEditor(); + const modifiedEditor = this._dataSource.getModifiedEditor(); + const c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); return c.getViewZones(); } - protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { + const originalEditor = this._dataSource.getOriginalEditor(); const overviewZoneColor = String(this._removeColor); - let result: IEditorDiffDecorations = { + const result: IEditorDiffDecorations = { decorations: [], overviewZones: [] }; - let originalModel = originalEditor.getModel()!; + const originalModel = originalEditor.getModel()!; + const originalViewModel = originalEditor._getViewModel()!; - for (let i = 0, length = lineChanges.length; i < length; i++) { - let lineChange = lineChanges[i]; + for (const lineChange of lineChanges) { if (isChangeOrDelete(lineChange)) { result.decorations.push({ @@ -1830,15 +1899,11 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements result.decorations.push(createDecoration(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charDeleteWholeLine)); } - result.overviewZones.push(new OverviewRulerZone( - lineChange.originalStartLineNumber, - lineChange.originalEndLineNumber, - overviewZoneColor - )); + const viewRange = getViewRange(originalModel, originalViewModel, lineChange.originalStartLineNumber, lineChange.originalEndLineNumber); + result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, overviewZoneColor)); if (lineChange.charChanges) { - for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - let charChange = lineChange.charChanges[j]; + for (const charChange of lineChange.charChanges) { if (isChangeOrDelete(charChange)) { if (ignoreTrimWhitespace) { for (let lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) { @@ -1868,18 +1933,19 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements return result; } - protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { + const modifiedEditor = this._dataSource.getModifiedEditor(); const overviewZoneColor = String(this._insertColor); - let result: IEditorDiffDecorations = { + const result: IEditorDiffDecorations = { decorations: [], overviewZones: [] }; - let modifiedModel = modifiedEditor.getModel()!; + const modifiedModel = modifiedEditor.getModel()!; + const modifiedViewModel = modifiedEditor._getViewModel()!; - for (let i = 0, length = lineChanges.length; i < length; i++) { - let lineChange = lineChanges[i]; + for (const lineChange of lineChanges) { if (isChangeOrInsert(lineChange)) { @@ -1890,15 +1956,12 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements if (!isChangeOrDelete(lineChange) || !lineChange.charChanges) { result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charInsertWholeLine)); } - result.overviewZones.push(new OverviewRulerZone( - lineChange.modifiedStartLineNumber, - lineChange.modifiedEndLineNumber, - overviewZoneColor - )); + + const viewRange = getViewRange(modifiedModel, modifiedViewModel, lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber); + result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, overviewZoneColor)); if (lineChange.charChanges) { - for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - let charChange = lineChange.charChanges[j]; + for (const charChange of lineChange.charChanges) { if (isChangeOrInsert(charChange)) { if (ignoreTrimWhitespace) { for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { @@ -1931,8 +1994,14 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements class SideBySideViewZonesComputer extends ViewZonesComputer { - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], originalLineHeight: number, modifiedForeignVZ: IEditorWhitespace[], modifiedLineHeight: number) { - super(lineChanges, originalForeignVZ, originalLineHeight, modifiedForeignVZ, modifiedLineHeight); + constructor( + lineChanges: editorCommon.ILineChange[], + originalForeignVZ: IEditorWhitespace[], + modifiedForeignVZ: IEditorWhitespace[], + originalEditor: CodeEditorWidget, + modifiedEditor: CodeEditorWidget, + ) { + super(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); } protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null { @@ -1962,18 +2031,18 @@ class SideBySideViewZonesComputer extends ViewZonesComputer { } } -class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle { +class DiffEditorWidgetInline extends DiffEditorWidgetStyle { - private decorationsLeft: number; + private _decorationsLeft: number; constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) { super(dataSource); - this.decorationsLeft = dataSource.getOriginalEditor().getLayoutInfo().decorationsLeft; + this._decorationsLeft = dataSource.getOriginalEditor().getLayoutInfo().decorationsLeft; this._register(dataSource.getOriginalEditor().onDidLayoutChange((layoutInfo: EditorLayoutInfo) => { - if (this.decorationsLeft !== layoutInfo.decorationsLeft) { - this.decorationsLeft = layoutInfo.decorationsLeft; + if (this._decorationsLeft !== layoutInfo.decorationsLeft) { + this._decorationsLeft = layoutInfo.decorationsLeft; dataSource.relayoutEditors(); } })); @@ -1983,21 +2052,26 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito // Nothing to do.. } - protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones { - let computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators); + protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], renderIndicators: boolean): IEditorsZones { + const originalEditor = this._dataSource.getOriginalEditor(); + const modifiedEditor = this._dataSource.getModifiedEditor(); + const computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators); return computer.getViewZones(); } - protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { const overviewZoneColor = String(this._removeColor); - let result: IEditorDiffDecorations = { + const result: IEditorDiffDecorations = { decorations: [], overviewZones: [] }; - for (let i = 0, length = lineChanges.length; i < length; i++) { - let lineChange = lineChanges[i]; + const originalEditor = this._dataSource.getOriginalEditor(); + const originalModel = originalEditor.getModel()!; + const originalViewModel = originalEditor._getViewModel()!; + + for (const lineChange of lineChanges) { // Add overview zones in the overview ruler if (isChangeOrDelete(lineChange)) { @@ -2006,29 +2080,27 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito options: DECORATIONS.lineDeleteMargin }); - result.overviewZones.push(new OverviewRulerZone( - lineChange.originalStartLineNumber, - lineChange.originalEndLineNumber, - overviewZoneColor - )); + const viewRange = getViewRange(originalModel, originalViewModel, lineChange.originalStartLineNumber, lineChange.originalEndLineNumber); + result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, overviewZoneColor)); } } return result; } - protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { + const modifiedEditor = this._dataSource.getModifiedEditor(); const overviewZoneColor = String(this._insertColor); - let result: IEditorDiffDecorations = { + const result: IEditorDiffDecorations = { decorations: [], overviewZones: [] }; - let modifiedModel = modifiedEditor.getModel()!; + const modifiedModel = modifiedEditor.getModel()!; + const modifiedViewModel = modifiedEditor._getViewModel()!; - for (let i = 0, length = lineChanges.length; i < length; i++) { - let lineChange = lineChanges[i]; + for (const lineChange of lineChanges) { // Add decorations & overview zones if (isChangeOrInsert(lineChange)) { @@ -2037,15 +2109,11 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert) }); - result.overviewZones.push(new OverviewRulerZone( - lineChange.modifiedStartLineNumber, - lineChange.modifiedEndLineNumber, - overviewZoneColor - )); + const viewRange = getViewRange(modifiedModel, modifiedViewModel, lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber); + result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, overviewZoneColor)); if (lineChange.charChanges) { - for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - let charChange = lineChange.charChanges[j]; + for (const charChange of lineChange.charChanges) { if (isChangeOrInsert(charChange)) { if (ignoreTrimWhitespace) { for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { @@ -2079,34 +2147,59 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito public layout(): number { // An editor should not be smaller than 5px - return Math.max(5, this.decorationsLeft); + return Math.max(5, this._decorationsLeft); } } +interface InlineModifiedViewZone extends IMyViewZone { + shouldNotShrink: boolean; + afterLineNumber: number; + heightInLines: number; + minWidthInPx: number; + domNode: HTMLElement; + marginDomNode: HTMLElement; + diff: IDiffLinesChange; +} + class InlineViewZonesComputer extends ViewZonesComputer { - private readonly originalModel: ITextModel; - private readonly modifiedEditorOptions: IComputedEditorOptions; - private readonly modifiedEditorTabSize: number; - private readonly renderIndicators: boolean; + private readonly _originalModel: ITextModel; + private readonly _renderIndicators: boolean; + private readonly _pendingLineChange: editorCommon.ILineChange[]; + private readonly _pendingViewZones: InlineModifiedViewZone[]; + private readonly _lineBreaksComputer: ILineBreaksComputer; - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) { - super(lineChanges, originalForeignVZ, originalEditor.getOption(EditorOption.lineHeight), modifiedForeignVZ, modifiedEditor.getOption(EditorOption.lineHeight)); - this.originalModel = originalEditor.getModel()!; - this.modifiedEditorOptions = modifiedEditor.getOptions(); - this.modifiedEditorTabSize = modifiedEditor.getModel()!.getOptions().tabSize; - this.renderIndicators = renderIndicators; + constructor( + lineChanges: editorCommon.ILineChange[], + originalForeignVZ: IEditorWhitespace[], + modifiedForeignVZ: IEditorWhitespace[], + originalEditor: CodeEditorWidget, + modifiedEditor: CodeEditorWidget, + renderIndicators: boolean + ) { + super(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); + this._originalModel = originalEditor.getModel()!; + this._renderIndicators = renderIndicators; + this._pendingLineChange = []; + this._pendingViewZones = []; + this._lineBreaksComputer = this._modifiedEditor._getViewModel()!.createLineBreaksComputer(); + } + + public getViewZones(): IEditorsZones { + const result = super.getViewZones(); + this._finalize(result); + return result; } protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null { - let result = document.createElement('div'); + const result = document.createElement('div'); result.className = 'inline-added-margin-view-zone'; return result; } protected _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null { - let marginDomNode = document.createElement('div'); + const marginDomNode = document.createElement('div'); marginDomNode.className = 'inline-added-margin-view-zone'; return { @@ -2118,57 +2211,17 @@ class InlineViewZonesComputer extends ViewZonesComputer { } protected _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null { - let decorations: InlineDecoration[] = []; - if (lineChange.charChanges) { - for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - let charChange = lineChange.charChanges[j]; - if (isChangeOrDelete(charChange)) { - decorations.push(new InlineDecoration( - new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), - 'char-delete', - InlineDecorationType.Regular - )); - } - } - } - - let sb = createStringBuilder(10000); - let marginDomNode = document.createElement('div'); - const layoutInfo = this.modifiedEditorOptions.get(EditorOption.layoutInfo); - const fontInfo = this.modifiedEditorOptions.get(EditorOption.fontInfo); - const lineDecorationsWidth = layoutInfo.decorationsWidth; - - let lineHeight = this.modifiedEditorOptions.get(EditorOption.lineHeight); - const typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; - let maxCharsPerLine = 0; - const originalContent: string[] = []; - for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { - maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorOptions, this.modifiedEditorTabSize, lineNumber, decorations, sb)); - originalContent.push(this.originalModel.getLineContent(lineNumber)); - - if (this.renderIndicators) { - let index = lineNumber - lineChange.originalStartLineNumber; - const marginElement = document.createElement('div'); - marginElement.className = `delete-sign ${diffRemoveIcon.classNames}`; - marginElement.setAttribute('style', `position:absolute;top:${index * lineHeight}px;width:${lineDecorationsWidth}px;height:${lineHeight}px;right:0;`); - marginDomNode.appendChild(marginElement); - } - } - maxCharsPerLine += this.modifiedEditorOptions.get(EditorOption.scrollBeyondLastColumn); - - let domNode = document.createElement('div'); + const domNode = document.createElement('div'); domNode.className = `view-lines line-delete ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`; - domNode.innerHTML = sb.build(); - Configuration.applyFontInfoSlow(domNode, fontInfo); + const marginDomNode = document.createElement('div'); marginDomNode.className = 'inline-deleted-margin-view-zone'; - Configuration.applyFontInfoSlow(marginDomNode, fontInfo); - return { + const viewZone: InlineModifiedViewZone = { shouldNotShrink: true, afterLineNumber: (lineChange.modifiedEndLineNumber === 0 ? lineChange.modifiedStartLineNumber : lineChange.modifiedStartLineNumber - 1), heightInLines: lineChangeOriginalLength, - minWidthInPx: (maxCharsPerLine * typicalHalfwidthCharacterWidth), + minWidthInPx: 0, domNode: domNode, marginDomNode: marginDomNode, diff: { @@ -2176,31 +2229,199 @@ class InlineViewZonesComputer extends ViewZonesComputer { originalEndLineNumber: lineChange.originalEndLineNumber, modifiedStartLineNumber: lineChange.modifiedStartLineNumber, modifiedEndLineNumber: lineChange.modifiedEndLineNumber, - originalContent: originalContent + originalModel: this._originalModel, + viewLineCounts: null, } }; + + for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { + this._lineBreaksComputer.addRequest(this._originalModel.getLineContent(lineNumber), null); + } + + this._pendingLineChange.push(lineChange); + this._pendingViewZones.push(viewZone); + + return viewZone; } - private _renderOriginalLine(count: number, originalModel: ITextModel, options: IComputedEditorOptions, tabSize: number, lineNumber: number, decorations: InlineDecoration[], sb: IStringBuilder): number { - const lineTokens = originalModel.getLineTokens(lineNumber); - const lineContent = lineTokens.getLineContent(); - const fontInfo = options.get(EditorOption.fontInfo); + private _finalize(result: IEditorsZones): void { + const modifiedEditorOptions = this._modifiedEditor.getOptions(); + const tabSize = this._modifiedEditor.getModel()!.getOptions().tabSize; + const fontInfo = modifiedEditorOptions.get(EditorOption.fontInfo); + const disableMonospaceOptimizations = modifiedEditorOptions.get(EditorOption.disableMonospaceOptimizations); + const typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; + const scrollBeyondLastColumn = modifiedEditorOptions.get(EditorOption.scrollBeyondLastColumn); + const mightContainNonBasicASCII = this._originalModel.mightContainNonBasicASCII(); + const mightContainRTL = this._originalModel.mightContainRTL(); + const lineHeight = modifiedEditorOptions.get(EditorOption.lineHeight); + const layoutInfo = modifiedEditorOptions.get(EditorOption.layoutInfo); + const lineDecorationsWidth = layoutInfo.decorationsWidth; + const stopRenderingLineAfter = modifiedEditorOptions.get(EditorOption.stopRenderingLineAfter); + const renderWhitespace = modifiedEditorOptions.get(EditorOption.renderWhitespace); + const renderControlCharacters = modifiedEditorOptions.get(EditorOption.renderControlCharacters); + const fontLigatures = modifiedEditorOptions.get(EditorOption.fontLigatures); - const actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1); + const lineBreaks = this._lineBreaksComputer.finalize(); + let lineBreakIndex = 0; + + for (let i = 0; i < this._pendingLineChange.length; i++) { + const lineChange = this._pendingLineChange[i]; + const viewZone = this._pendingViewZones[i]; + const domNode = viewZone.domNode; + Configuration.applyFontInfoSlow(domNode, fontInfo); + + const marginDomNode = viewZone.marginDomNode; + Configuration.applyFontInfoSlow(marginDomNode, fontInfo); + + const decorations: InlineDecoration[] = []; + if (lineChange.charChanges) { + for (const charChange of lineChange.charChanges) { + if (isChangeOrDelete(charChange)) { + decorations.push(new InlineDecoration( + new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), + 'char-delete', + InlineDecorationType.Regular + )); + } + } + } + const hasCharChanges = (decorations.length > 0); + + const sb = createStringBuilder(10000); + let maxCharsPerLine = 0; + let renderedLineCount = 0; + let viewLineCounts: number[] | null = null; + for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { + const lineIndex = lineNumber - lineChange.originalStartLineNumber; + const lineTokens = this._originalModel.getLineTokens(lineNumber); + const lineContent = lineTokens.getLineContent(); + const lineBreakData = lineBreaks[lineBreakIndex++]; + const actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1); + + if (lineBreakData) { + let lastBreakOffset = 0; + for (const breakOffset of lineBreakData.breakOffsets) { + const viewLineTokens = lineTokens.sliceAndInflate(lastBreakOffset, breakOffset, 0); + const viewLineContent = lineContent.substring(lastBreakOffset, breakOffset); + maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine( + renderedLineCount++, + viewLineContent, + viewLineTokens, + LineDecoration.extractWrapped(actualDecorations, lastBreakOffset, breakOffset), + hasCharChanges, + mightContainNonBasicASCII, + mightContainRTL, + fontInfo, + disableMonospaceOptimizations, + lineHeight, + lineDecorationsWidth, + stopRenderingLineAfter, + renderWhitespace, + renderControlCharacters, + fontLigatures, + tabSize, + sb, + marginDomNode + )); + lastBreakOffset = breakOffset; + } + if (!viewLineCounts) { + viewLineCounts = []; + } + // make sure all lines before this one have an entry in `viewLineCounts` + while (viewLineCounts.length < lineIndex) { + viewLineCounts[viewLineCounts.length] = 1; + } + viewLineCounts[lineIndex] = lineBreakData.breakOffsets.length; + viewZone.heightInLines += (lineBreakData.breakOffsets.length - 1); + const marginDomNode2 = document.createElement('div'); + marginDomNode2.className = 'line-delete'; + result.original.push({ + afterLineNumber: lineNumber, + afterColumn: 0, + heightInLines: lineBreakData.breakOffsets.length - 1, + domNode: createFakeLinesDiv(), + marginDomNode: marginDomNode2 + }); + } else { + maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine( + renderedLineCount++, + lineContent, + lineTokens, + actualDecorations, + hasCharChanges, + mightContainNonBasicASCII, + mightContainRTL, + fontInfo, + disableMonospaceOptimizations, + lineHeight, + lineDecorationsWidth, + stopRenderingLineAfter, + renderWhitespace, + renderControlCharacters, + fontLigatures, + tabSize, + sb, + marginDomNode + )); + } + } + maxCharsPerLine += scrollBeyondLastColumn; + + const html = sb.build(); + const trustedhtml = ttPolicy ? ttPolicy.createHTML(html) : html; + domNode.innerHTML = trustedhtml as unknown as string; + viewZone.minWidthInPx = (maxCharsPerLine * typicalHalfwidthCharacterWidth); + + if (viewLineCounts) { + // make sure all lines have an entry in `viewLineCounts` + const cnt = lineChange.originalEndLineNumber - lineChange.originalStartLineNumber; + while (viewLineCounts.length <= cnt) { + viewLineCounts[viewLineCounts.length] = 1; + } + } + viewZone.diff.viewLineCounts = viewLineCounts; + } + + result.original.sort((a, b) => { + return a.afterLineNumber - b.afterLineNumber; + }); + } + + private _renderOriginalLine( + renderedLineCount: number, + lineContent: string, + lineTokens: IViewLineTokens, + decorations: LineDecoration[], + hasCharChanges: boolean, + mightContainNonBasicASCII: boolean, + mightContainRTL: boolean, + fontInfo: FontInfo, + disableMonospaceOptimizations: boolean, + lineHeight: number, + lineDecorationsWidth: number, + stopRenderingLineAfter: number, + renderWhitespace: 'selection' | 'none' | 'boundary' | 'trailing' | 'all', + renderControlCharacters: boolean, + fontLigatures: string, + tabSize: number, + sb: IStringBuilder, + marginDomNode: HTMLElement + ): number { sb.appendASCIIString('
'); - const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, originalModel.mightContainNonBasicASCII()); - const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, originalModel.mightContainRTL()); + const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, mightContainNonBasicASCII); + const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, mightContainRTL); const output = renderViewLine(new RenderLineInput( - (fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations)), + (fontInfo.isMonospace && !disableMonospaceOptimizations), fontInfo.canUseHalfwidthRightwardsArrow, lineContent, false, @@ -2208,40 +2429,61 @@ class InlineViewZonesComputer extends ViewZonesComputer { containsRTL, 0, lineTokens, - actualDecorations, + decorations, tabSize, 0, fontInfo.spaceWidth, fontInfo.middotWidth, fontInfo.wsmiddotWidth, - options.get(EditorOption.stopRenderingLineAfter), - options.get(EditorOption.renderWhitespace), - options.get(EditorOption.renderControlCharacters), - options.get(EditorOption.fontLigatures) !== EditorFontLigatures.OFF, + stopRenderingLineAfter, + renderWhitespace, + renderControlCharacters, + fontLigatures !== EditorFontLigatures.OFF, null // Send no selections, original line cannot be selected ), sb); sb.appendASCIIString('
'); + if (this._renderIndicators) { + const marginElement = document.createElement('div'); + marginElement.className = `delete-sign ${ThemeIcon.asClassName(diffRemoveIcon)}`; + marginElement.setAttribute('style', `position:absolute;top:${renderedLineCount * lineHeight}px;width:${lineDecorationsWidth}px;height:${lineHeight}px;right:0;`); + marginDomNode.appendChild(marginElement); + } + const absoluteOffsets = output.characterMapping.getAbsoluteOffsets(); return absoluteOffsets.length > 0 ? absoluteOffsets[absoluteOffsets.length - 1] : 0; } } -export function isChangeOrInsert(lineChange: editorCommon.IChange): boolean { +function validateDiffWordWrap(value: 'off' | 'on' | 'inherit' | undefined, defaultValue: 'off' | 'on' | 'inherit'): 'off' | 'on' | 'inherit' { + return validateStringSetOption<'off' | 'on' | 'inherit'>(value, defaultValue, ['off', 'on', 'inherit']); +} + +function isChangeOrInsert(lineChange: editorCommon.IChange): boolean { return lineChange.modifiedEndLineNumber > 0; } -export function isChangeOrDelete(lineChange: editorCommon.IChange): boolean { +function isChangeOrDelete(lineChange: editorCommon.IChange): boolean { return lineChange.originalEndLineNumber > 0; } function createFakeLinesDiv(): HTMLElement { - let r = document.createElement('div'); + const r = document.createElement('div'); r.className = 'diagonal-fill'; return r; } +function getViewRange(model: ITextModel, viewModel: IViewModel, startLineNumber: number, endLineNumber: number): Range { + const lineCount = model.getLineCount(); + startLineNumber = Math.min(lineCount, Math.max(1, startLineNumber)); + endLineNumber = Math.min(lineCount, Math.max(1, endLineNumber)); + return viewModel.coordinatesConverter.convertModelRangeToViewRange(new Range( + startLineNumber, model.getLineMinColumn(startLineNumber), + endLineNumber, model.getLineMaxColumn(endLineNumber) + )); +} + registerThemingParticipant((theme, collector) => { const added = theme.getColor(diffInserted); if (added) { diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 693261364..fd53314e1 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -29,9 +29,10 @@ import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Constants } from 'vs/base/common/uint'; -import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; const DIFF_LINES_PADDING = 3; @@ -73,9 +74,9 @@ class Diff { } } -const diffReviewInsertIcon = registerIcon('diff-review-insert', Codicon.add); -const diffReviewRemoveIcon = registerIcon('diff-review-remove', Codicon.remove); -const diffReviewCloseIcon = registerIcon('diff-review-close', Codicon.close); +const diffReviewInsertIcon = registerIcon('diff-review-insert', Codicon.add, nls.localize('diffReviewInsertIcon', 'Icon for \'Insert\' in diff review.')); +const diffReviewRemoveIcon = registerIcon('diff-review-remove', Codicon.remove, nls.localize('diffReviewRemoveIcon', 'Icon for \'Remove\' in diff review.')); +const diffReviewCloseIcon = registerIcon('diff-review-close', Codicon.close, nls.localize('diffReviewCloseIcon', 'Icon for \'Close\' in diff review.')); export class DiffReview extends Disposable { @@ -104,7 +105,7 @@ export class DiffReview extends Disposable { this.actionBarContainer.domNode )); - this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review ' + diffReviewCloseIcon.classNames, true, () => { + this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review ' + ThemeIcon.asClassName(diffReviewCloseIcon), true, () => { this.hide(); return Promise.resolve(null); }), { label: false, icon: true }); @@ -647,7 +648,7 @@ export class DiffReview extends Disposable { let rowClassName: string = 'diff-review-row'; let lineNumbersExtraClassName: string = ''; const spacerClassName: string = 'diff-review-spacer'; - let spacerIcon: Codicon | null = null; + let spacerIcon: ThemeIcon | null = null; switch (type) { case DiffEntryType.Insert: rowClassName = 'diff-review-row line-insert'; @@ -723,7 +724,7 @@ export class DiffReview extends Disposable { if (spacerIcon) { const spacerCodicon = document.createElement('span'); - spacerCodicon.className = spacerIcon.classNames; + spacerCodicon.className = ThemeIcon.asClassName(spacerIcon); spacerCodicon.innerText = '\u00a0\u00a0'; spacer.appendChild(spacerCodicon); } else { diff --git a/src/vs/editor/browser/widget/inlineDiffMargin.ts b/src/vs/editor/browser/widget/inlineDiffMargin.ts index 7ae64d75c..baf5aadce 100644 --- a/src/vs/editor/browser/widget/inlineDiffMargin.ts +++ b/src/vs/editor/browser/widget/inlineDiffMargin.ts @@ -14,13 +14,15 @@ import { Range } from 'vs/editor/common/core/range'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Codicon } from 'vs/base/common/codicons'; +import { ITextModel } from 'vs/editor/common/model'; export interface IDiffLinesChange { readonly originalStartLineNumber: number; readonly originalEndLineNumber: number; readonly modifiedStartLineNumber: number; readonly modifiedEndLineNumber: number; - readonly originalContent: string[]; + readonly originalModel: ITextModel; + viewLineCounts: number[] | null; } export class InlineDiffMargin extends Disposable { @@ -45,12 +47,12 @@ export class InlineDiffMargin extends Disposable { } constructor( - private _viewZoneId: string, - private _marginDomNode: HTMLElement, - public editor: CodeEditorWidget, - public diff: IDiffLinesChange, - private _contextMenuService: IContextMenuService, - private _clipboardService: IClipboardService + private readonly _viewZoneId: string, + private readonly _marginDomNode: HTMLElement, + public readonly editor: CodeEditorWidget, + public readonly diff: IDiffLinesChange, + private readonly _contextMenuService: IContextMenuService, + private readonly _clipboardService: IClipboardService ) { super(); @@ -79,7 +81,9 @@ export class InlineDiffMargin extends Disposable { undefined, true, async () => { - await this._clipboardService.writeText(diff.originalContent.join(lineFeed) + lineFeed); + const range = new Range(diff.originalStartLineNumber, 1, diff.originalEndLineNumber + 1, 1); + const deletedText = diff.originalModel.getValueInRange(range); + await this._clipboardService.writeText(deletedText); } )); @@ -92,7 +96,8 @@ export class InlineDiffMargin extends Disposable { undefined, true, async () => { - await this._clipboardService.writeText(diff.originalContent[currentLineNumberOffset]); + const lineContent = diff.originalModel.getLineContent(diff.originalStartLineNumber + currentLineNumberOffset); + await this._clipboardService.writeText(lineContent); } ); @@ -102,13 +107,15 @@ export class InlineDiffMargin extends Disposable { const readOnly = editor.getOption(EditorOption.readOnly); if (!readOnly) { actions.push(new Action('diff.inline.revertChange', nls.localize('diff.inline.revertChange.label', "Revert this change"), undefined, true, async () => { + const range = new Range(diff.originalStartLineNumber, 1, diff.originalEndLineNumber, diff.originalModel.getLineMaxColumn(diff.originalEndLineNumber)); + const deletedText = diff.originalModel.getValueInRange(range); if (diff.modifiedEndLineNumber === 0) { // deletion only const column = editor.getModel()!.getLineMaxColumn(diff.modifiedStartLineNumber); editor.executeEdits('diffEditor', [ { range: new Range(diff.modifiedStartLineNumber, column, diff.modifiedStartLineNumber, column), - text: lineFeed + diff.originalContent.join(lineFeed) + text: lineFeed + deletedText } ]); } else { @@ -116,7 +123,7 @@ export class InlineDiffMargin extends Disposable { editor.executeEdits('diffEditor', [ { range: new Range(diff.modifiedStartLineNumber, 1, diff.modifiedEndLineNumber, column), - text: diff.originalContent.join(lineFeed) + text: deletedText } ]); } @@ -189,6 +196,15 @@ export class InlineDiffMargin extends Disposable { const lineNumberOffset = Math.floor(offset / lineHeight); const newTop = lineNumberOffset * lineHeight; this._diffActions.style.top = `${newTop}px`; + if (this.diff.viewLineCounts) { + let acc = 0; + for (let i = 0; i < this.diff.viewLineCounts.length; i++) { + acc += this.diff.viewLineCounts[i]; + if (lineNumberOffset < acc) { + return i; + } + } + } return lineNumberOffset; } } diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 3bde5dfd8..bf1d608fc 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -502,6 +502,16 @@ const editorConfiguration: IConfigurationNode = { default: true, description: nls.localize('wordBasedSuggestions', "Controls whether completions should be computed based on words in the document.") }, + 'editor.wordBasedSuggestionsMode': { + enum: ['currentDocument', 'matchingDocuments', 'allDocuments'], + default: 'matchingDocuments', + enumDescriptions: [ + nls.localize('wordBasedSuggestionsMode.currentDocument', 'Only suggest words from the active document.'), + nls.localize('wordBasedSuggestionsMode.matchingDocuments', 'Suggest words from all open documents of the same language.'), + nls.localize('wordBasedSuggestionsMode.allDocuments', 'Suggest words from all open documents.') + ], + description: nls.localize('wordBasedSuggestionsMode', "Controls form what documents word based completions are computed.") + }, 'editor.semanticHighlighting.enabled': { enum: [true, false, 'configuredByTheme'], enumDescriptions: [ @@ -546,6 +556,16 @@ const editorConfiguration: IConfigurationNode = { type: 'boolean', default: false, description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.") + }, + 'diffEditor.wordWrap': { + type: 'string', + enum: ['off', 'on', 'inherit'], + default: 'inherit', + markdownEnumDescriptions: [ + nls.localize('wordWrap.off', "Lines will never wrap."), + nls.localize('wordWrap.on', "Lines will wrap at the viewport width."), + nls.localize('wordWrap.inherit', "Lines will wrap according to the `#editor.wordWrap#` setting."), + ] } } }; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index a49bf9ce5..1c6b481c7 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -144,9 +144,13 @@ export interface IEditorOptions { */ readOnly?: boolean; /** - * Rename matching regions on type. + * Enable linked editing. * Defaults to false. */ + linkedEditing?: boolean; + /** + * deprecated, use linkedEditing instead + */ renameOnType?: boolean; /** * Should the editor render validation decorations. @@ -260,6 +264,14 @@ export interface IEditorOptions { * Defaults to "off". */ wordWrap?: 'off' | 'on' | 'wordWrapColumn' | 'bounded'; + /** + * Override the `wordWrap` setting. + */ + wordWrapOverride1?: 'off' | 'on' | 'inherit'; + /** + * Override the `wordWrapOverride1` setting. + */ + wordWrapOverride2?: 'off' | 'on' | 'inherit'; /** * Control the wrapping of the editor. * When `wordWrap` = "off", the lines will never wrap. @@ -269,11 +281,6 @@ export interface IEditorOptions { * Defaults to 80. */ wordWrapColumn?: number; - /** - * Force word wrapping when the text appears to be of a minified/generated file. - * Defaults to true. - */ - wordWrapMinified?: boolean; /** * Control indentation of wrapped lines. Can be: 'none', 'same', 'indent' or 'deepIndent'. * Defaults to 'same' in vscode and to 'none' in monaco-editor. @@ -370,6 +377,10 @@ export interface IEditorOptions { * Suggest options. */ suggest?: ISuggestOptions; + /** + * Smart select opptions; + */ + smartSelect?: ISmartSelectOptions; /** * */ @@ -416,6 +427,11 @@ export interface IEditorOptions { * Defaults to advanced. */ autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'; + /** + * Emulate selection behaviour of tab characters when using spaces for indentation. + * This means selection will stick to tab stops. + */ + stickyTabStops?: boolean; /** * Enable format on type. * Defaults to false. @@ -491,6 +507,14 @@ export interface IEditorOptions { * Defaults to true. */ codeLens?: boolean; + /** + * Code lens font family. Defaults to editor font family. + */ + codeLensFontFamily?: string; + /** + * Code lens font size. Default to 90% of the editor font size + */ + codeLensFontSize?: number; /** * Control the behavior and rendering of the code action lightbulb. */ @@ -644,15 +668,19 @@ export interface IDiffEditorOptions extends IEditorOptions { */ originalEditable?: boolean; /** - * Original editor should be have code lens enabled? + * Should the diff editor enable code lens? * Defaults to false. */ - originalCodeLens?: boolean; + diffCodeLens?: boolean; /** - * Modified editor should be have code lens enabled? - * Defaults to false. + * Is the diff editor inside another editor + * Defaults to false */ - modifiedCodeLens?: boolean; + isInEmbeddedEditor?: boolean; + /** + * Control the wrapping of the diff editor. + */ + diffWordWrap?: 'off' | 'on' | 'inherit'; } //#endregion @@ -828,18 +856,21 @@ class SimpleEditorOption implements IEditorOption extends SimpleEditorOption { - - public static boolean(value: any, defaultValue: boolean): boolean { - if (typeof value === 'undefined') { - return defaultValue; - } - if (value === 'false') { - // treat the string 'false' as false - return false; - } - return Boolean(value); +/** + * @internal + */ +export function boolean(value: any, defaultValue: boolean): boolean { + if (typeof value === 'undefined') { + return defaultValue; } + if (value === 'false') { + // treat the string 'false' as false + return false; + } + return Boolean(value); +} + +class EditorBooleanOption extends SimpleEditorOption { constructor(id: K1, name: PossibleKeyName, defaultValue: boolean, schema: IConfigurationPropertySchema | undefined = undefined) { if (typeof schema !== 'undefined') { @@ -850,7 +881,7 @@ class EditorBooleanOption extends SimpleEditorOption extends SimpleEditorOption extends SimpleEditorOption { - - public static stringSet(value: T | undefined, defaultValue: T, allowedValues: ReadonlyArray): T { - if (typeof value !== 'string') { - return defaultValue; - } - if (allowedValues.indexOf(value) === -1) { - return defaultValue; - } - return value; +/** + * @internal + */ +export function stringSet(value: T | undefined, defaultValue: T, allowedValues: ReadonlyArray): T { + if (typeof value !== 'string') { + return defaultValue; } + if (allowedValues.indexOf(value) === -1) { + return defaultValue; + } + return value; +} + +class EditorStringEnumOption extends SimpleEditorOption { private readonly _allowedValues: ReadonlyArray; @@ -975,7 +1009,7 @@ class EditorStringEnumOption extends } public validate(input: any): V { - return EditorStringEnumOption.stringSet(input, this.defaultValue, this._allowedValues); + return stringSet(input, this.defaultValue, this._allowedValues); } } @@ -1113,8 +1147,8 @@ class EditorComments extends BaseEditorOption } const input = _input as IEditorFindOptions; return { - cursorMoveOnType: EditorBooleanOption.boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType), - seedSearchStringFromSelection: EditorBooleanOption.boolean(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection), + cursorMoveOnType: boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType), + seedSearchStringFromSelection: boolean(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection), autoFindInSelection: typeof _input.autoFindInSelection === 'boolean' ? (_input.autoFindInSelection ? 'always' : 'never') - : EditorStringEnumOption.stringSet<'never' | 'always' | 'multiline'>(input.autoFindInSelection, this.defaultValue.autoFindInSelection, ['never', 'always', 'multiline']), - globalFindClipboard: EditorBooleanOption.boolean(input.globalFindClipboard, this.defaultValue.globalFindClipboard), - addExtraSpaceOnTop: EditorBooleanOption.boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop), - loop: EditorBooleanOption.boolean(input.loop, this.defaultValue.loop), + : stringSet<'never' | 'always' | 'multiline'>(input.autoFindInSelection, this.defaultValue.autoFindInSelection, ['never', 'always', 'multiline']), + globalFindClipboard: boolean(input.globalFindClipboard, this.defaultValue.globalFindClipboard), + addExtraSpaceOnTop: boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop), + loop: boolean(input.loop, this.defaultValue.loop), }; } } @@ -1406,14 +1440,14 @@ export class EditorFontLigatures extends BaseEditorOption(input.multiple, this.defaultValue.multiple!, ['peek', 'gotoAndPeek', 'goto']), - multipleDefinitions: input.multipleDefinitions ?? EditorStringEnumOption.stringSet(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), - multipleTypeDefinitions: input.multipleTypeDefinitions ?? EditorStringEnumOption.stringSet(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), - multipleDeclarations: input.multipleDeclarations ?? EditorStringEnumOption.stringSet(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']), - multipleImplementations: input.multipleImplementations ?? EditorStringEnumOption.stringSet(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']), - multipleReferences: input.multipleReferences ?? EditorStringEnumOption.stringSet(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multiple: stringSet(input.multiple, this.defaultValue.multiple!, ['peek', 'gotoAndPeek', 'goto']), + multipleDefinitions: input.multipleDefinitions ?? stringSet(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multipleTypeDefinitions: input.multipleTypeDefinitions ?? stringSet(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multipleDeclarations: input.multipleDeclarations ?? stringSet(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multipleImplementations: input.multipleImplementations ?? stringSet(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multipleReferences: input.multipleReferences ?? stringSet(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']), alternativeDefinitionCommand: EditorStringOption.string(input.alternativeDefinitionCommand, this.defaultValue.alternativeDefinitionCommand), alternativeTypeDefinitionCommand: EditorStringOption.string(input.alternativeTypeDefinitionCommand, this.defaultValue.alternativeTypeDefinitionCommand), alternativeDeclarationCommand: EditorStringOption.string(input.alternativeDeclarationCommand, this.defaultValue.alternativeDeclarationCommand), @@ -1722,9 +1756,9 @@ class EditorHover extends BaseEditorOption(input.size, this.defaultValue.size, ['proportional', 'fill', 'fit']), - side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']), - showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']), - renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters), + enabled: boolean(input.enabled, this.defaultValue.enabled), + size: stringSet<'proportional' | 'fill' | 'fit'>(input.size, this.defaultValue.size, ['proportional', 'fill', 'fit']), + side: stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']), + showSlider: stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']), + renderCharacters: boolean(input.renderCharacters, this.defaultValue.renderCharacters), scale: EditorIntOption.clampedInt(input.scale, 1, 1, 3), maxColumn: EditorIntOption.clampedInt(input.maxColumn, this.defaultValue.maxColumn, 1, 10000), }; @@ -2598,8 +2634,8 @@ class EditorParameterHints extends BaseEditorOption>; + +class SmartSelect extends BaseEditorOption { + + constructor() { + super( + EditorOption.smartSelect, 'smartSelect', + { + selectLeadingAndTrailingWhitespace: true + }, + { + 'editor.smartSelect.selectLeadingAndTrailingWhitespace': { + description: nls.localize('selectLeadingAndTrailingWhitespace', "Whether leading and trailing whitespace should always be selected."), + default: true, + type: 'boolean' + } + } + ); + } + + public validate(input: any): Readonly> { + if (!input || typeof input !== 'object') { + return this.defaultValue; + } + return { + selectLeadingAndTrailingWhitespace: boolean((input as ISmartSelectOptions).selectLeadingAndTrailingWhitespace, this.defaultValue.selectLeadingAndTrailingWhitespace) }; } } @@ -3552,6 +3646,8 @@ export const enum EditorOption { automaticLayout, autoSurround, codeLens, + codeLensFontFamily, + codeLensFontSize, colorDecorators, columnSelection, comments, @@ -3594,6 +3690,7 @@ export const enum EditorOption { lineHeight, lineNumbers, lineNumbersMinChars, + linkedEditing, links, matchBrackets, minimap, @@ -3634,7 +3731,9 @@ export const enum EditorOption { showFoldingControls, showUnused, snippetSuggestions, + smartSelect, smoothScrolling, + stickyTabStops, stopRenderingLineAfter, suggest, suggestFontSize, @@ -3650,7 +3749,8 @@ export const enum EditorOption { wordWrapBreakAfterCharacters, wordWrapBreakBeforeCharacters, wordWrapColumn, - wordWrapMinified, + wordWrapOverride1, + wordWrapOverride2, wrappingIndent, wrappingStrategy, showDeprecated, @@ -3776,10 +3876,25 @@ export const EditorOptions = { description: nls.localize('autoSurround', "Controls whether the editor should automatically surround selections when typing quotes or brackets.") } )), + stickyTabStops: register(new EditorBooleanOption( + EditorOption.stickyTabStops, 'stickyTabStops', false, + { description: nls.localize('stickyTabStops', "Emulate selection behaviour of tab characters when using spaces for indentation. Selection will stick to tab stops.") } + )), codeLens: register(new EditorBooleanOption( EditorOption.codeLens, 'codeLens', true, { description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.") } )), + codeLensFontFamily: register(new EditorStringOption( + EditorOption.codeLensFontFamily, 'codeLensFontFamily', '', + { description: nls.localize('codeLensFontFamily', "Controls the font family for CodeLens.") } + )), + codeLensFontSize: register(new EditorIntOption(EditorOption.codeLensFontSize, 'codeLensFontSize', 0, 0, 100, { + type: 'number', + default: 0, + minimum: 0, + maximum: 100, + description: nls.localize('codeLensFontSize', "Controls the font size in pixels for CodeLens. When set to `0`, the 90% of `#editor.fontSize#` is used.") + })), colorDecorators: register(new EditorBooleanOption( EditorOption.colorDecorators, 'colorDecorators', true, { description: nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") } @@ -3914,7 +4029,7 @@ export const EditorOptions = { )), hover: register(new EditorHover()), inDiffEditor: register(new EditorBooleanOption( - EditorOption.inDiffEditor, 'inDiffEditor', false, + EditorOption.inDiffEditor, 'inDiffEditor', false )), letterSpacing: register(new EditorFloatOption( EditorOption.letterSpacing, 'letterSpacing', @@ -3929,6 +4044,10 @@ export const EditorOptions = { EditorOption.lineNumbersMinChars, 'lineNumbersMinChars', 5, 1, 300 )), + linkedEditing: register(new EditorBooleanOption( + EditorOption.linkedEditing, 'linkedEditing', false, + { description: nls.localize('linkedEditing', "Controls whether the editor has linked editing enabled. Depending on the language, related symbols, e.g. HTML tags, are updated while editing.") } + )), links: register(new EditorBooleanOption( EditorOption.links, 'links', true, { description: nls.localize('links', "Controls whether the editor should detect links and make them clickable.") } @@ -4030,7 +4149,7 @@ export const EditorOptions = { )), renameOnType: register(new EditorBooleanOption( EditorOption.renameOnType, 'renameOnType', false, - { description: nls.localize('renameOnType', "Controls whether the editor auto renames on type.") } + { description: nls.localize('renameOnType', "Controls whether the editor auto renames on type."), markdownDeprecationMessage: nls.localize('renameOnTypeDeprecate', "Deprecated, use `editor.linkedEditing` instead.") } )), renderControlCharacters: register(new EditorBooleanOption( EditorOption.renderControlCharacters, 'renderControlCharacters', false, @@ -4153,6 +4272,7 @@ export const EditorOptions = { description: nls.localize('snippetSuggestions', "Controls whether snippets are shown with other suggestions and how they are sorted.") } )), + smartSelect: register(new SmartSelect()), smoothScrolling: register(new EditorBooleanOption( EditorOption.smoothScrolling, 'smoothScrolling', false, { description: nls.localize('smoothScrolling', "Controls whether the editor will scroll using an animation.") } @@ -4170,7 +4290,7 @@ export const EditorOptions = { suggestLineHeight: register(new EditorIntOption( EditorOption.suggestLineHeight, 'suggestLineHeight', 0, 0, 1000, - { markdownDescription: nls.localize('suggestLineHeight', "Line height for the suggest widget. When set to `0`, the value of `#editor.lineHeight#` is used.") } + { markdownDescription: nls.localize('suggestLineHeight', "Line height for the suggest widget. When set to `0`, the value of `#editor.lineHeight#` is used. The minimum value is 8.") } )), suggestOnTriggerCharacters: register(new EditorBooleanOption( EditorOption.suggestOnTriggerCharacters, 'suggestOnTriggerCharacters', true, @@ -4279,8 +4399,15 @@ export const EditorOptions = { }, "Controls the wrapping column of the editor when `#editor.wordWrap#` is `wordWrapColumn` or `bounded`.") } )), - wordWrapMinified: register(new EditorBooleanOption( - EditorOption.wordWrapMinified, 'wordWrapMinified', true, + wordWrapOverride1: register(new EditorStringEnumOption( + EditorOption.wordWrapOverride1, 'wordWrapOverride1', + 'inherit' as 'off' | 'on' | 'inherit', + ['off', 'on', 'inherit'] as const + )), + wordWrapOverride2: register(new EditorStringEnumOption( + EditorOption.wordWrapOverride2, 'wordWrapOverride2', + 'inherit' as 'off' | 'on' | 'inherit', + ['off', 'on', 'inherit'] as const )), wrappingIndent: register(new EditorEnumOption( EditorOption.wrappingIndent, 'wrappingIndent', diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 7a966ed22..2bb67894d 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -531,7 +531,7 @@ export class Cursor extends Disposable { } const closeChar = m[1]; - const autoClosingPairsCandidates = this.context.cursorConfig.autoClosingPairsClose2.get(closeChar); + const autoClosingPairsCandidates = this.context.cursorConfig.autoClosingPairs.autoClosingPairsCloseSingleChar.get(closeChar); if (!autoClosingPairsCandidates || autoClosingPairsCandidates.length !== 1) { return null; } diff --git a/src/vs/editor/common/controller/cursorAtomicMoveOperations.ts b/src/vs/editor/common/controller/cursorAtomicMoveOperations.ts new file mode 100644 index 000000000..4b3a23527 --- /dev/null +++ b/src/vs/editor/common/controller/cursorAtomicMoveOperations.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from 'vs/base/common/charCode'; +import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; + +export const enum Direction { + Left, + Right, + Nearest, +} + +export class AtomicTabMoveOperations { + /** + * Get the visible column at the position. If we get to a non-whitespace character first + * or past the end of string then return -1. + * + * **Note** `position` and the return value are 0-based. + */ + public static whitespaceVisibleColumn(lineContent: string, position: number, tabSize: number): [number, number, number] { + const lineLength = lineContent.length; + let visibleColumn = 0; + let prevTabStopPosition = -1; + let prevTabStopVisibleColumn = -1; + for (let i = 0; i < lineLength; i++) { + if (i === position) { + return [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn]; + } + if (visibleColumn % tabSize === 0) { + prevTabStopPosition = i; + prevTabStopVisibleColumn = visibleColumn; + } + const chCode = lineContent.charCodeAt(i); + switch (chCode) { + case CharCode.Space: + visibleColumn += 1; + break; + case CharCode.Tab: + // Skip to the next multiple of tabSize. + visibleColumn = CursorColumns.nextRenderTabStop(visibleColumn, tabSize); + break; + default: + return [-1, -1, -1]; + } + } + if (position === lineLength) { + return [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn]; + } + return [-1, -1, -1]; + } + + /** + * Return the position that should result from a move left, right or to the + * nearest tab, if atomic tabs are enabled. Left and right are used for the + * arrow key movements, nearest is used for mouse selection. It returns + * -1 if atomic tabs are not relevant and you should fall back to normal + * behaviour. + * + * **Note**: `position` and the return value are 0-based. + */ + public static atomicPosition(lineContent: string, position: number, tabSize: number, direction: Direction): number { + const lineLength = lineContent.length; + + // Get the 0-based visible column corresponding to the position, or return + // -1 if it is not in the initial whitespace. + const [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn] = AtomicTabMoveOperations.whitespaceVisibleColumn(lineContent, position, tabSize); + + if (visibleColumn === -1) { + return -1; + } + + // Is the output left or right of the current position. The case for nearest + // where it is the same as the current position is handled in the switch. + let left: boolean; + switch (direction) { + case Direction.Left: + left = true; + break; + case Direction.Right: + left = false; + break; + case Direction.Nearest: + // The code below assumes the output position is either left or right + // of the input position. If it is the same, return immediately. + if (visibleColumn % tabSize === 0) { + return position; + } + // Go to the nearest indentation. + left = visibleColumn % tabSize <= (tabSize / 2); + break; + } + + // If going left, we can just use the info about the last tab stop position and + // last tab stop visible column that we computed in the first walk over the whitespace. + if (left) { + if (prevTabStopPosition === -1) { + return -1; + } + // If the direction is left, we need to keep scanning right to ensure + // that targetVisibleColumn + tabSize is before non-whitespace. + // This is so that when we press left at the end of a partial + // indentation it only goes one character. For example ' foo' with + // tabSize 4, should jump from position 6 to position 5, not 4. + let currentVisibleColumn = prevTabStopVisibleColumn; + for (let i = prevTabStopPosition; i < lineLength; ++i) { + if (currentVisibleColumn === prevTabStopVisibleColumn + tabSize) { + // It is a full indentation. + return prevTabStopPosition; + } + + const chCode = lineContent.charCodeAt(i); + switch (chCode) { + case CharCode.Space: + currentVisibleColumn += 1; + break; + case CharCode.Tab: + currentVisibleColumn = CursorColumns.nextRenderTabStop(currentVisibleColumn, tabSize); + break; + default: + return -1; + } + } + if (currentVisibleColumn === prevTabStopVisibleColumn + tabSize) { + return prevTabStopPosition; + } + // It must have been a partial indentation. + return -1; + } + + // We are going right. + const targetVisibleColumn = CursorColumns.nextRenderTabStop(visibleColumn, tabSize); + + // We can just continue from where whitespaceVisibleColumn got to. + let currentVisibleColumn = visibleColumn; + for (let i = position; i < lineLength; i++) { + if (currentVisibleColumn === targetVisibleColumn) { + return i; + } + + const chCode = lineContent.charCodeAt(i); + switch (chCode) { + case CharCode.Space: + currentVisibleColumn += 1; + break; + case CharCode.Tab: + currentVisibleColumn = CursorColumns.nextRenderTabStop(currentVisibleColumn, tabSize); + break; + default: + return -1; + } + } + // This condition handles when the target column is at the end of the line. + if (currentVisibleColumn === targetVisibleColumn) { + return lineLength; + } + return -1; + } +} diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index 92d36e239..e35e89e06 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -14,7 +14,7 @@ import { ICommand, IConfiguration } from 'vs/editor/common/editorCommon'; import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; -import { IAutoClosingPair, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; +import { AutoClosingPairs, IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; import { Constants } from 'vs/base/common/uint'; @@ -62,6 +62,7 @@ export class CursorConfiguration { public readonly tabSize: number; public readonly indentSize: number; public readonly insertSpaces: boolean; + public readonly stickyTabStops: boolean; public readonly pageSize: number; public readonly lineHeight: number; public readonly useTabStops: boolean; @@ -75,8 +76,7 @@ export class CursorConfiguration { public readonly autoClosingOvertype: EditorAutoClosingOvertypeStrategy; public readonly autoSurround: EditorAutoSurroundStrategy; public readonly autoIndent: EditorAutoIndentStrategy; - public readonly autoClosingPairsOpen2: Map; - public readonly autoClosingPairsClose2: Map; + public readonly autoClosingPairs: AutoClosingPairs; public readonly surroundingPairs: CharacterMap; public readonly shouldAutoCloseBefore: { quote: (ch: string) => boolean, bracket: (ch: string) => boolean }; @@ -114,6 +114,7 @@ export class CursorConfiguration { this.tabSize = modelOptions.tabSize; this.indentSize = modelOptions.indentSize; this.insertSpaces = modelOptions.insertSpaces; + this.stickyTabStops = options.get(EditorOption.stickyTabStops); this.lineHeight = options.get(EditorOption.lineHeight); this.pageSize = Math.max(1, Math.floor(layoutInfo.height / this.lineHeight) - 2); this.useTabStops = options.get(EditorOption.useTabStops); @@ -136,9 +137,7 @@ export class CursorConfiguration { bracket: CursorConfiguration._getShouldAutoClose(languageIdentifier, this.autoClosingBrackets) }; - const autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id); - this.autoClosingPairsOpen2 = autoClosingPairs.autoClosingPairsOpen; - this.autoClosingPairsClose2 = autoClosingPairs.autoClosingPairsClose; + this.autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id); let surroundingPairs = CursorConfiguration._getSurroundingPairs(languageIdentifier); if (surroundingPairs) { @@ -557,14 +556,14 @@ export class CursorColumns { } /** - * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) */ public static prevRenderTabStop(column: number, tabSize: number): number { return column - 1 - (column - 1) % tabSize; } /** - * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) */ public static prevIndentTabStop(column: number, indentSize: number): number { return column - 1 - (column - 1) % indentSize; diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index 1147e78ae..1a4a86d87 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -122,7 +122,7 @@ export class DeleteOperations { public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, Array] { - if (this.isAutoClosingPairDelete(config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairsOpen2, model, selections)) { + if (this.isAutoClosingPairDelete(config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairs.autoClosingPairsOpenByEnd, model, selections)) { return this._runAutoClosingPairDelete(config, model, selections); } diff --git a/src/vs/editor/common/controller/cursorMoveCommands.ts b/src/vs/editor/common/controller/cursorMoveCommands.ts index bc9bbb1c3..909a2255d 100644 --- a/src/vs/editor/common/controller/cursorMoveCommands.ts +++ b/src/vs/editor/common/controller/cursorMoveCommands.ts @@ -412,7 +412,11 @@ export class CursorMoveCommands { const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection(); let newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns); - if (skipWrappingPointStop && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) { + if (skipWrappingPointStop + && noOfColumns === 1 + && cursor.viewState.position.column === viewModel.getLineMinColumn(cursor.viewState.position.lineNumber) + && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber + ) { // moved over to the previous view line const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position); if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) { @@ -445,7 +449,11 @@ export class CursorMoveCommands { const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection(); let newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns); - if (skipWrappingPointStop && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) { + if (skipWrappingPointStop + && noOfColumns === 1 + && cursor.viewState.position.column === viewModel.getLineMaxColumn(cursor.viewState.position.lineNumber) + && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber + ) { // moved over to the next view line const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position); if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) { diff --git a/src/vs/editor/common/controller/cursorMoveOperations.ts b/src/vs/editor/common/controller/cursorMoveOperations.ts index 20ca28947..9229c2e35 100644 --- a/src/vs/editor/common/controller/cursorMoveOperations.ts +++ b/src/vs/editor/common/controller/cursorMoveOperations.ts @@ -8,6 +8,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import * as strings from 'vs/base/common/strings'; import { Constants } from 'vs/base/common/uint'; +import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations'; export class CursorPosition { _cursorPositionBrand: void; @@ -35,8 +36,20 @@ export class MoveOperations { return new Position(lineNumber, column); } + public static leftPositionAtomicSoftTabs(model: ICursorSimpleModel, lineNumber: number, column: number, tabSize: number): Position { + const minColumn = model.getLineMinColumn(lineNumber); + const lineContent = model.getLineContent(lineNumber); + const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - minColumn, tabSize, Direction.Left); + if (newPosition === -1) { + return this.leftPosition(model, lineNumber, column); + } + return new Position(lineNumber, minColumn + newPosition); + } + public static left(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition { - const pos = MoveOperations.leftPosition(model, lineNumber, column); + const pos = config.stickyTabStops + ? MoveOperations.leftPositionAtomicSoftTabs(model, lineNumber, column, config.tabSize) + : MoveOperations.leftPosition(model, lineNumber, column); return new CursorPosition(pos.lineNumber, pos.column, 0); } @@ -67,8 +80,20 @@ export class MoveOperations { return new Position(lineNumber, column); } + public static rightPositionAtomicSoftTabs(model: ICursorSimpleModel, lineNumber: number, column: number, tabSize: number, indentSize: number): Position { + const minColumn = model.getLineMinColumn(lineNumber); + const lineContent = model.getLineContent(lineNumber); + const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - minColumn, tabSize, Direction.Right); + if (newPosition === -1) { + return this.rightPosition(model, lineNumber, column); + } + return new Position(lineNumber, minColumn + newPosition); + } + public static right(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition { - const pos = MoveOperations.rightPosition(model, lineNumber, column); + const pos = config.stickyTabStops + ? MoveOperations.rightPositionAtomicSoftTabs(model, lineNumber, column, config.tabSize, config.indentSize) + : MoveOperations.rightPosition(model, lineNumber, column); return new CursorPosition(pos.lineNumber, pos.column, 0); } diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index d383ffc63..75da04850 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -128,7 +128,7 @@ export class TypeOperations { if (text.charCodeAt(text.length - 1) === CharCode.CarriageReturn) { text = text.substr(0, text.length - 1); } - let lines = text.split(/\r\n|\r|\n/); + let lines = strings.splitLines(text); if (lines.length === selections.length) { return lines; } @@ -439,7 +439,7 @@ export class TypeOperations { return false; } - if (!config.autoClosingPairsClose2.has(ch)) { + if (!config.autoClosingPairs.autoClosingPairsCloseSingleChar.has(ch)) { return false; } @@ -498,31 +498,20 @@ export class TypeOperations { }); } - private static _autoClosingPairIsSymmetric(autoClosingPair: StandardAutoClosingPairConditional): boolean { - const { open, close } = autoClosingPair; - return (open.indexOf(close) >= 0 || close.indexOf(open) >= 0); - } + private static _isBeforeClosingBrace(config: CursorConfiguration, lineAfter: string) { + // If the start of lineAfter can be interpretted as both a starting or ending brace, default to returning false + const nextChar = lineAfter.charAt(0); + const potentialStartingBraces = config.autoClosingPairs.autoClosingPairsOpenByStart.get(nextChar) || []; + const potentialClosingBraces = config.autoClosingPairs.autoClosingPairsCloseByStart.get(nextChar) || []; - private static _isBeforeClosingBrace(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional, characterAfter: string) { - const otherAutoClosingPairs = config.autoClosingPairsClose2.get(characterAfter); - if (!otherAutoClosingPairs) { - return false; - } + const isBeforeStartingBrace = potentialStartingBraces.some(x => lineAfter.startsWith(x.open)); + const isBeforeClosingBrace = potentialClosingBraces.some(x => lineAfter.startsWith(x.close)); - const thisBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(autoClosingPair); - for (const otherAutoClosingPair of otherAutoClosingPairs) { - const otherBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(otherAutoClosingPair); - if (!thisBraceIsSymmetric && otherBraceIsSymmetric) { - continue; - } - return true; - } - - return false; + return !isBeforeStartingBrace && isBeforeClosingBrace; } private static _findAutoClosingPairOpen(config: CursorConfiguration, model: ITextModel, positions: Position[], ch: string): StandardAutoClosingPairConditional | null { - const autoClosingPairCandidates = config.autoClosingPairsOpen2.get(ch); + const autoClosingPairCandidates = config.autoClosingPairs.autoClosingPairsOpenByEnd.get(ch); if (!autoClosingPairCandidates) { return null; } @@ -548,7 +537,29 @@ export class TypeOperations { return autoClosingPair; } - private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): StandardAutoClosingPairConditional | null { + private static _findSubAutoClosingPairClose(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional): string { + if (autoClosingPair.open.length <= 1) { + return ''; + } + const lastChar = autoClosingPair.close.charAt(autoClosingPair.close.length - 1); + // get candidates with the same last character as close + const subPairCandidates = config.autoClosingPairs.autoClosingPairsCloseByEnd.get(lastChar) || []; + let subPairMatch: StandardAutoClosingPairConditional | null = null; + for (const x of subPairCandidates) { + if (x.open !== autoClosingPair.open && autoClosingPair.open.includes(x.open) && autoClosingPair.close.endsWith(x.close)) { + if (!subPairMatch || x.open.length > subPairMatch.open.length) { + subPairMatch = x; + } + } + } + if (subPairMatch) { + return subPairMatch.close; + } else { + return ''; + } + } + + private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): string | null { const chIsQuote = isQuote(ch); const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; if (autoCloseConfig === 'never') { @@ -560,6 +571,9 @@ export class TypeOperations { return null; } + const subAutoClosingPairClose = this._findSubAutoClosingPairClose(config, autoClosingPair); + let isSubAutoClosingPairPresent = true; + const shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; for (let i = 0, len = selections.length; i < len; i++) { @@ -570,11 +584,16 @@ export class TypeOperations { const position = selection.getPosition(); const lineText = model.getLineContent(position.lineNumber); + const lineAfter = lineText.substring(position.column - 1); - // Only consider auto closing the pair if a space follows or if another autoclosed pair follows + if (!lineAfter.startsWith(subAutoClosingPairClose)) { + isSubAutoClosingPairPresent = false; + } + + // Only consider auto closing the pair if an allowed character follows or if another autoclosed pair closing brace follows if (lineText.length > position.column - 1) { const characterAfter = lineText.charAt(position.column - 1); - const isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, autoClosingPair, characterAfter); + const isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, lineAfter); if (!isBeforeCloseBrace && !shouldAutoCloseBefore(characterAfter)) { return null; @@ -612,14 +631,18 @@ export class TypeOperations { } } - return autoClosingPair; + if (isSubAutoClosingPairPresent) { + return autoClosingPair.close.substring(0, autoClosingPair.close.length - subAutoClosingPairClose.length); + } else { + return autoClosingPair.close; + } } - private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean, autoClosingPair: StandardAutoClosingPairConditional): EditOperationResult { + private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean, autoClosingPairClose: string): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; - commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPair.close); + commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPairClose); } return new EditOperationResult(EditOperationType.Typing, commands, { shouldPushStackElementBefore: true, @@ -794,9 +817,9 @@ export class TypeOperations { }); } - const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, false); - if (autoClosingPairOpenCharType) { - return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairOpenCharType); + const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, false); + if (autoClosingPairClose !== null) { + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairClose); } return null; @@ -838,9 +861,9 @@ export class TypeOperations { } if (!isDoingComposition) { - const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, true); - if (autoClosingPairOpenCharType) { - return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairOpenCharType); + const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, true); + if (autoClosingPairClose) { + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairClose); } } diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index c67d8f968..73820f9af 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -384,7 +384,7 @@ export class WordOperations { return selection; } - if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpen, ctx.model, [ctx.selection])) { + if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpenByEnd, ctx.model, [ctx.selection])) { const position = ctx.selection.getPosition(); return new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column + 1); } @@ -438,6 +438,122 @@ export class WordOperations { return new Range(lineNumber, column, position.lineNumber, position.column); } + public static deleteInsideWord(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection): Range { + if (!selection.isEmpty()) { + return selection; + } + + const position = new Position(selection.positionLineNumber, selection.positionColumn); + + let r = this._deleteInsideWordWhitespace(model, position); + if (r) { + return r; + } + + return this._deleteInsideWordDetermineDeleteRange(wordSeparators, model, position); + } + + private static _charAtIsWhitespace(str: string, index: number): boolean { + const charCode = str.charCodeAt(index); + return (charCode === CharCode.Space || charCode === CharCode.Tab); + } + + private static _deleteInsideWordWhitespace(model: ICursorSimpleModel, position: Position): Range | null { + const lineContent = model.getLineContent(position.lineNumber); + const lineContentLength = lineContent.length; + + if (lineContentLength === 0) { + // empty line + return null; + } + + let leftIndex = Math.max(position.column - 2, 0); + if (!this._charAtIsWhitespace(lineContent, leftIndex)) { + // touches a non-whitespace character to the left + return null; + } + + let rightIndex = Math.min(position.column - 1, lineContentLength - 1); + if (!this._charAtIsWhitespace(lineContent, rightIndex)) { + // touches a non-whitespace character to the right + return null; + } + + // walk over whitespace to the left + while (leftIndex > 0 && this._charAtIsWhitespace(lineContent, leftIndex - 1)) { + leftIndex--; + } + + // walk over whitespace to the right + while (rightIndex + 1 < lineContentLength && this._charAtIsWhitespace(lineContent, rightIndex + 1)) { + rightIndex++; + } + + return new Range(position.lineNumber, leftIndex + 1, position.lineNumber, rightIndex + 2); + } + + private static _deleteInsideWordDetermineDeleteRange(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position): Range { + const lineContent = model.getLineContent(position.lineNumber); + const lineLength = lineContent.length; + if (lineLength === 0) { + // empty line + if (position.lineNumber > 1) { + return new Range(position.lineNumber - 1, model.getLineMaxColumn(position.lineNumber - 1), position.lineNumber, 1); + } else { + if (position.lineNumber < model.getLineCount()) { + return new Range(position.lineNumber, 1, position.lineNumber + 1, 1); + } else { + // empty model + return new Range(position.lineNumber, 1, position.lineNumber, 1); + } + } + } + + const touchesWord = (word: IFindWordResult) => { + return (word.start + 1 <= position.column && position.column <= word.end + 1); + }; + const createRangeWithPosition = (startColumn: number, endColumn: number) => { + startColumn = Math.min(startColumn, position.column); + endColumn = Math.max(endColumn, position.column); + return new Range(position.lineNumber, startColumn, position.lineNumber, endColumn); + }; + const deleteWordAndAdjacentWhitespace = (word: IFindWordResult) => { + let startColumn = word.start + 1; + let endColumn = word.end + 1; + let expandedToTheRight = false; + while (endColumn - 1 < lineLength && this._charAtIsWhitespace(lineContent, endColumn - 1)) { + expandedToTheRight = true; + endColumn++; + } + if (!expandedToTheRight) { + while (startColumn > 1 && this._charAtIsWhitespace(lineContent, startColumn - 2)) { + startColumn--; + } + } + return createRangeWithPosition(startColumn, endColumn); + }; + + const prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, position); + if (prevWordOnLine && touchesWord(prevWordOnLine)) { + return deleteWordAndAdjacentWhitespace(prevWordOnLine); + } + const nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, position); + if (nextWordOnLine && touchesWord(nextWordOnLine)) { + return deleteWordAndAdjacentWhitespace(nextWordOnLine); + } + if (prevWordOnLine && nextWordOnLine) { + return createRangeWithPosition(prevWordOnLine.end + 1, nextWordOnLine.start + 1); + } + if (prevWordOnLine) { + return createRangeWithPosition(prevWordOnLine.start + 1, prevWordOnLine.end + 1); + } + if (nextWordOnLine) { + return createRangeWithPosition(nextWordOnLine.start + 1, nextWordOnLine.end + 1); + } + + return createRangeWithPosition(1, lineLength + 1); + } + public static _deleteWordPartLeft(model: ICursorSimpleModel, selection: Selection): Range { if (!selection.isEmpty()) { return selection; diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index 487e6d4f0..a3a3b3de1 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -24,6 +24,7 @@ export namespace EditorContextKeys { export const textInputFocus = new RawContextKey('textInputFocus', false); export const readOnly = new RawContextKey('editorReadonly', false); + export const inDiffEditor = new RawContextKey('inDiffEditor', false); export const columnSelection = new RawContextKey('editorColumnSelection', false); export const writable = readOnly.toNegated(); export const hasNonEmptySelection = new RawContextKey('editorHasSelection', false); diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index fd5c9ccbb..8f4d53a84 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -695,6 +695,11 @@ export interface ITextModel { */ getEOL(): string; + /** + * Get the end of line sequence predominantly used in the text buffer. + */ + getEndOfLineSequence(): EndOfLineSequence; + /** * Get the minimum legal column for line at `lineNumber` */ @@ -850,7 +855,12 @@ export interface ITextModel { /** * @internal */ - hasSemanticTokens(): boolean; + hasCompleteSemanticTokens(): boolean; + + /** + * @internal + */ + hasSomeSemanticTokens(): boolean; /** * Flush all tokenization state. @@ -1089,12 +1099,17 @@ export interface ITextModel { detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void; /** - * Push a stack element onto the undo stack. This acts as an undo/redo point. - * The idea is to use `pushEditOperations` to edit the model and then to - * `pushStackElement` to create an undo/redo stop point. + * Close the current undo-redo element. + * This offers a way to create an undo/redo stop point. */ pushStackElement(): void; + /** + * Open the current undo-redo element. + * This offers a way to remove the current undo/redo stop point. + */ + popStackElement(): void; + /** * Push edit operations, basically editing the model. This is the preferred way * of editing the model. The edit operations will land on the undo stack. @@ -1138,7 +1153,7 @@ export interface ITextModel { _applyRedo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; /** - * Undo edit operations until the first previous stop point created by `pushStackElement`. + * Undo edit operations until the previous undo/redo point. * The inverse edit operations will be pushed on the redo stack. * @internal */ @@ -1151,7 +1166,7 @@ export interface ITextModel { canUndo(): boolean; /** - * Redo edit operations until the next stop point created by `pushStackElement`. + * Redo edit operations until the next undo/redo point. * The inverse edit operations will be pushed on the undo stack. * @internal */ diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index 0928d7d0e..d06f447b0 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -199,6 +199,12 @@ export class SingleModelEditStackElement implements IResourceUndoRedoElement { } } + public open(): void { + if (!(this._data instanceof SingleModelEditStackData)) { + this._data = SingleModelEditStackData.deserialize(this._data); + } + } + public undo(): void { if (URI.isUri(this.model)) { // don't have a model @@ -315,6 +321,10 @@ export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement { this._isOpen = false; } + public open(): void { + // cannot reopen + } + public undo(): void { this._isOpen = false; @@ -386,6 +396,13 @@ export class EditStack { } } + public popStackElement(): void { + const lastElement = this._undoRedoService.getLastElement(this._model.uri); + if (isEditStackElement(lastElement)) { + lastElement.open(); + } + } + public clear(): void { this._undoRedoService.removeElements(this._model.uri); } diff --git a/src/vs/editor/common/model/mirrorTextModel.ts b/src/vs/editor/common/model/mirrorTextModel.ts index 6a1272552..9e14ea636 100644 --- a/src/vs/editor/common/model/mirrorTextModel.ts +++ b/src/vs/editor/common/model/mirrorTextModel.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { splitLines } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; @@ -131,7 +132,7 @@ export class MirrorTextModel { // Nothing to insert return; } - let insertLines = insertText.split(/\r\n|\r|\n/); + let insertLines = splitLines(insertText); if (insertLines.length === 1) { // Inserting text on one line this._setLineText(position.lineNumber - 1, diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index 62ab29299..2b73835b2 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -12,7 +12,7 @@ import { PieceTreeBase, StringBuffer } from 'vs/editor/common/model/pieceTreeTex import { SearchData } from 'vs/editor/common/model/textModelSearch'; import { countEOL, StringEOL } from 'vs/editor/common/model/tokensStore'; import { TextChange } from 'vs/editor/common/model/textChange'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; export interface IValidatedEditOperation { sortIndex: number; @@ -32,26 +32,24 @@ export interface IReverseSingleEditOperation extends IValidEditOperation { sortIndex: number; } -export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { - private readonly _pieceTree: PieceTreeBase; +export class PieceTreeTextBuffer extends Disposable implements ITextBuffer { + private _pieceTree: PieceTreeBase; private readonly _BOM: string; private _mightContainRTL: boolean; private _mightContainUnusualLineTerminators: boolean; private _mightContainNonBasicASCII: boolean; - private readonly _onDidChangeContent: Emitter = new Emitter(); + private readonly _onDidChangeContent: Emitter = this._register(new Emitter()); public readonly onDidChangeContent: Event = this._onDidChangeContent.event; constructor(chunks: StringBuffer[], BOM: string, eol: '\r\n' | '\n', containsRTL: boolean, containsUnusualLineTerminators: boolean, isBasicASCII: boolean, eolNormalized: boolean) { + super(); this._BOM = BOM; this._mightContainNonBasicASCII = !isBasicASCII; this._mightContainRTL = containsRTL; this._mightContainUnusualLineTerminators = containsUnusualLineTerminators; this._pieceTree = new PieceTreeBase(chunks, eol, eolNormalized); } - dispose(): void { - this._onDidChangeContent.dispose(); - } // #region TextBuffer public equals(other: ITextBuffer): boolean { diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 24f25a4e5..35dd46640 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -387,6 +387,9 @@ export class TextModel extends Disposable implements model.ITextModel { this._isDisposed = true; super.dispose(); this._isDisposing = false; + // Manually release reference to previous text buffer to avoid large leaks + // in case someone leaks a TextModel reference + this._buffer = createTextBuffer('', this._options.defaultEOL); } private _assertNotDisposed(): void { @@ -830,6 +833,15 @@ export class TextModel extends Disposable implements model.ITextModel { return this._buffer.getEOL(); } + public getEndOfLineSequence(): model.EndOfLineSequence { + this._assertNotDisposed(); + return ( + this._buffer.getEOL() === '\n' + ? model.EndOfLineSequence.LF + : model.EndOfLineSequence.CRLF + ); + } + public getLineMinColumn(lineNumber: number): number { this._assertNotDisposed(); return 1; @@ -1213,6 +1225,10 @@ export class TextModel extends Disposable implements model.ITextModel { this._commandManager.pushStackElement(); } + public popStackElement(): void { + this._commandManager.popStackElement(); + } + public pushEOL(eol: model.EndOfLineSequence): void { const currentEOL = (this.getEOL() === '\n' ? model.EndOfLineSequence.LF : model.EndOfLineSequence.CRLF); if (currentEOL === eol) { @@ -1868,12 +1884,16 @@ export class TextModel extends Disposable implements model.ITextModel { }); } - public hasSemanticTokens(): boolean { + public hasCompleteSemanticTokens(): boolean { return this._tokens2.isComplete(); } + public hasSomeSemanticTokens(): boolean { + return !this._tokens2.isEmpty(); + } + public setPartialSemanticTokens(range: Range, tokens: MultilineTokens2[]): void { - if (this.hasSemanticTokens()) { + if (this.hasCompleteSemanticTokens()) { return; } const changedRange = this._tokens2.setPartial(range, tokens); diff --git a/src/vs/editor/common/model/tokensStore.ts b/src/vs/editor/common/model/tokensStore.ts index a49ef27a7..bbec0e369 100644 --- a/src/vs/editor/common/model/tokensStore.ts +++ b/src/vs/editor/common/model/tokensStore.ts @@ -884,6 +884,10 @@ export class TokensStore2 { this._isComplete = false; } + public isEmpty(): boolean { + return (this._pieces.length === 0); + } + public set(pieces: MultilineTokens2[] | null, isComplete: boolean): void { this._pieces = pieces || []; this._isComplete = isComplete; diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 0ab1352cf..fe0d7335d 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -19,7 +19,7 @@ import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationReg import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { iconRegistry, Codicon } from 'vs/base/common/codicons'; - +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; /** * Open ended enum at runtime * @internal @@ -819,17 +819,32 @@ export interface DocumentHighlightProvider { } /** - * The rename provider interface defines the contract between extensions and - * the live-rename feature. + * The linked editing range provider interface defines the contract between extensions and + * the linked editing feature. */ -export interface OnTypeRenameProvider { - - wordPattern?: RegExp; +export interface LinkedEditingRangeProvider { /** - * Provide a list of ranges that can be live-renamed together. + * Provide a list of ranges that can be edited together. */ - provideOnTypeRenameRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<{ ranges: IRange[]; wordPattern?: RegExp; }>; + provideLinkedEditingRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + +/** + * Represents a list of ranges that can be edited together along with a word pattern to describe valid contents. + */ +export interface LinkedEditingRanges { + /** + * A list of ranges that can be edited together. The ranges must have + * identical length and text content. The ranges cannot overlap + */ + ranges: IRange[]; + + /** + * An optional word pattern that describes valid contents for the given ranges. + * If no pattern is provided, the language configuration's word pattern will be used. + */ + wordPattern?: RegExp; } /** @@ -1359,7 +1374,10 @@ export interface WorkspaceEditMetadata { needsConfirmation: boolean; label: string; description?: string; - iconPath?: { id: string } | URI | { light: URI, dark: URI }; + /** + * @internal + */ + iconPath?: ThemeIcon | URI | { light: URI, dark: URI }; } export interface WorkspaceFileEditOptions { @@ -1367,6 +1385,10 @@ export interface WorkspaceFileEditOptions { ignoreIfNotExists?: boolean; ignoreIfExists?: boolean; recursive?: boolean; + copy?: boolean; + folder?: boolean; + skipTrashBin?: boolean; + maxSize?: number; } export interface WorkspaceFileEdit { @@ -1715,7 +1737,7 @@ export const DocumentHighlightProviderRegistry = new LanguageFeatureRegistry(); +export const LinkedEditingRangeProviderRegistry = new LanguageFeatureRegistry(); /** * @internal diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index 295fbdc01..a383c57e9 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -294,17 +294,32 @@ export class StandardAutoClosingPairConditional { * @internal */ export class AutoClosingPairs { + // it is useful to be able to get pairs using either end of open and close - public readonly autoClosingPairsOpen: Map; - public readonly autoClosingPairsClose: Map; + /** Key is first character of open */ + public readonly autoClosingPairsOpenByStart: Map; + /** Key is last character of open */ + public readonly autoClosingPairsOpenByEnd: Map; + /** Key is first character of close */ + public readonly autoClosingPairsCloseByStart: Map; + /** Key is last character of close */ + public readonly autoClosingPairsCloseByEnd: Map; + /** Key is close. Only has pairs that are a single character */ + public readonly autoClosingPairsCloseSingleChar: Map; constructor(autoClosingPairs: StandardAutoClosingPairConditional[]) { - this.autoClosingPairsOpen = new Map(); - this.autoClosingPairsClose = new Map(); + this.autoClosingPairsOpenByStart = new Map(); + this.autoClosingPairsOpenByEnd = new Map(); + this.autoClosingPairsCloseByStart = new Map(); + this.autoClosingPairsCloseByEnd = new Map(); + this.autoClosingPairsCloseSingleChar = new Map(); for (const pair of autoClosingPairs) { - appendEntry(this.autoClosingPairsOpen, pair.open.charAt(pair.open.length - 1), pair); - if (pair.close.length === 1) { - appendEntry(this.autoClosingPairsClose, pair.close, pair); + appendEntry(this.autoClosingPairsOpenByStart, pair.open.charAt(0), pair); + appendEntry(this.autoClosingPairsOpenByEnd, pair.open.charAt(pair.open.length - 1), pair); + appendEntry(this.autoClosingPairsCloseByStart, pair.close.charAt(0), pair); + appendEntry(this.autoClosingPairsCloseByEnd, pair.close.charAt(pair.close.length - 1), pair); + if (pair.close.length === 1 && pair.open.length === 1) { + appendEntry(this.autoClosingPairsCloseSingleChar, pair.close, pair); } } } diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index 2d7600d32..ccd20e376 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -622,6 +622,12 @@ export class LanguageConfigurationRegistryImpl { return null; } const scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn); + + if (scopedLineTokens.firstCharOffset) { + // this line has mixed languages and indentation rules will not work + return null; + } + const indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId); if (!indentRulesSupport) { return null; diff --git a/src/vs/editor/common/modes/supports/indentRules.ts b/src/vs/editor/common/modes/supports/indentRules.ts index 687115bd4..3241358cc 100644 --- a/src/vs/editor/common/modes/supports/indentRules.ts +++ b/src/vs/editor/common/modes/supports/indentRules.ts @@ -12,6 +12,14 @@ export const enum IndentConsts { UNINDENT_MASK = 0b00001000, } +function resetGlobalRegex(reg: RegExp) { + if (reg.global) { + reg.lastIndex = 0; + } + + return true; +} + export class IndentRulesSupport { private readonly _indentationRules: IndentationRule; @@ -22,7 +30,7 @@ export class IndentRulesSupport { public shouldIncrease(text: string): boolean { if (this._indentationRules) { - if (this._indentationRules.increaseIndentPattern && this._indentationRules.increaseIndentPattern.test(text)) { + if (this._indentationRules.increaseIndentPattern && resetGlobalRegex(this._indentationRules.increaseIndentPattern) && this._indentationRules.increaseIndentPattern.test(text)) { return true; } // if (this._indentationRules.indentNextLinePattern && this._indentationRules.indentNextLinePattern.test(text)) { @@ -33,14 +41,14 @@ export class IndentRulesSupport { } public shouldDecrease(text: string): boolean { - if (this._indentationRules && this._indentationRules.decreaseIndentPattern && this._indentationRules.decreaseIndentPattern.test(text)) { + if (this._indentationRules && this._indentationRules.decreaseIndentPattern && resetGlobalRegex(this._indentationRules.decreaseIndentPattern) && this._indentationRules.decreaseIndentPattern.test(text)) { return true; } return false; } public shouldIndentNextLine(text: string): boolean { - if (this._indentationRules && this._indentationRules.indentNextLinePattern && this._indentationRules.indentNextLinePattern.test(text)) { + if (this._indentationRules && this._indentationRules.indentNextLinePattern && resetGlobalRegex(this._indentationRules.indentNextLinePattern) && this._indentationRules.indentNextLinePattern.test(text)) { return true; } @@ -49,7 +57,7 @@ export class IndentRulesSupport { public shouldIgnore(text: string): boolean { // the text matches `unIndentedLinePattern` - if (this._indentationRules && this._indentationRules.unIndentedLinePattern && this._indentationRules.unIndentedLinePattern.test(text)) { + if (this._indentationRules && this._indentationRules.unIndentedLinePattern && resetGlobalRegex(this._indentationRules.unIndentedLinePattern) && this._indentationRules.unIndentedLinePattern.test(text)) { return true; } diff --git a/src/vs/editor/common/modes/textToHtmlTokenizer.ts b/src/vs/editor/common/modes/textToHtmlTokenizer.ts index dd64e79de..c48666416 100644 --- a/src/vs/editor/common/modes/textToHtmlTokenizer.ts +++ b/src/vs/editor/common/modes/textToHtmlTokenizer.ts @@ -101,7 +101,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens function _tokenizeToString(text: string, tokenizationSupport: IReducedTokenizationSupport): string { let result = `
`; - let lines = text.split(/\r\n|\r|\n/); + let lines = strings.splitLines(text); let currentState = tokenizationSupport.getInitialState(); for (let i = 0, len = lines.length; i < len; i++) { let line = lines[i]; diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 69592e735..68a3d1253 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -530,36 +530,30 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { private static readonly _suggestionsLimit = 10000; - public async textualSuggest(modelUrl: string, position: IPosition, wordDef: string, wordDefFlags: string): Promise<{ words: string[], duration: number } | null> { - const model = this._getModel(modelUrl); - if (!model) { - return null; - } + public async textualSuggest(modelUrls: string[], leadingWord: string | undefined, wordDef: string, wordDefFlags: string): Promise<{ words: string[], duration: number } | null> { const sw = new StopWatch(true); - const words: string[] = []; - const seen = new Set(); const wordDefRegExp = new RegExp(wordDef, wordDefFlags); + const seen = new Set(); - const wordAt = model.getWordAtPosition(position, wordDefRegExp); - if (wordAt) { - seen.add(model.getValueInRange(wordAt)); - } - - for (let word of model.words(wordDefRegExp)) { - if (seen.has(word)) { + outer: for (let url of modelUrls) { + const model = this._getModel(url); + if (!model) { continue; } - seen.add(word); - if (!isNaN(Number(word))) { - continue; - } - words.push(word); - if (seen.size > EditorSimpleWorker._suggestionsLimit) { - break; + + for (let word of model.words(wordDefRegExp)) { + if (word === leadingWord || !isNaN(Number(word))) { + continue; + } + seen.add(word); + if (seen.size > EditorSimpleWorker._suggestionsLimit) { + break outer; + } } } - return { words, duration: sw.elapsed() }; + + return { words: Array.from(seen), duration: sw.elapsed() }; } diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index 5976df3c2..dc6a0f706 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IntervalTimer } from 'vs/base/common/async'; +import { IntervalTimer, timeout } from 'vs/base/common/async'; import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { SimpleWorkerClient, logOnceWebWorkerWarning, IWorkerClient } from 'vs/base/common/worker/simpleWorker'; import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; -import { IPosition, Position } from 'vs/editor/common/core/position'; +import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { IChange } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; @@ -105,7 +105,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker const sw = StopWatch.create(true); const result = this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits)); result.finally(() => this._logService.trace('FORMAT#computeMoreMinimalEdits', resource.toString(true), sw.elapsed())); - return result; + return Promise.race([result, timeout(1000).then(() => edits)]); } else { return Promise.resolve(undefined); @@ -148,20 +148,47 @@ class WordBasedCompletionItemProvider implements modes.CompletionItemProvider { } async provideCompletionItems(model: ITextModel, position: Position): Promise { - const { wordBasedSuggestions } = this._configurationService.getValue<{ wordBasedSuggestions?: boolean }>(model.uri, position, 'editor'); - if (!wordBasedSuggestions) { + type WordBasedSuggestionsConfig = { + wordBasedSuggestions?: boolean, + wordBasedSuggestionsMode?: 'currentDocument' | 'matchingDocuments' | 'allDocuments' + }; + const config = this._configurationService.getValue(model.uri, position, 'editor'); + if (!config.wordBasedSuggestions) { return undefined; } - if (!canSyncModel(this._modelService, model.uri)) { - return undefined; // File too large + + const models: URI[] = []; + if (config.wordBasedSuggestionsMode === 'currentDocument') { + // only current file and only if not too large + if (canSyncModel(this._modelService, model.uri)) { + models.push(model.uri); + } + } else { + // either all files or files of same language + for (const candidate of this._modelService.getModels()) { + if (!canSyncModel(this._modelService, candidate.uri)) { + continue; + } + if (candidate === model) { + models.unshift(candidate.uri); + + } else if (config.wordBasedSuggestionsMode === 'allDocuments' || candidate.getLanguageIdentifier().id === model.getLanguageIdentifier().id) { + models.push(candidate.uri); + } + } } + if (models.length === 0) { + return undefined; // File too large, no other files + } + + const wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); const word = model.getWordAtPosition(position); const replace = !word ? Range.fromPositions(position) : new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn); const insert = replace.setEndPosition(position.lineNumber, position.column); const client = await this._workerManager.withWorker(); - const data = await client.textualSuggest(model.uri, position); + const data = await client.textualSuggest(models, word?.word, wordDefRegExp); if (!data) { return undefined; } @@ -463,17 +490,11 @@ export class EditorWorkerClient extends Disposable { }); } - public textualSuggest(resource: URI, position: IPosition): Promise<{ words: string[], duration: number } | null> { - return this._withSyncedResources([resource]).then(proxy => { - let model = this._modelService.getModel(resource); - if (!model) { - return null; - } - let wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); - let wordDef = wordDefRegExp.source; - let wordDefFlags = regExpFlags(wordDefRegExp); - return proxy.textualSuggest(resource.toString(), position, wordDef, wordDefFlags); - }); + public async textualSuggest(resources: URI[], leadingWord: string | undefined, wordDefRegExp: RegExp): Promise<{ words: string[], duration: number } | null> { + const proxy = await this._withSyncedResources(resources); + const wordDef = wordDefRegExp.source; + const wordDefFlags = regExpFlags(wordDefRegExp); + return proxy.textualSuggest(resources.map(r => r.toString()), leadingWord, wordDef, wordDefFlags); } computeWordRanges(resource: URI, range: IRange): Promise<{ [word: string]: IRange[] } | null> { diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index b20a0b7c5..60aca88ad 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -93,6 +93,6 @@ export function detectModeId(modelService: IModelService, modeService: IModeServ return modeService.getModeIdByFilepathOrFirstLine(resource); } -export function cssEscape(val: string): string { - return val.replace(/\s/g, '\\$&'); // make sure to not introduce CSS classes from files that contain whitespace +export function cssEscape(str: string): string { + return str.replace(/[\11\12\14\15\40]/g, '/'); // HTML class names can not contain certain whitespace characters, use / instead, which doesn't exist in file names. } diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 7836fa5de..866727686 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -790,6 +790,10 @@ class ModelSemanticColoring extends Disposable { } const provider = this._getSemanticColoringProvider(); if (!provider) { + if (this._currentDocumentResponse) { + // there are semantic tokens set + this._model.setSemanticTokens(null, false); + } return; } this._currentDocumentRequestCancellationTokenSource = new CancellationTokenSource(); diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 01bf8eabf..8c772e3c3 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -179,113 +179,119 @@ export enum EditorOption { automaticLayout = 9, autoSurround = 10, codeLens = 11, - colorDecorators = 12, - columnSelection = 13, - comments = 14, - contextmenu = 15, - copyWithSyntaxHighlighting = 16, - cursorBlinking = 17, - cursorSmoothCaretAnimation = 18, - cursorStyle = 19, - cursorSurroundingLines = 20, - cursorSurroundingLinesStyle = 21, - cursorWidth = 22, - disableLayerHinting = 23, - disableMonospaceOptimizations = 24, - dragAndDrop = 25, - emptySelectionClipboard = 26, - extraEditorClassName = 27, - fastScrollSensitivity = 28, - find = 29, - fixedOverflowWidgets = 30, - folding = 31, - foldingStrategy = 32, - foldingHighlight = 33, - unfoldOnClickAfterEndOfLine = 34, - fontFamily = 35, - fontInfo = 36, - fontLigatures = 37, - fontSize = 38, - fontWeight = 39, - formatOnPaste = 40, - formatOnType = 41, - glyphMargin = 42, - gotoLocation = 43, - hideCursorInOverviewRuler = 44, - highlightActiveIndentGuide = 45, - hover = 46, - inDiffEditor = 47, - letterSpacing = 48, - lightbulb = 49, - lineDecorationsWidth = 50, - lineHeight = 51, - lineNumbers = 52, - lineNumbersMinChars = 53, - links = 54, - matchBrackets = 55, - minimap = 56, - mouseStyle = 57, - mouseWheelScrollSensitivity = 58, - mouseWheelZoom = 59, - multiCursorMergeOverlapping = 60, - multiCursorModifier = 61, - multiCursorPaste = 62, - occurrencesHighlight = 63, - overviewRulerBorder = 64, - overviewRulerLanes = 65, - padding = 66, - parameterHints = 67, - peekWidgetDefaultFocus = 68, - definitionLinkOpensInPeek = 69, - quickSuggestions = 70, - quickSuggestionsDelay = 71, - readOnly = 72, - renameOnType = 73, - renderControlCharacters = 74, - renderIndentGuides = 75, - renderFinalNewline = 76, - renderLineHighlight = 77, - renderLineHighlightOnlyWhenFocus = 78, - renderValidationDecorations = 79, - renderWhitespace = 80, - revealHorizontalRightPadding = 81, - roundedSelection = 82, - rulers = 83, - scrollbar = 84, - scrollBeyondLastColumn = 85, - scrollBeyondLastLine = 86, - scrollPredominantAxis = 87, - selectionClipboard = 88, - selectionHighlight = 89, - selectOnLineNumbers = 90, - showFoldingControls = 91, - showUnused = 92, - snippetSuggestions = 93, - smoothScrolling = 94, - stopRenderingLineAfter = 95, - suggest = 96, - suggestFontSize = 97, - suggestLineHeight = 98, - suggestOnTriggerCharacters = 99, - suggestSelection = 100, - tabCompletion = 101, - tabIndex = 102, - unusualLineTerminators = 103, - useTabStops = 104, - wordSeparators = 105, - wordWrap = 106, - wordWrapBreakAfterCharacters = 107, - wordWrapBreakBeforeCharacters = 108, - wordWrapColumn = 109, - wordWrapMinified = 110, - wrappingIndent = 111, - wrappingStrategy = 112, - showDeprecated = 113, - editorClassName = 114, - pixelRatio = 115, - tabFocusMode = 116, - layoutInfo = 117, - wrappingInfo = 118 + codeLensFontFamily = 12, + codeLensFontSize = 13, + colorDecorators = 14, + columnSelection = 15, + comments = 16, + contextmenu = 17, + copyWithSyntaxHighlighting = 18, + cursorBlinking = 19, + cursorSmoothCaretAnimation = 20, + cursorStyle = 21, + cursorSurroundingLines = 22, + cursorSurroundingLinesStyle = 23, + cursorWidth = 24, + disableLayerHinting = 25, + disableMonospaceOptimizations = 26, + dragAndDrop = 27, + emptySelectionClipboard = 28, + extraEditorClassName = 29, + fastScrollSensitivity = 30, + find = 31, + fixedOverflowWidgets = 32, + folding = 33, + foldingStrategy = 34, + foldingHighlight = 35, + unfoldOnClickAfterEndOfLine = 36, + fontFamily = 37, + fontInfo = 38, + fontLigatures = 39, + fontSize = 40, + fontWeight = 41, + formatOnPaste = 42, + formatOnType = 43, + glyphMargin = 44, + gotoLocation = 45, + hideCursorInOverviewRuler = 46, + highlightActiveIndentGuide = 47, + hover = 48, + inDiffEditor = 49, + letterSpacing = 50, + lightbulb = 51, + lineDecorationsWidth = 52, + lineHeight = 53, + lineNumbers = 54, + lineNumbersMinChars = 55, + linkedEditing = 56, + links = 57, + matchBrackets = 58, + minimap = 59, + mouseStyle = 60, + mouseWheelScrollSensitivity = 61, + mouseWheelZoom = 62, + multiCursorMergeOverlapping = 63, + multiCursorModifier = 64, + multiCursorPaste = 65, + occurrencesHighlight = 66, + overviewRulerBorder = 67, + overviewRulerLanes = 68, + padding = 69, + parameterHints = 70, + peekWidgetDefaultFocus = 71, + definitionLinkOpensInPeek = 72, + quickSuggestions = 73, + quickSuggestionsDelay = 74, + readOnly = 75, + renameOnType = 76, + renderControlCharacters = 77, + renderIndentGuides = 78, + renderFinalNewline = 79, + renderLineHighlight = 80, + renderLineHighlightOnlyWhenFocus = 81, + renderValidationDecorations = 82, + renderWhitespace = 83, + revealHorizontalRightPadding = 84, + roundedSelection = 85, + rulers = 86, + scrollbar = 87, + scrollBeyondLastColumn = 88, + scrollBeyondLastLine = 89, + scrollPredominantAxis = 90, + selectionClipboard = 91, + selectionHighlight = 92, + selectOnLineNumbers = 93, + showFoldingControls = 94, + showUnused = 95, + snippetSuggestions = 96, + smartSelect = 97, + smoothScrolling = 98, + stickyTabStops = 99, + stopRenderingLineAfter = 100, + suggest = 101, + suggestFontSize = 102, + suggestLineHeight = 103, + suggestOnTriggerCharacters = 104, + suggestSelection = 105, + tabCompletion = 106, + tabIndex = 107, + unusualLineTerminators = 108, + useTabStops = 109, + wordSeparators = 110, + wordWrap = 111, + wordWrapBreakAfterCharacters = 112, + wordWrapBreakBeforeCharacters = 113, + wordWrapColumn = 114, + wordWrapOverride1 = 115, + wordWrapOverride2 = 116, + wrappingIndent = 117, + wrappingStrategy = 118, + showDeprecated = 119, + editorClassName = 120, + pixelRatio = 121, + tabFocusMode = 122, + layoutInfo = 123, + wrappingInfo = 124 } /** diff --git a/src/vs/editor/common/view/viewEvents.ts b/src/vs/editor/common/view/viewEvents.ts index 804d1b977..bcdc69bb3 100644 --- a/src/vs/editor/common/view/viewEvents.ts +++ b/src/vs/editor/common/view/viewEvents.ts @@ -11,6 +11,8 @@ import { ScrollType } from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; export const enum ViewEventType { + ViewCompositionStart, + ViewCompositionEnd, ViewConfigurationChanged, ViewCursorStateChanged, ViewDecorationsChanged, @@ -29,6 +31,16 @@ export const enum ViewEventType { ViewZonesChanged, } +export class ViewCompositionStartEvent { + public readonly type = ViewEventType.ViewCompositionStart; + constructor() { } +} + +export class ViewCompositionEndEvent { + public readonly type = ViewEventType.ViewCompositionEnd; + constructor() { } +} + export class ViewConfigurationChangedEvent { public readonly type = ViewEventType.ViewConfigurationChanged; @@ -285,7 +297,9 @@ export class ViewZonesChangedEvent { } export type ViewEvent = ( - ViewConfigurationChangedEvent + ViewCompositionStartEvent + | ViewCompositionEndEvent + | ViewConfigurationChangedEvent | ViewCursorStateChangedEvent | ViewDecorationsChangedEvent | ViewFlushedEvent diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index 74428ae28..bd72baa14 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -42,6 +42,24 @@ export class LineDecoration { return true; } + public static extractWrapped(arr: LineDecoration[], startOffset: number, endOffset: number): LineDecoration[] { + if (arr.length === 0) { + return arr; + } + const startColumn = startOffset + 1; + const endColumn = endOffset + 1; + const lineLength = endOffset - startOffset; + const r = []; + let rLength = 0; + for (const dec of arr) { + if (dec.endColumn <= startColumn || dec.startColumn >= endColumn) { + continue; + } + r[rLength++] = new LineDecoration(Math.max(1, dec.startColumn - startColumn + 1), Math.min(lineLength + 1, dec.endColumn - startColumn + 1), dec.className, dec.type); + } + return r; + } + public static filter(lineDecorations: InlineDecoration[], lineNumber: number, minLineColumn: number, maxLineColumn: number): LineDecoration[] { if (lineDecorations.length === 0) { return []; diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index ceef064de..a564e9a88 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -546,6 +546,23 @@ export class LinesLayout { return verticalOffset > totalHeight; } + public isInTopPadding(verticalOffset: number): boolean { + if (this._paddingTop === 0) { + return false; + } + this._checkPendingChanges(); + return (verticalOffset < this._paddingTop); + } + + public isInBottomPadding(verticalOffset: number): boolean { + if (this._paddingBottom === 0) { + return false; + } + this._checkPendingChanges(); + const totalHeight = this.getLinesTotalHeight(); + return (verticalOffset >= totalHeight - this._paddingBottom); + } + /** * Find the first line number that is at or after vertical offset `verticalOffset`. * i.e. if getVerticalOffsetForLine(line) is x and getVerticalOffsetForLine(line + 1) is y, then diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index 0964eac91..24059d23a 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -364,6 +364,13 @@ export class ViewLayout extends Disposable implements IViewLayout { public isAfterLines(verticalOffset: number): boolean { return this._linesLayout.isAfterLines(verticalOffset); } + public isInTopPadding(verticalOffset: number): boolean { + return this._linesLayout.isInTopPadding(verticalOffset); + } + isInBottomPadding(verticalOffset: number): boolean { + return this._linesLayout.isInBottomPadding(verticalOffset); + } + public getLineNumberAtVerticalOffset(verticalOffset: number): number { return this._linesLayout.getLineNumberAtOrAfterVerticalOffset(verticalOffset); } diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 1b75f0f7c..49b43c10e 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -127,7 +127,7 @@ export class RenderLineInput { this.containsRTL = containsRTL; this.fauxIndentLength = fauxIndentLength; this.lineTokens = lineTokens; - this.lineDecorations = lineDecorations; + this.lineDecorations = lineDecorations.sort(LineDecoration.compare); this.tabSize = tabSize; this.startVisibleColumn = startVisibleColumn; this.spaceWidth = spaceWidth; diff --git a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts index 549496d00..67a3b90fa 100644 --- a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -7,8 +7,9 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; -import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { ILineBreaksComputer, LineBreakData } from 'vs/editor/common/viewModel/viewModel'; const enum CharacterClass { NONE = 0, diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 3a3eab70a..50765f8ee 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -12,69 +12,11 @@ import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecoration, IModelDe import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; +import { ICoordinatesConverter, ILineBreaksComputer, IOverviewRulerDecorations, LineBreakData, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { IDisposable } from 'vs/base/common/lifecycle'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { EditorTheme } from 'vs/editor/common/view/viewContext'; -export class OutputPosition { - outputLineIndex: number; - outputOffset: number; - - constructor(outputLineIndex: number, outputOffset: number) { - this.outputLineIndex = outputLineIndex; - this.outputOffset = outputOffset; - } -} - -export class LineBreakData { - constructor( - public breakOffsets: number[], - public breakOffsetsVisibleColumn: number[], - public wrappedTextIndentLength: number - ) { } - - public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number { - if (outputLineIndex === 0) { - return outputOffset; - } else { - return breakOffsets[outputLineIndex - 1] + outputOffset; - } - } - - public static getOutputPositionOfInputOffset(breakOffsets: number[], inputOffset: number): OutputPosition { - let low = 0; - let high = breakOffsets.length - 1; - let mid = 0; - let midStart = 0; - - while (low <= high) { - mid = low + ((high - low) / 2) | 0; - - const midStop = breakOffsets[mid]; - midStart = mid > 0 ? breakOffsets[mid - 1] : 0; - - if (inputOffset < midStart) { - high = mid - 1; - } else if (inputOffset >= midStop) { - low = mid + 1; - } else { - break; - } - } - - return new OutputPosition(mid, inputOffset - midStart); - } -} - -export interface ILineBreaksComputer { - /** - * Pass in `previousLineBreakData` if the only difference is in breaking columns!!! - */ - addRequest(lineText: string, previousLineBreakData: LineBreakData | null): void; - finalize(): (LineBreakData | null)[]; -} - export interface ILineBreaksComputerFactory { createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer; } @@ -174,6 +116,10 @@ export class CoordinatesConverter implements ICoordinatesConverter { public modelPositionIsVisible(modelPosition: Position): boolean { return this._lines.modelPositionIsVisible(modelPosition.lineNumber, modelPosition.column); } + + public getModelLineViewLineCount(modelLineNumber: number): number { + return this._lines.getModelLineViewLineCount(modelLineNumber); + } } const enum IndentGuideRepeatOption { @@ -473,6 +419,14 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return this.lines[modelLineNumber - 1].isVisible(); } + public getModelLineViewLineCount(modelLineNumber: number): number { + if (modelLineNumber < 1 || modelLineNumber > this.lines.length) { + // invalid arguments + return 1; + } + return this.lines[modelLineNumber - 1].getViewLineCount(); + } + public setTabSize(newTabSize: number): boolean { if (this.tabSize === newTabSize) { return false; @@ -1431,6 +1385,9 @@ export class IdentityCoordinatesConverter implements ICoordinatesConverter { return true; } + public getModelLineViewLineCount(modelLineNumber: number): number { + return 1; + } } export class IdentityLinesCollection implements IViewModelLinesCollection { diff --git a/src/vs/editor/common/viewModel/viewEventHandler.ts b/src/vs/editor/common/viewModel/viewEventHandler.ts index aad7a0b49..e73ba9679 100644 --- a/src/vs/editor/common/viewModel/viewEventHandler.ts +++ b/src/vs/editor/common/viewModel/viewEventHandler.ts @@ -33,10 +33,15 @@ export class ViewEventHandler extends Disposable { // --- begin event handlers + public onCompositionStart(e: viewEvents.ViewCompositionStartEvent): boolean { + return false; + } + public onCompositionEnd(e: viewEvents.ViewCompositionEndEvent): boolean { + return false; + } public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { return false; } - public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { return false; } @@ -94,6 +99,18 @@ export class ViewEventHandler extends Disposable { switch (e.type) { + case viewEvents.ViewEventType.ViewCompositionStart: + if (this.onCompositionStart(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewCompositionEnd: + if (this.onCompositionEnd(e)) { + shouldRender = true; + } + break; + case viewEvents.ViewEventType.ViewConfigurationChanged: if (this.onConfigurationChanged(e)) { shouldRender = true; diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 91ab8513c..125cb5880 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -61,6 +61,8 @@ export interface IViewLayout { getWhitespaces(): IEditorWhitespace[]; isAfterLines(verticalOffset: number): boolean; + isInTopPadding(verticalOffset: number): boolean; + isInBottomPadding(verticalOffset: number): boolean; getLineNumberAtVerticalOffset(verticalOffset: number): number; getVerticalOffsetForLineNumber(lineNumber: number): number; getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null; @@ -82,6 +84,65 @@ export interface ICoordinatesConverter { convertModelPositionToViewPosition(modelPosition: Position): Position; convertModelRangeToViewRange(modelRange: Range): Range; modelPositionIsVisible(modelPosition: Position): boolean; + getModelLineViewLineCount(modelLineNumber: number): number; +} + +export class OutputPosition { + outputLineIndex: number; + outputOffset: number; + + constructor(outputLineIndex: number, outputOffset: number) { + this.outputLineIndex = outputLineIndex; + this.outputOffset = outputOffset; + } +} + +export class LineBreakData { + constructor( + public breakOffsets: number[], + public breakOffsetsVisibleColumn: number[], + public wrappedTextIndentLength: number + ) { } + + public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number { + if (outputLineIndex === 0) { + return outputOffset; + } else { + return breakOffsets[outputLineIndex - 1] + outputOffset; + } + } + + public static getOutputPositionOfInputOffset(breakOffsets: number[], inputOffset: number): OutputPosition { + let low = 0; + let high = breakOffsets.length - 1; + let mid = 0; + let midStart = 0; + + while (low <= high) { + mid = low + ((high - low) / 2) | 0; + + const midStop = breakOffsets[mid]; + midStart = mid > 0 ? breakOffsets[mid - 1] : 0; + + if (inputOffset < midStart) { + high = mid - 1; + } else if (inputOffset >= midStop) { + low = mid + 1; + } else { + break; + } + } + + return new OutputPosition(mid, inputOffset - midStart); + } +} + +export interface ILineBreaksComputer { + /** + * Pass in `previousLineBreakData` if the only difference is in breaking columns!!! + */ + addRequest(lineText: string, previousLineBreakData: LineBreakData | null): void; + finalize(): (LineBreakData | null)[]; } export interface IViewModel extends ICursorSimpleModel { @@ -103,6 +164,8 @@ export interface IViewModel extends ICursorSimpleModel { setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void; tokenizeViewport(): void; setHasFocus(hasFocus: boolean): void; + onCompositionStart(): void; + onCompositionEnd(): void; onDidColorThemeChange(): void; getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[]; @@ -142,6 +205,7 @@ export interface IViewModel extends ICursorSimpleModel { //#endregion + createLineBreaksComputer(): ILineBreaksComputer; //#region cursor getPrimaryCursorState(): CursorState; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index f7f130f08..932f6917e 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -21,7 +21,7 @@ import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTok import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; -import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; +import { ICoordinatesConverter, ILineBreaksComputer, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as platform from 'vs/base/common/platform'; @@ -153,6 +153,10 @@ export class ViewModel extends Disposable implements IViewModel { this._eventDispatcher.dispose(); } + public createLineBreaksComputer(): ILineBreaksComputer { + return this._lines.createLineBreaksComputer(); + } + public addViewEventHandler(eventHandler: ViewEventHandler): void { this._eventDispatcher.addViewEventHandler(eventHandler); } @@ -179,6 +183,14 @@ export class ViewModel extends Disposable implements IViewModel { this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus)); } + public onCompositionStart(): void { + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionStartEvent()); + } + + public onCompositionEnd(): void { + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionEndEvent()); + } + public onDidColorThemeChange(): void { this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent()); } diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index a488d79ca..25b7687bc 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -9,7 +9,6 @@ import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } fr import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; @@ -17,6 +16,7 @@ import * as modes from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './types'; import { IProgress, Progress } from 'vs/platform/progress/common/progress'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; export const codeActionCommandId = 'editor.action.codeAction'; export const refactorCommandId = 'editor.action.refactor'; @@ -223,8 +223,8 @@ function getDocumentation( return undefined; } -registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise> { - const { resource, rangeOrSelection, kind, itemResolveCount } = args; +CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (accessor, ...args): Promise> { + const [resource, rangeOrSelection, kind, itemResolveCount] = args; if (!(resource instanceof URI)) { throw illegalArgument(); } diff --git a/src/vs/editor/contrib/codeAction/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/codeActionMenu.ts index 186eacd2e..e4a05011d 100644 --- a/src/vs/editor/contrib/codeAction/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/codeActionMenu.ts @@ -35,10 +35,14 @@ class CodeActionAction extends Action { public readonly action: CodeAction, callback: () => Promise, ) { - super(action.command ? action.command.id : action.title, action.title, undefined, !action.disabled, callback); + super(action.command ? action.command.id : action.title, stripNewlines(action.title), undefined, !action.disabled, callback); } } +function stripNewlines(str: string): string { + return str.replace(/\r\n|\r|\n/g, ' '); +} + export interface CodeActionShowOptions { readonly includeDisabledActions: boolean; } @@ -224,3 +228,5 @@ export class CodeActionKeybindingResolver { }, undefined as ResolveCodeActionKeybinding | undefined); } } + + diff --git a/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts index a0092488f..b8785616b 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts @@ -90,6 +90,7 @@ function createCodeActionKeybinding(keycode: KeyCode, command: string, commandAr commandArgs, undefined, false, - null); + null, + false); } diff --git a/src/vs/editor/contrib/codelens/codeLensCache.ts b/src/vs/editor/contrib/codelens/codeLensCache.ts index 5f8c14765..c932cf496 100644 --- a/src/vs/editor/contrib/codelens/codeLensCache.ts +++ b/src/vs/editor/contrib/codelens/codeLensCache.ts @@ -9,7 +9,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { CodeLensModel } from 'vs/editor/contrib/codelens/codelens'; import { LRUCache } from 'vs/base/common/map'; import { CodeLensProvider, CodeLensList, CodeLens } from 'vs/editor/common/modes'; -import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { Range } from 'vs/editor/common/core/range'; import { runWhenIdle } from 'vs/base/common/async'; import { once } from 'vs/base/common/functional'; @@ -62,7 +62,7 @@ export class CodeLensCache implements ICodeLensCache { // store lens data on shutdown once(storageService.onWillSaveState)(e => { if (e.reason === WillSaveStateReason.SHUTDOWN) { - storageService.store(key, this._serialize(), StorageScope.WORKSPACE); + storageService.store(key, this._serialize(), StorageScope.WORKSPACE, StorageTarget.MACHINE); } }); } diff --git a/src/vs/editor/contrib/codelens/codelens.ts b/src/vs/editor/contrib/codelens/codelens.ts index fbcfb1991..f8a4f09b7 100644 --- a/src/vs/editor/contrib/codelens/codelens.ts +++ b/src/vs/editor/contrib/codelens/codelens.ts @@ -7,11 +7,12 @@ import { mergeSort } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { ITextModel } from 'vs/editor/common/model'; import { CodeLensProvider, CodeLensProviderRegistry, CodeLens, CodeLensList } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { assertType } from 'vs/base/common/types'; export interface CodeLensItem { symbol: CodeLens; @@ -79,14 +80,12 @@ export async function getCodeLensModel(model: ITextModel, token: CancellationTok return result; } -registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { +CommandsRegistry.registerCommand('_executeCodeLensProvider', function (accessor, ...args: [URI, number | undefined | null]) { + let [uri, itemResolveCount] = args; + assertType(URI.isUri(uri)); + assertType(typeof itemResolveCount === 'number' || !itemResolveCount); - let { resource, itemResolveCount } = args; - if (!(resource instanceof URI)) { - throw illegalArgument(); - } - - const model = accessor.get(IModelService).getModel(resource); + const model = accessor.get(IModelService).getModel(uri); if (!model) { throw illegalArgument(); } @@ -99,7 +98,7 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { let resolve: Promise[] = []; for (const item of value.lenses) { - if (typeof itemResolveCount === 'undefined' || Boolean(item.symbol.command)) { + if (itemResolveCount === undefined || itemResolveCount === null || Boolean(item.symbol.command)) { result.push(item.symbol); } else if (itemResolveCount-- > 0 && item.provider.resolveCodeLens) { resolve.push(Promise.resolve(item.provider.resolveCodeLens(model, item.symbol, CancellationToken.None)).then(symbol => result.push(symbol || item.symbol))); diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 360889a81..3fddfdff3 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -53,7 +53,7 @@ export class CodeLensContribution implements IEditorContribution { this._disposables.add(this._editor.onDidChangeModel(() => this._onModelChange())); this._disposables.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); this._disposables.add(this._editor.onDidChangeConfiguration((e) => { - if (e.hasChanged(EditorOption.fontInfo)) { + if (e.hasChanged(EditorOption.fontInfo) || e.hasChanged(EditorOption.codeLensFontSize) || e.hasChanged(EditorOption.codeLensFontFamily)) { this._updateLensStyle(); } if (e.hasChanged(EditorOption.codeLens)) { @@ -77,21 +77,42 @@ export class CodeLensContribution implements IEditorContribution { this._disposables.dispose(); this._oldCodeLensModels.dispose(); this._currentCodeLensModel?.dispose(); + this._styleElement.remove(); + } + + private _getLayoutInfo() { + let fontSize = this._editor.getOption(EditorOption.codeLensFontSize); + let codeLensHeight: number; + if (!fontSize || fontSize < 5) { + fontSize = (this._editor.getOption(EditorOption.fontSize) * .9) | 0; + codeLensHeight = this._editor.getOption(EditorOption.lineHeight); + } else { + codeLensHeight = (fontSize * Math.max(1.3, this._editor.getOption(EditorOption.lineHeight) / this._editor.getOption(EditorOption.fontSize))) | 0; + } + return { codeLensHeight, fontSize }; } private _updateLensStyle(): void { - const options = this._editor.getOptions(); - const fontInfo = options.get(EditorOption.fontInfo); - const lineHeight = options.get(EditorOption.lineHeight); + const { codeLensHeight, fontSize } = this._getLayoutInfo(); + const fontFamily = this._editor.getOption(EditorOption.codeLensFontFamily); + const editorFontInfo = this._editor.getOption(EditorOption.fontInfo); - const height = Math.round(lineHeight * 1.1); - const fontSize = Math.round(fontInfo.fontSize * 0.9); - const newStyle = ` - .monaco-editor .codelens-decoration.${this._styleClassName} { height: ${height}px; line-height: ${lineHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;} - .monaco-editor .codelens-decoration.${this._styleClassName} > a > .codicon { line-height: ${lineHeight}px; font-size: ${fontSize}px; } + let newStyle = ` + .monaco-editor .codelens-decoration.${this._styleClassName} { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontSize * 0.5)}px; font-feature-settings: ${editorFontInfo.fontFeatureSettings} } + .monaco-editor .codelens-decoration.${this._styleClassName} span.codicon { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; } `; + if (fontFamily) { + newStyle += `.monaco-editor .codelens-decoration.${this._styleClassName} { font-family: '${fontFamily}'}`; + } this._styleElement.textContent = newStyle; + + // + this._editor.changeViewZones(accessor => { + for (let lens of this._lenses) { + lens.updateHeight(codeLensHeight, accessor); + } + }); } private _localDispose(): void { @@ -165,7 +186,7 @@ export class CodeLensContribution implements IEditorContribution { // render lenses this._renderCodeLensSymbols(result); - this._resolveCodeLensesInViewportSoon(); + this._resolveCodeLensesInViewport(); }, onUnexpectedError); }, this._getCodeLensModelDelays.get(model)); @@ -199,11 +220,12 @@ export class CodeLensContribution implements IEditorContribution { }); }); - // Compute new `visible` code lenses - this._resolveCodeLensesInViewportSoon(); // Ask for all references again scheduler.schedule(); })); + this._localToDispose.add(this._editor.onDidFocusEditorWidget(() => { + scheduler.schedule(); + })); this._localToDispose.add(this._editor.onDidScrollChange(e => { if (e.scrollTopChanged && this._lenses.length > 0) { this._resolveCodeLensesInViewportSoon(); @@ -283,6 +305,7 @@ export class CodeLensContribution implements IEditorContribution { } const scrollState = StableEditorScrollState.capture(this._editor); + const layoutInfo = this._getLayoutInfo(); this._editor.changeDecorations(decorationsAccessor => { this._editor.changeViewZones(viewZoneAccessor => { @@ -304,7 +327,7 @@ export class CodeLensContribution implements IEditorContribution { groupsIndex++; codeLensIndex++; } else { - this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._resolveCodeLensesInViewportSoon())); + this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, layoutInfo.codeLensHeight, () => this._resolveCodeLensesInViewportSoon())); codeLensIndex++; groupsIndex++; } @@ -318,7 +341,7 @@ export class CodeLensContribution implements IEditorContribution { // Create extra symbols while (groupsIndex < groups.length) { - this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._resolveCodeLensesInViewportSoon())); + this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, layoutInfo.codeLensHeight, () => this._resolveCodeLensesInViewportSoon())); groupsIndex++; } diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 215e48f22..f77b2dfc5 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -18,20 +18,20 @@ import { renderCodicons } from 'vs/base/browser/codicons'; class CodeLensViewZone implements IViewZone { - readonly heightInLines: number; readonly suppressMouseDown: boolean; readonly domNode: HTMLElement; afterLineNumber: number; + heightInPx: number; private _lastHeight?: number; - private readonly _onHeight: Function; + private readonly _onHeight: () => void; - constructor(afterLineNumber: number, onHeight: Function) { + constructor(afterLineNumber: number, heightInPx: number, onHeight: () => void) { this.afterLineNumber = afterLineNumber; - this._onHeight = onHeight; + this.heightInPx = heightInPx; - this.heightInLines = 1; + this._onHeight = onHeight; this.suppressMouseDown = true; this.domNode = document.createElement('div'); } @@ -179,8 +179,8 @@ export class CodeLensWidget { private readonly _editor: IActiveCodeEditor; private readonly _className: string; - private readonly _viewZone!: CodeLensViewZone; - private readonly _viewZoneId!: string; + private readonly _viewZone: CodeLensViewZone; + private readonly _viewZoneId: string; private _contentWidget?: CodeLensContentWidget; private _decorationIds: string[]; @@ -193,7 +193,8 @@ export class CodeLensWidget { className: string, helper: CodeLensHelper, viewZoneChangeAccessor: IViewZoneChangeAccessor, - updateCallback: Function + heightInPx: number, + updateCallback: () => void ) { this._editor = editor; this._className = className; @@ -224,7 +225,7 @@ export class CodeLensWidget { } }); - this._viewZone = new CodeLensViewZone(range!.startLineNumber - 1, updateCallback); + this._viewZone = new CodeLensViewZone(range!.startLineNumber - 1, heightInPx, updateCallback); this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone); if (lenses.length > 0) { @@ -236,7 +237,9 @@ export class CodeLensWidget { private _createContentWidgetIfNecessary(): void { if (!this._contentWidget) { this._contentWidget = new CodeLensContentWidget(this._editor, this._className, this._viewZone.afterLineNumber + 1); - this._editor.addContentWidget(this._contentWidget!); + this._editor.addContentWidget(this._contentWidget); + } else { + this._editor.layoutContentWidget(this._contentWidget); } } @@ -277,6 +280,14 @@ export class CodeLensWidget { }); } + updateHeight(height: number, viewZoneChangeAccessor: IViewZoneChangeAccessor): void { + this._viewZone.heightInPx = height; + viewZoneChangeAccessor.layoutZone(this._viewZoneId); + if (this._contentWidget) { + this._editor.layoutContentWidget(this._contentWidget); + } + } + computeIfNecessary(model: ITextModel): CodeLensItem[] | null { if (!this._viewZone.domNode.hasAttribute('monaco-visible-view-zone')) { return null; diff --git a/src/vs/editor/contrib/colorPicker/color.ts b/src/vs/editor/contrib/colorPicker/color.ts index d6b490128..7a4383b16 100644 --- a/src/vs/editor/contrib/colorPicker/color.ts +++ b/src/vs/editor/contrib/colorPicker/color.ts @@ -6,11 +6,11 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { ColorProviderRegistry, DocumentColorProvider, IColorInformation, IColorPresentation } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; export interface IColorData { @@ -36,9 +36,9 @@ export function getColorPresentations(model: ITextModel, colorInfo: IColorInform return Promise.resolve(provider.provideColorPresentations(model, colorInfo, token)); } -registerLanguageCommand('_executeDocumentColorProvider', function (accessor, args) { +CommandsRegistry.registerCommand('_executeDocumentColorProvider', function (accessor, ...args) { - const { resource } = args; + const [resource] = args; if (!(resource instanceof URI)) { throw illegalArgument(); } @@ -62,15 +62,16 @@ registerLanguageCommand('_executeDocumentColorProvider', function (accessor, arg }); -registerLanguageCommand('_executeColorPresentationProvider', function (accessor, args) { +CommandsRegistry.registerCommand('_executeColorPresentationProvider', function (accessor, ...args) { - const { resource, color, range } = args; - if (!(resource instanceof URI) || !Array.isArray(color) || color.length !== 4 || !Range.isIRange(range)) { + const [color, context] = args; + const { uri, range } = context; + if (!(uri instanceof URI) || !Array.isArray(color) || color.length !== 4 || !Range.isIRange(range)) { throw illegalArgument(); } const [red, green, blue, alpha] = color; - const model = accessor.get(IModelService).getModel(resource); + const model = accessor.get(IModelService).getModel(uri); if (!model) { throw illegalArgument(); } diff --git a/src/vs/editor/contrib/colorPicker/colorContributions.ts b/src/vs/editor/contrib/colorPicker/colorContributions.ts new file mode 100644 index 000000000..4a997061b --- /dev/null +++ b/src/vs/editor/contrib/colorPicker/colorContributions.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// import color detector contribution +import 'vs/editor/contrib/colorPicker/colorDetector'; + +import { Disposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ModesHoverController } from 'vs/editor/contrib/hover/hover'; +import { Range } from 'vs/editor/common/core/range'; +import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation'; + +export class ColorContribution extends Disposable implements IEditorContribution { + + public static readonly ID: string = 'editor.contrib.colorContribution'; + + static readonly RECOMPUTE_TIME = 1000; // ms + + constructor(private readonly _editor: ICodeEditor, + ) { + super(); + this._register(_editor.onMouseDown((e) => this.onMouseDown(e))); + } + + dispose(): void { + super.dispose(); + } + + private onMouseDown(mouseEvent: IEditorMouseEvent) { + const targetType = mouseEvent.target.type; + + if (targetType !== MouseTargetType.CONTENT_TEXT) { + return; + } + + const hoverOnColorDecorator = [...mouseEvent.target.element?.classList.values() || []].find(className => className.startsWith('ced-colorBox')); + if (!hoverOnColorDecorator) { + return; + } + + if (!mouseEvent.target.range) { + return; + } + + const hoverController = this._editor.getContribution(ModesHoverController.ID); + if (!hoverController.contentWidget.isColorPickerVisible()) { + const range = new Range(mouseEvent.target.range.startLineNumber, mouseEvent.target.range.startColumn + 1, mouseEvent.target.range.endLineNumber, mouseEvent.target.range.endColumn + 1); + hoverController.showContentHover(range, HoverStartMode.Delayed, false); + } + } +} + +registerEditorContribution(ColorContribution.ID, ColorContribution); diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index 4b678d0ed..ccd09304a 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -46,13 +46,13 @@ export class ColorDetector extends Disposable implements IEditorContribution { @IConfigurationService private readonly _configurationService: IConfigurationService ) { super(); - this._register(_editor.onDidChangeModel((e) => { + this._register(_editor.onDidChangeModel(() => { this._isEnabled = this.isEnabled(); this.onModelChanged(); })); - this._register(_editor.onDidChangeModelLanguage((e) => this.onModelChanged())); - this._register(ColorProviderRegistry.onDidChange((e) => this.onModelChanged())); - this._register(_editor.onDidChangeConfiguration((e) => { + this._register(_editor.onDidChangeModelLanguage(() => this.onModelChanged())); + this._register(ColorProviderRegistry.onDidChange(() => this.onModelChanged())); + this._register(_editor.onDidChangeConfiguration(() => { let prevIsEnabled = this._isEnabled; this._isEnabled = this.isEnabled(); if (prevIsEnabled !== this._isEnabled) { @@ -110,7 +110,7 @@ export class ColorDetector extends Disposable implements IEditorContribution { return; } - this._localToDispose.add(this._editor.onDidChangeModelContent((e) => { + this._localToDispose.add(this._editor.onDidChangeModelContent(() => { if (!this._timeoutTimer) { this._timeoutTimer = new TimeoutTimer(); this._timeoutTimer.cancelAndSet(() => { diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 4bcdaa397..454b3d339 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -335,7 +335,7 @@ export class ColorPickerWidget extends Widget { body: ColorPickerBody; - constructor(container: Node, private readonly model: ColorPickerModel, private pixelRatio: number, themeService: IThemeService) { + constructor(container: Node, readonly model: ColorPickerModel, private pixelRatio: number, themeService: IThemeService) { super(); this._register(onDidChangeZoomLevel(() => this.layout())); diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index 1dbe337e1..d09c10af9 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -7,6 +7,7 @@ import * as nls from 'vs/nls'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { Range } from 'vs/editor/common/core/range'; import { ICommand } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand'; @@ -31,17 +32,38 @@ abstract class CommentLineAction extends EditorAction { const model = editor.getModel(); const commands: ICommand[] = []; - const selections = editor.getSelections(); const modelOptions = model.getOptions(); const commentsOptions = editor.getOption(EditorOption.comments); + const selections = editor.getSelections().map((selection, index) => ({ selection, index, ignoreFirstLine: false })); + selections.sort((a, b) => Range.compareRangesUsingStarts(a.selection, b.selection)); + + // Remove selections that would result in copying the same line + let prev = selections[0]; + for (let i = 1; i < selections.length; i++) { + const curr = selections[i]; + if (prev.selection.endLineNumber === curr.selection.startLineNumber) { + // these two selections would copy the same line + if (prev.index < curr.index) { + // prev wins + curr.ignoreFirstLine = true; + } else { + // curr wins + prev.ignoreFirstLine = true; + prev = curr; + } + } + } + + for (const selection of selections) { commands.push(new LineCommentCommand( - selection, + selection.selection, modelOptions.tabSize, this._type, commentsOptions.insertSpace, - commentsOptions.ignoreEmptyLines + commentsOptions.ignoreEmptyLines, + selection.ignoreFirstLine )); } diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index 8952727a9..0b81435aa 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -57,13 +57,15 @@ export class LineCommentCommand implements ICommand { private _selectionId: string | null; private _deltaColumn: number; private _moveEndPositionDown: boolean; + private _ignoreFirstLine: boolean; constructor( selection: Selection, tabSize: number, type: Type, insertSpace: boolean, - ignoreEmptyLines: boolean + ignoreEmptyLines: boolean, + ignoreFirstLine?: boolean ) { this._selection = selection; this._tabSize = tabSize; @@ -73,6 +75,7 @@ export class LineCommentCommand implements ICommand { this._deltaColumn = 0; this._moveEndPositionDown = false; this._ignoreEmptyLines = ignoreEmptyLines; + this._ignoreFirstLine = ignoreFirstLine || false; } /** @@ -108,7 +111,7 @@ export class LineCommentCommand implements ICommand { * Analyze lines and decide which lines are relevant and what the toggle should do. * Also, build up several offsets and lengths useful in the generation of editor operations. */ - public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number, ignoreEmptyLines: boolean): IPreflightData { + public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number, ignoreEmptyLines: boolean, ignoreFirstLine: boolean): IPreflightData { let onlyWhitespaceLines = true; let shouldRemoveComments: boolean; @@ -124,6 +127,12 @@ export class LineCommentCommand implements ICommand { const lineData = lines[i]; const lineNumber = startLineNumber + i; + if (lineNumber === startLineNumber && ignoreFirstLine) { + // first line ignored + lineData.ignore = true; + continue; + } + const lineContent = model.getLineContent(lineNumber); const lineContentStartOffset = strings.firstNonWhitespaceIndex(lineContent); @@ -178,7 +187,7 @@ export class LineCommentCommand implements ICommand { /** * Analyze all lines and decide exactly what to do => not supported | insert line comments | remove line comments */ - public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number, ignoreEmptyLines: boolean): IPreflightData { + public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number, ignoreEmptyLines: boolean, ignoreFirstLine: boolean): IPreflightData { const lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber); if (lines === null) { return { @@ -186,7 +195,7 @@ export class LineCommentCommand implements ICommand { }; } - return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber, ignoreEmptyLines); + return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber, ignoreEmptyLines, ignoreFirstLine); } /** @@ -323,6 +332,12 @@ export class LineCommentCommand implements ICommand { let s = this._selection; this._moveEndPositionDown = false; + if (s.startLineNumber === s.endLineNumber && this._ignoreFirstLine) { + builder.addEditOperation(new Range(s.startLineNumber, model.getLineMaxColumn(s.startLineNumber), s.startLineNumber + 1, 1), s.startLineNumber === model.getLineCount() ? '' : '\n'); + this._selectionId = builder.trackSelection(s); + return; + } + if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) { this._moveEndPositionDown = true; s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1)); @@ -334,7 +349,8 @@ export class LineCommentCommand implements ICommand { model, s.startLineNumber, s.endLineNumber, - this._ignoreEmptyLines + this._ignoreEmptyLines, + this._ignoreFirstLine ); if (data.supported) { diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index 0689b4209..2a4329ba9 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -91,7 +91,7 @@ suite('Editor Contrib - Line Comment Command', () => { ' ', ' c', '\t\td' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true, false); if (!r.supported) { throw new Error(`unexpected`); } @@ -122,7 +122,7 @@ suite('Editor Contrib - Line Comment Command', () => { ' rem ', ' !@# c', '\t\t!@#d' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true, false); if (!r.supported) { throw new Error(`unexpected`); } diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index b4bdd4336..b1b4a06b7 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -51,6 +51,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); this._register(this._editor.onMouseDrag((e: IEditorMouseEvent) => this._onEditorMouseDrag(e))); this._register(this._editor.onMouseDrop((e: IPartialEditorMouseEvent) => this._onEditorMouseDrop(e))); + this._register(this._editor.onMouseDropCanceled(() => this._onEditorMouseDropCanceled())); this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e))); this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e))); this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur())); @@ -144,6 +145,16 @@ export class DragAndDropController extends Disposable implements IEditorContribu } } + private _onEditorMouseDropCanceled() { + this._editor.updateOptions({ + mouseStyle: 'text' + }); + + this._removeDecoration(); + this._dragSelection = null; + this._mouseDown = false; + } + private _onEditorMouseDrop(mouseEvent: IPartialEditorMouseEvent): void { if (mouseEvent.target && (this._hitContent(mouseEvent.target) || this._hitMargin(mouseEvent.target)) && mouseEvent.target.position) { let newCursorPosition = new Position(mouseEvent.target.position.lineNumber, mouseEvent.target.position.column); diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index 4f7b574cd..9982068e5 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -22,22 +22,23 @@ import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/con import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; const SEARCH_STRING_MAX_LENGTH = 524288; -export function getSelectionSearchString(editor: ICodeEditor): string | null { +export function getSelectionSearchString(editor: ICodeEditor, seedSearchStringFromSelection: 'single' | 'multiple' = 'single'): string | null { if (!editor.hasModel()) { return null; } const selection = editor.getSelection(); // if selection spans multiple lines, default search string to empty - if (selection.startLineNumber === selection.endLineNumber) { + + if ((seedSearchStringFromSelection === 'single' && selection.startLineNumber === selection.endLineNumber) + || seedSearchStringFromSelection === 'multiple') { if (selection.isEmpty()) { const wordAtPosition = editor.getConfiguredWordAtPosition(selection.getStartPosition()); if (wordAtPosition) { @@ -61,7 +62,7 @@ export const enum FindStartFocusAction { export interface IFindStartOptions { forceRevealReplace: boolean; - seedSearchStringFromSelection: boolean; + seedSearchStringFromSelection: 'none' | 'single' | 'multiple'; seedSearchStringFromGlobalClipboard: boolean; shouldFocus: FindStartFocusAction; shouldAnimate: boolean; @@ -82,6 +83,10 @@ export class CommonFindController extends Disposable implements IEditorContribut private readonly _clipboardService: IClipboardService; protected readonly _contextKeyService: IContextKeyService; + get editor() { + return this._editor; + } + public static get(editor: ICodeEditor): CommonFindController { return editor.getContribution(CommonFindController.ID); } @@ -122,7 +127,7 @@ export class CommonFindController extends Disposable implements IEditorContribut if (shouldRestartFind) { this._start({ forceRevealReplace: false, - seedSearchStringFromSelection: false && this._editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, @@ -163,16 +168,16 @@ export class CommonFindController extends Disposable implements IEditorContribut private saveQueryState(e: FindReplaceStateChangedEvent) { if (e.isRegex) { - this._storageService.store('editor.isRegex', this._state.actualIsRegex, StorageScope.WORKSPACE); + this._storageService.store('editor.isRegex', this._state.actualIsRegex, StorageScope.WORKSPACE, StorageTarget.USER); } if (e.wholeWord) { - this._storageService.store('editor.wholeWord', this._state.actualWholeWord, StorageScope.WORKSPACE); + this._storageService.store('editor.wholeWord', this._state.actualWholeWord, StorageScope.WORKSPACE, StorageTarget.USER); } if (e.matchCase) { - this._storageService.store('editor.matchCase', this._state.actualMatchCase, StorageScope.WORKSPACE); + this._storageService.store('editor.matchCase', this._state.actualMatchCase, StorageScope.WORKSPACE, StorageTarget.USER); } if (e.preserveCase) { - this._storageService.store('editor.preserveCase', this._state.actualPreserveCase, StorageScope.WORKSPACE); + this._storageService.store('editor.preserveCase', this._state.actualPreserveCase, StorageScope.WORKSPACE, StorageTarget.USER); } } @@ -262,7 +267,7 @@ export class CommonFindController extends Disposable implements IEditorContribut this._state.change({ searchString: searchString }, false); } - public highlightFindOptions(): void { + public highlightFindOptions(ignoreWhenVisible: boolean = false): void { // overwritten in subclass } @@ -278,8 +283,8 @@ export class CommonFindController extends Disposable implements IEditorContribut isRevealed: true }; - if (opts.seedSearchStringFromSelection) { - let selectionSearchString = getSelectionSearchString(this._editor); + if (opts.seedSearchStringFromSelection === 'single') { + let selectionSearchString = getSelectionSearchString(this._editor, opts.seedSearchStringFromSelection); if (selectionSearchString) { if (this._state.isRegex) { stateChanges.searchString = strings.escapeRegExpCharacters(selectionSearchString); @@ -287,6 +292,11 @@ export class CommonFindController extends Disposable implements IEditorContribut stateChanges.searchString = selectionSearchString; } } + } else if (opts.seedSearchStringFromSelection === 'multiple' && !opts.updateSearchScope) { + let selectionSearchString = getSelectionSearchString(this._editor, opts.seedSearchStringFromSelection); + if (selectionSearchString) { + stateChanges.searchString = selectionSearchString; + } } if (!stateChanges.searchString && opts.seedSearchStringFromGlobalClipboard) { @@ -404,7 +414,6 @@ export class FindController extends CommonFindController implements IFindControl @IThemeService private readonly _themeService: IThemeService, @INotificationService private readonly _notificationService: INotificationService, @IStorageService _storageService: IStorageService, - @IStorageKeysSyncRegistryService private readonly _storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IClipboardService clipboardService: IClipboardService, ) { super(editor, _contextKeyService, _storageService, clipboardService); @@ -449,11 +458,11 @@ export class FindController extends CommonFindController implements IFindControl } } - public highlightFindOptions(): void { + public highlightFindOptions(ignoreWhenVisible: boolean = false): void { if (!this._widget) { this._createFindWidget(); } - if (this._state.isRevealed) { + if (this._state.isRevealed && !ignoreWhenVisible) { this._widget!.highlightFindOptions(); } else { this._findOptionsWidget!.highlightFindOptions(); @@ -461,9 +470,17 @@ export class FindController extends CommonFindController implements IFindControl } private _createFindWidget() { - this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService, this._storageKeysSyncRegistryService)); + this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService)); this._findOptionsWidget = this._register(new FindOptionsWidget(this._editor, this._state, this._keybindingService, this._themeService)); } + + saveViewState(): any { + return this._widget?.getViewState(); + } + + restoreViewState(state: any): void { + this._widget?.setViewState(state); + } } export class StartFindAction extends MultiEditorAction { @@ -493,7 +510,7 @@ export class StartFindAction extends MultiEditorAction { if (controller) { await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).globalFindClipboard, shouldFocus: FindStartFocusAction.FocusFindInput, shouldAnimate: true, @@ -523,12 +540,12 @@ export class StartFindWithSelectionAction extends EditorAction { }); } - public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise { let controller = CommonFindController.get(editor); if (controller) { await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: true, + seedSearchStringFromSelection: 'multiple', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, @@ -546,7 +563,7 @@ export abstract class MatchFindAction extends EditorAction { if (controller && !this._run(controller)) { await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: true, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, @@ -578,7 +595,13 @@ export class NextMatchFindAction extends MatchFindAction { } protected _run(controller: CommonFindController): boolean { - return controller.moveToNextMatch(); + const result = controller.moveToNextMatch(); + if (result) { + controller.editor.pushUndoStop(); + return true; + } + + return false; } } @@ -599,7 +622,13 @@ export class NextMatchFindAction2 extends MatchFindAction { } protected _run(controller: CommonFindController): boolean { - return controller.moveToNextMatch(); + const result = controller.moveToNextMatch(); + if (result) { + controller.editor.pushUndoStop(); + return true; + } + + return false; } } @@ -659,7 +688,7 @@ export abstract class SelectionMatchFindAction extends EditorAction { if (!this._run(controller)) { await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, @@ -765,7 +794,7 @@ export class StartFindReplaceAction extends MultiEditorAction { if (controller) { await controller.start({ forceRevealReplace: true, - seedSearchStringFromSelection: seedSearchStringFromSelection, + seedSearchStringFromSelection: seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection, shouldFocus: shouldFocus, shouldAnimate: true, diff --git a/src/vs/editor/contrib/find/findOptionsWidget.ts b/src/vs/editor/contrib/find/findOptionsWidget.ts index d541e0d06..660eed855 100644 --- a/src/vs/editor/contrib/find/findOptionsWidget.ts +++ b/src/vs/editor/contrib/find/findOptionsWidget.ts @@ -213,7 +213,7 @@ registerThemingParticipant((theme, collector) => { const widgetShadowColor = theme.getColor(widgetShadow); if (widgetShadowColor) { - collector.addRule(`.monaco-editor .findOptionsWidget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + collector.addRule(`.monaco-editor .findOptionsWidget { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`); } const hcBorder = theme.getColor(contrastBorder); diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 21fbb0060..597af6c32 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -31,23 +31,22 @@ import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contri import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; -import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon, widgetClose } from 'vs/platform/theme/common/iconRegistry'; -const findSelectionIcon = registerIcon('find-selection', Codicon.selection); -const findCollapsedIcon = registerIcon('find-collapsed', Codicon.chevronRight); -const findExpandedIcon = registerIcon('find-expanded', Codicon.chevronDown); +const findSelectionIcon = registerIcon('find-selection', Codicon.selection, nls.localize('findSelectionIcon', 'Icon for \'Find in Selection\' in the editor find widget.')); +const findCollapsedIcon = registerIcon('find-collapsed', Codicon.chevronRight, nls.localize('findCollapsedIcon', 'Icon to indicate that the editor find widget is collapsed.')); +const findExpandedIcon = registerIcon('find-expanded', Codicon.chevronDown, nls.localize('findExpandedIcon', 'Icon to indicate that the editor find widget is expanded.')); -export const findCloseIcon = registerIcon('find-close', Codicon.close); -export const findReplaceIcon = registerIcon('find-replace', Codicon.replace); -export const findReplaceAllIcon = registerIcon('find-replace-all', Codicon.replaceAll); -export const findPreviousMatchIcon = registerIcon('find-previous-match', Codicon.arrowUp); -export const findNextMatchIcon = registerIcon('find-next-match', Codicon.arrowDown); +export const findReplaceIcon = registerIcon('find-replace', Codicon.replace, nls.localize('findReplaceIcon', 'Icon for \'Replace\' in the editor find widget.')); +export const findReplaceAllIcon = registerIcon('find-replace-all', Codicon.replaceAll, nls.localize('findReplaceAllIcon', 'Icon for \'Replace All\' in the editor find widget.')); +export const findPreviousMatchIcon = registerIcon('find-previous-match', Codicon.arrowUp, nls.localize('findPreviousMatchIcon', 'Icon for \'Find Previous\' in the editor find widget.')); +export const findNextMatchIcon = registerIcon('find-next-match', Codicon.arrowDown, nls.localize('findNextMatchIcon', 'Icon for \'Find Next\' in the editor find widget.')); export interface IFindController { replace(): void; @@ -164,7 +163,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL themeService: IThemeService, storageService: IStorageService, notificationService: INotificationService, - storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); this._codeEditor = codeEditor; @@ -176,7 +174,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._storageService = storageService; this._notificationService = notificationService; - storageKeysSyncRegistryService.registerStorageKey({ key: ctrlEnterReplaceAllWarningPromptedKey, version: 1 }); this._ctrlEnterReplaceAllWarningPrompted = !!storageService.getBoolean(ctrlEnterReplaceAllWarningPromptedKey, StorageScope.GLOBAL); this._isVisible = false; @@ -228,7 +225,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL if (this._isVisible) { let globalBufferTerm = await this._controller.getGlobalBufferTerm(); if (globalBufferTerm && globalBufferTerm !== this._state.searchString) { - this._state.change({ searchString: globalBufferTerm }, true); + this._state.change({ searchString: globalBufferTerm }, false); this._findInput.select(); } } @@ -383,7 +380,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL } private _delayedUpdateHistory() { - this._updateHistoryDelayer.trigger(this._updateHistory.bind(this)); + this._updateHistoryDelayer.trigger(this._updateHistory.bind(this)).then(undefined, onUnexpectedError); } private _updateHistory() { @@ -487,7 +484,15 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._toggleReplaceBtn.setEnabled(this._isVisible && canReplace); } + private _revealTimeouts: any[] = []; + private _reveal(): void { + this._revealTimeouts.forEach(e => { + clearTimeout(e); + }); + + this._revealTimeouts = []; + if (!this._isVisible) { this._isVisible = true; @@ -512,15 +517,15 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._tryUpdateWidgetWidth(); this._updateButtons(); - setTimeout(() => { + this._revealTimeouts.push(setTimeout(() => { this._domNode.classList.add('visible'); this._domNode.setAttribute('aria-hidden', 'false'); - }, 0); + }, 0)); // validate query again as it's being dismissed when we hide the find widget. - setTimeout(() => { + this._revealTimeouts.push(setTimeout(() => { this._findInput.validate(); - }, 200); + }, 200)); this._codeEditor.layoutOverlayWidget(this); @@ -555,6 +560,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL } private _hide(focusTheEditor: boolean): void { + this._revealTimeouts.forEach(e => { + clearTimeout(e); + }); + + this._revealTimeouts = []; + if (this._isVisible) { this._isVisible = false; @@ -571,7 +582,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL } } - private _layoutViewZone() { + private _layoutViewZone(targetScrollTop?: number) { const addExtraSpaceOnTop = this._codeEditor.getOption(EditorOption.find).addExtraSpaceOnTop; if (!addExtraSpaceOnTop) { @@ -591,7 +602,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL viewZone.heightInPx = this._getHeight(); this._viewZoneId = accessor.addZone(viewZone); // scroll top adjust to make sure the editor doesn't scroll when adding viewzone at the beginning. - this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + viewZone.heightInPx); + this._codeEditor.setScrollTop(targetScrollTop || this._codeEditor.getScrollTop() + viewZone.heightInPx); }); } @@ -880,7 +891,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL ); this._ctrlEnterReplaceAllWarningPrompted = true; - this._storageService.store(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL); + this._storageService.store(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL, StorageTarget.USER); } @@ -1006,7 +1017,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Previous button this._prevBtn = this._register(new SimpleButton({ label: NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.PreviousMatchFindAction), - className: findPreviousMatchIcon.classNames, + icon: findPreviousMatchIcon, onTrigger: () => { this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().then(undefined, onUnexpectedError); } @@ -1015,7 +1026,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Next button this._nextBtn = this._register(new SimpleButton({ label: NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.NextMatchFindAction), - className: findNextMatchIcon.classNames, + icon: findNextMatchIcon, onTrigger: () => { this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().then(undefined, onUnexpectedError); } @@ -1033,7 +1044,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Toggle selection button this._toggleSelectionFind = this._register(new Checkbox({ - icon: findSelectionIcon, + icon: ThemeIcon.asCSSIcon(findSelectionIcon), title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand), isChecked: false })); @@ -1066,7 +1077,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Close button this._closeBtn = this._register(new SimpleButton({ label: NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.CloseFindWidgetCommand), - className: findCloseIcon.classNames, + icon: widgetClose, onTrigger: () => { this._state.change({ isRevealed: false, searchScope: null }, false); }, @@ -1130,7 +1141,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Replace one button this._replaceBtn = this._register(new SimpleButton({ label: NLS_REPLACE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceOneAction), - className: findReplaceIcon.classNames, + icon: findReplaceIcon, onTrigger: () => { this._controller.replace(); }, @@ -1145,7 +1156,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Replace all button this._replaceAllBtn = this._register(new SimpleButton({ label: NLS_REPLACE_ALL_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceAllAction), - className: findReplaceAllIcon.classNames, + icon: findReplaceAllIcon, onTrigger: () => { this._controller.replaceAll(); } @@ -1255,11 +1266,35 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL const value = this._codeEditor.getOption(EditorOption.accessibilitySupport); this._findInput.setFocusInputOnOptionClick(value !== AccessibilitySupport.Enabled); } + + getViewState() { + let widgetViewZoneVisible = false; + if (this._viewZone && this._viewZoneId) { + widgetViewZoneVisible = this._viewZone.heightInPx > this._codeEditor.getScrollTop(); + } + + return { + widgetViewZoneVisible, + scrollTop: this._codeEditor.getScrollTop() + }; + } + + setViewState(state?: { widgetViewZoneVisible: boolean; scrollTop: number }) { + if (!state) { + return; + } + + if (state.widgetViewZoneVisible) { + // we should add the view zone + this._layoutViewZone(state.scrollTop); + } + } } export interface ISimpleButtonOpts { readonly label: string; - readonly className: string; + readonly className?: string; + readonly icon?: ThemeIcon; readonly onTrigger: () => void; readonly onKeyDown?: (e: IKeyboardEvent) => void; } @@ -1273,10 +1308,18 @@ export class SimpleButton extends Widget { super(); this._opts = opts; + let className = 'button'; + if (this._opts.className) { + className = className + ' ' + this._opts.className; + } + if (this._opts.icon) { + className = className + ' ' + ThemeIcon.asClassName(this._opts.icon); + } + this._domNode = document.createElement('div'); this._domNode.title = this._opts.label; this._domNode.tabIndex = 0; - this._domNode.className = 'button ' + this._opts.className; + this._domNode.className = className; this._domNode.setAttribute('role', 'button'); this._domNode.setAttribute('aria-label', this._opts.label); @@ -1318,11 +1361,11 @@ export class SimpleButton extends Widget { public setExpanded(expanded: boolean): void { this._domNode.setAttribute('aria-expanded', String(!!expanded)); if (expanded) { - this._domNode.classList.remove(...findCollapsedIcon.classNames.split(' ')); - this._domNode.classList.add(...findExpandedIcon.classNames.split(' ')); + this._domNode.classList.remove(...ThemeIcon.asClassNameArray(findCollapsedIcon)); + this._domNode.classList.add(...ThemeIcon.asClassNameArray(findExpandedIcon)); } else { - this._domNode.classList.remove(...findExpandedIcon.classNames.split(' ')); - this._domNode.classList.add(...findCollapsedIcon.classNames.split(' ')); + this._domNode.classList.remove(...ThemeIcon.asClassNameArray(findExpandedIcon)); + this._domNode.classList.add(...ThemeIcon.asClassNameArray(findCollapsedIcon)); } } } @@ -1345,7 +1388,7 @@ registerThemingParticipant((theme, collector) => { const widgetShadowColor = theme.getColor(widgetShadow); if (widgetShadowColor) { - collector.addRule(`.monaco-editor .find-widget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + collector.addRule(`.monaco-editor .find-widget { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`); } const findMatchHighlightBorder = theme.getColor(editorFindMatchHighlightBorder); diff --git a/src/vs/editor/contrib/find/test/findController.test.ts b/src/vs/editor/contrib/find/test/findController.test.ts index ee0c48626..8d9216ea0 100644 --- a/src/vs/editor/contrib/find/test/findController.test.ts +++ b/src/vs/editor/contrib/find/test/findController.test.ts @@ -12,7 +12,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 { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/findController'; +import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction, StartFindWithSelectionAction } from 'vs/editor/contrib/find/findController'; import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; import { withAsyncTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -61,14 +61,20 @@ suite('FindController', async () => { let serviceCollection = new ServiceCollection(); serviceCollection.set(IStorageService, { _serviceBrand: undefined, - onDidChangeStorage: Event.None, + onDidChangeTarget: Event.None, + onDidChangeValue: Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], - getNumber: (key: string) => undefined, + getNumber: (key: string) => undefined!, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, - remove: () => undefined - } as any); + remove: () => undefined, + isNew: () => false, + flush: () => { return Promise.resolve(); }, + keys: () => [], + logStorage: () => { }, + migrate: () => { throw new Error(); } + } as IStorageService); if (platform.isMacintosh) { serviceCollection.set(IClipboardService, { @@ -272,7 +278,7 @@ suite('FindController', async () => { findController.setSearchString(testRegexString); await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.FocusFindInput, shouldAnimate: false, @@ -298,7 +304,7 @@ suite('FindController', async () => { let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, @@ -428,6 +434,59 @@ suite('FindController', async () => { findController.dispose(); }); }); + + test('issue #47400, CMD+E supports feeding multiple line of text into the find widget', async () => { + await withAsyncTestCodeEditor([ + 'ABC', + 'ABC', + 'XYZ', + 'ABC', + 'ABC' + ], { serviceCollection: serviceCollection }, async (editor) => { + clipboardState = ''; + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let startFindAction = new StartFindAction(); + + // change selection + editor.setSelection(new Selection(1, 1, 1, 1)); + + // cmd+f - open find widget + await startFindAction.run(null, editor); + + editor.setSelection(new Selection(1, 1, 2, 4)); + let startFindWithSelectionAction = new StartFindWithSelectionAction(); + await startFindWithSelectionAction.run(null, editor); + let findState = findController.getState(); + + assert.deepEqual(findState.searchString.split(/\r\n|\r|\n/g), ['ABC', 'ABC']); + + editor.setSelection(new Selection(3, 1, 3, 1)); + await startFindWithSelectionAction.run(null, editor); + + findController.dispose(); + }); + }); + + test('issue #109756, CMD+E with empty cursor should always work', async () => { + await withAsyncTestCodeEditor([ + 'ABC', + 'ABC', + 'XYZ', + 'ABC', + 'ABC' + ], { serviceCollection: serviceCollection }, async (editor) => { + clipboardState = ''; + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + editor.setSelection(new Selection(1, 2, 1, 2)); + + let startFindWithSelectionAction = new StartFindWithSelectionAction(); + startFindWithSelectionAction.run(null, editor); + + let findState = findController.getState(); + assert.deepEqual(findState.searchString, 'ABC'); + findController.dispose(); + }); + }); }); suite('FindController query options persistence', async () => { @@ -438,14 +497,20 @@ suite('FindController query options persistence', async () => { let serviceCollection = new ServiceCollection(); serviceCollection.set(IStorageService, { _serviceBrand: undefined, - onDidChangeStorage: Event.None, + onDidChangeTarget: Event.None, + onDidChangeValue: Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], - getNumber: (key: string) => undefined, + getNumber: (key: string) => undefined!, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, - remove: () => undefined - } as any); + remove: () => undefined, + isNew: () => false, + flush: () => { return Promise.resolve(); }, + keys: () => [], + logStorage: () => { }, + migrate: () => { throw new Error(); } + } as IStorageService); test('matchCase', async () => { await withAsyncTestCodeEditor([ @@ -524,9 +589,9 @@ suite('FindController query options persistence', async () => { ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => { // clipboardState = ''; let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); - const findConfig = { + const findConfig: IFindStartOptions = { forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, @@ -558,7 +623,7 @@ suite('FindController query options persistence', async () => { await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, @@ -582,7 +647,7 @@ suite('FindController query options persistence', async () => { await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, @@ -607,7 +672,7 @@ suite('FindController query options persistence', async () => { await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index 0bfa5f681..1d48e0611 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -32,7 +32,7 @@ import { InitializingRangeProvider, ID_INIT_PROVIDER } from 'vs/editor/contrib/f import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerColor, editorSelectionBackground, transparent, iconForeground } from 'vs/platform/theme/common/colorRegistry'; const CONTEXT_FOLDING_ENABLED = new RawContextKey('foldingEnabled', false); @@ -916,8 +916,8 @@ registerThemingParticipant((theme, collector) => { const editorFoldColor = theme.getColor(editorFoldForeground); if (editorFoldColor) { collector.addRule(` - .monaco-editor .cldr${foldingExpandedIcon.cssSelector}, - .monaco-editor .cldr${foldingCollapsedIcon.cssSelector} { + .monaco-editor .cldr${ThemeIcon.asCSSSelector(foldingExpandedIcon)}, + .monaco-editor .cldr${ThemeIcon.asCSSSelector(foldingCollapsedIcon)} { color: ${editorFoldColor} !important; } `); diff --git a/src/vs/editor/contrib/folding/foldingDecorations.ts b/src/vs/editor/contrib/folding/foldingDecorations.ts index c34e2c212..22601d129 100644 --- a/src/vs/editor/contrib/folding/foldingDecorations.ts +++ b/src/vs/editor/contrib/folding/foldingDecorations.ts @@ -7,18 +7,20 @@ import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationsChangeA import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IDecorationProvider } from 'vs/editor/contrib/folding/foldingModel'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; - -export const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown); -export const foldingCollapsedIcon = registerIcon('folding-collapsed', Codicon.chevronRight); +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +export const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown, localize('foldingExpandedIcon', 'Icon for expanded ranges in the editor glyph margin.')); +export const foldingCollapsedIcon = registerIcon('folding-collapsed', Codicon.chevronRight, localize('foldingCollapsedIcon', 'Icon for collapsed ranges in the editor glyph margin.')); export class FoldingDecorationProvider implements IDecorationProvider { private static readonly COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, afterContentClassName: 'inline-folded', isWholeLine: true, - firstLineDecorationClassName: foldingCollapsedIcon.classNames + firstLineDecorationClassName: ThemeIcon.asClassName(foldingCollapsedIcon) }); private static readonly COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION = ModelDecorationOptions.register({ @@ -26,19 +28,19 @@ export class FoldingDecorationProvider implements IDecorationProvider { afterContentClassName: 'inline-folded', className: 'folded-background', isWholeLine: true, - firstLineDecorationClassName: foldingCollapsedIcon.classNames + firstLineDecorationClassName: ThemeIcon.asClassName(foldingCollapsedIcon) }); private static readonly EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, isWholeLine: true, - firstLineDecorationClassName: foldingExpandedIcon.classNames + firstLineDecorationClassName: ThemeIcon.asClassName(foldingExpandedIcon) }); private static readonly EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, isWholeLine: true, - firstLineDecorationClassName: 'alwaysShowFoldIcons ' + foldingExpandedIcon.classNames + firstLineDecorationClassName: 'alwaysShowFoldIcons ' + ThemeIcon.asClassName(foldingExpandedIcon) }); private static readonly HIDDEN_RANGE_DECORATION = ModelDecorationOptions.register({ diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index bedb20cdd..cdfdac89b 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -215,13 +215,12 @@ class FormatDocumentAction extends EditorAction { alias: 'Format Document', precondition: ContextKeyExpr.and(EditorContextKeys.notInCompositeEditor, EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider), kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider), + kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, weight: KeybindingWeight.EditorContrib }, contextMenuOpts: { - when: EditorContextKeys.hasDocumentFormattingProvider, group: '1_modification', order: 1.3 } @@ -249,12 +248,12 @@ class FormatSelectionAction extends EditorAction { alias: 'Format Selection', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider), kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentSelectionFormattingProvider), + kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F), weight: KeybindingWeight.EditorContrib }, contextMenuOpts: { - when: ContextKeyExpr.and(EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasNonEmptySelection), + when: EditorContextKeys.hasNonEmptySelection, group: '1_modification', order: 1.31 } diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 001ed3f27..259773d90 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -20,9 +20,10 @@ import { MarkerNavigationWidget } from './gotoErrorWidget'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { MenuId } from 'vs/platform/actions/common/actions'; import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMarkerNavigationService, MarkerList } from 'vs/editor/contrib/gotoError/markerNavigationService'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export class MarkerController implements IEditorContribution { @@ -199,7 +200,7 @@ export class NextMarkerAction extends MarkerNavigationAction { menuOpts: { menuId: MarkerNavigationWidget.TitleMenu, title: NextMarkerAction.LABEL, - icon: registerIcon('marker-navigation-next', Codicon.chevronDown), + icon: registerIcon('marker-navigation-next', Codicon.chevronDown, nls.localize('nextMarkerIcon', 'Icon for goto next marker.')), group: 'navigation', order: 1 } @@ -224,7 +225,7 @@ class PrevMarkerAction extends MarkerNavigationAction { menuOpts: { menuId: MarkerNavigationWidget.TitleMenu, title: NextMarkerAction.LABEL, - icon: registerIcon('marker-navigation-previous', Codicon.chevronUp), + icon: registerIcon('marker-navigation-previous', Codicon.chevronUp, nls.localize('previousMarkerIcon', 'Icon for goto previous marker.')), group: 'navigation', order: 2 } diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 66bdc31f0..10a503f67 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -30,6 +30,7 @@ import { MenuId, IMenuService } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { splitLines } from 'vs/base/common/strings'; class MessageWidget { @@ -102,7 +103,7 @@ class MessageWidget { } } - const lines = message.split(/\r\n|\r|\n/g); + const lines = splitLines(message); this._lines = lines.length; this._longestLineLength = 0; for (const line of lines) { @@ -296,7 +297,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { protected _fillHead(container: HTMLElement): void { super._fillHead(container); - this._disposables.add(this._actionbarWidget!.actionRunner.onDidBeforeRun(e => this.editor.focus())); + this._disposables.add(this._actionbarWidget!.actionRunner.onBeforeRun(e => this.editor.focus())); const actions: IAction[] = []; const menu = this._menuService.createMenu(MarkerNavigationWidget.TitleMenu, this._contextKeyService); diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 232aab8d0..835033505 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -162,6 +162,9 @@ abstract class SymbolNavigationAction extends EditorAction { if (!range) { range = reference.range; } + if (!range) { + return undefined; + } const targetEditor = await editorService.openCodeEditor({ resource: reference.uri, diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts index b7942a37a..ead7b894a 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts @@ -10,7 +10,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IContextKey, IContextKeyService, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ReferencesModel, OneReference } from '../referencesModel'; @@ -101,7 +101,7 @@ export abstract class ReferencesController implements IEditorContribution { this._disposables.add(this._widget.onDidClose(() => { modelPromise.cancel(); if (this._widget) { - this._storageService.store(storageKey, JSON.stringify(this._widget.layoutData), StorageScope.GLOBAL); + this._storageService.store(storageKey, JSON.stringify(this._widget.layoutData), StorageScope.GLOBAL, StorageTarget.MACHINE); this._widget = undefined; } this.closeWidget(); @@ -117,17 +117,17 @@ export abstract class ReferencesController implements IEditorContribution { if (event.source !== 'editor' || !this._configurationService.getValue('editor.stablePeek')) { // when stable peek is configured we don't close // the peek window on selecting the editor - this.openReference(element, false); + this.openReference(element, false, false); } break; case 'side': - this.openReference(element, true); + this.openReference(element, true, false); break; case 'goto': if (peekMode) { this._gotoReference(element); } else { - this.openReference(element, false); + this.openReference(element, false, true); } break; } @@ -285,7 +285,7 @@ export abstract class ReferencesController implements IEditorContribution { }); } - openReference(ref: Location, sideBySide: boolean): void { + openReference(ref: Location, sideBySide: boolean, pinned: boolean): void { // clear stage if (!sideBySide) { this.closeWidget(); @@ -294,7 +294,7 @@ export abstract class ReferencesController implements IEditorContribution { const { uri, range } = ref; this._editorService.openCodeEditor({ resource: uri, - options: { selection: range } + options: { selection: range, pinned } }, this._editor, sideBySide); } } @@ -404,7 +404,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const listService = accessor.get(IListService); const focus = listService.lastFocusedList?.getFocus(); if (Array.isArray(focus) && focus[0] instanceof OneReference) { - withController(accessor, controller => controller.openReference(focus[0], true)); + withController(accessor, controller => controller.openReference(focus[0], true, true)); } } }); @@ -413,6 +413,6 @@ CommandsRegistry.registerCommand('openReference', (accessor) => { const listService = accessor.get(IListService); const focus = listService.lastFocusedList?.getFocus(); if (Array.isArray(focus) && focus[0] instanceof OneReference) { - withController(accessor, controller => controller.openReference(focus[0], false)); + withController(accessor, controller => controller.openReference(focus[0], false, true)); } }); diff --git a/src/vs/editor/contrib/gotoSymbol/referencesModel.ts b/src/vs/editor/contrib/gotoSymbol/referencesModel.ts index 5c4bbe4d0..334928f3c 100644 --- a/src/vs/editor/contrib/gotoSymbol/referencesModel.ts +++ b/src/vs/editor/contrib/gotoSymbol/referencesModel.ts @@ -41,10 +41,20 @@ export class OneReference { } get ariaMessage(): string { - return localize( - 'aria.oneReference', "symbol in {0} on line {1} at column {2}", - basename(this.uri), this.range.startLineNumber, this.range.startColumn - ); + + const preview = this.parent.getPreview(this)?.preview(this.range); + + if (!preview) { + return localize( + 'aria.oneReference', "symbol in {0} on line {1} at column {2}", + basename(this.uri), this.range.startLineNumber, this.range.startColumn + ); + } else { + return localize( + 'aria.oneReference.preview', "symbol in {0} on line {1} at column {2}, {3}", + basename(this.uri), this.range.startLineNumber, this.range.startColumn, preview.value + ); + } } } diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index 92af0fba8..c57fbadbb 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -162,6 +162,15 @@ export class ModesHoverController implements IEditorContribution { return; } + + if ( + !this._isHoverSticky && targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === ModesContentHoverWidget.ID + && this._contentWidget.value?.isColorPickerVisible() + ) { + // though the hover is not sticky, the color picker needs to. + return; + } + if (this._isHoverSticky && targetType === MouseTargetType.OVERLAY_WIDGET && mouseEvent.target.detail === ModesGlyphHoverWidget.ID) { // mouse moved on top of overlay hover widget return; diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 2cb7e7938..ad2d6b272 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -31,12 +31,12 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; import { MarkerController, NextMarkerAction } from 'vs/editor/contrib/gotoError/gotoError'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import { QuickFixAction, QuickFixController } from 'vs/editor/contrib/codeAction/codeActionCommands'; import { CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/types'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { IIdentifiedSingleEditOperation, TrackedRangeStickiness } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Constants } from 'vs/base/common/uint'; import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -251,6 +251,23 @@ export class ModesContentHoverWidget extends ContentHoverWidget { })); this._register(TokenizationRegistry.onDidChange((e) => { if (this.isVisible && this._lastRange && this._messages.length > 0) { + this._messages = this._messages.map(msg => { + // If a color hover is visible, we need to update the message that + // created it so that the color matches the last chosen color + if (msg instanceof ColorHover && !!this._lastRange?.intersectRanges(msg.range) && this._colorPicker?.model.color) { + const color = this._colorPicker.model.color; + const newColor = { + red: color.rgba.r / 255, + green: color.rgba.g / 255, + blue: color.rgba.b / 255, + alpha: color.rgba.a + }; + return new ColorHover(msg.range, newColor, msg.provider); + } else { + return msg; + } + }); + this._hover.contentsDomNode.textContent = ''; this._renderMessages(this._lastRange, this._messages); } @@ -406,15 +423,17 @@ export class ModesContentHoverWidget extends ContentHoverWidget { model.presentation.textEdit.range.endLineNumber, model.presentation.textEdit.range.endColumn ); - newRange = newRange.setEndPosition(newRange.endLineNumber, newRange.startColumn + model.presentation.textEdit.text.length); + const trackedRange = this._editor.getModel()!._setTrackedRange(null, newRange, TrackedRangeStickiness.GrowsOnlyWhenTypingAfter); + this._editor.pushUndoStop(); + this._editor.executeEdits('colorpicker', textEdits); + newRange = this._editor.getModel()!._getTrackedRange(trackedRange) || newRange; } else { textEdits = [{ identifier: null, range, text: model.presentation.label, forceMoveMarkers: false }]; newRange = range.setEndPosition(range.endLineNumber, range.startColumn + model.presentation.label.length); + this._editor.pushUndoStop(); + this._editor.executeEdits('colorpicker', textEdits); } - this._editor.pushUndoStop(); - this._editor.executeEdits('colorpicker', textEdits); - if (model.presentation.additionalTextEdits) { textEdits = [...model.presentation.additionalTextEdits as IIdentifiedSingleEditOperation[]]; this._editor.executeEdits('colorpicker', textEdits); @@ -461,7 +480,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { const markdownHoverElement = $('div.hover-row.markdown-hover'); const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents')); const renderer = markdownDisposeables.add(new MarkdownRenderer({ editor: this._editor }, this._modeService, this._openerService)); - markdownDisposeables.add(renderer.onDidRenderCodeBlock(() => { + markdownDisposeables.add(renderer.onDidRenderAsync(() => { hoverContentsElement.className = 'hover-contents code-hover-contents'; this._hover.onContentsChanged(); })); @@ -559,6 +578,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { return hoverElement; } + private recentMarkerCodeActionsInfo: { marker: IMarker, hasCodeActions: boolean } | undefined = undefined; private renderMarkerStatusbar(markerHover: MarkerHover): HTMLElement { const hoverElement = $('div.hover-row.status-bar'); const disposables = new DisposableStore(); @@ -577,24 +597,28 @@ export class ModesContentHoverWidget extends ContentHoverWidget { if (!this._editor.getOption(EditorOption.readOnly)) { const quickfixPlaceholderElement = dom.append(actionsElement, $('div')); - quickfixPlaceholderElement.style.opacity = '0'; - quickfixPlaceholderElement.style.transition = 'opacity 0.2s'; - setTimeout(() => quickfixPlaceholderElement.style.opacity = '1', 200); - quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes..."); - disposables.add(toDisposable(() => quickfixPlaceholderElement.remove())); - + if (this.recentMarkerCodeActionsInfo) { + if (IMarkerData.makeKey(this.recentMarkerCodeActionsInfo.marker) === IMarkerData.makeKey(markerHover.marker)) { + if (!this.recentMarkerCodeActionsInfo.hasCodeActions) { + quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available"); + } + } else { + this.recentMarkerCodeActionsInfo = undefined; + } + } + const updatePlaceholderDisposable = disposables.add(disposableTimeout(() => quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes..."), 64)); const codeActionsPromise = this.getCodeActions(markerHover.marker); disposables.add(toDisposable(() => codeActionsPromise.cancel())); codeActionsPromise.then(actions => { - quickfixPlaceholderElement.style.transition = ''; - quickfixPlaceholderElement.style.opacity = '1'; + updatePlaceholderDisposable.dispose(); + this.recentMarkerCodeActionsInfo = { marker: markerHover.marker, hasCodeActions: actions.validActions.length > 0 }; - if (!actions.validActions.length) { + if (!this.recentMarkerCodeActionsInfo.hasCodeActions) { actions.dispose(); quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available"); return; } - quickfixPlaceholderElement.remove(); + quickfixPlaceholderElement.style.display = 'none'; let showing = false; disposables.add(toDisposable(() => { diff --git a/src/vs/editor/contrib/indentation/indentation.ts b/src/vs/editor/contrib/indentation/indentation.ts index 11c8b67f9..720c8e769 100644 --- a/src/vs/editor/contrib/indentation/indentation.ts +++ b/src/vs/editor/contrib/indentation/indentation.ts @@ -84,7 +84,7 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu } if (currentLineText !== adjustedLineContent) { - indentEdits.push(EditOperation.replace(new Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(globalIndent, indentSize, insertSpaces))); + indentEdits.push(EditOperation.replaceMove(new Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(globalIndent, indentSize, insertSpaces))); } } else { globalIndent = strings.getLeadingWhitespace(currentLineText); @@ -115,7 +115,7 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu } if (oldIndentation !== idealIndentForNextLine) { - indentEdits.push(EditOperation.replace(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(idealIndentForNextLine, indentSize, insertSpaces))); + indentEdits.push(EditOperation.replaceMove(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(idealIndentForNextLine, indentSize, insertSpaces))); } // calculate idealIndentForNextLine @@ -472,7 +472,6 @@ export class AutoIndentOnPaste implements IEditorContribution { } const autoIndent = this.editor.getOption(EditorOption.autoIndent); const { tabSize, indentSize, insertSpaces } = model.getOptions(); - this.editor.pushUndoStop(); let textEdits: TextEdit[] = []; let indentConverter = { @@ -583,9 +582,12 @@ export class AutoIndentOnPaste implements IEditorContribution { } } - let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection()!); - this.editor.executeCommand('autoIndentOnPaste', cmd); - this.editor.pushUndoStop(); + if (textEdits.length > 0) { + this.editor.pushUndoStop(); + let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection()!); + this.editor.executeCommand('autoIndentOnPaste', cmd); + this.editor.pushUndoStop(); + } } private shouldIgnoreLine(model: ITextModel, lineNumber: number): boolean { diff --git a/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts b/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts index 6b76a75b7..d88851410 100644 --- a/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts @@ -12,15 +12,17 @@ export class CopyLinesCommand implements ICommand { private readonly _selection: Selection; private readonly _isCopyingDown: boolean; + private readonly _noop: boolean; private _selectionDirection: SelectionDirection; private _selectionId: string | null; private _startLineNumberDelta: number; private _endLineNumberDelta: number; - constructor(selection: Selection, isCopyingDown: boolean) { + constructor(selection: Selection, isCopyingDown: boolean, noop?: boolean) { this._selection = selection; this._isCopyingDown = isCopyingDown; + this._noop = noop || false; this._selectionDirection = SelectionDirection.LTR; this._selectionId = null; this._startLineNumberDelta = 0; @@ -51,10 +53,14 @@ export class CopyLinesCommand implements ICommand { } } - if (!this._isCopyingDown) { - builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + sourceText); + if (this._noop) { + builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber + 1, 1), s.endLineNumber === model.getLineCount() ? '' : '\n'); } else { - builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), sourceText + '\n'); + if (!this._isCopyingDown) { + builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + sourceText); + } else { + builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), sourceText + '\n'); + } } this._selectionId = builder.trackSelection(s); diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index bc7fd660d..9c365d507 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -63,10 +63,7 @@ abstract class AbstractCopyLinesAction extends EditorAction { const commands: ICommand[] = []; for (const selection of selections) { - if (selection.ignore) { - continue; - } - commands.push(new CopyLinesCommand(selection.selection, this.down)); + commands.push(new CopyLinesCommand(selection.selection, this.down, selection.ignore)); } editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts index d9600938c..681c8f027 100644 --- a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { CompleteEnterAction, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { IIndentConverter, LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils'; @@ -135,7 +135,8 @@ export class MoveLinesCommand implements ICommand { // to s.startLineNumber builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), insertingText + '\n'); - let ret = this.matchEnterRule(model, indentConverter, tabSize, s.startLineNumber, s.startLineNumber, insertingText); + let ret = this.matchEnterRuleMovingDown(model, indentConverter, tabSize, s.startLineNumber, movingLineNumber, insertingText); + // check if the line being moved before matches onEnter rules, if so let's adjust the indentation by onEnter rules. if (ret !== null) { if (ret !== 0) { @@ -229,31 +230,7 @@ export class MoveLinesCommand implements ICommand { }; } - private matchEnterRule(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, oneLineAboveText?: string) { - let validPrecedingLine = oneLineAbove; - while (validPrecedingLine >= 1) { - // ship empty lines as empty lines just inherit indentation - let lineContent; - if (validPrecedingLine === oneLineAbove && oneLineAboveText !== undefined) { - lineContent = oneLineAboveText; - } else { - lineContent = model.getLineContent(validPrecedingLine); - } - - let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineContent); - if (nonWhitespaceIdx >= 0) { - break; - } - validPrecedingLine--; - } - - if (validPrecedingLine < 1 || line > model.getLineCount()) { - return null; - } - - let maxColumn = model.getLineMaxColumn(validPrecedingLine); - let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); - + private parseEnterResult(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, enter: CompleteEnterAction | null) { if (enter) { let enterPrefix = enter.indentation; @@ -283,6 +260,72 @@ export class MoveLinesCommand implements ICommand { return null; } + /** + * + * @param model + * @param indentConverter + * @param tabSize + * @param line the line moving down + * @param futureAboveLineNumber the line which will be at the `line` position + * @param futureAboveLineText + */ + private matchEnterRuleMovingDown(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, futureAboveLineNumber: number, futureAboveLineText: string) { + if (strings.lastNonWhitespaceIndex(futureAboveLineText) >= 0) { + // break + let maxColumn = model.getLineMaxColumn(futureAboveLineNumber); + let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(futureAboveLineNumber, maxColumn, futureAboveLineNumber, maxColumn)); + return this.parseEnterResult(model, indentConverter, tabSize, line, enter); + } else { + // go upwards, starting from `line - 1` + let validPrecedingLine = line - 1; + while (validPrecedingLine >= 1) { + let lineContent = model.getLineContent(validPrecedingLine); + let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineContent); + + if (nonWhitespaceIdx >= 0) { + break; + } + + validPrecedingLine--; + } + + if (validPrecedingLine < 1 || line > model.getLineCount()) { + return null; + } + + let maxColumn = model.getLineMaxColumn(validPrecedingLine); + let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); + return this.parseEnterResult(model, indentConverter, tabSize, line, enter); + } + } + + private matchEnterRule(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, oneLineAboveText?: string) { + let validPrecedingLine = oneLineAbove; + while (validPrecedingLine >= 1) { + // ship empty lines as empty lines just inherit indentation + let lineContent; + if (validPrecedingLine === oneLineAbove && oneLineAboveText !== undefined) { + lineContent = oneLineAboveText; + } else { + lineContent = model.getLineContent(validPrecedingLine); + } + + let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineContent); + if (nonWhitespaceIdx >= 0) { + break; + } + validPrecedingLine--; + } + + if (validPrecedingLine < 1 || line > model.getLineCount()) { + return null; + } + + let maxColumn = model.getLineMaxColumn(validPrecedingLine); + let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); + return this.parseEnterResult(model, indentConverter, tabSize, line, enter); + } + private trimLeft(str: string) { return str.replace(/^\s+/, ''); } diff --git a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts index dd0a86ac0..e8bdc29c8 100644 --- a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts @@ -329,6 +329,7 @@ suite('Editor contrib - Move Lines Command honors Indentation Rules', () => { mode.dispose(); }); + test('move line should still work as before if there is no indentation rules', () => { testMoveLinesUpWithIndentCommand( null!, @@ -351,3 +352,55 @@ suite('Editor contrib - Move Lines Command honors Indentation Rules', () => { ); }); }); + +class EnterRulesMode extends MockMode { + private static readonly _id = new LanguageIdentifier('moveLinesEnterMode', 8); + constructor() { + super(EnterRulesMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + indentationRules: { + decreaseIndentPattern: /^\s*\[$/, + increaseIndentPattern: /^\s*\]$/, + }, + brackets: [ + ['{', '}'] + ] + })); + } +} + +suite('Editor - contrib - Move Lines Command honors onEnter Rules', () => { + + test('issue #54829. move block across block', () => { + let mode = new EnterRulesMode(); + + testMoveLinesDownWithIndentCommand( + mode.getLanguageIdentifier(), + + [ + 'if (true) {', + ' if (false) {', + ' if (1) {', + ' console.log(\'b\');', + ' }', + ' console.log(\'a\');', + ' }', + '}' + ], + new Selection(3, 9, 5, 10), + [ + 'if (true) {', + ' if (false) {', + ' console.log(\'a\');', + ' if (1) {', + ' console.log(\'b\');', + ' }', + ' }', + '}' + ], + new Selection(4, 9, 6, 10), + ); + + mode.dispose(); + }); +}); diff --git a/src/vs/editor/contrib/rename/onTypeRename.ts b/src/vs/editor/contrib/linkedEditing/linkedEditing.ts similarity index 81% rename from src/vs/editor/contrib/rename/onTypeRename.ts rename to src/vs/editor/contrib/linkedEditing/linkedEditing.ts index 105dfb2dd..441460812 100644 --- a/src/vs/editor/contrib/rename/onTypeRename.ts +++ b/src/vs/editor/contrib/linkedEditing/linkedEditing.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/onTypeRename'; import * as nls from 'vs/nls'; import { registerEditorContribution, registerModelAndPositionCommand, EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import * as arrays from 'vs/base/common/arrays'; @@ -15,7 +14,7 @@ import { Position, IPosition } from 'vs/editor/common/core/position'; import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { OnTypeRenameProviderRegistry } from 'vs/editor/common/modes'; +import { LinkedEditingRangeProviderRegistry, LinkedEditingRanges } from 'vs/editor/common/modes'; import { first, createCancelablePromise, CancelablePromise, Delayer } from 'vs/base/common/async'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -31,19 +30,21 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic import { Color } from 'vs/base/common/color'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -export const CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE = new RawContextKey('onTypeRenameInputVisible', false); +export const CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE = new RawContextKey('LinkedEditingInputVisible', false); -export class OnTypeRenameContribution extends Disposable implements IEditorContribution { +const DECORATION_CLASS_NAME = 'linked-editing-decoration'; - public static readonly ID = 'editor.contrib.onTypeRename'; +export class LinkedEditingContribution extends Disposable implements IEditorContribution { + + public static readonly ID = 'editor.contrib.linkedEditing'; private static readonly DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges, - className: 'on-type-rename-decoration' + className: DECORATION_CLASS_NAME }); - static get(editor: ICodeEditor): OnTypeRenameContribution { - return editor.getContribution(OnTypeRenameContribution.ID); + static get(editor: ICodeEditor): LinkedEditingContribution { + return editor.getContribution(LinkedEditingContribution.ID); } private _debounceDuration = 200; @@ -92,11 +93,11 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._register(this._editor.onDidChangeModel(() => this.reinitialize())); this._register(this._editor.onDidChangeConfiguration(e => { - if (e.hasChanged(EditorOption.renameOnType)) { + if (e.hasChanged(EditorOption.linkedEditing) || e.hasChanged(EditorOption.renameOnType)) { this.reinitialize(); } })); - this._register(OnTypeRenameProviderRegistry.onDidChange(() => this.reinitialize())); + this._register(LinkedEditingRangeProviderRegistry.onDidChange(() => this.reinitialize())); this._register(this._editor.onDidChangeModelLanguage(() => this.reinitialize())); this.reinitialize(); @@ -104,7 +105,7 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr private reinitialize() { const model = this._editor.getModel(); - const isEnabled = model !== null && this._editor.getOption(EditorOption.renameOnType) && OnTypeRenameProviderRegistry.has(model); + const isEnabled = model !== null && (this._editor.getOption(EditorOption.linkedEditing) || this._editor.getOption(EditorOption.renameOnType)) && LinkedEditingRangeProviderRegistry.has(model); if (isEnabled === this._enabled) { return; } @@ -219,9 +220,10 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr } try { + this._editor.popUndoStop(); this._ignoreChangeEvent = true; const prevEditOperationType = this._editor._getViewModel().getPrevEditOperationType(); - this._editor.executeEdits('onTypeRename', edits); + this._editor.executeEdits('linkedEditing', edits); this._editor._getViewModel().setPrevEditOperationType(prevEditOperationType); } finally { this._ignoreChangeEvent = false; @@ -282,7 +284,7 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._currentRequestModelVersion = modelVersionId; const request = createCancelablePromise(async token => { try { - const response = await getOnTypeRenameRanges(model, position, token); + const response = await getLinkedEditingRanges(model, position, token); if (request !== this._currentRequest) { return; } @@ -312,12 +314,12 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr } if (!foundReferenceRange) { - // Cannot do on type rename if the ranges are not where the cursor is... + // Cannot do linked editing if the ranges are not where the cursor is... this.clearRanges(); return; } - const decorations: IModelDeltaDecoration[] = ranges.map(range => ({ range: range, options: OnTypeRenameContribution.DECORATION })); + const decorations: IModelDeltaDecoration[] = ranges.map(range => ({ range: range, options: LinkedEditingContribution.DECORATION })); this._visibleContextKey.set(true); this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, decorations); } catch (err) { @@ -361,12 +363,12 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr // } } -export class OnTypeRenameAction extends EditorAction { +export class LinkedEditingAction extends EditorAction { constructor() { super({ - id: 'editor.action.onTypeRename', - label: nls.localize('onTypeRename.label', "On Type Rename Symbol"), - alias: 'On Type Rename Symbol', + id: 'editor.action.linkedEditing', + label: nls.localize('linkedEditing.label', "Start Linked Editing"), + alias: 'Start Linked Editing', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasRenameProvider), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, @@ -397,7 +399,7 @@ export class OnTypeRenameAction extends EditorAction { } run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { - const controller = OnTypeRenameContribution.get(editor); + const controller = LinkedEditingContribution.get(editor); if (controller) { return Promise.resolve(controller.updateRanges(true)); } @@ -405,9 +407,9 @@ export class OnTypeRenameAction extends EditorAction { } } -const OnTypeRenameCommand = EditorCommand.bindToContribution(OnTypeRenameContribution.get); -registerEditorCommand(new OnTypeRenameCommand({ - id: 'cancelOnTypeRenameInput', +const LinkedEditingCommand = EditorCommand.bindToContribution(LinkedEditingContribution.get); +registerEditorCommand(new LinkedEditingCommand({ + id: 'cancelLinkedEditingInput', precondition: CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE, handler: x => x.clearRanges(), kbOpts: { @@ -419,45 +421,31 @@ registerEditorCommand(new OnTypeRenameCommand({ })); -export function getOnTypeRenameRanges(model: ITextModel, position: Position, token: CancellationToken): Promise<{ - ranges: IRange[], - wordPattern?: RegExp -} | undefined | null> { - const orderedByScore = OnTypeRenameProviderRegistry.ordered(model); +function getLinkedEditingRanges(model: ITextModel, position: Position, token: CancellationToken): Promise { + const orderedByScore = LinkedEditingRangeProviderRegistry.ordered(model); - // in order of score ask the occurrences provider + // in order of score ask the linked editing range provider // until someone response with a good result - // (good = none empty array) - return first<{ - ranges: IRange[], - wordPattern?: RegExp - } | undefined>(orderedByScore.map(provider => () => { - return Promise.resolve(provider.provideOnTypeRenameRanges(model, position, token)).then((res) => { - if (!res) { - return undefined; - } - - return { - ranges: res.ranges, - wordPattern: res.wordPattern || provider.wordPattern - }; - }, (err) => { - onUnexpectedExternalError(err); + // (good = not null) + return first(orderedByScore.map(provider => async () => { + try { + return await provider.provideLinkedEditingRanges(model, position, token); + } catch (e) { + onUnexpectedExternalError(e); return undefined; - }); - + } }), result => !!result && arrays.isNonEmptyArray(result?.ranges)); } -export const editorOnTypeRenameBackground = registerColor('editor.onTypeRenameBackground', { dark: Color.fromHex('#f00').transparent(0.3), light: Color.fromHex('#f00').transparent(0.3), hc: Color.fromHex('#f00').transparent(0.3) }, nls.localize('editorOnTypeRenameBackground', 'Background color when the editor auto renames on type.')); +export const editorLinkedEditingBackground = registerColor('editor.linkedEditingBackground', { dark: Color.fromHex('#f00').transparent(0.3), light: Color.fromHex('#f00').transparent(0.3), hc: Color.fromHex('#f00').transparent(0.3) }, nls.localize('editorLinkedEditingBackground', 'Background color when the editor auto renames on type.')); registerThemingParticipant((theme, collector) => { - const editorOnTypeRenameBackgroundColor = theme.getColor(editorOnTypeRenameBackground); - if (editorOnTypeRenameBackgroundColor) { - collector.addRule(`.monaco-editor .on-type-rename-decoration { background: ${editorOnTypeRenameBackgroundColor}; border-left-color: ${editorOnTypeRenameBackgroundColor}; }`); + const editorLinkedEditingBackgroundColor = theme.getColor(editorLinkedEditingBackground); + if (editorLinkedEditingBackgroundColor) { + collector.addRule(`.monaco-editor .${DECORATION_CLASS_NAME} { background: ${editorLinkedEditingBackgroundColor}; border-left-color: ${editorLinkedEditingBackgroundColor}; }`); } }); -registerModelAndPositionCommand('_executeRenameOnTypeProvider', (model, position) => getOnTypeRenameRanges(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeLinkedEditingProvider', (model, position) => getLinkedEditingRanges(model, position, CancellationToken.None)); -registerEditorContribution(OnTypeRenameContribution.ID, OnTypeRenameContribution); -registerEditorAction(OnTypeRenameAction); +registerEditorContribution(LinkedEditingContribution.ID, LinkedEditingContribution); +registerEditorAction(LinkedEditingAction); diff --git a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts b/src/vs/editor/contrib/linkedEditing/test/linkedEditing.test..ts similarity index 91% rename from src/vs/editor/contrib/rename/test/onTypeRename.test.ts rename to src/vs/editor/contrib/linkedEditing/test/linkedEditing.test..ts index 05dba5e4b..9b00cb4b8 100644 --- a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts +++ b/src/vs/editor/contrib/linkedEditing/test/linkedEditing.test..ts @@ -10,12 +10,13 @@ import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Handler } from 'vs/editor/common/editorCommon'; import * as modes from 'vs/editor/common/modes'; -import { OnTypeRenameContribution } from 'vs/editor/contrib/rename/onTypeRename'; +import { LinkedEditingContribution } from 'vs/editor/contrib/linkedEditing/linkedEditing'; import { createTestCodeEditor, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; import { ITextModel } from 'vs/editor/common/model'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; const mockFile = URI.parse('test:somefile.ttt'); const mockFileSelector = { scheme: 'test' }; @@ -29,7 +30,12 @@ interface TestEditor { redo(): void; } -suite('On type rename', () => { +const languageIdentifier = new modes.LanguageIdentifier('linkedEditingTestLangage', 74); +LanguageConfigurationRegistry.register(languageIdentifier, { + wordPattern: /[a-zA-Z]+/ +}); + +suite('linked editing', () => { const disposables = new DisposableStore(); setup(() => { @@ -42,8 +48,8 @@ suite('On type rename', () => { function createMockEditor(text: string | string[]): ITestCodeEditor { const model = typeof text === 'string' - ? createTextModel(text, undefined, undefined, mockFile) - : createTextModel(text.join('\n'), undefined, undefined, mockFile); + ? createTextModel(text, undefined, languageIdentifier, mockFile) + : createTextModel(text.join('\n'), undefined, languageIdentifier, mockFile); const editor = createTestCodeEditor({ model }); disposables.add(model); @@ -55,18 +61,16 @@ suite('On type rename', () => { function testCase( name: string, - initialState: { text: string | string[], responseWordPattern?: RegExp, providerWordPattern?: RegExp }, + initialState: { text: string | string[], responseWordPattern?: RegExp }, operations: (editor: TestEditor) => Promise, expectedEndText: string | string[] ) { test(name, async () => { - disposables.add(modes.OnTypeRenameProviderRegistry.register(mockFileSelector, { - wordPattern: initialState.providerWordPattern, - provideOnTypeRenameRanges(model: ITextModel, pos: IPosition) { + disposables.add(modes.LinkedEditingRangeProviderRegistry.register(mockFileSelector, { + provideLinkedEditingRanges(model: ITextModel, pos: IPosition) { const wordAtPos = model.getWordAtPosition(pos); if (wordAtPos) { const matches = model.findMatches(wordAtPos.word, false, false, true, USUAL_WORD_SEPARATORS, false); - assert.ok(matches.length > 0); return { ranges: matches.map(m => m.range), wordPattern: initialState.responseWordPattern }; } return { ranges: [], wordPattern: initialState.responseWordPattern }; @@ -74,25 +78,25 @@ suite('On type rename', () => { })); const editor = createMockEditor(initialState.text); - editor.updateOptions({ renameOnType: true }); - const ontypeRenameContribution = editor.registerAndInstantiateContribution( - OnTypeRenameContribution.ID, - OnTypeRenameContribution + editor.updateOptions({ linkedEditing: true }); + const linkedEditingContribution = editor.registerAndInstantiateContribution( + LinkedEditingContribution.ID, + LinkedEditingContribution ); - ontypeRenameContribution.setDebounceDuration(0); + linkedEditingContribution.setDebounceDuration(0); const testEditor: TestEditor = { setPosition(pos: Position) { editor.setPosition(pos); - return ontypeRenameContribution.currentUpdateTriggerPromise; + return linkedEditingContribution.currentUpdateTriggerPromise; }, setSelection(sel: IRange) { editor.setSelection(sel); - return ontypeRenameContribution.currentUpdateTriggerPromise; + return linkedEditingContribution.currentUpdateTriggerPromise; }, trigger(source: string | null | undefined, handlerId: string, payload: any) { editor.trigger(source, handlerId, payload); - return ontypeRenameContribution.currentSyncTriggerPromise; + return linkedEditingContribution.currentSyncTriggerPromise; }, undo() { CoreEditingCommands.Undo.runEditorCommand(null, editor, null); @@ -247,7 +251,7 @@ suite('On type rename', () => { // testCase('Selection insert - across two boundary', state, async (editor) => { // const pos = new Position(1, 2); // await editor.setPosition(pos); - // await ontypeRenameContribution.updateLinkedUI(pos); + // await linkedEditingContribution.updateLinkedUI(pos); // await editor.setSelection(new Range(1, 4, 1, 9)); // await editor.trigger('keyboard', Handler.Type, { text: 'i' }); // }, ''); @@ -299,7 +303,7 @@ suite('On type rename', () => { const state3 = { ...state, - providerWordPattern: /[a-yA-Y]+/ + responseWordPattern: /[a-yA-Y]+/ }; testCase('Breakout with stop pattern - insert', state3, async (editor) => { @@ -334,7 +338,6 @@ suite('On type rename', () => { const state4 = { ...state, - providerWordPattern: /[a-yA-Y]+/, responseWordPattern: /[a-eA-E]+/ }; @@ -380,7 +383,7 @@ suite('On type rename', () => { // testCase('Delete - left all', state, async (editor) => { // const pos = new Position(1, 3); // await editor.setPosition(pos); - // await ontypeRenameContribution.updateLinkedUI(pos); + // await linkedEditingContribution.updateLinkedUI(pos); // await editor.trigger('keyboard', 'deleteAllLeft', {}); // }, '>'); @@ -390,7 +393,7 @@ suite('On type rename', () => { // testCase('Delete - left all then undo', state, async (editor) => { // const pos = new Position(1, 5); // await editor.setPosition(pos); - // await ontypeRenameContribution.updateLinkedUI(pos); + // await linkedEditingContribution.updateLinkedUI(pos); // await editor.trigger('keyboard', 'deleteAllLeft', {}); // editor.undo(); // }, '>'); diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index 283e8ef31..04e8b8aa0 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -47,7 +47,17 @@ function getHoverMessage(link: Link, useMetaKey: boolean): MarkdownString { : nls.localize('links.navigate.kb.alt', "alt + click"); if (link.url) { - const hoverMessage = new MarkdownString('', true).appendMarkdown(`[${label}](${link.url.toString()}) (${kb})`); + let nativeLabel = ''; + if (/^command:/i.test(link.url.toString())) { + // Don't show complete command arguments in the native tooltip + const match = link.url.toString().match(/^command:([^?#]+)/); + if (match) { + const commandId = match[1]; + const nativeLabelText = nls.localize('tooltip.explanation', "Execute command {0}", commandId); + nativeLabel = ` "${nativeLabelText}"`; + } + } + const hoverMessage = new MarkdownString('', true).appendMarkdown(`[${label}](${link.url.toString()}${nativeLabel}) (${kb})`); return hoverMessage; } else { return new MarkdownString().appendText(`${label} (${kb})`); diff --git a/src/vs/editor/contrib/message/messageController.css b/src/vs/editor/contrib/message/messageController.css index a3910415d..924349d11 100644 --- a/src/vs/editor/contrib/message/messageController.css +++ b/src/vs/editor/contrib/message/messageController.css @@ -8,6 +8,12 @@ z-index: 10000; } +.monaco-editor .monaco-editor-overlaymessage.below { + padding-bottom: 0; + padding-top: 8px; + z-index: 10000; +} + @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } @@ -37,3 +43,13 @@ border-width: 8px; position: absolute; } + +.monaco-editor .monaco-editor-overlaymessage:not(.below) .anchor.top, +.monaco-editor .monaco-editor-overlaymessage.below .anchor.below { + display: none; +} + +.monaco-editor .monaco-editor-overlaymessage.below .anchor.top { + display: inherit; + top: -8px; +} diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index 6bec24fd0..319f0d521 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -7,7 +7,7 @@ import 'vs/css!./messageController'; import * as nls from 'vs/nls'; import { TimeoutTimer } from 'vs/base/common/async'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { IDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; @@ -20,7 +20,7 @@ import { inputValidationInfoBorder, inputValidationInfoBackground, inputValidati import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; -export class MessageController extends Disposable implements IEditorContribution { +export class MessageController implements IEditorContribution { public static readonly ID = 'editor.contrib.messageController'; @@ -32,21 +32,24 @@ export class MessageController extends Disposable implements IEditorContribution private readonly _editor: ICodeEditor; private readonly _visible: IContextKey; - private readonly _messageWidget = this._register(new MutableDisposable()); - private readonly _messageListeners = this._register(new DisposableStore()); + private readonly _messageWidget = new MutableDisposable(); + private readonly _messageListeners = new DisposableStore(); + private readonly _editorListener: IDisposable; constructor( editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService ) { - super(); + this._editor = editor; this._visible = MessageController.MESSAGE_VISIBLE.bindTo(contextKeyService); - this._register(this._editor.onDidAttemptReadOnlyEdit(() => this._onDidAttemptReadOnlyEdit())); + this._editorListener = this._editor.onDidAttemptReadOnlyEdit(() => this._onDidAttemptReadOnlyEdit()); } dispose(): void { - super.dispose(); + this._editorListener.dispose(); + this._messageListeners.dispose(); + this._messageWidget.dispose(); this._visible.reset(); } @@ -150,14 +153,18 @@ class MessageWidget implements IContentWidget { this._domNode = document.createElement('div'); this._domNode.classList.add('monaco-editor-overlaymessage'); + const anchorTop = document.createElement('div'); + anchorTop.classList.add('anchor', 'top'); + this._domNode.appendChild(anchorTop); + const message = document.createElement('div'); message.classList.add('message'); message.textContent = text; this._domNode.appendChild(message); - const anchor = document.createElement('div'); - anchor.classList.add('anchor'); - this._domNode.appendChild(anchor); + const anchorBottom = document.createElement('div'); + anchorBottom.classList.add('anchor', 'below'); + this._domNode.appendChild(anchorBottom); this._editor.addContentWidget(this); this._domNode.classList.add('fadeIn'); @@ -178,6 +185,11 @@ class MessageWidget implements IContentWidget { getPosition(): IContentWidgetPosition { return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE, ContentWidgetPositionPreference.BELOW] }; } + + afterRender(position: ContentWidgetPositionPreference | null): void { + this._domNode.classList.toggle('below', position === ContentWidgetPositionPreference.BELOW); + } + } registerEditorContribution(MessageController.ID, MessageController); @@ -186,7 +198,8 @@ registerThemingParticipant((theme, collector) => { const border = theme.getColor(inputValidationInfoBorder); if (border) { let borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1; - collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor { border-top-color: ${border}; }`); + collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor.below { border-top-color: ${border}; }`); + collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor.top { border-bottom-color: ${border}; }`); collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .message { border: ${borderWidth}px solid ${border}; }`); } const background = theme.getColor(inputValidationInfoBackground); diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index 6a7c295e8..3606348fb 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -1016,6 +1016,11 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut }); this.decorations = this.editor.deltaDecorations(this.decorations, decorations); + + const currentFindState = CommonFindController.get(this.editor).getState(); + if (currentFindState.isRegex || currentFindState.matchCase || currentFindState.wholeWord) { + CommonFindController.get(this.editor).highlightFindOptions(true); + } } private static readonly _SELECTION_HIGHLIGHT_OVERVIEW = ModelDecorationOptions.register({ diff --git a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts index 8b25ecac5..6e1e0a9be 100644 --- a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts @@ -61,7 +61,8 @@ suite('Multicursor selection', () => { let serviceCollection = new ServiceCollection(); serviceCollection.set(IStorageService, { _serviceBrand: undefined, - onDidChangeStorage: Event.None, + onDidChangeValue: Event.None, + onDidChangeTarget: Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], @@ -70,8 +71,9 @@ suite('Multicursor selection', () => { remove: (key) => undefined, logStorage: () => undefined, migrate: (toWorkspace) => Promise.resolve(undefined), - flush: () => undefined, - isNew: () => true + flush: () => Promise.resolve(undefined), + isNew: () => true, + keys: () => [] } as IStorageService); test('issue #8817: Cursor position changes when you cancel multicursor', () => { diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 2d0d4bcd3..54b7a807b 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -20,17 +20,18 @@ import * as nls from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { editorHoverBackground, editorHoverBorder, textCodeBlockBackground, textLinkForeground, editorHoverForeground } from 'vs/platform/theme/common/colorRegistry'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ParameterHintsModel, TriggerContext } from 'vs/editor/contrib/parameterHints/parameterHintsModel'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; -import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { assertIsDefined } from 'vs/base/common/types'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; const $ = dom.$; -const parameterHintsNextIcon = registerIcon('parameter-hints-next', Codicon.chevronDown); -const parameterHintsPreviousIcon = registerIcon('parameter-hints-previous', Codicon.chevronUp); +const parameterHintsNextIcon = registerIcon('parameter-hints-next', Codicon.chevronDown, nls.localize('parameterHintsNextIcon', 'Icon for show next parameter hint.')); +const parameterHintsPreviousIcon = registerIcon('parameter-hints-previous', Codicon.chevronUp, nls.localize('parameterHintsPreviousIcon', 'Icon for show previous parameter hint.')); export class ParameterHintsWidget extends Disposable implements IContentWidget { @@ -84,9 +85,9 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { wrapper.tabIndex = -1; const controls = dom.append(wrapper, $('.controls')); - const previous = dom.append(controls, $('.button' + parameterHintsPreviousIcon.cssSelector)); + const previous = dom.append(controls, $('.button' + ThemeIcon.asCSSSelector(parameterHintsPreviousIcon))); const overloads = dom.append(controls, $('.overloads')); - const next = dom.append(controls, $('.button' + parameterHintsNextIcon.cssSelector)); + const next = dom.append(controls, $('.button' + ThemeIcon.asCSSSelector(parameterHintsNextIcon))); const onPreviousClick = stop(domEvent(previous, 'click')); this._register(onPreviousClick(this.previous, this)); diff --git a/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts b/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts index 8aaf09f8b..d513ef4b9 100644 --- a/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts +++ b/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts @@ -5,12 +5,15 @@ import { first } from 'vs/base/common/async'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import { Position } from 'vs/editor/common/core/position'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { URI } from 'vs/base/common/uri'; +import { assertType } from 'vs/base/common/types'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; export const Context = { Visible: new RawContextKey('parameterHintsVisible', false), @@ -32,17 +35,29 @@ export function provideSignatureHelp( })); } -registerDefaultLanguageCommand('_executeSignatureHelpProvider', async (model, position, args) => { - const result = await provideSignatureHelp(model, position, { - triggerKind: modes.SignatureHelpTriggerKind.Invoke, - isRetrigger: false, - triggerCharacter: args['triggerCharacter'] - }, CancellationToken.None); +CommandsRegistry.registerCommand('_executeSignatureHelpProvider', async (accessor, ...args: [URI, IPosition, string?]) => { + const [uri, position, triggerCharacter] = args; + assertType(URI.isUri(uri)); + assertType(Position.isIPosition(position)); + assertType(typeof triggerCharacter === 'string' || !triggerCharacter); - if (!result) { - return undefined; + const ref = await accessor.get(ITextModelService).createModelReference(uri); + try { + + const result = await provideSignatureHelp(ref.object.textEditorModel, Position.lift(position), { + triggerKind: modes.SignatureHelpTriggerKind.Invoke, + isRetrigger: false, + triggerCharacter, + }, CancellationToken.None); + + if (!result) { + return undefined; + } + + setTimeout(() => result.dispose(), 0); + return result.value; + + } finally { + ref.dispose(); } - - setTimeout(() => result.dispose(), 0); - return result.value; }); diff --git a/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts b/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts index 039dfbd30..ef8cf91b8 100644 --- a/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts @@ -26,6 +26,20 @@ export interface IEditorNavigationQuickAccessOptions { canAcceptInBackground?: boolean; } +export interface IQuickAccessTextEditorContext { + + /** + * The current active editor. + */ + readonly editor: IEditor; + + /** + * If defined, allows to restore the original view state + * the text editor had before quick access opened. + */ + restoreViewState?: () => void; +} + /** * A reusable quick access provider for the editor with support * for adding decorations for navigating in the currently active file @@ -69,6 +83,7 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu // With text control const editor = this.activeTextEditorControl; if (editor && this.canProvideWithTextEditor(editor)) { + const context: IQuickAccessTextEditorContext = { editor }; // Restore any view state if this picker was closed // without actually going to a line @@ -84,18 +99,20 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState()); })); - disposables.add(once(token.onCancellationRequested)(() => { + context.restoreViewState = () => { if (lastKnownEditorViewState && editor === this.activeTextEditorControl) { editor.restoreViewState(lastKnownEditorViewState); } - })); + }; + + disposables.add(once(token.onCancellationRequested)(() => context.restoreViewState?.())); } // Clean up decorations on dispose disposables.add(toDisposable(() => this.clearDecorations(editor))); // Ask subclass for entries - disposables.add(this.provideWithTextEditor(editor, picker, token)); + disposables.add(this.provideWithTextEditor(context, picker, token)); } // Without text control @@ -116,14 +133,14 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu /** * Subclasses to implement to provide picks for the picker when an editor is active. */ - protected abstract provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable; + protected abstract provideWithTextEditor(context: IQuickAccessTextEditorContext, picker: IQuickPick, token: CancellationToken): IDisposable; /** * Subclasses to implement to provide picks for the picker when no editor is active. */ protected abstract provideWithoutTextEditor(picker: IQuickPick, token: CancellationToken): IDisposable; - protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { + protected gotoLocation({ editor }: IQuickAccessTextEditorContext, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { editor.setSelection(options.range); editor.revealRangeInCenter(options.range, ScrollType.Smooth); if (!options.preserveFocus) { diff --git a/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts b/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts index 0c17d7c0b..2885cc4fb 100644 --- a/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts @@ -9,7 +9,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { DisposableStore, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IEditor, ScrollType } from 'vs/editor/common/editorCommon'; import { IRange } from 'vs/editor/common/core/range'; -import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; +import { AbstractEditorNavigationQuickAccessProvider, IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; import { IPosition } from 'vs/editor/common/core/position'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; @@ -33,7 +33,8 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor return Disposable.None; } - protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable { + protected provideWithTextEditor(context: IQuickAccessTextEditorContext, picker: IQuickPick, token: CancellationToken): IDisposable { + const editor = context.editor; const disposables = new DisposableStore(); // Goto line once picked @@ -44,7 +45,7 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor return; } - this.gotoLocation(editor, { range: this.toRange(item.lineNumber, item.column), keyMods: picker.keyMods, preserveFocus: event.inBackground }); + this.gotoLocation(context, { range: this.toRange(item.lineNumber, item.column), keyMods: picker.keyMods, preserveFocus: event.inBackground }); if (!event.inBackground) { picker.hide(); diff --git a/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts index ca85c3ce8..9fb3e3d3c 100644 --- a/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts @@ -7,10 +7,10 @@ import { localize } from 'vs/nls'; import { IQuickPick, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { DisposableStore, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IEditor, ScrollType } from 'vs/editor/common/editorCommon'; +import { ScrollType } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { AbstractEditorNavigationQuickAccessProvider, IEditorNavigationQuickAccessOptions } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; +import { AbstractEditorNavigationQuickAccessProvider, IEditorNavigationQuickAccessOptions, IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; import { DocumentSymbol, SymbolKinds, SymbolTag, DocumentSymbolProviderRegistry, SymbolKind } from 'vs/editor/common/modes'; import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { trim, format } from 'vs/base/common/strings'; @@ -48,7 +48,8 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit return Disposable.None; } - protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable { + protected provideWithTextEditor(context: IQuickAccessTextEditorContext, picker: IQuickPick, token: CancellationToken): IDisposable { + const editor = context.editor; const model = this.getModel(editor); if (!model) { return Disposable.None; @@ -56,16 +57,16 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit // Provide symbols from model if available in registry if (DocumentSymbolProviderRegistry.has(model)) { - return this.doProvideWithEditorSymbols(editor, model, picker, token); + return this.doProvideWithEditorSymbols(context, model, picker, token); } // Otherwise show an entry for a model without registry // But give a chance to resolve the symbols at a later // point if possible - return this.doProvideWithoutEditorSymbols(editor, model, picker, token); + return this.doProvideWithoutEditorSymbols(context, model, picker, token); } - private doProvideWithoutEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { + private doProvideWithoutEditorSymbols(context: IQuickAccessTextEditorContext, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { const disposables = new DisposableStore(); // Generic pick for not having any symbol information @@ -82,7 +83,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit return; } - disposables.add(this.doProvideWithEditorSymbols(editor, model, picker, token)); + disposables.add(this.doProvideWithEditorSymbols(context, model, picker, token)); })(); return disposables; @@ -116,14 +117,15 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit return symbolProviderRegistryPromise; } - private doProvideWithEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { + private doProvideWithEditorSymbols(context: IQuickAccessTextEditorContext, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { + const editor = context.editor; const disposables = new DisposableStore(); // Goto symbol once picked disposables.add(picker.onDidAccept(event => { const [item] = picker.selectedItems; if (item && item.range) { - this.gotoLocation(editor, { range: item.range.selection, keyMods: picker.keyMods, preserveFocus: event.inBackground }); + this.gotoLocation(context, { range: item.range.selection, keyMods: picker.keyMods, preserveFocus: event.inBackground }); if (!event.inBackground) { picker.hide(); @@ -134,7 +136,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit // Goto symbol side by side if enabled disposables.add(picker.onDidTriggerItemButton(({ item }) => { if (item && item.range) { - this.gotoLocation(editor, { range: item.range.selection, keyMods: picker.keyMods, forceSideBySide: true }); + this.gotoLocation(context, { range: item.range.selection, keyMods: picker.keyMods, forceSideBySide: true }); picker.hide(); } diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 214d00ffc..415473087 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -358,7 +358,7 @@ registerModelAndPositionCommand('_executeDocumentRenameProvider', function (mode }); -//todo@joh use editor options world +//todo@jrieken use editor options world Registry.as(Extensions.Configuration).registerConfiguration({ id: 'editor', properties: { diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index c794e4ccc..a73fab128 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -100,7 +100,7 @@ export class RenameInputField implements IContentWidget { const widgetShadowColor = theme.getColor(widgetShadow); this._domNode.style.backgroundColor = String(theme.getColor(editorWidgetBackground) ?? ''); - this._domNode.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : ''; + this._domNode.style.boxShadow = widgetShadowColor ? ` 0 0 8px 2px ${widgetShadowColor}` : ''; this._domNode.style.color = String(theme.getColor(inputForeground) ?? ''); this._input.style.backgroundColor = String(theme.getColor(inputBackground) ?? ''); diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 714a27d7e..713d85032 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -23,9 +23,7 @@ import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSe import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bracketSelections'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; -import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { Registry } from 'vs/platform/registry/common/platform'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; class SelectionRanges { @@ -50,49 +48,36 @@ class SelectionRanges { class SmartSelectController implements IEditorContribution { - public static readonly ID = 'editor.contrib.smartSelectController'; + static readonly ID = 'editor.contrib.smartSelectController'; static get(editor: ICodeEditor): SmartSelectController { return editor.getContribution(SmartSelectController.ID); } - private readonly _editor: ICodeEditor; - private _state?: SelectionRanges[]; private _selectionListener?: IDisposable; private _ignoreSelection: boolean = false; - constructor( - editor: ICodeEditor, - @ITextResourceConfigurationService private readonly _configService: ITextResourceConfigurationService, - ) { - this._editor = editor; - } + constructor(private readonly _editor: ICodeEditor) { } dispose(): void { this._selectionListener?.dispose(); } - run(forward: boolean): Promise | void { + async run(forward: boolean): Promise { if (!this._editor.hasModel()) { return; } const selections = this._editor.getSelections(); const model = this._editor.getModel(); - if (!modes.SelectionRangeRegistry.has(model)) { return; } - - let promise: Promise = Promise.resolve(undefined); - if (!this._state) { - const selectLeadingAndTrailingWhitespace = this._configService.getValue(model.uri, 'editor.smartSelect.selectLeadingAndTrailingWhitespace') ?? true; - - promise = provideSelectionRanges(model, selections.map(s => s.getPosition()), { selectLeadingAndTrailingWhitespace }, CancellationToken.None).then(ranges => { + await provideSelectionRanges(model, selections.map(s => s.getPosition()), this._editor.getOption(EditorOption.smartSelect), CancellationToken.None).then(ranges => { if (!arrays.isNonEmptyArray(ranges) || ranges.length !== selections.length) { // invalid result return; @@ -125,21 +110,18 @@ class SmartSelectController implements IEditorContribution { }); } - return promise.then(() => { - if (!this._state) { - // no state - return; - } - this._state = this._state.map(state => state.mov(forward)); - const selections = this._state.map(state => Selection.fromPositions(state.ranges[state.index].getStartPosition(), state.ranges[state.index].getEndPosition())); - this._ignoreSelection = true; - try { - this._editor.setSelections(selections); - } finally { - this._ignoreSelection = false; - } - - }); + if (!this._state) { + // no state + return; + } + this._state = this._state.map(state => state.mov(forward)); + const newSelections = this._state.map(state => Selection.fromPositions(state.ranges[state.index].getStartPosition(), state.ranges[state.index].getEndPosition())); + this._ignoreSelection = true; + try { + this._editor.setSelections(newSelections); + } finally { + this._ignoreSelection = false; + } } } @@ -186,21 +168,6 @@ class GrowSelectionAction extends AbstractSmartSelect { } } -//todo@jrieken use proper editor config instead. however, to keep the number -// of changes low use the quick config definition -Registry.as(Extensions.Configuration).registerConfiguration({ - id: 'editor', - properties: { - 'editor.smartSelect.selectLeadingAndTrailingWhitespace': { - scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, - description: nls.localize('selectLeadingAndTrailingWhitespace', "Weather leading and trailing whitespace should always be selected."), - default: true, - type: 'boolean' - } - } -}); - - // renamed command id CommandsRegistry.registerCommandAlias('editor.action.smartSelect.grow', 'editor.action.smartSelect.expand'); @@ -241,7 +208,7 @@ export interface SelectionRangesOptions { selectLeadingAndTrailingWhitespace: boolean } -export function provideSelectionRanges(model: ITextModel, positions: Position[], options: SelectionRangesOptions, token: CancellationToken): Promise { +export async function provideSelectionRanges(model: ITextModel, positions: Position[], options: SelectionRangesOptions, token: CancellationToken): Promise { const providers = modes.SelectionRangeRegistry.all(model); @@ -271,66 +238,65 @@ export function provideSelectionRanges(model: ITextModel, positions: Position[], }, onUnexpectedExternalError)); } - return Promise.all(work).then(() => { + await Promise.all(work); - return allRawRanges.map(oneRawRanges => { + return allRawRanges.map(oneRawRanges => { - if (oneRawRanges.length === 0) { - return []; + if (oneRawRanges.length === 0) { + return []; + } + + // sort all by start/end position + oneRawRanges.sort((a, b) => { + if (Position.isBefore(a.getStartPosition(), b.getStartPosition())) { + return 1; + } else if (Position.isBefore(b.getStartPosition(), a.getStartPosition())) { + return -1; + } else if (Position.isBefore(a.getEndPosition(), b.getEndPosition())) { + return -1; + } else if (Position.isBefore(b.getEndPosition(), a.getEndPosition())) { + return 1; + } else { + return 0; } - - // sort all by start/end position - oneRawRanges.sort((a, b) => { - if (Position.isBefore(a.getStartPosition(), b.getStartPosition())) { - return 1; - } else if (Position.isBefore(b.getStartPosition(), a.getStartPosition())) { - return -1; - } else if (Position.isBefore(a.getEndPosition(), b.getEndPosition())) { - return -1; - } else if (Position.isBefore(b.getEndPosition(), a.getEndPosition())) { - return 1; - } else { - return 0; - } - }); - - // remove ranges that don't contain the former range or that are equal to the - // former range - let oneRanges: Range[] = []; - let last: Range | undefined; - for (const range of oneRawRanges) { - if (!last || (Range.containsRange(range, last) && !Range.equalsRange(range, last))) { - oneRanges.push(range); - last = range; - } - } - - if (!options.selectLeadingAndTrailingWhitespace) { - return oneRanges; - } - - // add ranges that expand trivia at line starts and ends whenever a range - // wraps onto the a new line - let oneRangesWithTrivia: Range[] = [oneRanges[0]]; - for (let i = 1; i < oneRanges.length; i++) { - const prev = oneRanges[i - 1]; - const cur = oneRanges[i]; - if (cur.startLineNumber !== prev.startLineNumber || cur.endLineNumber !== prev.endLineNumber) { - // add line/block range without leading/failing whitespace - const rangeNoWhitespace = new Range(prev.startLineNumber, model.getLineFirstNonWhitespaceColumn(prev.startLineNumber), prev.endLineNumber, model.getLineLastNonWhitespaceColumn(prev.endLineNumber)); - if (rangeNoWhitespace.containsRange(prev) && !rangeNoWhitespace.equalsRange(prev) && cur.containsRange(rangeNoWhitespace) && !cur.equalsRange(rangeNoWhitespace)) { - oneRangesWithTrivia.push(rangeNoWhitespace); - } - // add line/block range - const rangeFull = new Range(prev.startLineNumber, 1, prev.endLineNumber, model.getLineMaxColumn(prev.endLineNumber)); - if (rangeFull.containsRange(prev) && !rangeFull.equalsRange(rangeNoWhitespace) && cur.containsRange(rangeFull) && !cur.equalsRange(rangeFull)) { - oneRangesWithTrivia.push(rangeFull); - } - } - oneRangesWithTrivia.push(cur); - } - return oneRangesWithTrivia; }); + + // remove ranges that don't contain the former range or that are equal to the + // former range + let oneRanges: Range[] = []; + let last: Range | undefined; + for (const range of oneRawRanges) { + if (!last || (Range.containsRange(range, last) && !Range.equalsRange(range, last))) { + oneRanges.push(range); + last = range; + } + } + + if (!options.selectLeadingAndTrailingWhitespace) { + return oneRanges; + } + + // add ranges that expand trivia at line starts and ends whenever a range + // wraps onto the a new line + let oneRangesWithTrivia: Range[] = [oneRanges[0]]; + for (let i = 1; i < oneRanges.length; i++) { + const prev = oneRanges[i - 1]; + const cur = oneRanges[i]; + if (cur.startLineNumber !== prev.startLineNumber || cur.endLineNumber !== prev.endLineNumber) { + // add line/block range without leading/failing whitespace + const rangeNoWhitespace = new Range(prev.startLineNumber, model.getLineFirstNonWhitespaceColumn(prev.startLineNumber), prev.endLineNumber, model.getLineLastNonWhitespaceColumn(prev.endLineNumber)); + if (rangeNoWhitespace.containsRange(prev) && !rangeNoWhitespace.equalsRange(prev) && cur.containsRange(rangeNoWhitespace) && !cur.equalsRange(rangeNoWhitespace)) { + oneRangesWithTrivia.push(rangeNoWhitespace); + } + // add line/block range + const rangeFull = new Range(prev.startLineNumber, 1, prev.endLineNumber, model.getLineMaxColumn(prev.endLineNumber)); + if (rangeFull.containsRange(prev) && !rangeFull.equalsRange(rangeNoWhitespace) && cur.containsRange(rangeFull) && !cur.equalsRange(rangeFull)) { + oneRangesWithTrivia.push(rangeFull); + } + } + oneRangesWithTrivia.push(cur); + } + return oneRangesWithTrivia; }); } diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 7f91f835c..90abafa25 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -24,6 +24,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer'; +import { CharCode } from 'vs/base/common/charCode'; registerThemingParticipant((theme, collector) => { @@ -38,10 +39,6 @@ registerThemingParticipant((theme, collector) => { export class OneSnippet { - private readonly _editor: IActiveCodeEditor; - private readonly _snippet: TextmateSnippet; - private readonly _offset: number; - private _placeholderDecorations?: Map; private _placeholderGroups: Placeholder[][]; _placeholderGroupsIdx: number; @@ -54,12 +51,11 @@ export class OneSnippet { inactiveFinal: ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'finish-snippet-placeholder' }), }; - constructor(editor: IActiveCodeEditor, snippet: TextmateSnippet, offset: number) { - this._editor = editor; - this._snippet = snippet; - this._offset = offset; - - this._placeholderGroups = groupBy(snippet.placeholders, Placeholder.compareByIndex); + constructor( + private readonly _editor: IActiveCodeEditor, private readonly _snippet: TextmateSnippet, + private readonly _offset: number, private readonly _snippetLineLeadingWhitespace: string + ) { + this._placeholderGroups = groupBy(_snippet.placeholders, Placeholder.compareByIndex); this._placeholderGroupsIdx = -1; } @@ -113,8 +109,12 @@ export class OneSnippet { const id = this._placeholderDecorations!.get(placeholder)!; const range = this._editor.getModel().getDecorationRange(id)!; const currentValue = this._editor.getModel().getValueInRange(range); - - operations.push(EditOperation.replaceMove(range, placeholder.transform.resolve(currentValue))); + const transformedValueLines = placeholder.transform.resolve(currentValue).split(/\r\n|\r|\n/); + // fix indentation for transformed lines + for (let i = 1; i < transformedValueLines.length; i++) { + transformedValueLines[i] = this._editor.getModel().normalizeIndentation(this._snippetLineLeadingWhitespace + transformedValueLines[i]); + } + operations.push(EditOperation.replace(range, transformedValueLines.join(this._editor.getModel().getEOL()))); } } if (operations.length > 0) { @@ -300,7 +300,7 @@ export class OneSnippet { }); } - public getEnclosingRange(): Range | undefined { + getEnclosingRange(): Range | undefined { let result: Range | undefined; const model = this._editor.getModel(); for (const decorationId of this._placeholderDecorations!.values()) { @@ -333,32 +333,53 @@ const _defaultOptions: ISnippetSessionInsertOptions = { export class SnippetSession { - static adjustWhitespace(model: ITextModel, position: IPosition, snippet: TextmateSnippet, adjustIndentation: boolean, adjustNewlines: boolean): void { + static adjustWhitespace(model: ITextModel, position: IPosition, snippet: TextmateSnippet, adjustIndentation: boolean, adjustNewlines: boolean): string { const line = model.getLineContent(position.lineNumber); const lineLeadingWhitespace = getLeadingWhitespace(line, 0, position.column - 1); + // the snippet as inserted + let snippetTextString: string | undefined; + snippet.walk(marker => { - if (marker instanceof Text && !(marker.parent instanceof Choice)) { - // adjust indentation of text markers, except for choise elements - // which get adjusted when being selected - const lines = marker.value.split(/\r\n|\r|\n/); + // all text elements that are not inside choice + if (!(marker instanceof Text) || marker.parent instanceof Choice) { + return true; + } - if (adjustIndentation) { - for (let i = 1; i < lines.length; i++) { - let templateLeadingWhitespace = getLeadingWhitespace(lines[i]); - lines[i] = model.normalizeIndentation(lineLeadingWhitespace + templateLeadingWhitespace) + lines[i].substr(templateLeadingWhitespace.length); + const lines = marker.value.split(/\r\n|\r|\n/); + + if (adjustIndentation) { + // adjust indentation of snippet test + // -the snippet-start doesn't get extra-indented (lineLeadingWhitespace), only normalized + // -all N+1 lines get extra-indented and normalized + // -the text start get extra-indented and normalized when following a linebreak + const offset = snippet.offset(marker); + if (offset === 0) { + // snippet start + lines[0] = model.normalizeIndentation(lines[0]); + + } else { + // check if text start is after a linebreak + snippetTextString = snippetTextString ?? snippet.toString(); + let prevChar = snippetTextString.charCodeAt(offset - 1); + if (prevChar === CharCode.LineFeed || prevChar === CharCode.CarriageReturn) { + lines[0] = model.normalizeIndentation(lineLeadingWhitespace + lines[0]); } } - - if (adjustNewlines) { - const newValue = lines.join(model.getEOL()); - if (newValue !== marker.value) { - marker.parent.replace(marker, [new Text(newValue)]); - } + for (let i = 1; i < lines.length; i++) { + lines[i] = model.normalizeIndentation(lineLeadingWhitespace + lines[i]); } } + + const newValue = lines.join(model.getEOL()); + if (newValue !== marker.value) { + marker.parent.replace(marker, [new Text(newValue)]); + snippetTextString = undefined; + } return true; }); + + return lineLeadingWhitespace; } static adjustSelection(model: ITextModel, selection: Selection, overwriteBefore: number, overwriteAfter: number): Selection { @@ -443,7 +464,7 @@ export class SnippetSession { // happens when being asked for (default) or when this is a secondary // cursor and the leading whitespace is different const start = snippetSelection.getStartPosition(); - SnippetSession.adjustWhitespace( + const snippetLineLeadingWhitespace = SnippetSession.adjustWhitespace( model, start, snippet, adjustWhitespace || (idx > 0 && firstLineFirstNonWhitespace !== model.getLineFirstNonWhitespaceColumn(selection.positionLineNumber)), true @@ -467,7 +488,7 @@ export class SnippetSession { // the one with lowest start position edits[idx] = EditOperation.replace(snippetSelection, snippet.toString()); edits[idx].identifier = { major: idx, minor: 0 }; // mark the edit so only our undo edits will be used to generate end cursors - snippets[idx] = new OneSnippet(editor, snippet, offset); + snippets[idx] = new OneSnippet(editor, snippet, offset, snippetLineLeadingWhitespace); } return { edits, snippets }; diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index cd5d505bc..bdfe96e7c 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -10,7 +10,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { Selection } from 'vs/editor/common/core/selection'; import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snippetParser'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, splitLines } from 'vs/base/common/strings'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -111,7 +111,7 @@ export class SelectionBasedVariableResolver implements VariableResolver { return false; } if (marker instanceof Text) { - varLeadingWhitespace = getLeadingWhitespace(marker.value.split(/\r\n|\r|\n/).pop()!); + varLeadingWhitespace = getLeadingWhitespace(splitLines(marker.value).pop()!); } return true; }); diff --git a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts index c430c2638..7ec294769 100644 --- a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts @@ -441,11 +441,18 @@ suite('SnippetController2', function () { }); test('leading TAB by snippets won\'t replace by spaces #101870', function () { - this.skip(); const ctrl = new SnippetController2(editor, logService, contextKeys); model.setValue(''); model.updateOptions({ insertSpaces: true, tabSize: 4 }); ctrl.insert('\tHello World\n\tNew Line'); assert.strictEqual(model.getValue(), ' Hello World\n New Line'); }); + + test('leading TAB by snippets won\'t replace by spaces #101870 (part 2)', function () { + const ctrl = new SnippetController2(editor, logService, contextKeys); + model.setValue(''); + model.updateOptions({ insertSpaces: true, tabSize: 4 }); + ctrl.insert('\tHello World\n\tNew Line\n${1:\tmore}'); + assert.strictEqual(model.getValue(), ' Hello World\n New Line\n more'); + }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index f72727ccd..458555cb0 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts @@ -561,6 +561,36 @@ suite('SnippetSession', function () { assertSelections(editor, new Selection(2, 1, 2, 1)); }); + test('Snippet tab stop selection issue #96545, snippets, transform adjacent to previous placeholder', function () { + editor.getModel()!.setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + const session = new SnippetSession(editor, '${1:{}${2:fff}${1/{/}/}'); + session.insert(); + + assertSelections(editor, new Selection(1, 1, 1, 2), new Selection(1, 5, 1, 6)); + session.next(); + + assert.equal(model.getValue(), '{fff}'); + assertSelections(editor, new Selection(1, 2, 1, 5)); + editor.trigger('test', 'type', { text: 'ggg' }); + session.next(); + + assert.equal(model.getValue(), '{ggg}'); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 6, 1, 6)); + }); + + test('Snippet tab stop selection issue #96545', function () { + editor.getModel().setValue(''); + const session = new SnippetSession(editor, '${1:{}${2:fff}${1/[\\{]/}/}$0'); + session.insert(); + assert.equal(editor.getModel().getValue(), '{fff{'); + + assertSelections(editor, new Selection(1, 1, 1, 2), new Selection(1, 5, 1, 6)); + session.next(); + assertSelections(editor, new Selection(1, 2, 1, 5)); + }); + test('Snippet placeholder index incorrect after using 2+ snippets in a row that each end with a placeholder, #30769', function () { editor.getModel()!.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -652,4 +682,52 @@ suite('SnippetSession', function () { new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined, overtypingCapturer: undefined }).insert(); assert.equal(model.getValue(), 'console.far'); }); + + test('Tabs don\'t get replaced with spaces in snippet transformations #103818', function () { + const model = editor.getModel()!; + model.setValue('\n{\n \n}'); + model.updateOptions({ insertSpaces: true, tabSize: 2 }); + editor.setSelections([new Selection(1, 1, 1, 1), new Selection(3, 6, 3, 6)]); + const session = new SnippetSession(editor, [ + 'function animate () {', + '\tvar ${1:a} = 12;', + '\tconsole.log(${1/(.*)/\n\t\t$1\n\t/})', + '}' + ].join('\n')); + + session.insert(); + + assert.strictEqual(model.getValue(), [ + 'function animate () {', + ' var a = 12;', + ' console.log(a)', + '}', + '{', + ' function animate () {', + ' var a = 12;', + ' console.log(a)', + ' }', + '}', + ].join('\n')); + + editor.trigger('test', 'type', { text: 'bbb' }); + session.next(); + + assert.strictEqual(model.getValue(), [ + 'function animate () {', + ' var bbb = 12;', + ' console.log(', + ' bbb', + ' )', + '}', + '{', + ' function animate () {', + ' var bbb = 12;', + ' console.log(', + ' bbb', + ' )', + ' }', + '}', + ].join('\n')); + }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 0bb4cfe34..8e939a5ac 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -9,10 +9,11 @@ import { Selection } from 'vs/editor/common/core/selection'; import { SelectionBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, ClipboardBasedVariableResolver, TimeBasedVariableResolver, WorkspaceBasedVariableResolver } from 'vs/editor/contrib/snippet/snippetVariables'; import { SnippetParser, Variable, VariableResolver } from 'vs/editor/contrib/snippet/snippetParser'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ILabelService } from 'vs/platform/label/common/label'; import { mock } from 'vs/base/test/common/mock'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; suite('Snippet Variables Resolver', function () { diff --git a/src/vs/editor/contrib/suggest/resizable.ts b/src/vs/editor/contrib/suggest/resizable.ts index 25e1dcb3d..382f88289 100644 --- a/src/vs/editor/contrib/suggest/resizable.ts +++ b/src/vs/editor/contrib/suggest/resizable.ts @@ -6,7 +6,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Dimension } from 'vs/base/browser/dom'; -import { Orientation, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; +import { Orientation, OrthogonalEdge, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; export interface IResizeEvent { @@ -43,8 +43,8 @@ export class ResizableHTMLElement { this.domNode = document.createElement('div'); this._eastSash = new Sash(this.domNode, { getVerticalSashLeft: () => this._size.width }, { orientation: Orientation.VERTICAL }); this._westSash = new Sash(this.domNode, { getVerticalSashLeft: () => 0 }, { orientation: Orientation.VERTICAL }); - this._northSash = new Sash(this.domNode, { getHorizontalSashTop: () => 0 }, { orientation: Orientation.HORIZONTAL }); - this._southSash = new Sash(this.domNode, { getHorizontalSashTop: () => this._size.height }, { orientation: Orientation.HORIZONTAL }); + this._northSash = new Sash(this.domNode, { getHorizontalSashTop: () => 0 }, { orientation: Orientation.HORIZONTAL, orthogonalEdge: OrthogonalEdge.North }); + this._southSash = new Sash(this.domNode, { getHorizontalSashTop: () => this._size.height }, { orientation: Orientation.HORIZONTAL, orthogonalEdge: OrthogonalEdge.South }); this._northSash.orthogonalStartSash = this._westSash; this._northSash.orthogonalEndSash = this._eastSash; diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index 650701dea..d6c466f33 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -6,7 +6,6 @@ import { onUnexpectedExternalError, canceled, isPromiseCanceledError } from 'vs/base/common/errors'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; import * as modes from 'vs/editor/common/modes'; import { Position, IPosition } from 'vs/editor/common/core/position'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -18,6 +17,10 @@ import { isDisposable, DisposableStore, IDisposable } from 'vs/base/common/lifec import { MenuId } from 'vs/platform/actions/common/actions'; import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { StopWatch } from 'vs/base/common/stopwatch'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { assertType } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; export const Context = { Visible: new RawContextKey('suggestWidgetVisible', false), @@ -341,31 +344,42 @@ export function getSuggestionComparator(snippetConfig: SnippetSortOrder): (a: Co return _snippetComparators.get(snippetConfig)!; } -registerDefaultLanguageCommand('_executeCompletionItemProvider', async (model, position, args) => { - - const result: modes.CompletionList = { - incomplete: false, - suggestions: [] - }; - - const resolving: Promise[] = []; - const maxItemsToResolve = args['maxItemsToResolve'] || 0; - - const completions = await provideSuggestionItems(model, position); - for (const item of completions.items) { - if (resolving.length < maxItemsToResolve) { - resolving.push(item.resolve(CancellationToken.None)); - } - result.incomplete = result.incomplete || item.container.incomplete; - result.suggestions.push(item.completion); - } +CommandsRegistry.registerCommand('_executeCompletionItemProvider', async (accessor, ...args: [URI, IPosition, string?, number?]) => { + const [uri, position, triggerCharacter, maxItemsToResolve] = args; + assertType(URI.isUri(uri)); + assertType(Position.isIPosition(position)); + assertType(typeof triggerCharacter === 'string' || !triggerCharacter); + assertType(typeof maxItemsToResolve === 'number' || !maxItemsToResolve); + const ref = await accessor.get(ITextModelService).createModelReference(uri); try { - await Promise.all(resolving); - return result; + + const result: modes.CompletionList = { + incomplete: false, + suggestions: [] + }; + + const resolving: Promise[] = []; + const completions = await provideSuggestionItems(ref.object.textEditorModel, Position.lift(position), undefined, { triggerCharacter, triggerKind: triggerCharacter ? modes.CompletionTriggerKind.TriggerCharacter : modes.CompletionTriggerKind.Invoke }); + for (const item of completions.items) { + if (resolving.length < (maxItemsToResolve ?? 0)) { + resolving.push(item.resolve(CancellationToken.None)); + } + result.incomplete = result.incomplete || item.container.incomplete; + result.suggestions.push(item.completion); + } + + try { + await Promise.all(resolving); + return result; + } finally { + setTimeout(() => completions.disposable.dispose(), 100); + } + } finally { - setTimeout(() => completions.disposable.dispose(), 100); + ref.dispose(); } + }); interface SuggestController extends IEditorContribution { diff --git a/src/vs/editor/contrib/suggest/suggestMemory.ts b/src/vs/editor/contrib/suggest/suggestMemory.ts index b2c98b605..dfb293635 100644 --- a/src/vs/editor/contrib/suggest/suggestMemory.ts +++ b/src/vs/editor/contrib/suggest/suggestMemory.ts @@ -5,7 +5,7 @@ import { LRUCache, TernarySearchTree } from 'vs/base/common/map'; -import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { ITextModel } from 'vs/editor/common/model'; import { IPosition } from 'vs/editor/common/core/position'; import { CompletionItemKind, completionKindFromString } from 'vs/editor/common/modes'; @@ -82,8 +82,7 @@ export class LRUMemory extends Memory { private _seq = 0; memorize(model: ITextModel, pos: IPosition, item: CompletionItem): void { - const { label } = item.completion; - const key = `${model.getLanguageIdentifier().language}/${label}`; + const key = `${model.getLanguageIdentifier().language}/${item.textLabel}`; this._cache.set(key, { touch: this._seq++, type: item.completion.kind, @@ -111,7 +110,7 @@ export class LRUMemory extends Memory { // consider only top items break; } - const key = `${model.getLanguageIdentifier().language}/${items[i].completion.label}`; + const key = `${model.getLanguageIdentifier().language}/${items[i].textLabel}`; const item = this._cache.peek(key); if (item && item.touch > seq && item.type === items[i].completion.kind && item.insertText === items[i].completion.insertText) { seq = item.touch; @@ -295,7 +294,7 @@ export class SuggestMemoryService implements ISuggestMemoryService { const share = this._configService.getValue('editor.suggest.shareSuggestSelections'); const scope = share ? StorageScope.GLOBAL : StorageScope.WORKSPACE; const raw = JSON.stringify(this._strategy); - this._storageService.store(`${SuggestMemoryService._storagePrefix}/${this._strategy.name}`, raw, scope); + this._storageService.store(`${SuggestMemoryService._storagePrefix}/${this._strategy.name}`, raw, scope, StorageTarget.MACHINE); } } } diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index 6cad7c8b6..9a88e1ccc 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -20,7 +20,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { isLowSurrogate, isHighSurrogate } from 'vs/base/common/strings'; +import { isLowSurrogate, isHighSurrogate, getLeadingWhitespace } from 'vs/base/common/strings'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ILogService } from 'vs/platform/log/common/log'; @@ -486,8 +486,14 @@ export class SuggestModel implements IDisposable { }).catch(onUnexpectedError); } + private _telemetryGate: number = 0; + private _reportDurationsTelemetry(durations: CompletionDurations): void { + if (this._telemetryGate++ % 230 !== 0) { + return; + } + setTimeout(() => { type Durations = { data: string; }; type DurationsClassification = { data: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' } }; @@ -553,7 +559,8 @@ export class SuggestModel implements IDisposable { return; } - if (ctx.leadingWord.startColumn < this._context.leadingWord.startColumn) { + if (getLeadingWhitespace(ctx.leadingLineContent) !== getLeadingWhitespace(this._context.leadingLineContent)) { + // cancel IntelliSense when line start changes // happens when the current word gets outdented this.cancel(); return; diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index a62c3c686..02f6233f2 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -11,7 +11,7 @@ import * as strings from 'vs/base/common/strings'; import * as dom from 'vs/base/browser/dom'; import { Event, Emitter } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IListEvent, IListMouseEvent, IListGestureEvent } from 'vs/base/browser/ui/list/list'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -22,7 +22,7 @@ import { CompletionModel } from './completionModel'; import { attachListStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -32,6 +32,7 @@ import { getAriaId, ItemRenderer } from './suggestWidgetRenderer'; import { ResizableHTMLElement } from './resizable'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IPosition } from 'vs/editor/common/core/position'; +import { clamp } from 'vs/base/common/numbers'; /** * Suggest widget colors @@ -82,7 +83,7 @@ class PersistedWidgetSize { } store(size: dom.Dimension) { - this._service.store(this._key, JSON.stringify(size), StorageScope.GLOBAL); + this._service.store(this._key, JSON.stringify(size), StorageScope.GLOBAL, StorageTarget.MACHINE); } reset(): void { @@ -95,49 +96,48 @@ export class SuggestWidget implements IDisposable { private static LOADING_MESSAGE: string = nls.localize('suggestWidget.loading', "Loading..."); private static NO_SUGGESTIONS_MESSAGE: string = nls.localize('suggestWidget.noSuggestions', "No suggestions."); - private state: State = State.Hidden; - private isAuto: boolean = false; - private loadingTimeout: IDisposable = Disposable.None; - private currentSuggestionDetails?: CancelablePromise; - private focusedItem?: CompletionItem; - private ignoreFocusEvents: boolean = false; - private completionModel?: CompletionModel; + private _state: State = State.Hidden; + private _isAuto: boolean = false; + private _loadingTimeout?: IDisposable; + private _currentSuggestionDetails?: CancelablePromise; + private _focusedItem?: CompletionItem; + private _ignoreFocusEvents: boolean = false; + private _completionModel?: CompletionModel; private _cappedHeight?: { wanted: number, capped: number }; + private _explainMode: boolean = false; readonly element: ResizableHTMLElement; - private readonly messageElement: HTMLElement; - private readonly listElement: HTMLElement; - private readonly list: List; - private readonly status: SuggestWidgetStatus; + private readonly _messageElement: HTMLElement; + private readonly _listElement: HTMLElement; + private readonly _list: List; + private readonly _status: SuggestWidgetStatus; private readonly _details: SuggestDetailsOverlay; private readonly _contentWidget: SuggestContentWidget; - - private readonly ctxSuggestWidgetVisible: IContextKey; - private readonly ctxSuggestWidgetDetailsVisible: IContextKey; - private readonly ctxSuggestWidgetMultipleSuggestions: IContextKey; - - private readonly showTimeout = new TimeoutTimer(); - private readonly _disposables = new DisposableStore(); - private readonly _persistedSize: PersistedWidgetSize; - private readonly onDidSelectEmitter = new Emitter(); - private readonly onDidFocusEmitter = new Emitter(); - private readonly onDidHideEmitter = new Emitter(); - private readonly onDidShowEmitter = new Emitter(); + private readonly _ctxSuggestWidgetVisible: IContextKey; + private readonly _ctxSuggestWidgetDetailsVisible: IContextKey; + private readonly _ctxSuggestWidgetMultipleSuggestions: IContextKey; - readonly onDidSelect: Event = this.onDidSelectEmitter.event; - readonly onDidFocus: Event = this.onDidFocusEmitter.event; - readonly onDidHide: Event = this.onDidHideEmitter.event; - readonly onDidShow: Event = this.onDidShowEmitter.event; + private readonly _showTimeout = new TimeoutTimer(); + private readonly _disposables = new DisposableStore(); - private detailsFocusBorderColor?: string; - private detailsBorderColor?: string; - private explainMode: boolean = false; + private readonly _onDidSelect = new Emitter(); + private readonly _onDidFocus = new Emitter(); + private readonly _onDidHide = new Emitter(); + private readonly _onDidShow = new Emitter(); + + readonly onDidSelect: Event = this._onDidSelect.event; + readonly onDidFocus: Event = this._onDidFocus.event; + readonly onDidHide: Event = this._onDidHide.event; + readonly onDidShow: Event = this._onDidShow.event; private readonly _onDetailsKeydown = new Emitter(); - public readonly onDetailsKeyDown: Event = this._onDetailsKeydown.event; + readonly onDetailsKeyDown: Event = this._onDetailsKeydown.event; + + private _detailsFocusBorderColor?: string; + private _detailsBorderColor?: string; constructor( private readonly editor: ICodeEditor, @@ -152,47 +152,55 @@ export class SuggestWidget implements IDisposable { this._contentWidget = new SuggestContentWidget(this, editor); this._persistedSize = new PersistedWidgetSize(_storageService, editor); - let persistedSize: dom.Dimension | undefined; - let currentSize: dom.Dimension | undefined; - let persistHeight = false; - let persistWidth = false; + class ResizeState { + constructor( + readonly persistedSize: dom.Dimension | undefined, + readonly currentSize: dom.Dimension, + public persistHeight = false, + public persistWidth = false, + ) { } + } + + let state: ResizeState | undefined; this._disposables.add(this.element.onDidWillResize(() => { this._contentWidget.lockPreference(); - persistedSize = this._persistedSize.restore(); - currentSize = this.element.size; + state = new ResizeState(this._persistedSize.restore(), this.element.size); })); this._disposables.add(this.element.onDidResize(e => { this._resize(e.dimension.width, e.dimension.height); - persistHeight = persistHeight || !!e.north || !!e.south; - persistWidth = persistWidth || !!e.east || !!e.west; - if (e.done) { + if (state) { + state.persistHeight = state.persistHeight || !!e.north || !!e.south; + state.persistWidth = state.persistWidth || !!e.east || !!e.west; + } + + if (!e.done) { + return; + } + + if (state) { // only store width or height value that have changed and also // only store changes that are above a certain threshold - const threshold = Math.floor(this.getLayoutInfo().itemHeight / 2); + const { itemHeight, defaultSize } = this.getLayoutInfo(); + const threshold = Math.round(itemHeight / 2); let { width, height } = this.element.size; - if (persistedSize && currentSize) { - if (!persistHeight || Math.abs(currentSize.height - height) <= threshold) { - height = persistedSize.height; - } - if (!persistWidth || Math.abs(currentSize.width - width) <= threshold) { - width = persistedSize.width; - } + if (!state.persistHeight || Math.abs(state.currentSize.height - height) <= threshold) { + height = state.persistedSize?.height ?? defaultSize.height; + } + if (!state.persistWidth || Math.abs(state.currentSize.width - width) <= threshold) { + width = state.persistedSize?.width ?? defaultSize.width; } this._persistedSize.store(new dom.Dimension(width, height)); - - // reset working state - this._contentWidget.unlockPreference(); - persistedSize = undefined; - currentSize = undefined; - persistHeight = false; - persistWidth = false; } + + // reset working state + this._contentWidget.unlockPreference(); + state = undefined; })); - this.messageElement = dom.append(this.element.domNode, dom.$('.message')); - this.listElement = dom.append(this.element.domNode, dom.$('.tree')); + this._messageElement = dom.append(this.element.domNode, dom.$('.message')); + this._listElement = dom.append(this.element.domNode, dom.$('.tree')); const details = instantiationService.createInstance(SuggestDetailsWidget, this.editor); details.onDidClose(this.toggleDetails, this, this._disposables); @@ -205,7 +213,7 @@ export class SuggestWidget implements IDisposable { this._disposables.add(renderer); this._disposables.add(renderer.onDidToggleDetails(() => this.toggleDetails())); - this.list = new List('SuggestWidget', this.listElement, { + this._list = new List('SuggestWidget', this._listElement, { getHeight: (_element: CompletionItem): number => this.getLayoutInfo().itemHeight, getTemplateId: (_element: CompletionItem): string => 'suggestion' }, [renderer], { @@ -232,22 +240,22 @@ export class SuggestWidget implements IDisposable { } }); - this.status = instantiationService.createInstance(SuggestWidgetStatus, this.element.domNode); + this._status = instantiationService.createInstance(SuggestWidgetStatus, this.element.domNode); const applyStatusBarStyle = () => this.element.domNode.classList.toggle('with-status-bar', this.editor.getOption(EditorOption.suggest).showStatusBar); applyStatusBarStyle(); - this._disposables.add(attachListStyler(this.list, _themeService, { + this._disposables.add(attachListStyler(this._list, _themeService, { listInactiveFocusBackground: editorSuggestWidgetSelectedBackground, listInactiveFocusOutline: activeContrastBorder })); - this._disposables.add(_themeService.onDidColorThemeChange(t => this.onThemeChange(t))); - this.onThemeChange(_themeService.getColorTheme()); + this._disposables.add(_themeService.onDidColorThemeChange(t => this._onThemeChange(t))); + this._onThemeChange(_themeService.getColorTheme()); - this._disposables.add(this.list.onMouseDown(e => this.onListMouseDownOrTap(e))); - this._disposables.add(this.list.onTap(e => this.onListMouseDownOrTap(e))); - this._disposables.add(this.list.onDidChangeSelection(e => this.onListSelection(e))); - this._disposables.add(this.list.onDidChangeFocus(e => this.onListFocus(e))); - this._disposables.add(this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged())); + this._disposables.add(this._list.onMouseDown(e => this._onListMouseDownOrTap(e))); + this._disposables.add(this._list.onTap(e => this._onListMouseDownOrTap(e))); + this._disposables.add(this._list.onDidChangeSelection(e => this._onListSelection(e))); + this._disposables.add(this._list.onDidChangeFocus(e => this._onListFocus(e))); + this._disposables.add(this.editor.onDidChangeCursorSelection(() => this._onCursorSelectionChanged())); this._disposables.add(this.editor.onDidChangeConfiguration(e => { if (e.hasChanged(EditorOption.suggest)) { applyStatusBarStyle(); @@ -255,31 +263,31 @@ export class SuggestWidget implements IDisposable { } })); - this.ctxSuggestWidgetVisible = SuggestContext.Visible.bindTo(_contextKeyService); - this.ctxSuggestWidgetDetailsVisible = SuggestContext.DetailsVisible.bindTo(_contextKeyService); - this.ctxSuggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(_contextKeyService); + this._ctxSuggestWidgetVisible = SuggestContext.Visible.bindTo(_contextKeyService); + this._ctxSuggestWidgetDetailsVisible = SuggestContext.DetailsVisible.bindTo(_contextKeyService); + this._ctxSuggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(_contextKeyService); this._disposables.add(dom.addStandardDisposableListener(this._details.widget.domNode, 'keydown', e => { this._onDetailsKeydown.fire(e); })); - this._disposables.add(this.editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(e))); + this._disposables.add(this.editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); } dispose(): void { this._details.widget.dispose(); this._details.dispose(); - this.list.dispose(); - this.status.dispose(); + this._list.dispose(); + this._status.dispose(); this._disposables.dispose(); - this.loadingTimeout.dispose(); - this.showTimeout.dispose(); + this._loadingTimeout?.dispose(); + this._showTimeout.dispose(); this._contentWidget.dispose(); this.element.dispose(); } - private onEditorMouseDown(mouseEvent: IEditorMouseEvent): void { + private _onEditorMouseDown(mouseEvent: IEditorMouseEvent): void { if (this._details.widget.domNode.contains(mouseEvent.target.element)) { // Clicking inside details this._details.widget.domNode.focus(); @@ -291,13 +299,13 @@ export class SuggestWidget implements IDisposable { } } - private onCursorSelectionChanged(): void { - if (this.state !== State.Hidden) { + private _onCursorSelectionChanged(): void { + if (this._state !== State.Hidden) { this._contentWidget.layout(); } } - private onListMouseDownOrTap(e: IListMouseEvent | IListGestureEvent): void { + private _onListMouseDownOrTap(e: IListMouseEvent | IListGestureEvent): void { if (typeof e.element === 'undefined' || typeof e.index === 'undefined') { return; } @@ -306,83 +314,78 @@ export class SuggestWidget implements IDisposable { e.browserEvent.preventDefault(); e.browserEvent.stopPropagation(); - this.select(e.element, e.index); + this._select(e.element, e.index); } - private onListSelection(e: IListEvent): void { - if (!e.elements.length) { - return; + private _onListSelection(e: IListEvent): void { + if (e.elements.length) { + this._select(e.elements[0], e.indexes[0]); } - - this.select(e.elements[0], e.indexes[0]); } - private select(item: CompletionItem, index: number): void { - const completionModel = this.completionModel; - - if (!completionModel) { - return; + private _select(item: CompletionItem, index: number): void { + const completionModel = this._completionModel; + if (completionModel) { + this._onDidSelect.fire({ item, index, model: completionModel }); + this.editor.focus(); } - - this.onDidSelectEmitter.fire({ item, index, model: completionModel }); - this.editor.focus(); } - private onThemeChange(theme: IColorTheme) { + private _onThemeChange(theme: IColorTheme) { const backgroundColor = theme.getColor(editorSuggestWidgetBackground); if (backgroundColor) { this.element.domNode.style.backgroundColor = backgroundColor.toString(); - this.messageElement.style.backgroundColor = backgroundColor.toString(); + this._messageElement.style.backgroundColor = backgroundColor.toString(); this._details.widget.domNode.style.backgroundColor = backgroundColor.toString(); } const borderColor = theme.getColor(editorSuggestWidgetBorder); if (borderColor) { this.element.domNode.style.borderColor = borderColor.toString(); - this.messageElement.style.borderColor = borderColor.toString(); - this.status.element.style.borderTopColor = borderColor.toString(); + this._messageElement.style.borderColor = borderColor.toString(); + this._status.element.style.borderTopColor = borderColor.toString(); this._details.widget.domNode.style.borderColor = borderColor.toString(); - this.detailsBorderColor = borderColor.toString(); + this._detailsBorderColor = borderColor.toString(); } const focusBorderColor = theme.getColor(focusBorder); if (focusBorderColor) { - this.detailsFocusBorderColor = focusBorderColor.toString(); + this._detailsFocusBorderColor = focusBorderColor.toString(); } this._details.widget.borderWidth = theme.type === 'hc' ? 2 : 1; } - private onListFocus(e: IListEvent): void { - if (this.ignoreFocusEvents) { + private _onListFocus(e: IListEvent): void { + if (this._ignoreFocusEvents) { return; } if (!e.elements.length) { - if (this.currentSuggestionDetails) { - this.currentSuggestionDetails.cancel(); - this.currentSuggestionDetails = undefined; - this.focusedItem = undefined; + if (this._currentSuggestionDetails) { + this._currentSuggestionDetails.cancel(); + this._currentSuggestionDetails = undefined; + this._focusedItem = undefined; } this.editor.setAriaOptions({ activeDescendant: undefined }); return; } - if (!this.completionModel) { + if (!this._completionModel) { return; } const item = e.elements[0]; const index = e.indexes[0]; - if (item !== this.focusedItem) { + if (item !== this._focusedItem) { - this.currentSuggestionDetails?.cancel(); - this.currentSuggestionDetails = undefined; + this._currentSuggestionDetails?.cancel(); + this._currentSuggestionDetails = undefined; - this.focusedItem = item; + this._focusedItem = item; - this.list.reveal(index); + this._list.reveal(index); - this.currentSuggestionDetails = createCancelablePromise(async token => { + this._currentSuggestionDetails = createCancelablePromise(async token => { const loading = disposableTimeout(() => { if (this._isDetailsVisible()) { this.showDetails(true); @@ -394,16 +397,16 @@ export class SuggestWidget implements IDisposable { return result; }); - this.currentSuggestionDetails.then(() => { - if (index >= this.list.length || item !== this.list.element(index)) { + this._currentSuggestionDetails.then(() => { + if (index >= this._list.length || item !== this._list.element(index)) { return; } // item can have extra information, so re-render - this.ignoreFocusEvents = true; - this.list.splice(index, 1, [item]); - this.list.setFocus([index]); - this.ignoreFocusEvents = false; + this._ignoreFocusEvents = true; + this._list.splice(index, 1, [item]); + this._list.setFocus([index]); + this._ignoreFocusEvents = false; if (this._isDetailsVisible()) { this.showDetails(false); @@ -416,63 +419,63 @@ export class SuggestWidget implements IDisposable { } // emit an event - this.onDidFocusEmitter.fire({ item, index, model: this.completionModel }); + this._onDidFocus.fire({ item, index, model: this._completionModel }); } private _setState(state: State): void { - if (this.state === state) { + if (this._state === state) { return; } - this.state = state; + this._state = state; this.element.domNode.classList.toggle('frozen', state === State.Frozen); this.element.domNode.classList.remove('message'); switch (state) { case State.Hidden: - dom.hide(this.messageElement, this.listElement, this.status.element); + dom.hide(this._messageElement, this._listElement, this._status.element); this._details.hide(true); this._contentWidget.hide(); - this.ctxSuggestWidgetVisible.reset(); - this.ctxSuggestWidgetMultipleSuggestions.reset(); + this._ctxSuggestWidgetVisible.reset(); + this._ctxSuggestWidgetMultipleSuggestions.reset(); this.element.domNode.classList.remove('visible'); - this.list.splice(0, this.list.length); - this.focusedItem = undefined; + this._list.splice(0, this._list.length); + this._focusedItem = undefined; this._cappedHeight = undefined; - this.explainMode = false; + this._explainMode = false; break; case State.Loading: this.element.domNode.classList.add('message'); - this.messageElement.textContent = SuggestWidget.LOADING_MESSAGE; - dom.hide(this.listElement, this.status.element); - dom.show(this.messageElement); + this._messageElement.textContent = SuggestWidget.LOADING_MESSAGE; + dom.hide(this._listElement, this._status.element); + dom.show(this._messageElement); this._details.hide(); this._show(); - this.focusedItem = undefined; + this._focusedItem = undefined; break; case State.Empty: this.element.domNode.classList.add('message'); - this.messageElement.textContent = SuggestWidget.NO_SUGGESTIONS_MESSAGE; - dom.hide(this.listElement, this.status.element); - dom.show(this.messageElement); + this._messageElement.textContent = SuggestWidget.NO_SUGGESTIONS_MESSAGE; + dom.hide(this._listElement, this._status.element); + dom.show(this._messageElement); this._details.hide(); this._show(); - this.focusedItem = undefined; + this._focusedItem = undefined; break; case State.Open: - dom.hide(this.messageElement); - dom.show(this.listElement, this.status.element); + dom.hide(this._messageElement); + dom.show(this._listElement, this._status.element); this._show(); break; case State.Frozen: - dom.hide(this.messageElement); - dom.show(this.listElement, this.status.element); + dom.hide(this._messageElement); + dom.show(this._listElement, this._status.element); this._show(); break; case State.Details: - dom.hide(this.messageElement); - dom.show(this.listElement, this.status.element); + dom.hide(this._messageElement); + dom.show(this._listElement, this._status.element); this._details.show(); this._show(); break; @@ -482,176 +485,176 @@ export class SuggestWidget implements IDisposable { private _show(): void { this._contentWidget.show(); this._layout(this._persistedSize.restore()); - this.ctxSuggestWidgetVisible.set(true); + this._ctxSuggestWidgetVisible.set(true); - this.showTimeout.cancelAndSet(() => { + this._showTimeout.cancelAndSet(() => { this.element.domNode.classList.add('visible'); - this.onDidShowEmitter.fire(this); + this._onDidShow.fire(this); }, 100); } showTriggered(auto: boolean, delay: number) { - if (this.state !== State.Hidden) { + if (this._state !== State.Hidden) { return; } this._contentWidget.setPosition(this.editor.getPosition()); - this.isAuto = !!auto; + this._isAuto = !!auto; - if (!this.isAuto) { - this.loadingTimeout = disposableTimeout(() => this._setState(State.Loading), delay); + if (!this._isAuto) { + this._loadingTimeout = disposableTimeout(() => this._setState(State.Loading), delay); } } showSuggestions(completionModel: CompletionModel, selectionIndex: number, isFrozen: boolean, isAuto: boolean): void { this._contentWidget.setPosition(this.editor.getPosition()); - this.loadingTimeout.dispose(); + this._loadingTimeout?.dispose(); - this.currentSuggestionDetails?.cancel(); - this.currentSuggestionDetails = undefined; + this._currentSuggestionDetails?.cancel(); + this._currentSuggestionDetails = undefined; - if (this.completionModel !== completionModel) { - this.completionModel = completionModel; + if (this._completionModel !== completionModel) { + this._completionModel = completionModel; } - if (isFrozen && this.state !== State.Empty && this.state !== State.Hidden) { + if (isFrozen && this._state !== State.Empty && this._state !== State.Hidden) { this._setState(State.Frozen); return; } - const visibleCount = this.completionModel.items.length; + const visibleCount = this._completionModel.items.length; const isEmpty = visibleCount === 0; - this.ctxSuggestWidgetMultipleSuggestions.set(visibleCount > 1); + this._ctxSuggestWidgetMultipleSuggestions.set(visibleCount > 1); if (isEmpty) { this._setState(isAuto ? State.Hidden : State.Empty); - this.completionModel = undefined; + this._completionModel = undefined; return; } - this.focusedItem = undefined; - this.list.splice(0, this.list.length, this.completionModel.items); + this._focusedItem = undefined; + this._list.splice(0, this._list.length, this._completionModel.items); this._setState(isFrozen ? State.Frozen : State.Open); - this.list.reveal(selectionIndex, 0); - this.list.setFocus([selectionIndex]); + this._list.reveal(selectionIndex, 0); + this._list.setFocus([selectionIndex]); this._layout(this.element.size); // Reset focus border - if (this.detailsBorderColor) { - this._details.widget.domNode.style.borderColor = this.detailsBorderColor; + if (this._detailsBorderColor) { + this._details.widget.domNode.style.borderColor = this._detailsBorderColor; } } selectNextPage(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Details: this._details.widget.pageDown(); return true; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusNextPage(); + this._list.focusNextPage(); return true; } } selectNext(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusNext(1, true); + this._list.focusNext(1, true); return true; } } selectLast(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Details: this._details.widget.scrollBottom(); return true; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusLast(); + this._list.focusLast(); return true; } } selectPreviousPage(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Details: this._details.widget.pageUp(); return true; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusPreviousPage(); + this._list.focusPreviousPage(); return true; } } selectPrevious(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusPrevious(1, true); + this._list.focusPrevious(1, true); return false; } } selectFirst(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Details: this._details.widget.scrollTop(); return true; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusFirst(); + this._list.focusFirst(); return true; } } getFocusedItem(): ISelectedSuggestion | undefined { - if (this.state !== State.Hidden - && this.state !== State.Empty - && this.state !== State.Loading - && this.completionModel + if (this._state !== State.Hidden + && this._state !== State.Empty + && this._state !== State.Loading + && this._completionModel ) { return { - item: this.list.getFocusedElements()[0], - index: this.list.getFocus()[0], - model: this.completionModel + item: this._list.getFocusedElements()[0], + index: this._list.getFocus()[0], + model: this._completionModel }; } return undefined; } toggleDetailsFocus(): void { - if (this.state === State.Details) { + if (this._state === State.Details) { this._setState(State.Open); - if (this.detailsBorderColor) { - this._details.widget.domNode.style.borderColor = this.detailsBorderColor; + if (this._detailsBorderColor) { + this._details.widget.domNode.style.borderColor = this._detailsBorderColor; } - } else if (this.state === State.Open && this._isDetailsVisible()) { + } else if (this._state === State.Open && this._isDetailsVisible()) { this._setState(State.Details); - if (this.detailsFocusBorderColor) { - this._details.widget.domNode.style.borderColor = this.detailsFocusBorderColor; + if (this._detailsFocusBorderColor) { + this._details.widget.domNode.style.borderColor = this._detailsFocusBorderColor; } } } @@ -659,14 +662,14 @@ export class SuggestWidget implements IDisposable { toggleDetails(): void { if (this._isDetailsVisible()) { // hide details widget - this.ctxSuggestWidgetDetailsVisible.set(false); + this._ctxSuggestWidgetDetailsVisible.set(false); this._setDetailsVisible(false); this._details.hide(); this.element.domNode.classList.remove('shows-details'); - } else if (canExpandCompletionItem(this.list.getFocusedElements()[0]) && (this.state === State.Open || this.state === State.Details || this.state === State.Frozen)) { + } else if (canExpandCompletionItem(this._list.getFocusedElements()[0]) && (this._state === State.Open || this._state === State.Details || this._state === State.Frozen)) { // show details widget (iff possible) - this.ctxSuggestWidgetDetailsVisible.set(true); + this._ctxSuggestWidgetDetailsVisible.set(true); this._setDetailsVisible(true); this.showDetails(false); } @@ -677,7 +680,7 @@ export class SuggestWidget implements IDisposable { if (loading) { this._details.widget.renderLoading(); } else { - this._details.widget.renderItem(this.list.getFocusedElements()[0], this.explainMode); + this._details.widget.renderItem(this._list.getFocusedElements()[0], this._explainMode); } this._positionDetails(); this.editor.focus(); @@ -685,8 +688,8 @@ export class SuggestWidget implements IDisposable { } toggleExplainMode(): void { - if (this.list.getFocusedElements()[0] && this._isDetailsVisible()) { - this.explainMode = !this.explainMode; + if (this._list.getFocusedElements()[0] && this._isDetailsVisible()) { + this._explainMode = !this._explainMode; this.showDetails(false); } } @@ -696,13 +699,13 @@ export class SuggestWidget implements IDisposable { } hideWidget(): void { - this.loadingTimeout.dispose(); + this._loadingTimeout?.dispose(); this._setState(State.Hidden); - this.onDidHideEmitter.fire(this); + this._onDidHide.fire(this); } isFrozen(): boolean { - return this.state === State.Frozen; + return this._state === State.Frozen; } _afterRender(position: ContentWidgetPositionPreference | null) { @@ -712,7 +715,7 @@ export class SuggestWidget implements IDisposable { } return; } - if (this.state === State.Empty || this.state === State.Loading) { + if (this._state === State.Empty || this._state === State.Loading) { // no special positioning when widget isn't showing list return; } @@ -738,12 +741,12 @@ export class SuggestWidget implements IDisposable { const info = this.getLayoutInfo(); // status bar - this.status.element.style.lineHeight = `${info.itemHeight}px`; + this._status.element.style.lineHeight = `${info.itemHeight}px`; - if (this.state === State.Empty || this.state === State.Loading) { + if (this._state === State.Empty || this._state === State.Loading) { // showing a message only height = info.itemHeight + info.borderHeight; - width = 230; + width = info.defaultSize.width / 2; this.element.enableSashes(false, false, false, false); this.element.minSize = this.element.maxSize = new dom.Dimension(width, height); this._contentWidget.setPreference(ContentWidgetPositionPreference.BELOW); @@ -754,16 +757,16 @@ export class SuggestWidget implements IDisposable { // width math const maxWidth = bodyBox.width - info.borderHeight - 2 * info.horizontalPadding; if (width === undefined) { - width = 430; + width = info.defaultSize.width; } if (width > maxWidth) { width = maxWidth; } - const preferredWidth = this.completionModel ? this.completionModel.stats.pLabelLen * info.typicalHalfwidthCharacterWidth : width; + const preferredWidth = this._completionModel ? this._completionModel.stats.pLabelLen * info.typicalHalfwidthCharacterWidth : width; // height math - const fullHeight = info.statusBarHeight + this.list.contentHeight + info.borderHeight; - const preferredHeight = info.statusBarHeight + 12 * info.itemHeight + info.borderHeight; + const fullHeight = info.statusBarHeight + this._list.contentHeight + info.borderHeight; + const preferredHeight = info.defaultSize.height; const minHeight = info.itemHeight + info.statusBarHeight; const editorBox = dom.getDomNodePagePosition(this.editor.getDomNode()); const cursorBox = this.editor.getScrolledVisiblePosition(this.editor.getPosition()); @@ -819,8 +822,8 @@ export class SuggestWidget implements IDisposable { height = Math.min(maxHeight, height); const { statusBarHeight } = this.getLayoutInfo(); - this.list.layout(height - statusBarHeight, width); - this.listElement.style.height = `${height - statusBarHeight}px`; + this._list.layout(height - statusBarHeight, width); + this._listElement.style.height = `${height - statusBarHeight}px`; this.element.layout(height, width); this._contentWidget.layout(); @@ -835,8 +838,8 @@ export class SuggestWidget implements IDisposable { getLayoutInfo() { const fontInfo = this.editor.getOption(EditorOption.fontInfo); - const itemHeight = this.editor.getOption(EditorOption.suggestLineHeight) || fontInfo.lineHeight; - const statusBarHeight = !this.editor.getOption(EditorOption.suggest).showStatusBar || this.state === State.Empty || this.state === State.Loading ? 0 : itemHeight; + const itemHeight = clamp(this.editor.getOption(EditorOption.suggestLineHeight) || fontInfo.lineHeight, 8, 1000); + const statusBarHeight = !this.editor.getOption(EditorOption.suggest).showStatusBar || this._state === State.Empty || this._state === State.Loading ? 0 : itemHeight; const borderWidth = this._details.widget.borderWidth; const borderHeight = 2 * borderWidth; @@ -847,7 +850,8 @@ export class SuggestWidget implements IDisposable { borderHeight, typicalHalfwidthCharacterWidth: fontInfo.typicalHalfwidthCharacterWidth, verticalPadding: 22, - horizontalPadding: 14 + horizontalPadding: 14, + defaultSize: new dom.Dimension(430, statusBarHeight + 12 * itemHeight + borderHeight) }; } @@ -856,7 +860,7 @@ export class SuggestWidget implements IDisposable { } private _setDetailsVisible(value: boolean) { - this._storageService.store('expandSuggestionDocs', value, StorageScope.GLOBAL); + this._storageService.store('expandSuggestionDocs', value, StorageScope.GLOBAL, StorageTarget.USER); } } diff --git a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts index 72500145f..9eed3c990 100644 --- a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts +++ b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts @@ -91,6 +91,7 @@ export class SuggestDetailsWidget { const lineHeightPx = `${lineHeight}px`; this.domNode.style.fontSize = fontSizePx; + this.domNode.style.lineHeight = lineHeightPx; this.domNode.style.fontWeight = fontWeight; this.domNode.style.fontFeatureSettings = fontInfo.fontFeatureSettings; this._type.style.fontFamily = fontFamily; @@ -127,9 +128,10 @@ export class SuggestDetailsWidget { if (explainMode) { let md = ''; - md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || item.completion.label}' with '${item.word}'` : ' (no prefix)'}\n`; + md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name}' with '${item.word}'` : ' (no prefix)'}\n`; md += `distance: ${item.distance}, see localityBonus-setting\n`; md += `index: ${item.idx}, based on ${item.completion.sortText && `sortText: "${item.completion.sortText}"` || 'label'}\n`; + md += `commit characters: ${item.completion.commitCharacters}\n`; documentation = new MarkdownString().appendCodeblock('empty', md); detail = `Provider: ${item.provider._debugDisplayName}`; } @@ -168,7 +170,7 @@ export class SuggestDetailsWidget { const renderedContents = this._markdownRenderer.render(documentation); this._docs.appendChild(renderedContents.element); this._renderDisposeable.add(renderedContents); - this._renderDisposeable.add(this._markdownRenderer.onDidRenderCodeBlock(() => { + this._renderDisposeable.add(this._markdownRenderer.onDidRenderAsync(() => { this.layout(this._size.width, this._type.clientHeight + this._docs.clientHeight); this._onDidChangeContents.fire(this); })); diff --git a/src/vs/editor/contrib/suggest/suggestWidgetRenderer.ts b/src/vs/editor/contrib/suggest/suggestWidgetRenderer.ts index 66e6e51ce..26cdc77ae 100644 --- a/src/vs/editor/contrib/suggest/suggestWidgetRenderer.ts +++ b/src/vs/editor/contrib/suggest/suggestWidgetRenderer.ts @@ -11,7 +11,7 @@ import { IListRenderer } from 'vs/base/browser/ui/list/list'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CompletionItem } from './suggest'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { CompletionItemKind, completionKindToCssClass, CompletionItemTag } from 'vs/editor/common/modes'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; @@ -21,32 +21,40 @@ import { URI } from 'vs/base/common/uri'; import { FileKind } from 'vs/platform/files/common/files'; import { flatten } from 'vs/base/common/arrays'; import { canExpandCompletionItem } from './suggestWidgetDetails'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export function getAriaId(index: number): string { return `suggest-aria-id:${index}`; } -export const suggestMoreInfoIcon = registerIcon('suggest-more-info', Codicon.chevronRight); +export const suggestMoreInfoIcon = registerIcon('suggest-more-info', Codicon.chevronRight, nls.localize('suggestMoreInfoIcon', 'Icon for more information in the suggest widget.')); -const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i; +const _completionItemColor = new class ColorExtractor { -function extractColor(item: CompletionItem, out: string[]): boolean { - const label = typeof item.completion.label === 'string' - ? item.completion.label - : item.completion.label.name; + private static _regexRelaxed = /(#([\da-fA-F]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))/; + private static _regexStrict = new RegExp(`^${ColorExtractor._regexRelaxed.source}$`, 'i'); - if (label.match(colorRegExp)) { - out[0] = label; - return true; + extract(item: CompletionItem, out: string[]): boolean { + if (item.textLabel.match(ColorExtractor._regexStrict)) { + out[0] = item.textLabel; + return true; + } + if (item.completion.detail && item.completion.detail.match(ColorExtractor._regexStrict)) { + out[0] = item.completion.detail; + return true; + } + if (typeof item.completion.documentation === 'string') { + const match = ColorExtractor._regexRelaxed.exec(item.completion.documentation); + if (match && (match.index === 0 || match.index + match[0].length === item.completion.documentation.length)) { + out[0] = match[0]; + return true; + } + } + return false; } - if (typeof item.completion.documentation === 'string' && item.completion.documentation.match(colorRegExp)) { - out[0] = item.completion.documentation; - return true; - } - return false; -} +}; export interface ISuggestionTemplateData { @@ -116,7 +124,7 @@ export class ItemRenderer implements IListRenderer { @@ -165,7 +173,7 @@ export class ItemRenderer implements IListRenderer { const _deleteWordRight = new DeleteWordRight(); const _deleteWordStartRight = new DeleteWordStartRight(); const _deleteWordEndRight = new DeleteWordEndRight(); + const _deleteInsideWord = new DeleteInsideWord(); function runEditorCommand(editor: ICodeEditor, command: EditorCommand): void { command.runEditorCommand(null, editor, null); @@ -88,6 +89,9 @@ suite('WordOperations', () => { function deleteWordEndRight(editor: ICodeEditor): void { runEditorCommand(editor, _deleteWordEndRight); } + function deleteInsideWord(editor: ICodeEditor): void { + _deleteInsideWord.run(null!, editor, null); + } test('cursorWordLeft - simple', () => { const EXPECTED = [ @@ -750,4 +754,119 @@ suite('WordOperations', () => { model.dispose(); mode.dispose(); }); + + test('deleteInsideWord - empty line', () => { + withTestCodeEditor([ + 'Line1', + '', + 'Line2' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(2, 1)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'Line1\nLine2'); + }); + }); + + test('deleteInsideWord - in whitespace 1', () => { + withTestCodeEditor([ + 'Just some text.' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 6)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'Justsome text.'); + }); + }); + + test('deleteInsideWord - in whitespace 2', () => { + withTestCodeEditor([ + 'Just some text.' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 6)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'Justsome text.'); + }); + }); + + test('deleteInsideWord - in whitespace 3', () => { + withTestCodeEditor([ + 'Just "some text.' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 6)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'Just"some text.'); + deleteInsideWord(editor); + assert.equal(model.getValue(), '"some text.'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'some text.'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'text.'); + deleteInsideWord(editor); + assert.equal(model.getValue(), '.'); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + }); + }); + + test('deleteInsideWord - in non-words', () => { + withTestCodeEditor([ + 'x=3+4+5+6' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 7)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'x=3+45+6'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'x=3++6'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'x=36'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'x='); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'x'); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + }); + }); + + test('deleteInsideWord - in words 1', () => { + withTestCodeEditor([ + 'This is interesting' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 7)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'This interesting'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'This'); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + }); + }); + + test('deleteInsideWord - in words 2', () => { + withTestCodeEditor([ + 'This is interesting' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 7)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'This interesting'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'This'); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + }); + }); }); diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index c7db9649e..82dcbddc5 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorCommand, ICommandOptions, ServicesAccessor, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; +import { EditorCommand, ICommandOptions, ServicesAccessor, registerEditorCommand, EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; import { CursorState } from 'vs/editor/common/controller/cursorCommon'; import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; @@ -480,6 +481,36 @@ export class DeleteWordRight extends DeleteWordRightCommand { } } +export class DeleteInsideWord extends EditorAction { + + constructor() { + super({ + id: 'deleteInsideWord', + precondition: EditorContextKeys.writable, + label: nls.localize('deleteInsideWord', "Delete Word"), + alias: 'Delete Word' + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { + if (!editor.hasModel()) { + return; + } + const wordSeparators = getMapForWordSeparators(editor.getOption(EditorOption.wordSeparators)); + const model = editor.getModel(); + const selections = editor.getSelections(); + + const commands = selections.map((sel) => { + const deleteRange = WordOperations.deleteInsideWord(wordSeparators, model, sel); + return new ReplaceCommand(deleteRange, ''); + }); + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } +} + registerEditorCommand(new CursorWordStartLeft()); registerEditorCommand(new CursorWordEndLeft()); registerEditorCommand(new CursorWordLeft()); @@ -502,3 +533,4 @@ registerEditorCommand(new DeleteWordLeft()); registerEditorCommand(new DeleteWordStartRight()); registerEditorCommand(new DeleteWordEndRight()); registerEditorCommand(new DeleteWordRight()); +registerEditorAction(DeleteInsideWord); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index a591facd8..904283213 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -14,7 +14,7 @@ import 'vs/editor/contrib/caretOperations/transpose'; import 'vs/editor/contrib/clipboard/clipboard'; import 'vs/editor/contrib/codeAction/codeActionContributions'; import 'vs/editor/contrib/codelens/codelensController'; -import 'vs/editor/contrib/colorPicker/colorDetector'; +import 'vs/editor/contrib/colorPicker/colorContributions'; import 'vs/editor/contrib/comment/comment'; import 'vs/editor/contrib/contextmenu/contextmenu'; import 'vs/editor/contrib/cursorUndo/cursorUndo'; @@ -31,10 +31,10 @@ import 'vs/editor/contrib/hover/hover'; import 'vs/editor/contrib/indentation/indentation'; import 'vs/editor/contrib/inPlaceReplace/inPlaceReplace'; import 'vs/editor/contrib/linesOperations/linesOperations'; +import 'vs/editor/contrib/linkedEditing/linkedEditing'; import 'vs/editor/contrib/links/links'; import 'vs/editor/contrib/multicursor/multicursor'; import 'vs/editor/contrib/parameterHints/parameterHints'; -import 'vs/editor/contrib/rename/onTypeRename'; import 'vs/editor/contrib/rename/rename'; import 'vs/editor/contrib/smartSelect/smartSelect'; import 'vs/editor/contrib/snippet/snippetController2'; diff --git a/src/vs/editor/editor.api.ts b/src/vs/editor/editor.api.ts index 19d57ab28..0e4c48c34 100644 --- a/src/vs/editor/editor.api.ts +++ b/src/vs/editor/editor.api.ts @@ -8,8 +8,6 @@ import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase' import { createMonacoEditorAPI } from 'vs/editor/standalone/browser/standaloneEditor'; import { createMonacoLanguagesAPI } from 'vs/editor/standalone/browser/standaloneLanguages'; -const global: any = self; - // Set defaults for standalone editor EditorOptions.wrappingIndent.defaultValue = WrappingIndent.None; EditorOptions.glyphMargin.defaultValue = false; @@ -34,10 +32,10 @@ export const Token = api.Token; export const editor = api.editor; export const languages = api.languages; -global.monaco = api; +self.monaco = api; -if (typeof global.require !== 'undefined' && typeof global.require.config === 'function') { - global.require.config({ +if (typeof self.require !== 'undefined' && typeof self.require.config === 'function') { + self.require.config({ ignoreDuplicateModules: [ 'vscode-languageserver-types', 'vscode-languageserver-types/main', diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index fdde96abc..22bad13e9 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -15,6 +15,8 @@ import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; import { MonarchTokenizer } from 'vs/editor/standalone/common/monarch/monarchLexer'; +const ttPolicy = window.trustedTypes?.createPolicy('standaloneColorizer', { createHTML: value => value }); + export interface IColorizerOptions { tabSize?: number; } @@ -40,7 +42,8 @@ export class Colorizer { let text = domNode.firstChild ? domNode.firstChild.nodeValue : ''; domNode.className += ' ' + theme; let render = (str: string) => { - domNode.innerHTML = str; + const trustedhtml = ttPolicy ? ttPolicy.createHTML(str) : str; + domNode.innerHTML = trustedhtml as unknown as string; }; return this.colorize(modeService, text || '', mimeType, options).then(render, (err) => console.error(err)); } @@ -54,7 +57,7 @@ export class Colorizer { if (strings.startsWithUTF8BOM(text)) { text = text.substr(1); } - let lines = text.split(/\r\n|\r|\n/); + let lines = strings.splitLines(text); let language = modeService.getModeId(mimeType); if (!language) { return Promise.resolve(_fakeColorize(lines, tabSize)); diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 8248e2744..ba51dfd9e 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -332,7 +332,8 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { when: when, weight1: 1000, weight2: 0, - extensionId: null + extensionId: null, + isBuiltinExtension: false }); toDispose.add(toDisposable(() => { @@ -380,11 +381,11 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { if (!keybinding) { // This might be a removal keybinding item in user settings => accept it - result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault, null); + result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault, null, false); } else { const resolvedKeybindings = this.resolveKeybinding(keybinding); for (const resolvedKeybinding of resolvedKeybindings) { - result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault, null); + result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault, null, false); } } } diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index e06bd35a8..9344ee801 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -113,6 +113,10 @@ export interface IGlobalEditorOptions { * Defaults to true. */ wordBasedSuggestions?: boolean; + /** + * Controls whether word based completions should be included from opened documents of the same language or any language. + */ + wordBasedSuggestionsOnlySameLanguage?: boolean; /** * Controls whether the semanticHighlighting is shown for the languages that support it. * true: semanticHighlighting is enabled for all themes diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index fb2aaac02..6d5fc9c32 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -41,6 +41,7 @@ import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl'; +import { splitLines } from 'vs/base/common/strings'; type Omit = Pick>; @@ -288,7 +289,7 @@ export function tokenize(text: string, languageId: string): Token[][] { modeService.triggerMode(languageId); let tokenizationSupport = getSafeTokenizationSupport(languageId); - let lines = text.split(/\r\n|\r|\n/); + let lines = splitLines(text); let result: Token[][] = []; let state = tokenizationSupport.getInitialState(); for (let i = 0, len = lines.length; i < len; i++) { diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index b95826e91..dbf225a6c 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -75,9 +75,11 @@ export function setLanguageConfiguration(languageId: string, configuration: Lang */ export class EncodedTokenizationSupport2Adapter implements modes.ITokenizationSupport { + private readonly _languageIdentifier: modes.LanguageIdentifier; private readonly _actual: EncodedTokensProvider; - constructor(actual: EncodedTokensProvider) { + constructor(languageIdentifier: modes.LanguageIdentifier, actual: EncodedTokensProvider) { + this._languageIdentifier = languageIdentifier; this._actual = actual; } @@ -86,6 +88,9 @@ export class EncodedTokenizationSupport2Adapter implements modes.ITokenizationSu } public tokenize(line: string, state: modes.IState, offsetDelta: number): TokenizationResult { + if (typeof this._actual.tokenize === 'function') { + return TokenizationSupport2Adapter.adaptTokenize(this._languageIdentifier.language, <{ tokenize(line: string, state: modes.IState): ILineTokens; }>this._actual, line, state, offsetDelta); + } throw new Error('Not supported!'); } @@ -114,7 +119,7 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { return this._actual.getInitialState(); } - private _toClassicTokens(tokens: IToken[], language: string, offsetDelta: number): Token[] { + private static _toClassicTokens(tokens: IToken[], language: string, offsetDelta: number): Token[] { let result: Token[] = []; let previousStartIndex: number = 0; for (let i = 0, len = tokens.length; i < len; i++) { @@ -137,9 +142,9 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { return result; } - public tokenize(line: string, state: modes.IState, offsetDelta: number): TokenizationResult { - let actualResult = this._actual.tokenize(line, state); - let tokens = this._toClassicTokens(actualResult.tokens, this._languageIdentifier.language, offsetDelta); + public static adaptTokenize(language: string, actual: { tokenize(line: string, state: modes.IState): ILineTokens; }, line: string, state: modes.IState, offsetDelta: number): TokenizationResult { + let actualResult = actual.tokenize(line, state); + let tokens = TokenizationSupport2Adapter._toClassicTokens(actualResult.tokens, language, offsetDelta); let endState: modes.IState; // try to save an object if possible @@ -152,6 +157,10 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { return new TokenizationResult(tokens, endState); } + public tokenize(line: string, state: modes.IState, offsetDelta: number): TokenizationResult { + return TokenizationSupport2Adapter.adaptTokenize(this._languageIdentifier.language, this._actual, line, state, offsetDelta); + } + private _toBinaryTokens(tokens: IToken[], offsetDelta: number): Uint32Array { const languageId = this._languageIdentifier.id; const tokenTheme = this._standaloneThemeService.getColorTheme().tokenTheme; @@ -287,6 +296,10 @@ export interface EncodedTokensProvider { * Tokenize a line given the state at the beginning of the line. */ tokenizeEncoded(line: string, state: modes.IState): IEncodedLineTokens; + /** + * Tokenize a line given the state at the beginning of the line. + */ + tokenize?(line: string, state: modes.IState): ILineTokens; } function isEncodedTokensProvider(provider: TokensProvider | EncodedTokensProvider): provider is EncodedTokensProvider { @@ -307,7 +320,7 @@ export function setTokensProvider(languageId: string, provider: TokensProvider | } const create = (provider: TokensProvider | EncodedTokensProvider) => { if (isEncodedTokensProvider(provider)) { - return new EncodedTokenizationSupport2Adapter(provider); + return new EncodedTokenizationSupport2Adapter(languageIdentifier!, provider); } else { return new TokenizationSupport2Adapter(StaticServices.standaloneThemeService.get(), languageIdentifier!, provider); } @@ -392,10 +405,10 @@ export function registerDocumentHighlightProvider(languageId: string, provider: } /** - * Register an on type rename provider. + * Register an linked editing range provider. */ -export function registerOnTypeRenameProvider(languageId: string, provider: modes.OnTypeRenameProvider): IDisposable { - return modes.OnTypeRenameProviderRegistry.register(languageId, provider); +export function registerLinkedEditingRangeProvider(languageId: string, provider: modes.LinkedEditingRangeProvider): IDisposable { + return modes.LinkedEditingRangeProviderRegistry.register(languageId, provider); } /** @@ -566,7 +579,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { registerHoverProvider: registerHoverProvider, registerDocumentSymbolProvider: registerDocumentSymbolProvider, registerDocumentHighlightProvider: registerDocumentHighlightProvider, - registerOnTypeRenameProvider: registerOnTypeRenameProvider, + registerLinkedEditingRangeProvider: registerLinkedEditingRangeProvider, registerDefinitionProvider: registerDefinitionProvider, registerImplementationProvider: registerImplementationProvider, registerTypeDefinitionProvider: registerTypeDefinitionProvider, diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 6baca469b..4a0e6c930 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -54,7 +54,6 @@ import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; import { StandaloneQuickInputServiceImpl } from 'vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; export interface IEditorOverrideServices { [index: string]: any; @@ -167,8 +166,6 @@ export module StaticServices { export const storageService = define(IStorageService, () => new InMemoryStorageService()); - export const storageSyncService = define(IStorageKeysSyncRegistryService, () => new StorageKeysSyncRegistryService()); - export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), resourceConfigurationService.get(o), logService.get(o))); } diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 54ca70238..544d7b07a 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -16,7 +16,7 @@ import { ColorIdentifier, Extensions, IColorRegistry } from 'vs/platform/theme/c import { Extensions as ThemingExtensions, ICssStyleCollector, IFileIconTheme, IThemingRegistry, ITokenStyle } from 'vs/platform/theme/common/themeService'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { ColorScheme } from 'vs/platform/theme/common/theme'; -import { CodiconStyles } from 'vs/base/browser/ui/codicons/codiconStyles'; +import { getIconRegistry } from 'vs/platform/theme/common/iconRegistry'; const VS_THEME_NAME = 'vs'; const VS_DARK_THEME_NAME = 'vs-dark'; @@ -208,15 +208,18 @@ export class StandaloneThemeServiceImpl extends Disposable implements IStandalon this._knownThemes.set(VS_THEME_NAME, newBuiltInTheme(VS_THEME_NAME)); this._knownThemes.set(VS_DARK_THEME_NAME, newBuiltInTheme(VS_DARK_THEME_NAME)); this._knownThemes.set(HC_BLACK_THEME_NAME, newBuiltInTheme(HC_BLACK_THEME_NAME)); - this._codiconCSS = CodiconStyles.getCSS(); + + const iconRegistry = getIconRegistry(); + + this._codiconCSS = iconRegistry.getCSS(); this._themeCSS = ''; this._allCSS = `${this._codiconCSS}\n${this._themeCSS}`; this._globalStyleElement = null; this._styleElements = []; this.setTheme(VS_THEME_NAME); - CodiconStyles.onDidChange(() => { - this._codiconCSS = CodiconStyles.getCSS(); + iconRegistry.onDidChange(() => { + this._codiconCSS = iconRegistry.getCSS(); this._updateCSS(); }); } diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index a90e11dfd..cb202a3f8 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -2208,6 +2208,42 @@ suite('Editor Controller - Regression tests', () => { }); }); + test('issue #110376: multiple selections with wordwrap behave differently', () => { + // a single model line => 4 view lines + withTestCodeEditor([ + [ + 'just a sentence. just a ', + 'sentence. just a sentence.', + ].join('') + ], { wordWrap: 'wordWrapColumn', wordWrapColumn: 25 }, (editor, viewModel) => { + viewModel.setSelections('test', [ + new Selection(1, 1, 1, 16), + new Selection(1, 18, 1, 33), + new Selection(1, 35, 1, 50), + ]); + + moveLeft(editor, viewModel); + assertCursor(viewModel, [ + new Selection(1, 1, 1, 1), + new Selection(1, 18, 1, 18), + new Selection(1, 35, 1, 35), + ]); + + viewModel.setSelections('test', [ + new Selection(1, 1, 1, 16), + new Selection(1, 18, 1, 33), + new Selection(1, 35, 1, 50), + ]); + + moveRight(editor, viewModel); + assertCursor(viewModel, [ + new Selection(1, 16, 1, 16), + new Selection(1, 33, 1, 33), + new Selection(1, 50, 1, 50), + ]); + }); + }); + test('issue #98320: Multi-Cursor, Wrap lines and cursorSelectRight ==> cursors out of sync', () => { // a single model line => 4 view lines withTestCodeEditor([ @@ -4038,6 +4074,33 @@ suite('Editor Controller - Indentation Rules', () => { mode.dispose(); }); + test('issue #57197: indent rules regex should be stateless', () => { + usingCursor({ + text: [ + 'Project:', + ], + languageIdentifier: (new IndentRulesMode({ + decreaseIndentPattern: /^\s*}$/gm, + increaseIndentPattern: /^(?![^\S\n]*(?!--|––|——)(?:[-❍❑■⬜□☐▪▫–—≡→›✘xX✔✓☑+]|\[[ xX+-]?\])\s[^\n]*)[^\S\n]*(.+:)[^\S\n]*(?:(?=@[^\s*~(]+(?::\/\/[^\s*~(:]+)?(?:\([^)]*\))?)|$)/gm, + })).getLanguageIdentifier(), + modelOpts: { insertSpaces: false }, + editorOpts: { autoIndent: 'full' } + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 9, false); + assertCursor(viewModel, new Selection(1, 9, 1, 9)); + + viewModel.type('\n', 'keyboard'); + model.forceTokenization(model.getLineCount()); + assertCursor(viewModel, new Selection(2, 2, 2, 2)); + + moveTo(editor, viewModel, 1, 9, false); + assertCursor(viewModel, new Selection(1, 9, 1, 9)); + viewModel.type('\n', 'keyboard'); + model.forceTokenization(model.getLineCount()); + assertCursor(viewModel, new Selection(2, 2, 2, 2)); + }); + }); + test('', () => { class JSONMode extends MockMode { private static readonly _id = new LanguageIdentifier('indentRulesMode', 4); @@ -4633,7 +4696,7 @@ suite('autoClosingPairs', () => { 'v|ar |c = \'|asd\';|', 'v|ar d = "|asd";|', 'v|ar e = /*3*/ 3;|', - 'v|ar f = /** 3 */3;|', + 'v|ar f = /** 3| */3;|', 'v|ar g = (3+5|);|', 'v|ar h = { |a: \'v|alue\' |};|', ]; @@ -4814,13 +4877,13 @@ suite('autoClosingPairs', () => { let autoClosePositions = [ 'var a |=| [|]|;|', - 'var b |=| |`asd`|;|', - 'var c |=| |\'asd\'|;|', - 'var d |=| |"asd"|;|', + 'var b |=| `asd`|;|', + 'var c |=| \'asd\'|;|', + 'var d |=| "asd"|;|', 'var e |=| /*3*/| 3;|', 'var f |=| /**| 3 */3;|', 'var g |=| (3+5)|;|', - 'var h |=| {| a:| |\'value\'| |}|;|', + 'var h |=| {| a:| \'value\'| |}|;|', ]; for (let i = 0, len = autoClosePositions.length; i < len; i++) { const lineNumber = i + 1; @@ -4863,6 +4926,51 @@ suite('autoClosingPairs', () => { mode.dispose(); }); + test('issue #72177: multi-character autoclose with conflicting patterns', () => { + const languageId = new LanguageIdentifier('autoClosingModeMultiChar', 5); + class AutoClosingModeMultiChar extends MockMode { + constructor() { + super(languageId); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + autoClosingPairs: [ + { open: '(', close: ')' }, + { open: '(*', close: '*)' }, + { open: '<@', close: '@>' }, + { open: '<@@', close: '@@>' }, + ], + })); + } + } + + const mode = new AutoClosingModeMultiChar(); + + usingCursor({ + text: [ + '', + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (editor, model, viewModel) => { + viewModel.type('(', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '()'); + viewModel.type('*', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '(**)', `doesn't add entire close when already closed substring is there`); + + model.setValue('('); + viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]); + viewModel.type('*', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '(**)', `does add entire close if not already there`); + + model.setValue(''); + viewModel.type('<@', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '<@@>'); + viewModel.type('@', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '<@@@@>', `autocloses when before multi-character closing brace`); + viewModel.type('(', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '<@@()@@>', `autocloses when before multi-character closing brace`); + }); + mode.dispose(); + }); + test('issue #55314: Do not auto-close when ending with open', () => { const languageId = new LanguageIdentifier('myElectricMode', 5); class ElectricMode extends MockMode { @@ -4916,7 +5024,7 @@ suite('autoClosingPairs', () => { ], languageIdentifier: mode.getLanguageIdentifier() }, (editor, model, viewModel) => { - assertType(editor, model, viewModel, 1, 12, '"', '""', `does not over type and will auto close`); + assertType(editor, model, viewModel, 1, 12, '"', '"', `does not over type and will not auto close`); }); mode.dispose(); }); @@ -5277,7 +5385,7 @@ suite('autoClosingPairs', () => { assert.equal(model.getValue(), 'console.log(\'it\\\');'); viewModel.type('\'', 'keyboard'); - assert.equal(model.getValue(), 'console.log(\'it\\\'\'\');'); + assert.equal(model.getValue(), 'console.log(\'it\\\'\');'); }); mode.dispose(); }); diff --git a/src/vs/editor/test/browser/controller/imeTester.ts b/src/vs/editor/test/browser/controller/imeTester.ts index 4a3f4e196..eb0958400 100644 --- a/src/vs/editor/test/browser/controller/imeTester.ts +++ b/src/vs/editor/test/browser/controller/imeTester.ts @@ -9,6 +9,7 @@ import { ISimpleModel, PagedScreenReaderStrategy, TextAreaState } from 'vs/edito import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { EndOfLinePreference } from 'vs/editor/common/model'; +import * as dom from 'vs/base/browser/dom'; // To run this test, open imeTester.html @@ -50,12 +51,13 @@ class TestView { } public paint(output: HTMLElement) { - let r = ''; + dom.clearNode(output); for (let i = 1; i <= this._model.getLineCount(); i++) { - let content = this._model.getModelLineContent(i); - r += content + '
'; + const textNode = document.createTextNode(this._model.getModelLineContent(i)); + output.appendChild(textNode); + const br = document.createElement('br'); + output.appendChild(br); } - output.innerHTML = r; } } @@ -69,7 +71,12 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string let title = document.createElement('div'); title.className = 'title'; - title.innerHTML = description + '. Type ' + inputStr + ''; + const inputStrStrong = document.createElement('strong'); + inputStrStrong.innerText = inputStr; + + title.innerText = description + '. Type '; + title.appendChild(inputStrStrong); + container.appendChild(title); let startBtn = document.createElement('button'); @@ -140,7 +147,7 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string check.innerText = '[BAD]'; check.className = 'check bad'; } - check.innerHTML += expected; + check.appendChild(document.createTextNode(expected)); }; handler.onType((e) => { diff --git a/src/vs/editor/test/browser/editorTestServices.ts b/src/vs/editor/test/browser/editorTestServices.ts index e9fba5fbb..55ceb3734 100644 --- a/src/vs/editor/test/browser/editorTestServices.ts +++ b/src/vs/editor/test/browser/editorTestServices.ts @@ -22,6 +22,7 @@ export class TestCodeEditorService extends AbstractCodeEditorService { public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void { } public removeDecorationType(key: string): void { } public resolveDecorationOptions(decorationTypeKey: string, writable: boolean): IModelDecorationOptions { return {}; } + public resolveDecorationCSSRules(decorationTypeKey: string): CSSRuleList | null { return null; } } export class TestCommandService implements ICommandService { diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts index bc5224169..9c8c6c6ed 100644 --- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts +++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts @@ -149,25 +149,33 @@ suite('Decoration Render Options', () => { assert(readStyleSheet(styleSheet).indexOf(`{background:url('data:image/svg+xml;base64,PHN2ZyB4b+') center center no-repeat;}`) > 0); s.removeDecorationType('example'); + function assertBackground(url1: string, url2: string) { + const actual = readStyleSheet(styleSheet); + assert( + actual.indexOf(`{background:url('${url1}') center center no-repeat;}`) > 0 + || actual.indexOf(`{background:url('${url2}') center center no-repeat;}`) > 0 + ); + } + if (platform.isWindows) { // windows file path (used as string) s.registerDecorationType('example', { gutterIconPath: URI.file('c:\\files\\miles\\more.png') }); - assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///c:/files/miles/more.png') center center no-repeat;}`) > 0); + assertBackground('file:///c:/files/miles/more.png', 'vscode-file://vscode-app/c:/files/miles/more.png'); s.removeDecorationType('example'); // single quote must always be escaped/encoded s.registerDecorationType('example', { gutterIconPath: URI.file('c:\\files\\foo\\b\'ar.png') }); - assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///c:/files/foo/b%27ar.png') center center no-repeat;}`) > 0); + assertBackground('file:///c:/files/foo/b%27ar.png', 'vscode-file://vscode-app/c:/files/foo/b%27ar.png'); s.removeDecorationType('example'); } else { // unix file path (used as string) s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/bar.png') }); - assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///Users/foo/bar.png') center center no-repeat;}`) > 0); + assertBackground('file:///Users/foo/bar.png', 'vscode-file://vscode-app/Users/foo/bar.png'); s.removeDecorationType('example'); // single quote must always be escaped/encoded s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/b\'ar.png') }); - assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///Users/foo/b%27ar.png') center center no-repeat;}`) > 0); + assertBackground('file:///Users/foo/b%27ar.png', 'vscode-file://vscode-app/Users/foo/b%27ar.png'); s.removeDecorationType('example'); } diff --git a/src/vs/editor/test/common/controller/cursorAtomicMoveOperations.test.ts b/src/vs/editor/test/common/controller/cursorAtomicMoveOperations.test.ts new file mode 100644 index 000000000..e9c60b6e3 --- /dev/null +++ b/src/vs/editor/test/common/controller/cursorAtomicMoveOperations.test.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations'; + +suite('Cursor move command test', () => { + + test('Test whitespaceVisibleColumn', () => { + const testCases = [ + { + lineContent: ' ', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1], + expectedVisibleColumn: [0, 1, 2, 3, 4, 5, 6, 7, 8, -1], + }, + { + lineContent: ' ', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, 0, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, 0, -1], + expectedVisibleColumn: [0, 1, 2, -1], + }, + { + lineContent: '\t', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, -1], + expectedVisibleColumn: [0, 4, -1], + }, + { + lineContent: '\t ', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, 1, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, 4, -1], + expectedVisibleColumn: [0, 4, 5, -1], + }, + { + lineContent: ' \t\t ', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, 0, 2, 3, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, 0, 4, 8, -1], + expectedVisibleColumn: [0, 1, 4, 8, 9, -1], + }, + { + lineContent: ' \tA', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, 0, -1, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, 0, -1, -1], + expectedVisibleColumn: [0, 1, 4, -1, -1], + }, + { + lineContent: 'A', + tabSize: 4, + expectedPrevTabStopPosition: [-1, -1, -1], + expectedPrevTabStopVisibleColumn: [-1, -1, -1], + expectedVisibleColumn: [0, -1, -1], + }, + { + lineContent: '', + tabSize: 4, + expectedPrevTabStopPosition: [-1, -1], + expectedPrevTabStopVisibleColumn: [-1, -1], + expectedVisibleColumn: [0, -1], + }, + ]; + + for (const testCase of testCases) { + const maxPosition = testCase.expectedVisibleColumn.length; + for (let position = 0; position < maxPosition; position++) { + const actual = AtomicTabMoveOperations.whitespaceVisibleColumn(testCase.lineContent, position, testCase.tabSize); + const expected = [ + testCase.expectedPrevTabStopPosition[position], + testCase.expectedPrevTabStopVisibleColumn[position], + testCase.expectedVisibleColumn[position] + ]; + assert.deepStrictEqual(actual, expected); + } + } + }); + + test('Test atomicPosition', () => { + const testCases = [ + { + lineContent: ' ', + tabSize: 4, + expectedLeft: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1], + expectedRight: [4, 4, 4, 4, 8, 8, 8, 8, -1, -1], + expectedNearest: [0, 0, 0, 4, 4, 4, 4, 8, 8, -1], + }, + { + lineContent: ' \t', + tabSize: 4, + expectedLeft: [-1, 0, 0, -1], + expectedRight: [2, 2, -1, -1], + expectedNearest: [0, 0, 2, -1], + }, + { + lineContent: '\t ', + tabSize: 4, + expectedLeft: [-1, 0, -1, -1], + expectedRight: [1, -1, -1, -1], + expectedNearest: [0, 1, -1, -1], + }, + { + lineContent: ' \t ', + tabSize: 4, + expectedLeft: [-1, 0, 0, -1, -1], + expectedRight: [2, 2, -1, -1, -1], + expectedNearest: [0, 0, 2, -1, -1], + }, + { + lineContent: ' A', + tabSize: 4, + expectedLeft: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1, -1], + expectedRight: [4, 4, 4, 4, 8, 8, 8, 8, -1, -1, -1], + expectedNearest: [0, 0, 0, 4, 4, 4, 4, 8, 8, -1, -1], + }, + { + lineContent: ' foo', + tabSize: 4, + expectedLeft: [-1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1], + expectedRight: [4, 4, 4, 4, -1, -1, -1, -1, -1, -1, -1], + expectedNearest: [0, 0, 0, 4, 4, -1, -1, -1, -1, -1, -1], + }, + ]; + + for (const testCase of testCases) { + for (const { direction, expected } of [ + { + direction: Direction.Left, + expected: testCase.expectedLeft, + }, + { + direction: Direction.Right, + expected: testCase.expectedRight, + }, + { + direction: Direction.Nearest, + expected: testCase.expectedNearest, + }, + ]) { + + const actual = expected.map((_, i) => AtomicTabMoveOperations.atomicPosition(testCase.lineContent, i, testCase.tabSize, direction)); + assert.deepStrictEqual(actual, expected); + } + } + }); +}); diff --git a/src/vs/editor/test/common/diff/diffComputer.test.ts b/src/vs/editor/test/common/diff/diffComputer.test.ts index a4d574c24..9361e05d6 100644 --- a/src/vs/editor/test/common/diff/diffComputer.test.ts +++ b/src/vs/editor/test/common/diff/diffComputer.test.ts @@ -852,4 +852,31 @@ suite('Editor Diff - DiffComputer', () => { ]; assertDiff(original, modified, expected, false, false, false); }); + + test('gives preference to matching longer lines', () => { + let original = [ + 'A', + 'A', + 'BB', + 'C', + ]; + let modified = [ + 'A', + 'BB', + 'A', + 'D', + 'E', + 'A', + 'C', + ]; + let expected = [ + createLineChange( + 2, 2, 1, 0 + ), + createLineChange( + 3, 0, 3, 6 + ) + ]; + assertDiff(original, modified, expected, false, false, false); + }); }); diff --git a/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts b/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts index 49841dabc..4f87bf638 100644 --- a/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts +++ b/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from 'vs/base/common/charCode'; +import { splitLines } from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { DefaultEndOfLine, ITextBuffer, ITextBufferBuilder, ValidAnnotatedEditOperation } from 'vs/editor/common/model'; @@ -34,7 +35,7 @@ export function getRandomString(minLength: number, maxLength: number): string { export function generateRandomEdits(chunks: string[], editCnt: number): ValidAnnotatedEditOperation[] { let lines: string[] = []; for (const chunk of chunks) { - let newLines = chunk.split(/\r\n|\r|\n/); + let newLines = splitLines(chunk); if (lines.length === 0) { lines.push(...newLines); } else { @@ -64,7 +65,7 @@ export function generateRandomEdits(chunks: string[], editCnt: number): ValidAnn export function generateSequentialInserts(chunks: string[], editCnt: number): ValidAnnotatedEditOperation[] { let lines: string[] = []; for (const chunk of chunks) { - let newLines = chunk.split(/\r\n|\r|\n/); + let newLines = splitLines(chunk); if (lines.length === 0) { lines.push(...newLines); } else { @@ -96,7 +97,7 @@ export function generateSequentialInserts(chunks: string[], editCnt: number): Va export function generateRandomReplaces(chunks: string[], editCnt: number, searchStringLen: number, replaceStringLen: number): ValidAnnotatedEditOperation[] { let lines: string[] = []; for (const chunk of chunks) { - let newLines = chunk.split(/\r\n|\r|\n/); + let newLines = splitLines(chunk); if (lines.length === 0) { lines.push(...newLines); } else { diff --git a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts index ddec1facc..019b23f4f 100644 --- a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts +++ b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts @@ -14,6 +14,7 @@ import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeText import { NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { SearchData } from 'vs/editor/common/model/textModelSearch'; +import { splitLines } from 'vs/base/common/strings'; const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n'; @@ -75,7 +76,7 @@ function trimLineFeed(text: string): string { //#region Assertion function testLinesContent(str: string, pieceTable: PieceTreeBase) { - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assert.equal(pieceTable.getLinesRawContent(), str); for (let i = 0; i < lines.length; i++) { @@ -997,7 +998,7 @@ suite('CRLF', () => { pieceTable.delete(2, 3); str = str.substring(0, 2) + str.substring(2 + 3); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); @@ -1012,7 +1013,7 @@ suite('CRLF', () => { pieceTable.delete(4, 1); str = str.substring(0, 4) + str.substring(4 + 1); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); @@ -1033,7 +1034,7 @@ suite('CRLF', () => { pieceTable.insert(3, '\r\r\r\n'); str = str.substring(0, 3) + '\r\r\r\n' + str.substring(3); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); @@ -1205,7 +1206,7 @@ suite('centralized lineStarts with CRLF', () => { pieceTable.delete(2, 3); str = str.substring(0, 2) + str.substring(2 + 3); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); @@ -1218,7 +1219,7 @@ suite('centralized lineStarts with CRLF', () => { pieceTable.delete(4, 1); str = str.substring(0, 4) + str.substring(4 + 1); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); @@ -1238,7 +1239,7 @@ suite('centralized lineStarts with CRLF', () => { pieceTable.insert(3, '\r\r\r\n'); str = str.substring(0, 3) + '\r\r\r\n' + str.substring(3); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); diff --git a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts index 2b2a748c6..54f73ed0e 100644 --- a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts +++ b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts @@ -162,7 +162,7 @@ suite('EditorSimpleWorker', () => { 'f f' // 2 ]); - return worker.textualSuggest(model.uri.toString(), { lineNumber: 2, column: 2 }, '[a-z]+', 'img').then((result) => { + return worker.textualSuggest([model.uri.toString()], 'f', '[a-z]+', 'img').then((result) => { if (!result) { assert.ok(false); return; diff --git a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts index c4c1af552..910165a0e 100644 --- a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts +++ b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts @@ -68,6 +68,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { horizontalSliderSize: EditorOptions.scrollbar.defaultValue.horizontalSliderSize, verticalScrollbarSize: input.verticalScrollbarWidth, verticalSliderSize: EditorOptions.scrollbar.defaultValue.verticalSliderSize, + scrollByPage: EditorOptions.scrollbar.defaultValue.scrollByPage, }; options._write(EditorOption.scrollbar, scrollbarOptions); const lineNumbersOptions: InternalEditorRenderLineNumbersOptions = { @@ -78,7 +79,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { options._write(EditorOption.wordWrap, 'off'); options._write(EditorOption.wordWrapColumn, 80); - options._write(EditorOption.wordWrapMinified, true); + options._write(EditorOption.wordWrapOverride1, 'inherit'); + options._write(EditorOption.wordWrapOverride2, 'inherit'); options._write(EditorOption.accessibilitySupport, 'auto'); const actual = EditorLayoutInfoComputer.computeLayout(options, { diff --git a/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts index 509a0fe0b..670f42407 100644 --- a/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts +++ b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts @@ -5,8 +5,9 @@ import * as assert from 'assert'; import { WrappingIndent, EditorOptions } from 'vs/editor/common/config/editorOptions'; import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; -import { ILineBreaksComputerFactory, LineBreakData } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { LineBreakData } from 'vs/editor/common/viewModel/viewModel'; function parseAnnotatedText(annotatedText: string): { text: string; indices: number[]; } { let text = ''; diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index 2c5cca1cb..3156be0e1 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -14,8 +14,8 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; -import { LineBreakData, ISimpleModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; -import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; +import { ISimpleModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { LineBreakData, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; diff --git a/src/vs/loader.js b/src/vs/loader.js index 5a97ccfcc..76db97736 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -198,6 +198,9 @@ var AMDLoader; if (!obj || typeof obj !== 'object' || obj instanceof RegExp) { return obj; } + if (!Array.isArray(obj) && Object.getPrototypeOf(obj) !== Object.prototype) { + return obj; + } var result = Array.isArray(obj) ? [] : {}; Utilities.forEachProperty(obj, function (key, value) { if (value && typeof value === 'object') { @@ -618,37 +621,8 @@ var AMDLoader; }; return OnlyOnceScriptLoader; }()); - var trustedTypesPolyfill = new /** @class */(function () { - function class_1() { - } - class_1.prototype.installIfNeeded = function () { - if (typeof globalThis.trustedTypes !== 'undefined') { - return; // already defined - } - var _defaultRules = { - createHTML: function () { throw new Error('Policy\'s TrustedTypePolicyOptions did not specify a \'createHTML\' member'); }, - createScript: function () { throw new Error('Policy\'s TrustedTypePolicyOptions did not specify a \'createScript\' member'); }, - createScriptURL: function () { throw new Error('Policy\'s TrustedTypePolicyOptions did not specify a \'createScriptURL\' member'); }, - }; - globalThis.trustedTypes = { - createPolicy: function (name, rules) { - var _a, _b, _c; - return { - name: name, - createHTML: (_a = rules.createHTML) !== null && _a !== void 0 ? _a : _defaultRules.createHTML, - createScript: (_b = rules.createScript) !== null && _b !== void 0 ? _b : _defaultRules.createScript, - createScriptURL: (_c = rules.createScriptURL) !== null && _c !== void 0 ? _c : _defaultRules.createScriptURL, - }; - } - }; - }; - return class_1; - }()); - //#endregion var BrowserScriptLoader = /** @class */ (function () { function BrowserScriptLoader() { - // polyfill trustedTypes-support if missing - trustedTypesPolyfill.installIfNeeded(); } /** * Attach load / error listeners to a script element and remove them when either one has fired. @@ -673,7 +647,7 @@ var AMDLoader; BrowserScriptLoader.prototype.load = function (moduleManager, scriptSrc, callback, errorback) { if (/^node\|/.test(scriptSrc)) { var opts = moduleManager.getConfig().getOptionsLiteral(); - var nodeRequire = (opts.nodeRequire || AMDLoader.global.nodeRequire); + var nodeRequire = ensureRecordedNodeRequire(moduleManager.getRecorder(), (opts.nodeRequire || AMDLoader.global.nodeRequire)); var pieces = scriptSrc.split('|'); var moduleExports_1 = null; try { @@ -691,12 +665,9 @@ var AMDLoader; script.setAttribute('async', 'async'); script.setAttribute('type', 'text/javascript'); this.attachListeners(script, callback, errorback); - var createTrustedScriptURL = moduleManager.getConfig().getOptionsLiteral().createTrustedScriptURL; - if (createTrustedScriptURL) { - if (!this.scriptSourceURLPolicy) { - this.scriptSourceURLPolicy = trustedTypes.createPolicy('amdLoader', { createScriptURL: createTrustedScriptURL }); - } - scriptSrc = this.scriptSourceURLPolicy.createScriptURL(scriptSrc); + var trustedTypesPolicy = moduleManager.getConfig().getOptionsLiteral().trustedTypesPolicy; + if (trustedTypesPolicy) { + scriptSrc = trustedTypesPolicy.createScriptURL(scriptSrc); } script.setAttribute('src', scriptSrc); // Propagate CSP nonce to dynamically created script tag. @@ -711,16 +682,11 @@ var AMDLoader; }()); var WorkerScriptLoader = /** @class */ (function () { function WorkerScriptLoader() { - // polyfill trustedTypes-support if missing - trustedTypesPolyfill.installIfNeeded(); } WorkerScriptLoader.prototype.load = function (moduleManager, scriptSrc, callback, errorback) { - var createTrustedScriptURL = moduleManager.getConfig().getOptionsLiteral().createTrustedScriptURL; - if (createTrustedScriptURL) { - if (!this.scriptSourceURLPolicy) { - this.scriptSourceURLPolicy = trustedTypes.createPolicy('amdLoader', { createScriptURL: createTrustedScriptURL }); - } - scriptSrc = this.scriptSourceURLPolicy.createScriptURL(scriptSrc); + var trustedTypesPolicy = moduleManager.getConfig().getOptionsLiteral().trustedTypesPolicy; + if (trustedTypesPolicy) { + scriptSrc = trustedTypesPolicy.createScriptURL(scriptSrc); } try { importScripts(scriptSrc); @@ -815,7 +781,7 @@ var AMDLoader; NodeScriptLoader.prototype.load = function (moduleManager, scriptSrc, callback, errorback) { var _this = this; var opts = moduleManager.getConfig().getOptionsLiteral(); - var nodeRequire = (opts.nodeRequire || AMDLoader.global.nodeRequire); + var nodeRequire = ensureRecordedNodeRequire(moduleManager.getRecorder(), (opts.nodeRequire || AMDLoader.global.nodeRequire)); var nodeInstrumenter = (opts.nodeInstrumenter || function (c) { return c; }); this._init(nodeRequire); this._initNodeRequire(nodeRequire, moduleManager); @@ -1022,6 +988,24 @@ var AMDLoader; NodeScriptLoader._SUFFIX = '\n});'; return NodeScriptLoader; }()); + function ensureRecordedNodeRequire(recorder, _nodeRequire) { + if (_nodeRequire.__$__isRecorded) { + // it is already recorded + return _nodeRequire; + } + var nodeRequire = function nodeRequire(what) { + recorder.record(33 /* NodeBeginNativeRequire */, what); + try { + return _nodeRequire(what); + } + finally { + recorder.record(34 /* NodeEndNativeRequire */, what); + } + }; + nodeRequire.__$__isRecorded = true; + return nodeRequire; + } + AMDLoader.ensureRecordedNodeRequire = ensureRecordedNodeRequire; function createScriptLoader(env) { return new OnlyOnceScriptLoader(env); } @@ -1853,18 +1837,10 @@ var AMDLoader; }; function init() { if (typeof AMDLoader.global.require !== 'undefined' || typeof require !== 'undefined') { - var _nodeRequire_1 = (AMDLoader.global.require || require); - if (typeof _nodeRequire_1 === 'function' && typeof _nodeRequire_1.resolve === 'function') { + var _nodeRequire = (AMDLoader.global.require || require); + if (typeof _nodeRequire === 'function' && typeof _nodeRequire.resolve === 'function') { // re-expose node's require function - var nodeRequire = function (what) { - moduleManager.getRecorder().record(33 /* NodeBeginNativeRequire */, what); - try { - return _nodeRequire_1(what); - } - finally { - moduleManager.getRecorder().record(34 /* NodeEndNativeRequire */, what); - } - }; + var nodeRequire = AMDLoader.ensureRecordedNodeRequire(moduleManager.getRecorder(), _nodeRequire); AMDLoader.global.nodeRequire = nodeRequire; RequireFunc.nodeRequire = nodeRequire; RequireFunc.__$__nodeRequire = nodeRequire; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b9a579f90..2cb6913f7 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1115,6 +1115,10 @@ declare namespace monaco.editor { * Defaults to true. */ wordBasedSuggestions?: boolean; + /** + * Controls whether word based completions should be included from opened documents of the same language or any language. + */ + wordBasedSuggestionsOnlySameLanguage?: boolean; /** * Controls whether the semanticHighlighting is shown for the languages that support it. * true: semanticHighlighting is enabled for all themes @@ -1685,6 +1689,10 @@ declare namespace monaco.editor { * @return EOL char sequence (e.g.: '\n' or '\r\n'). */ getEOL(): string; + /** + * Get the end of line sequence predominantly used in the text buffer. + */ + getEndOfLineSequence(): EndOfLineSequence; /** * Get the minimum legal column for line at `lineNumber` */ @@ -1882,11 +1890,15 @@ declare namespace monaco.editor { */ detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void; /** - * Push a stack element onto the undo stack. This acts as an undo/redo point. - * The idea is to use `pushEditOperations` to edit the model and then to - * `pushStackElement` to create an undo/redo stop point. + * Close the current undo-redo element. + * This offers a way to create an undo/redo stop point. */ pushStackElement(): void; + /** + * Open the current undo-redo element. + * This offers a way to remove the current undo/redo stop point. + */ + popStackElement(): void; /** * Push edit operations, basically editing the model. This is the preferred way * of editing the model. The edit operations will land on the undo stack. @@ -2683,9 +2695,13 @@ declare namespace monaco.editor { */ readOnly?: boolean; /** - * Rename matching regions on type. + * Enable linked editing. * Defaults to false. */ + linkedEditing?: boolean; + /** + * deprecated, use linkedEditing instead + */ renameOnType?: boolean; /** * Should the editor render validation decorations. @@ -2799,6 +2815,14 @@ declare namespace monaco.editor { * Defaults to "off". */ wordWrap?: 'off' | 'on' | 'wordWrapColumn' | 'bounded'; + /** + * Override the `wordWrap` setting. + */ + wordWrapOverride1?: 'off' | 'on' | 'inherit'; + /** + * Override the `wordWrapOverride1` setting. + */ + wordWrapOverride2?: 'off' | 'on' | 'inherit'; /** * Control the wrapping of the editor. * When `wordWrap` = "off", the lines will never wrap. @@ -2808,11 +2832,6 @@ declare namespace monaco.editor { * Defaults to 80. */ wordWrapColumn?: number; - /** - * Force word wrapping when the text appears to be of a minified/generated file. - * Defaults to true. - */ - wordWrapMinified?: boolean; /** * Control indentation of wrapped lines. Can be: 'none', 'same', 'indent' or 'deepIndent'. * Defaults to 'same' in vscode and to 'none' in monaco-editor. @@ -2909,6 +2928,10 @@ declare namespace monaco.editor { * Suggest options. */ suggest?: ISuggestOptions; + /** + * Smart select opptions; + */ + smartSelect?: ISmartSelectOptions; /** * */ @@ -2955,6 +2978,11 @@ declare namespace monaco.editor { * Defaults to advanced. */ autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'; + /** + * Emulate selection behaviour of tab characters when using spaces for indentation. + * This means selection will stick to tab stops. + */ + stickyTabStops?: boolean; /** * Enable format on type. * Defaults to false. @@ -3030,6 +3058,14 @@ declare namespace monaco.editor { * Defaults to true. */ codeLens?: boolean; + /** + * Code lens font family. Defaults to editor font family. + */ + codeLensFontFamily?: string; + /** + * Code lens font size. Default to 90% of the editor font size + */ + codeLensFontSize?: number; /** * Control the behavior and rendering of the code action lightbulb. */ @@ -3177,15 +3213,19 @@ declare namespace monaco.editor { */ originalEditable?: boolean; /** - * Original editor should be have code lens enabled? + * Should the diff editor enable code lens? * Defaults to false. */ - originalCodeLens?: boolean; + diffCodeLens?: boolean; /** - * Modified editor should be have code lens enabled? - * Defaults to false. + * Is the diff editor inside another editor + * Defaults to false */ - modifiedCodeLens?: boolean; + isInEmbeddedEditor?: boolean; + /** + * Control the wrapping of the diff editor. + */ + diffWordWrap?: 'off' | 'on' | 'inherit'; } /** @@ -3656,6 +3696,11 @@ declare namespace monaco.editor { * Defaults to `horizontalScrollbarSize`. */ horizontalSliderSize?: number; + /** + * Scroll gutter clicks move by page vs jump to position. + * Defaults to false. + */ + scrollByPage?: boolean; } export interface InternalEditorScrollbarOptions { @@ -3671,6 +3716,7 @@ declare namespace monaco.editor { readonly horizontalSliderSize: number; readonly verticalScrollbarSize: number; readonly verticalSliderSize: number; + readonly scrollByPage: boolean; } /** @@ -3705,6 +3751,10 @@ declare namespace monaco.editor { * Enable or disable the suggest status bar. */ showStatusBar?: boolean; + /** + * Show details inline with the label. Defaults to true. + */ + showInlineDetails?: boolean; /** * Show method-suggestions. */ @@ -3817,6 +3867,12 @@ declare namespace monaco.editor { export type InternalSuggestOptions = Readonly>; + export interface ISmartSelectOptions { + selectLeadingAndTrailingWhitespace?: boolean; + } + + export type SmartSelectOptions = Readonly>; + /** * Describes how to indent wrapped lines. */ @@ -3859,113 +3915,119 @@ declare namespace monaco.editor { automaticLayout = 9, autoSurround = 10, codeLens = 11, - colorDecorators = 12, - columnSelection = 13, - comments = 14, - contextmenu = 15, - copyWithSyntaxHighlighting = 16, - cursorBlinking = 17, - cursorSmoothCaretAnimation = 18, - cursorStyle = 19, - cursorSurroundingLines = 20, - cursorSurroundingLinesStyle = 21, - cursorWidth = 22, - disableLayerHinting = 23, - disableMonospaceOptimizations = 24, - dragAndDrop = 25, - emptySelectionClipboard = 26, - extraEditorClassName = 27, - fastScrollSensitivity = 28, - find = 29, - fixedOverflowWidgets = 30, - folding = 31, - foldingStrategy = 32, - foldingHighlight = 33, - unfoldOnClickAfterEndOfLine = 34, - fontFamily = 35, - fontInfo = 36, - fontLigatures = 37, - fontSize = 38, - fontWeight = 39, - formatOnPaste = 40, - formatOnType = 41, - glyphMargin = 42, - gotoLocation = 43, - hideCursorInOverviewRuler = 44, - highlightActiveIndentGuide = 45, - hover = 46, - inDiffEditor = 47, - letterSpacing = 48, - lightbulb = 49, - lineDecorationsWidth = 50, - lineHeight = 51, - lineNumbers = 52, - lineNumbersMinChars = 53, - links = 54, - matchBrackets = 55, - minimap = 56, - mouseStyle = 57, - mouseWheelScrollSensitivity = 58, - mouseWheelZoom = 59, - multiCursorMergeOverlapping = 60, - multiCursorModifier = 61, - multiCursorPaste = 62, - occurrencesHighlight = 63, - overviewRulerBorder = 64, - overviewRulerLanes = 65, - padding = 66, - parameterHints = 67, - peekWidgetDefaultFocus = 68, - definitionLinkOpensInPeek = 69, - quickSuggestions = 70, - quickSuggestionsDelay = 71, - readOnly = 72, - renameOnType = 73, - renderControlCharacters = 74, - renderIndentGuides = 75, - renderFinalNewline = 76, - renderLineHighlight = 77, - renderLineHighlightOnlyWhenFocus = 78, - renderValidationDecorations = 79, - renderWhitespace = 80, - revealHorizontalRightPadding = 81, - roundedSelection = 82, - rulers = 83, - scrollbar = 84, - scrollBeyondLastColumn = 85, - scrollBeyondLastLine = 86, - scrollPredominantAxis = 87, - selectionClipboard = 88, - selectionHighlight = 89, - selectOnLineNumbers = 90, - showFoldingControls = 91, - showUnused = 92, - snippetSuggestions = 93, - smoothScrolling = 94, - stopRenderingLineAfter = 95, - suggest = 96, - suggestFontSize = 97, - suggestLineHeight = 98, - suggestOnTriggerCharacters = 99, - suggestSelection = 100, - tabCompletion = 101, - tabIndex = 102, - unusualLineTerminators = 103, - useTabStops = 104, - wordSeparators = 105, - wordWrap = 106, - wordWrapBreakAfterCharacters = 107, - wordWrapBreakBeforeCharacters = 108, - wordWrapColumn = 109, - wordWrapMinified = 110, - wrappingIndent = 111, - wrappingStrategy = 112, - showDeprecated = 113, - editorClassName = 114, - pixelRatio = 115, - tabFocusMode = 116, - layoutInfo = 117, - wrappingInfo = 118 + codeLensFontFamily = 12, + codeLensFontSize = 13, + colorDecorators = 14, + columnSelection = 15, + comments = 16, + contextmenu = 17, + copyWithSyntaxHighlighting = 18, + cursorBlinking = 19, + cursorSmoothCaretAnimation = 20, + cursorStyle = 21, + cursorSurroundingLines = 22, + cursorSurroundingLinesStyle = 23, + cursorWidth = 24, + disableLayerHinting = 25, + disableMonospaceOptimizations = 26, + dragAndDrop = 27, + emptySelectionClipboard = 28, + extraEditorClassName = 29, + fastScrollSensitivity = 30, + find = 31, + fixedOverflowWidgets = 32, + folding = 33, + foldingStrategy = 34, + foldingHighlight = 35, + unfoldOnClickAfterEndOfLine = 36, + fontFamily = 37, + fontInfo = 38, + fontLigatures = 39, + fontSize = 40, + fontWeight = 41, + formatOnPaste = 42, + formatOnType = 43, + glyphMargin = 44, + gotoLocation = 45, + hideCursorInOverviewRuler = 46, + highlightActiveIndentGuide = 47, + hover = 48, + inDiffEditor = 49, + letterSpacing = 50, + lightbulb = 51, + lineDecorationsWidth = 52, + lineHeight = 53, + lineNumbers = 54, + lineNumbersMinChars = 55, + linkedEditing = 56, + links = 57, + matchBrackets = 58, + minimap = 59, + mouseStyle = 60, + mouseWheelScrollSensitivity = 61, + mouseWheelZoom = 62, + multiCursorMergeOverlapping = 63, + multiCursorModifier = 64, + multiCursorPaste = 65, + occurrencesHighlight = 66, + overviewRulerBorder = 67, + overviewRulerLanes = 68, + padding = 69, + parameterHints = 70, + peekWidgetDefaultFocus = 71, + definitionLinkOpensInPeek = 72, + quickSuggestions = 73, + quickSuggestionsDelay = 74, + readOnly = 75, + renameOnType = 76, + renderControlCharacters = 77, + renderIndentGuides = 78, + renderFinalNewline = 79, + renderLineHighlight = 80, + renderLineHighlightOnlyWhenFocus = 81, + renderValidationDecorations = 82, + renderWhitespace = 83, + revealHorizontalRightPadding = 84, + roundedSelection = 85, + rulers = 86, + scrollbar = 87, + scrollBeyondLastColumn = 88, + scrollBeyondLastLine = 89, + scrollPredominantAxis = 90, + selectionClipboard = 91, + selectionHighlight = 92, + selectOnLineNumbers = 93, + showFoldingControls = 94, + showUnused = 95, + snippetSuggestions = 96, + smartSelect = 97, + smoothScrolling = 98, + stickyTabStops = 99, + stopRenderingLineAfter = 100, + suggest = 101, + suggestFontSize = 102, + suggestLineHeight = 103, + suggestOnTriggerCharacters = 104, + suggestSelection = 105, + tabCompletion = 106, + tabIndex = 107, + unusualLineTerminators = 108, + useTabStops = 109, + wordSeparators = 110, + wordWrap = 111, + wordWrapBreakAfterCharacters = 112, + wordWrapBreakBeforeCharacters = 113, + wordWrapColumn = 114, + wordWrapOverride1 = 115, + wordWrapOverride2 = 116, + wrappingIndent = 117, + wrappingStrategy = 118, + showDeprecated = 119, + editorClassName = 120, + pixelRatio = 121, + tabFocusMode = 122, + layoutInfo = 123, + wrappingInfo = 124 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -3979,7 +4041,10 @@ declare namespace monaco.editor { autoIndent: IEditorOption; automaticLayout: IEditorOption; autoSurround: IEditorOption; + stickyTabStops: IEditorOption; codeLens: IEditorOption; + codeLensFontFamily: IEditorOption; + codeLensFontSize: IEditorOption; colorDecorators: IEditorOption; columnSelection: IEditorOption; comments: IEditorOption; @@ -4022,6 +4087,7 @@ declare namespace monaco.editor { lineHeight: IEditorOption; lineNumbers: IEditorOption; lineNumbersMinChars: IEditorOption; + linkedEditing: IEditorOption; links: IEditorOption; matchBrackets: IEditorOption; minimap: IEditorOption; @@ -4063,6 +4129,7 @@ declare namespace monaco.editor { showUnused: IEditorOption; showDeprecated: IEditorOption; snippetSuggestions: IEditorOption; + smartSelect: IEditorOption; smoothScrolling: IEditorOption; stopRenderingLineAfter: IEditorOption; suggest: IEditorOption; @@ -4079,7 +4146,8 @@ declare namespace monaco.editor { wordWrapBreakAfterCharacters: IEditorOption; wordWrapBreakBeforeCharacters: IEditorOption; wordWrapColumn: IEditorOption; - wordWrapMinified: IEditorOption; + wordWrapOverride1: IEditorOption; + wordWrapOverride2: IEditorOption; wrappingIndent: IEditorOption; wrappingStrategy: IEditorOption; editorClassName: IEditorOption; @@ -4676,9 +4744,13 @@ declare namespace monaco.editor { */ executeCommand(source: string | null | undefined, command: ICommand): void; /** - * Push an "undo stop" in the undo-redo stack. + * Create an "undo stop" in the undo-redo stack. */ pushUndoStop(): boolean; + /** + * Remove the "undo stop" in the undo-redo stack. + */ + popUndoStop(): boolean; /** * Execute edits on the editor. * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. @@ -4997,6 +5069,10 @@ declare namespace monaco.languages { * Tokenize a line given the state at the beginning of the line. */ tokenizeEncoded(line: string, state: IState): IEncodedLineTokens; + /** + * Tokenize a line given the state at the beginning of the line. + */ + tokenize?(line: string, state: IState): ILineTokens; } /** @@ -5040,9 +5116,9 @@ declare namespace monaco.languages { export function registerDocumentHighlightProvider(languageId: string, provider: DocumentHighlightProvider): IDisposable; /** - * Register an on type rename provider. + * Register an linked editing range provider. */ - export function registerOnTypeRenameProvider(languageId: string, provider: OnTypeRenameProvider): IDisposable; + export function registerLinkedEditingRangeProvider(languageId: string, provider: LinkedEditingRangeProvider): IDisposable; /** * Register a definition provider (used by e.g. go to definition). @@ -5786,18 +5862,30 @@ declare namespace monaco.languages { } /** - * The rename provider interface defines the contract between extensions and - * the live-rename feature. + * The linked editing range provider interface defines the contract between extensions and + * the linked editing feature. */ - export interface OnTypeRenameProvider { - wordPattern?: RegExp; + export interface LinkedEditingRangeProvider { /** - * Provide a list of ranges that can be live-renamed together. + * Provide a list of ranges that can be edited together. */ - provideOnTypeRenameRanges(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult<{ - ranges: IRange[]; - wordPattern?: RegExp; - }>; + provideLinkedEditingRanges(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * Represents a list of ranges that can be edited together along with a word pattern to describe valid contents. + */ + export interface LinkedEditingRanges { + /** + * A list of ranges that can be edited together. The ranges must have + * identical length and text content. The ranges cannot overlap + */ + ranges: IRange[]; + /** + * An optional word pattern that describes valid contents for the given ranges. + * If no pattern is provided, the language configuration's word pattern will be used. + */ + wordPattern?: RegExp; } /** @@ -6195,12 +6283,6 @@ declare namespace monaco.languages { needsConfirmation: boolean; label: string; description?: string; - iconPath?: { - id: string; - } | Uri | { - light: Uri; - dark: Uri; - }; } export interface WorkspaceFileEditOptions { @@ -6208,6 +6290,10 @@ declare namespace monaco.languages { ignoreIfNotExists?: boolean; ignoreIfExists?: boolean; recursive?: boolean; + copy?: boolean; + folder?: boolean; + skipTrashBin?: boolean; + maxSize?: number; } export interface WorkspaceFileEdit { diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 9da3b3d19..1344ea900 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -16,10 +16,12 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { isWindows, isLinux } from 'vs/base/common/platform'; export function createAndFillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, isPrimaryGroup?: (group: string) => boolean): IDisposable { const groups = menu.getActions(options); - const useAlternativeActions = ModifierKeyEmitter.getInstance().keyStatus.altKey; + const modifierKeyEmitter = ModifierKeyEmitter.getInstance(); + const useAlternativeActions = modifierKeyEmitter.keyStatus.altKey || ((isWindows || isLinux) && modifierKeyEmitter.keyStatus.shiftKey); fillInActions(groups, target, useAlternativeActions, isPrimaryGroup); return asDisposable(groups); } @@ -102,7 +104,7 @@ export class MenuEntryActionViewItem extends ActionViewItem { let mouseOver = false; - let alternativeKeyDown = this._altKey.keyStatus.altKey; + let alternativeKeyDown = this._altKey.keyStatus.altKey || ((isWindows || isLinux) && this._altKey.keyStatus.shiftKey); const updateAltState = () => { const wantsAltCommand = mouseOver && alternativeKeyDown; @@ -116,7 +118,7 @@ export class MenuEntryActionViewItem extends ActionViewItem { if (this._action.alt) { this._register(this._altKey.event(value => { - alternativeKeyDown = value.altKey; + alternativeKeyDown = value.altKey || ((isWindows || isLinux) && value.shiftKey); updateAltState(); })); } diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index aa382fdd5..c06a3511b 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -95,7 +95,7 @@ export class MenuId { static readonly MenubarSwitchGroupMenu = new MenuId('MenubarSwitchGroupMenu'); static readonly MenubarTerminalMenu = new MenuId('MenubarTerminalMenu'); static readonly MenubarViewMenu = new MenuId('MenubarViewMenu'); - static readonly MenubarWebNavigationMenu = new MenuId('MenubarWebNavigationMenu'); + static readonly MenubarHomeMenu = new MenuId('MenubarHomeMenu'); static readonly OpenEditorsContext = new MenuId('OpenEditorsContext'); static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext'); static readonly SCMChangeContext = new MenuId('SCMChangeContext'); diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index a7e9a9ee3..42f5fb335 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -57,9 +57,6 @@ export class BackupMainService implements IBackupMainService { // read empty workspaces backups first if (backups.emptyWorkspaceInfos) { this.emptyWindows = await this.validateEmptyWorkspaces(backups.emptyWorkspaceInfos); - } else if (Array.isArray(backups.emptyWorkspaces)) { - // read legacy entries - this.emptyWindows = await this.validateEmptyWorkspaces(backups.emptyWorkspaces.map(emptyWindow => ({ backupFolder: emptyWindow }))); } // read workspace backups @@ -67,8 +64,6 @@ export class BackupMainService implements IBackupMainService { try { if (Array.isArray(backups.rootURIWorkspaces)) { rootWorkspaces = backups.rootURIWorkspaces.map(workspace => ({ workspace: { id: workspace.id, configPath: URI.parse(workspace.configURIPath) }, remoteAuthority: workspace.remoteAuthority })); - } else if (Array.isArray(backups.rootWorkspaces)) { - rootWorkspaces = backups.rootWorkspaces.map(workspace => ({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } })); } } catch (e) { // ignore URI parsing exceptions @@ -81,18 +76,6 @@ export class BackupMainService implements IBackupMainService { try { if (Array.isArray(backups.folderURIWorkspaces)) { workspaceFolders = backups.folderURIWorkspaces.map(folder => URI.parse(folder)); - } else if (Array.isArray(backups.folderWorkspaces)) { - // migrate legacy folder paths - workspaceFolders = []; - for (const folderPath of backups.folderWorkspaces) { - const oldFolderHash = this.getLegacyFolderHash(folderPath); - const folderUri = URI.file(folderPath); - const newFolderHash = this.getFolderHash(folderUri); - if (newFolderHash !== oldFolderHash) { - await this.moveBackupFolder(this.getBackupPath(newFolderHash), this.getBackupPath(oldFolderHash)); - } - workspaceFolders.push(folderUri); - } } } catch (e) { // ignore URI parsing exceptions @@ -174,23 +157,6 @@ export class BackupMainService implements IBackupMainService { } } - private async moveBackupFolder(backupPath: string, moveFromPath: string): Promise { - - // Target exists: make sure to convert existing backups to empty window backups - if (await exists(backupPath)) { - await this.convertToEmptyWindowBackup(backupPath); - } - - // When we have data to migrate from, move it over to the target location - if (await exists(moveFromPath)) { - try { - await rename(moveFromPath, backupPath); - } catch (ex) { - this.logService.error(`Backup: Could not move backup folder to new location: ${ex.toString()}`); - } - } - } - unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void { const id = workspace.id; const index = this.workspaces.findIndex(workspace => workspace.workspace.id === id); @@ -475,8 +441,7 @@ export class BackupMainService implements IBackupMainService { return { rootURIWorkspaces: this.workspaces.map(workspace => ({ id: workspace.workspace.id, configURIPath: workspace.workspace.configPath.toString(), remoteAuthority: workspace.remoteAuthority })), folderURIWorkspaces: this.folders.map(folder => folder.toString()), - emptyWorkspaceInfos: this.emptyWindows, - emptyWorkspaces: this.emptyWindows.map(emptyWindow => emptyWindow.backupFolder) + emptyWorkspaceInfos: this.emptyWindows }; } @@ -496,8 +461,4 @@ export class BackupMainService implements IBackupMainService { return crypto.createHash('md5').update(key).digest('hex'); } - - protected getLegacyFolderHash(folderPath: string): string { - return crypto.createHash('md5').update(platform.isLinux ? folderPath : folderPath.toLowerCase()).digest('hex'); - } } diff --git a/src/vs/platform/backup/node/backup.ts b/src/vs/platform/backup/node/backup.ts index d40d01b18..b054be3ee 100644 --- a/src/vs/platform/backup/node/backup.ts +++ b/src/vs/platform/backup/node/backup.ts @@ -9,11 +9,6 @@ export interface IBackupWorkspacesFormat { rootURIWorkspaces: ISerializedWorkspace[]; folderURIWorkspaces: string[]; emptyWorkspaceInfos: IEmptyWindowBackupInfo[]; - - // deprecated - folderWorkspaces?: string[]; // use folderURIWorkspaces instead - emptyWorkspaces?: string[]; - rootWorkspaces?: { id: string, configPath: string }[]; // use rootURIWorkspaces instead } export interface IEmptyWindowBackupInfo { diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index 4db502cca..42e2636d8 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -53,10 +53,6 @@ suite('BackupMainService', () => { getFolderHash(folderUri: URI): string { return super.getFolderHash(folderUri); } - - toLegacyBackupPath(folderPath: string): string { - return path.join(this.backupHome, super.getLegacyFolderHash(folderPath)); - } } function toWorkspace(path: string): IWorkspaceIdentifier { @@ -266,68 +262,6 @@ suite('BackupMainService', () => { assert.equal(1, fs.readdirSync(path.join(backupHome, emptyBackups[0].backupFolder!)).length); }); - suite('migrate path to URI', () => { - - test('migration folder path to URI makes sure to preserve existing backups', async () => { - let path1 = path.join(parentDir, 'folder1'); - let path2 = path.join(parentDir, 'FOLDER2'); - let uri1 = URI.file(path1); - let uri2 = URI.file(path2); - - if (!fs.existsSync(path1)) { - fs.mkdirSync(path1); - } - if (!fs.existsSync(path2)) { - fs.mkdirSync(path2); - } - const backupFolder1 = service.toLegacyBackupPath(path1); - if (!fs.existsSync(backupFolder1)) { - fs.mkdirSync(backupFolder1); - fs.mkdirSync(path.join(backupFolder1, Schemas.file)); - await pfs.writeFile(path.join(backupFolder1, Schemas.file, 'unsaved1.txt'), 'Legacy'); - } - const backupFolder2 = service.toLegacyBackupPath(path2); - if (!fs.existsSync(backupFolder2)) { - fs.mkdirSync(backupFolder2); - fs.mkdirSync(path.join(backupFolder2, Schemas.file)); - await pfs.writeFile(path.join(backupFolder2, Schemas.file, 'unsaved2.txt'), 'Legacy'); - } - - const workspacesJson = { rootWorkspaces: [], folderWorkspaces: [path1, path2], emptyWorkspaces: [] }; - await pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)); - await service.initialize(); - const content = await pfs.readFile(backupWorkspacesPath, 'utf-8'); - const json = (JSON.parse(content)); - assert.deepEqual(json.folderURIWorkspaces, [uri1.toString(), uri2.toString()]); - const newBackupFolder1 = service.toBackupPath(uri1); - assert.ok(fs.existsSync(path.join(newBackupFolder1, Schemas.file, 'unsaved1.txt'))); - const newBackupFolder2 = service.toBackupPath(uri2); - assert.ok(fs.existsSync(path.join(newBackupFolder2, Schemas.file, 'unsaved2.txt'))); - }); - - test('migrate storage file', async () => { - let folderPath = path.join(parentDir, 'f1'); - ensureFolderExists(URI.file(folderPath)); - const backupFolderPath = service.toLegacyBackupPath(folderPath); - await createBackupFolder(backupFolderPath); - - let workspacePath = path.join(parentDir, 'f2.code-workspace'); - const workspace = toWorkspace(workspacePath); - await ensureWorkspaceExists(workspace); - - const workspacesJson = { rootWorkspaces: [{ id: workspace.id, configPath: workspacePath }], folderWorkspaces: [folderPath], emptyWorkspaces: [] }; - await pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)); - await service.initialize(); - const content = await pfs.readFile(backupWorkspacesPath, 'utf-8'); - const json = (JSON.parse(content)); - assert.deepEqual(json.folderURIWorkspaces, [URI.file(folderPath).toString()]); - assert.deepEqual(json.rootURIWorkspaces, [{ id: workspace.id, configURIPath: URI.file(workspacePath).toString() }]); - - assertEqualUris(service.getWorkspaceBackups().map(window => window.workspace.configPath), [workspace.configPath]); - }); - }); - - suite('loadSync', () => { test('getFolderBackupPaths() should return [] when workspaces.json doesn\'t exist', () => { assertEqualUris(service.getFolderBackupPaths(), []); @@ -650,12 +584,12 @@ suite('BackupMainService', () => { const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8'); const json = (JSON.parse(buffer)); - assert.deepEqual(json.emptyWorkspaces, ['bar']); + assert.deepEqual(json.emptyWorkspaceInfos, [{ backupFolder: 'bar' }]); service.unregisterEmptyWindowBackupSync('bar'); const content = await pfs.readFile(backupWorkspacesPath, 'utf-8'); const json2 = (JSON.parse(content)); - assert.deepEqual(json2.emptyWorkspaces, []); + assert.deepEqual(json2.emptyWorkspaceInfos, []); }); test('should fail gracefully when removing a path that doesn\'t exist', async () => { diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 7f14056f1..d5b333f73 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -114,7 +114,7 @@ export interface IConfigurationService { inspect(key: string, overrides?: IConfigurationOverrides): IConfigurationValue; - reloadConfiguration(folder?: IWorkspaceFolder): Promise; + reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise; keys(): { default: string[]; diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 3a7eaf968..07680c0f2 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -478,6 +478,9 @@ const configurationRegistry = new ConfigurationRegistry(); Registry.add(Extensions.Configuration, configurationRegistry); export function validateProperty(property: string): string | null { + if (!property.trim()) { + return nls.localize('config.property.empty', "Cannot register an empty property"); + } if (OVERRIDE_PROPERTY_PATTERN.test(property)) { return nls.localize('config.property.languageDefault', "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", property); } diff --git a/src/vs/platform/configuration/test/common/configurationModels.test.ts b/src/vs/platform/configuration/test/common/configurationModels.test.ts index 1ff040dce..c743e36b8 100644 --- a/src/vs/platform/configuration/test/common/configurationModels.test.ts +++ b/src/vs/platform/configuration/test/common/configurationModels.test.ts @@ -7,9 +7,10 @@ import { ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { URI } from 'vs/base/common/uri'; -import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { join } from 'vs/base/common/path'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; suite('ConfigurationModel', () => { diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index b4d8e42cf..21320c0d6 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; +import { Iterable } from 'vs/base/common/iterator'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { TernarySearchTree } from 'vs/base/common/map'; import { distinct } from 'vs/base/common/objects'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -90,10 +92,9 @@ class NullContext extends Context { } class ConfigAwareContextValuesContainer extends Context { - private static readonly _keyPrefix = 'config.'; - private readonly _values = new Map(); + private readonly _values = TernarySearchTree.forConfigKeys(); private readonly _listener: IDisposable; constructor( @@ -106,18 +107,26 @@ class ConfigAwareContextValuesContainer extends Context { this._listener = this._configurationService.onDidChangeConfiguration(event => { if (event.source === ConfigurationTarget.DEFAULT) { // new setting, reset everything - const allKeys = Array.from(this._values.keys()); + const allKeys = Array.from(Iterable.map(this._values, ([k]) => k)); this._values.clear(); emitter.fire(new ArrayContextKeyChangeEvent(allKeys)); } else { const changedKeys: string[] = []; for (const configKey of event.affectedKeys) { const contextKey = `config.${configKey}`; + + const cachedItems = this._values.findSuperstr(contextKey); + if (cachedItems !== undefined) { + changedKeys.push(...Iterable.map(cachedItems, ([key]) => key)); + this._values.deleteSuperstr(contextKey); + } + if (this._values.has(contextKey)) { - this._values.delete(contextKey); changedKeys.push(contextKey); + this._values.delete(contextKey); } } + emitter.fire(new ArrayContextKeyChangeEvent(changedKeys)); } }); @@ -149,6 +158,8 @@ class ConfigAwareContextValuesContainer extends Context { default: if (Array.isArray(configValue)) { value = JSON.stringify(configValue); + } else { + value = configValue; } } @@ -405,6 +416,9 @@ class ScopedContextKeyService extends AbstractContextKeyService { if (domNode) { this._domNode = domNode; + if (this._domNode.hasAttribute(KEYBINDING_CONTEXT_ATTR)) { + console.error('Element already has context attribute'); + } this._domNode.setAttribute(KEYBINDING_CONTEXT_ATTR, String(this._myContextId)); } } diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index dad27b174..ea7c83876 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -6,8 +6,9 @@ import { Event } from 'vs/base/common/event'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { isMacintosh, isLinux, isWindows, isWeb } from 'vs/base/common/platform'; +import { userAgent, isMacintosh, isLinux, isWindows, isWeb } from 'vs/base/common/platform'; +let _userAgent = userAgent || ''; const STATIC_VALUES = new Map(); STATIC_VALUES.set('false', false); STATIC_VALUES.set('true', true); @@ -16,6 +17,11 @@ STATIC_VALUES.set('isLinux', isLinux); STATIC_VALUES.set('isWindows', isWindows); STATIC_VALUES.set('isWeb', isWeb); STATIC_VALUES.set('isMacNative', isMacintosh && !isWeb); +STATIC_VALUES.set('isEdge', _userAgent.indexOf('Edg/') >= 0); +STATIC_VALUES.set('isFirefox', _userAgent.indexOf('Firefox') >= 0); +STATIC_VALUES.set('isChrome', _userAgent.indexOf('Chrome') >= 0); +STATIC_VALUES.set('isSafari', _userAgent.indexOf('Safari') >= 0); +STATIC_VALUES.set('isIPad', _userAgent.indexOf('iPad') >= 0); const hasOwnProperty = Object.prototype.hasOwnProperty; @@ -32,6 +38,10 @@ export const enum ContextKeyExprType { Or = 9, In = 10, NotIn = 11, + Greater = 12, + GreaterEquals = 13, + Smaller = 14, + SmallerEquals = 15, } export interface IContextKeyExprMapper { @@ -39,6 +49,10 @@ export interface IContextKeyExprMapper { mapNot(key: string): ContextKeyExpression; mapEquals(key: string, value: any): ContextKeyExpression; mapNotEquals(key: string, value: any): ContextKeyExpression; + mapGreater(key: string, value: any): ContextKeyExpression; + mapGreaterEquals(key: string, value: any): ContextKeyExpression; + mapSmaller(key: string, value: any): ContextKeyExpression; + mapSmallerEquals(key: string, value: any): ContextKeyExpression; mapRegex(key: string, regexp: RegExp | null): ContextKeyRegexExpr; mapIn(key: string, valueKey: string): ContextKeyInExpr; } @@ -57,7 +71,9 @@ export interface IContextKeyExpression { export type ContextKeyExpression = ( ContextKeyFalseExpr | ContextKeyTrueExpr | ContextKeyDefinedExpr | ContextKeyNotExpr | ContextKeyEqualsExpr | ContextKeyNotEqualsExpr | ContextKeyRegexExpr - | ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr | ContextKeyInExpr | ContextKeyNotInExpr + | ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr | ContextKeyInExpr + | ContextKeyNotInExpr | ContextKeyGreaterExpr | ContextKeyGreaterEqualsExpr + | ContextKeySmallerExpr | ContextKeySmallerEqualsExpr ); export abstract class ContextKeyExpr { @@ -102,6 +118,14 @@ export abstract class ContextKeyExpr { return ContextKeyOrExpr.create(expr); } + public static greater(key: string, value: any): ContextKeyExpression { + return ContextKeyGreaterExpr.create(key, value); + } + + public static less(key: string, value: any): ContextKeyExpression { + return ContextKeySmallerExpr.create(key, value); + } + public static deserialize(serialized: string | null | undefined, strict: boolean = false): ContextKeyExpression | undefined { if (!serialized) { return undefined; @@ -143,6 +167,26 @@ export abstract class ContextKeyExpr { return ContextKeyInExpr.create(pieces[0].trim(), pieces[1].trim()); } + if (/^[^<=>]+>=[^<=>]+$/.test(serializedOne)) { + const pieces = serializedOne.split('>='); + return ContextKeyGreaterEqualsExpr.create(pieces[0].trim(), pieces[1].trim()); + } + + if (/^[^<=>]+>[^<=>]+$/.test(serializedOne)) { + const pieces = serializedOne.split('>'); + return ContextKeyGreaterExpr.create(pieces[0].trim(), pieces[1].trim()); + } + + if (/^[^<=>]+<=[^<=>]+$/.test(serializedOne)) { + const pieces = serializedOne.split('<='); + return ContextKeySmallerEqualsExpr.create(pieces[0].trim(), pieces[1].trim()); + } + + if (/^[^<=>]+<[^<=>]+$/.test(serializedOne)) { + const pieces = serializedOne.split('<'); + return ContextKeySmallerExpr.create(pieces[0].trim(), pieces[1].trim()); + } + if (/^\!\s*/.test(serializedOne)) { return ContextKeyNotExpr.create(serializedOne.substr(1).trim()); } @@ -302,13 +346,7 @@ export class ContextKeyDefinedExpr implements IContextKeyExpression { if (other.type !== this.type) { return this.type - other.type; } - if (this.key < other.key) { - return -1; - } - if (this.key > other.key) { - return 1; - } - return 0; + return cmp1(this.key, other.key); } public equals(other: ContextKeyExpression): boolean { @@ -362,19 +400,7 @@ export class ContextKeyEqualsExpr implements IContextKeyExpression { if (other.type !== this.type) { return this.type - other.type; } - if (this.key < other.key) { - return -1; - } - if (this.key > other.key) { - return 1; - } - if (this.value < other.value) { - return -1; - } - if (this.value > other.value) { - return 1; - } - return 0; + return cmp2(this.key, this.value, other.key, other.value); } public equals(other: ContextKeyExpression): boolean { @@ -391,7 +417,7 @@ export class ContextKeyEqualsExpr implements IContextKeyExpression { } public serialize(): string { - return this.key + ' == \'' + this.value + '\''; + return `${this.key} == '${this.value}'`; } public keys(): string[] { @@ -422,19 +448,7 @@ export class ContextKeyInExpr implements IContextKeyExpression { if (other.type !== this.type) { return this.type - other.type; } - if (this.key < other.key) { - return -1; - } - if (this.key > other.key) { - return 1; - } - if (this.valueKey < other.valueKey) { - return -1; - } - if (this.valueKey > other.valueKey) { - return 1; - } - return 0; + return cmp2(this.key, this.valueKey, other.key, other.valueKey); } public equals(other: ContextKeyExpression): boolean { @@ -460,7 +474,7 @@ export class ContextKeyInExpr implements IContextKeyExpression { } public serialize(): string { - return this.key + ' in \'' + this.valueKey + '\''; + return `${this.key} in '${this.valueKey}'`; } public keys(): string[] { @@ -549,19 +563,7 @@ export class ContextKeyNotEqualsExpr implements IContextKeyExpression { if (other.type !== this.type) { return this.type - other.type; } - if (this.key < other.key) { - return -1; - } - if (this.key > other.key) { - return 1; - } - if (this.value < other.value) { - return -1; - } - if (this.value > other.value) { - return 1; - } - return 0; + return cmp2(this.key, this.value, other.key, other.value); } public equals(other: ContextKeyExpression): boolean { @@ -578,7 +580,7 @@ export class ContextKeyNotEqualsExpr implements IContextKeyExpression { } public serialize(): string { - return this.key + ' != \'' + this.value + '\''; + return `${this.key} != '${this.value}'`; } public keys(): string[] { @@ -613,13 +615,7 @@ export class ContextKeyNotExpr implements IContextKeyExpression { if (other.type !== this.type) { return this.type - other.type; } - if (this.key < other.key) { - return -1; - } - if (this.key > other.key) { - return 1; - } - return 0; + return cmp1(this.key, other.key); } public equals(other: ContextKeyExpression): boolean { @@ -634,7 +630,7 @@ export class ContextKeyNotExpr implements IContextKeyExpression { } public serialize(): string { - return '!' + this.key; + return `!${this.key}`; } public keys(): string[] { @@ -650,6 +646,200 @@ export class ContextKeyNotExpr implements IContextKeyExpression { } } +export class ContextKeyGreaterExpr implements IContextKeyExpression { + + public static create(key: string, value: any): ContextKeyExpression { + return new ContextKeyGreaterExpr(key, value); + } + + public readonly type = ContextKeyExprType.Greater; + + private constructor( + private readonly key: string, + private readonly value: any + ) { } + + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + return cmp2(this.key, this.value, other.key, other.value); + } + + public equals(other: ContextKeyExpression): boolean { + if (other.type === this.type) { + return (this.key === other.key && this.value === other.value); + } + return false; + } + + public evaluate(context: IContext): boolean { + return (parseFloat(context.getValue(this.key)) > parseFloat(this.value)); + } + + public serialize(): string { + return `${this.key} > ${this.value}`; + } + + public keys(): string[] { + return [this.key]; + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression { + return mapFnc.mapGreater(this.key, this.value); + } + + public negate(): ContextKeyExpression { + return ContextKeySmallerEqualsExpr.create(this.key, this.value); + } +} + +export class ContextKeyGreaterEqualsExpr implements IContextKeyExpression { + + public static create(key: string, value: any): ContextKeyExpression { + return new ContextKeyGreaterEqualsExpr(key, value); + } + + public readonly type = ContextKeyExprType.GreaterEquals; + + private constructor( + private readonly key: string, + private readonly value: any + ) { } + + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + return cmp2(this.key, this.value, other.key, other.value); + } + + public equals(other: ContextKeyExpression): boolean { + if (other.type === this.type) { + return (this.key === other.key && this.value === other.value); + } + return false; + } + + public evaluate(context: IContext): boolean { + return (parseFloat(context.getValue(this.key)) >= parseFloat(this.value)); + } + + public serialize(): string { + return `${this.key} >= ${this.value}`; + } + + public keys(): string[] { + return [this.key]; + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression { + return mapFnc.mapGreaterEquals(this.key, this.value); + } + + public negate(): ContextKeyExpression { + return ContextKeySmallerExpr.create(this.key, this.value); + } +} + +export class ContextKeySmallerExpr implements IContextKeyExpression { + + public static create(key: string, value: any): ContextKeyExpression { + return new ContextKeySmallerExpr(key, value); + } + + public readonly type = ContextKeyExprType.Smaller; + + private constructor( + private readonly key: string, + private readonly value: any + ) { + } + + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + return cmp2(this.key, this.value, other.key, other.value); + } + + public equals(other: ContextKeyExpression): boolean { + if (other.type === this.type) { + return (this.key === other.key && this.value === other.value); + } + return false; + } + + public evaluate(context: IContext): boolean { + return (parseFloat(context.getValue(this.key)) < parseFloat(this.value)); + } + + public serialize(): string { + return `${this.key} < ${this.value}`; + } + + public keys(): string[] { + return [this.key]; + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression { + return mapFnc.mapSmaller(this.key, this.value); + } + + public negate(): ContextKeyExpression { + return ContextKeyGreaterEqualsExpr.create(this.key, this.value); + } +} + +export class ContextKeySmallerEqualsExpr implements IContextKeyExpression { + + public static create(key: string, value: any): ContextKeyExpression { + return new ContextKeySmallerEqualsExpr(key, value); + } + + public readonly type = ContextKeyExprType.SmallerEquals; + + private constructor( + private readonly key: string, + private readonly value: any + ) { + } + + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + return cmp2(this.key, this.value, other.key, other.value); + } + + public equals(other: ContextKeyExpression): boolean { + if (other.type === this.type) { + return (this.key === other.key && this.value === other.value); + } + return false; + } + + public evaluate(context: IContext): boolean { + return (parseFloat(context.getValue(this.key)) <= parseFloat(this.value)); + } + + public serialize(): string { + return `${this.key} <= ${this.value}`; + } + + public keys(): string[] { + return [this.key]; + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression { + return mapFnc.mapSmallerEquals(this.key, this.value); + } + + public negate(): ContextKeyExpression { + return ContextKeyGreaterExpr.create(this.key, this.value); + } +} + export class ContextKeyRegexExpr implements IContextKeyExpression { public static create(key: string, regexp: RegExp | null): ContextKeyRegexExpr { @@ -1143,3 +1333,29 @@ export interface IContextKeyService { } export const SET_CONTEXT_COMMAND_ID = 'setContext'; + +function cmp1(key1: string, key2: string): number { + if (key1 < key2) { + return -1; + } + if (key1 > key2) { + return 1; + } + return 0; +} + +function cmp2(key1: string, value1: any, key2: string, value2: any): number { + if (key1 < key2) { + return -1; + } + if (key1 > key2) { + return 1; + } + if (value1 < value2) { + return -1; + } + if (value1 > value2) { + return 1; + } + return 0; +} diff --git a/src/vs/platform/contextkey/test/common/contextkey.test.ts b/src/vs/platform/contextkey/test/common/contextkey.test.ts index 2912df4b0..91a548be6 100644 --- a/src/vs/platform/contextkey/test/common/contextkey.test.ts +++ b/src/vs/platform/contextkey/test/common/contextkey.test.ts @@ -186,4 +186,84 @@ suite('ContextKeyExpr', () => { ); assert.equal(actual!.equals(expected!), true); }); + + test('Greater, GreaterEquals, Smaller, SmallerEquals evaluate', () => { + function checkEvaluate(expr: string, ctx: any, expected: any): void { + const _expr = ContextKeyExpr.deserialize(expr)!; + assert.equal(_expr.evaluate(createContext(ctx)), expected); + } + + checkEvaluate('a>1', {}, false); + checkEvaluate('a>1', { a: 0 }, false); + checkEvaluate('a>1', { a: 1 }, false); + checkEvaluate('a>1', { a: 2 }, true); + checkEvaluate('a>1', { a: '0' }, false); + checkEvaluate('a>1', { a: '1' }, false); + checkEvaluate('a>1', { a: '2' }, true); + checkEvaluate('a>1', { a: 'a' }, false); + + checkEvaluate('a>10', { a: 2 }, false); + checkEvaluate('a>10', { a: 11 }, true); + checkEvaluate('a>10', { a: '11' }, true); + checkEvaluate('a>10', { a: '2' }, false); + checkEvaluate('a>10', { a: '11' }, true); + + checkEvaluate('a>1.1', { a: 1 }, false); + checkEvaluate('a>1.1', { a: 2 }, true); + checkEvaluate('a>1.1', { a: 11 }, true); + checkEvaluate('a>1.1', { a: '1.1' }, false); + checkEvaluate('a>1.1', { a: '2' }, true); + checkEvaluate('a>1.1', { a: '11' }, true); + + checkEvaluate('a>b', { a: 'b' }, false); + checkEvaluate('a>b', { a: 'c' }, false); + checkEvaluate('a>b', { a: 1000 }, false); + + checkEvaluate('a >= 2', { a: '1' }, false); + checkEvaluate('a >= 2', { a: '2' }, true); + checkEvaluate('a >= 2', { a: '3' }, true); + + checkEvaluate('a < 2', { a: '1' }, true); + checkEvaluate('a < 2', { a: '2' }, false); + checkEvaluate('a < 2', { a: '3' }, false); + + checkEvaluate('a <= 2', { a: '1' }, true); + checkEvaluate('a <= 2', { a: '2' }, true); + checkEvaluate('a <= 2', { a: '3' }, false); + }); + + test('Greater, GreaterEquals, Smaller, SmallerEquals negate', () => { + function checkNegate(expr: string, expected: string): void { + const a = ContextKeyExpr.deserialize(expr)!; + const b = a.negate(); + assert.equal(b.serialize(), expected); + } + + checkNegate('a>1', 'a <= 1'); + checkNegate('a>1.1', 'a <= 1.1'); + checkNegate('a>b', 'a <= b'); + + checkNegate('a>=1', 'a < 1'); + checkNegate('a>=1.1', 'a < 1.1'); + checkNegate('a>=b', 'a < b'); + + checkNegate('a<1', 'a >= 1'); + checkNegate('a<1.1', 'a >= 1.1'); + checkNegate('a= b'); + + checkNegate('a<=1', 'a > 1'); + checkNegate('a<=1.1', 'a > 1.1'); + checkNegate('a<=b', 'a > b'); + }); + + test('issue #111899: context keys can use `<` or `>` ', () => { + const actual = ContextKeyExpr.deserialize('editorTextFocus && vim.active && vim.use')!; + assert.ok(actual.equals( + ContextKeyExpr.and( + ContextKeyExpr.has('editorTextFocus'), + ContextKeyExpr.has('vim.active'), + ContextKeyExpr.has('vim.use'), + )! + )); + }); }); diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index d307c2927..1788290cc 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -55,6 +55,7 @@ export class ContextMenuHandler { getAnchor: () => delegate.getAnchor(), canRelayout: false, anchorAlignment: delegate.anchorAlignment, + anchorAxisAlignment: delegate.anchorAxisAlignment, render: (container) => { let className = delegate.getMenuClassName ? delegate.getMenuClassName() : ''; @@ -79,7 +80,7 @@ export class ContextMenuHandler { const menuDisposables = new DisposableStore(); const actionRunner = delegate.actionRunner || new ActionRunner(); - actionRunner.onDidBeforeRun(this.onActionRun, this, menuDisposables); + actionRunner.onBeforeRun(this.onActionRun, this, menuDisposables); actionRunner.onDidRun(this.onDidActionRun, this, menuDisposables); menu = new Menu(container, actions, { actionViewItemProvider: delegate.getActionViewItem, diff --git a/src/vs/platform/contextview/browser/contextView.ts b/src/vs/platform/contextview/browser/contextView.ts index dcba62aa3..b33d652cf 100644 --- a/src/vs/platform/contextview/browser/contextView.ts +++ b/src/vs/platform/contextview/browser/contextView.ts @@ -6,7 +6,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; -import { AnchorAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { AnchorAlignment, AnchorAxisAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; export const IContextViewService = createDecorator('contextViewService'); @@ -31,6 +31,7 @@ export interface IContextViewDelegate { onHide?(data?: any): void; focus?(): void; anchorAlignment?: AnchorAlignment; + anchorAxisAlignment?: AnchorAxisAlignment; } export const IContextMenuService = createDecorator('contextMenuService'); diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index b3b4815fc..2fe576f1b 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -18,6 +18,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Iterable } from 'vs/base/common/iterator'; import { Schemas } from 'vs/base/common/network'; +import { ByteSize } from 'vs/platform/files/common/files'; export const ID = 'diagnosticsService'; export const IDiagnosticsService = createDecorator(ID); @@ -163,12 +164,10 @@ function asSortedItems(items: Map): WorkspaceStatItem[] { } export function getMachineInfo(): IMachineInfo { - const MB = 1024 * 1024; - const GB = 1024 * MB; const machineInfo: IMachineInfo = { os: `${osLib.type()} ${osLib.arch()} ${osLib.release()}`, - memory: `${(osLib.totalmem() / GB).toFixed(2)}GB (${(osLib.freemem() / GB).toFixed(2)}GB free)`, + memory: `${(osLib.totalmem() / ByteSize.GB).toFixed(2)}GB (${(osLib.freemem() / ByteSize.GB).toFixed(2)}GB free)`, vmHint: `${Math.round((virtualMachineHint.value() * 100))}%`, }; @@ -238,9 +237,6 @@ export class DiagnosticsService implements IDiagnosticsService { } private formatEnvironment(info: IMainProcessInfo): string { - const MB = 1024 * 1024; - const GB = 1024 * MB; - const output: string[] = []; output.push(`Version: ${product.nameShort} ${product.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`); output.push(`OS Version: ${osLib.type()} ${osLib.arch()} ${osLib.release()}`); @@ -248,7 +244,7 @@ export class DiagnosticsService implements IDiagnosticsService { if (cpus && cpus.length > 0) { output.push(`CPUs: ${cpus[0].model} (${cpus.length} x ${cpus[0].speed})`); } - output.push(`Memory (System): ${(osLib.totalmem() / GB).toFixed(2)}GB (${(osLib.freemem() / GB).toFixed(2)}GB free)`); + output.push(`Memory (System): ${(osLib.totalmem() / ByteSize.GB).toFixed(2)}GB (${(osLib.freemem() / ByteSize.GB).toFixed(2)}GB free)`); if (!isWindows) { output.push(`Load (avg): ${osLib.loadavg().map(l => Math.round(l)).join(', ')}`); // only provided on Linux/macOS } @@ -493,8 +489,6 @@ export class DiagnosticsService implements IDiagnosticsService { private formatProcessItem(mainPid: number, mapPidToWindowTitle: Map, output: string[], item: ProcessItem, indent: number): void { const isRoot = (indent === 0); - const MB = 1024 * 1024; - // Format name with indent let name: string; if (isRoot) { @@ -508,7 +502,7 @@ export class DiagnosticsService implements IDiagnosticsService { } const memory = process.platform === 'win32' ? item.mem : (osLib.totalmem() * (item.mem / 100)); - output.push(`${item.load.toFixed(0).padStart(5, ' ')}\t${(memory / MB).toFixed(0).padStart(6, ' ')}\t${item.pid.toFixed(0).padStart(6, ' ')}\t${name}`); + output.push(`${item.load.toFixed(0).padStart(5, ' ')}\t${(memory / ByteSize.MB).toFixed(0).padStart(6, ' ')}\t${item.pid.toFixed(0).padStart(6, ' ')}\t${name}`); // Recurse into children if any if (Array.isArray(item.children)) { diff --git a/src/vs/platform/dialogs/common/dialogs.ts b/src/vs/platform/dialogs/common/dialogs.ts index 127c8fa95..8f7bb194d 100644 --- a/src/vs/platform/dialogs/common/dialogs.ts +++ b/src/vs/platform/dialogs/common/dialogs.ts @@ -22,6 +22,29 @@ export interface ICheckbox { checked?: boolean; } +export interface IConfirmDialogArgs { + confirmation: IConfirmation; +} + +export interface IShowDialogArgs { + severity: Severity; + message: string; + buttons: string[]; + options?: IDialogOptions; +} + +export interface IInputDialogArgs extends IShowDialogArgs { + inputs: IInput[], +} + +export interface IDialog { + confirmArgs?: IConfirmDialogArgs; + showArgs?: IShowDialogArgs; + inputArgs?: IInputDialogArgs; +} + +export type IDialogResult = IConfirmationResult | IInputResult | IShowResult; + export interface IConfirmation { title?: string; type?: DialogType; @@ -166,6 +189,40 @@ export interface IInput { value?: string; } +/** + * A handler to bring up modal dialogs. + */ +export interface IDialogHandler { + /** + * Ask the user for confirmation with a modal dialog. + */ + confirm(confirmation: IConfirmation): Promise; + + /** + * Present a modal dialog to the user. + * + * @returns A promise with the selected choice index. If the user refused to choose, + * then a promise with index of `cancelId` option is returned. If there is no such + * option then promise with index `0` is returned. + */ + show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise; + + /** + * Present a modal dialog to the user asking for input. + * + * @returns A promise with the selected choice index. If the user refused to choose, + * then a promise with index of `cancelId` option is returned. If there is no such + * option then promise with index `0` is returned. In addition, the values for the + * inputs are returned as well. + */ + input(severity: Severity, message: string, buttons: string[], inputs: IInput[], options?: IDialogOptions): Promise; + + /** + * Present the about dialog to the user. + */ + about(): Promise; +} + /** * A service to bring up modal dialogs. * @@ -218,20 +275,23 @@ export interface IFileDialogService { /** * The default path for a new file based on previously used files. * @param schemeFilter The scheme of the file path. If no filter given, the scheme of the current window is used. + * Falls back to user home in the absence of enough information to find a better URI. */ - defaultFilePath(schemeFilter?: string): URI | undefined; + defaultFilePath(schemeFilter?: string): Promise; /** * The default path for a new folder based on previously used folders. * @param schemeFilter The scheme of the folder path. If no filter given, the scheme of the current window is used. + * Falls back to user home in the absence of enough information to find a better URI. */ - defaultFolderPath(schemeFilter?: string): URI | undefined; + defaultFolderPath(schemeFilter?: string): Promise; /** * The default path for a new workspace based on previously used workspaces. * @param schemeFilter The scheme of the workspace path. If no filter given, the scheme of the current window is used. + * Falls back to user home in the absence of enough information to find a better URI. */ - defaultWorkspacePath(schemeFilter?: string, filename?: string): URI | undefined; + defaultWorkspacePath(schemeFilter?: string, filename?: string): Promise; /** * Shows a file-folder selection dialog and opens the selected entry. diff --git a/src/vs/editor/contrib/rename/media/onTypeRename.css b/src/vs/platform/display/common/displayMainService.ts similarity index 68% rename from src/vs/editor/contrib/rename/media/onTypeRename.css rename to src/vs/platform/display/common/displayMainService.ts index 16bb01785..44d79f8d5 100644 --- a/src/vs/editor/contrib/rename/media/onTypeRename.css +++ b/src/vs/platform/display/common/displayMainService.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor .on-type-rename-decoration { - border-left: 1px solid transparent; - /* So border can be transparent */ - background-clip: padding-box; +import { Event } from 'vs/base/common/event'; + +export interface IDisplayMainService { + readonly _serviceBrand: undefined; + readonly onDidDisplayChanged: Event; } diff --git a/src/vs/platform/display/electron-main/displayMainService.ts b/src/vs/platform/display/electron-main/displayMainService.ts new file mode 100644 index 000000000..e360fc486 --- /dev/null +++ b/src/vs/platform/display/electron-main/displayMainService.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisplayMainService as ICommonDisplayMainService } from 'vs/platform/display/common/displayMainService'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { app, Display, screen } from 'electron'; +import { RunOnceScheduler } from 'vs/base/common/async'; + +export const IDisplayMainService = createDecorator('displayMainService'); + +export interface IDisplayMainService extends ICommonDisplayMainService { } + +export class DisplayMainService extends Disposable implements ICommonDisplayMainService { + + declare readonly _serviceBrand: undefined; + + private readonly _onDidDisplayChanged = this._register(new Emitter()); + readonly onDidDisplayChanged = this._onDidDisplayChanged.event; + + constructor() { + super(); + + const displayChangedScheduler = this._register(new RunOnceScheduler(() => { + this._onDidDisplayChanged.fire(); + }, 100)); + + app.whenReady().then(() => { + + const displayChangedListener = (event: Event, display: Display, changedMetrics?: string[]) => { + displayChangedScheduler.schedule(); + }; + + screen.on('display-metrics-changed', displayChangedListener); + this._register(toDisposable(() => screen.removeListener('display-metrics-changed', displayChangedListener))); + + screen.on('display-added', displayChangedListener); + this._register(toDisposable(() => screen.removeListener('display-added', displayChangedListener))); + + screen.on('display-removed', displayChangedListener); + this._register(toDisposable(() => screen.removeListener('display-removed', displayChangedListener))); + }); + } +} diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 409bb7e19..e719e129c 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -72,12 +72,13 @@ export interface NativeParsedArgs { 'driver'?: string; 'driver-verbose'?: boolean; 'remote'?: string; - 'disable-user-env-probe'?: boolean; 'force'?: boolean; 'do-not-sync'?: boolean; 'force-user-env'?: boolean; + 'force-disable-user-env'?: boolean; 'sync'?: 'on' | 'off'; '__sandbox'?: boolean; + 'logsPath'?: string; // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches 'no-proxy-server'?: boolean; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 21b4d719c..f4b344542 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -119,7 +119,7 @@ export interface INativeEnvironmentService extends IEnvironmentService { sharedIPCHandle: string; // --- Extensions - extensionsPath?: string; + extensionsPath: string; extensionsDownloadPath: string; builtinExtensionsPath: string; diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index 8240ed281..35b09d89d 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -19,6 +19,9 @@ export const IEnvironmentMainService = createDecorator( */ export interface IEnvironmentMainService extends INativeEnvironmentService { + // --- NLS cache path + cachedLanguagesPath: string; + // --- backup paths backupHome: string; backupWorkspacesPath: string; @@ -35,7 +38,10 @@ export interface IEnvironmentMainService extends INativeEnvironmentService { disableUpdates: boolean; } -export class EnvironmentMainService extends NativeEnvironmentService { +export class EnvironmentMainService extends NativeEnvironmentService implements IEnvironmentMainService { + + @memoize + get cachedLanguagesPath(): string { return join(this.userDataPath, 'clp'); } @memoize get backupHome(): string { return join(this.userDataPath, 'Backups'); } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 149e6ffb4..7263bcbf7 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -43,8 +43,6 @@ export const OPTIONS: OptionDescriptions> = { 'goto': { type: 'boolean', cat: 'o', alias: 'g', args: 'file:line[:character]', description: localize('goto', "Open a file at the path on the specified line and character position.") }, 'new-window': { type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") }, 'reuse-window': { type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") }, - 'folder-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") }, - 'file-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") }, 'wait': { type: 'boolean', cat: 'o', alias: 'w', description: localize('wait', "Wait for the files to be closed before returning.") }, 'waitMarkerFilePath': { type: 'string' }, 'locale': { type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") }, @@ -57,7 +55,7 @@ export const OPTIONS: OptionDescriptions> = { 'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, 'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") }, 'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") }, - 'install-extension': { type: 'string[]', cat: 'e', args: 'extension-id[@version] | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts. The identifier of an extension is always `${publisher}.${name}`. To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.") }, + 'install-extension': { type: 'string[]', cat: 'e', args: 'extension-id[@version] | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. The identifier of an extension is always `${publisher}.${name}`. Use `--force` argument to update to latest version. To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.") }, 'uninstall-extension': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('uninstallExtension', "Uninstalls an extension.") }, 'enable-proposed-api': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") }, @@ -79,6 +77,9 @@ export const OPTIONS: OptionDescriptions> = { 'telemetry': { type: 'boolean', cat: 't', description: localize('telemetry', "Shows all telemetry events which VS code collects.") }, 'remote': { type: 'string' }, + 'folder-uri': { type: 'string[]', cat: 'o', args: 'uri' }, + 'file-uri': { type: 'string[]', cat: 'o', args: 'uri' }, + 'locate-extension': { type: 'string[]' }, 'extensionDevelopmentPath': { type: 'string[]' }, 'extensionTestsPath': { type: 'string' }, @@ -96,7 +97,6 @@ export const OPTIONS: OptionDescriptions> = { 'disable-crash-reporter': { type: 'boolean' }, 'crash-reporter-directory': { type: 'string' }, 'crash-reporter-id': { type: 'string' }, - 'disable-user-env-probe': { type: 'boolean' }, 'skip-add-to-recently-opened': { type: 'boolean' }, 'unity-launch': { type: 'boolean' }, 'open-url': { type: 'boolean' }, @@ -110,8 +110,10 @@ export const OPTIONS: OptionDescriptions> = { 'trace-category-filter': { type: 'string' }, 'trace-options': { type: 'string' }, 'force-user-env': { type: 'boolean' }, + 'force-disable-user-env': { type: 'boolean' }, 'open-devtools': { type: 'boolean' }, '__sandbox': { type: 'boolean' }, + 'logsPath': { type: 'string' }, // chromium flags 'no-proxy-server': { type: 'boolean' }, diff --git a/src/vs/platform/environment/node/argvHelper.ts b/src/vs/platform/environment/node/argvHelper.ts index a3c16f98d..3ace20cfc 100644 --- a/src/vs/platform/environment/node/argvHelper.ts +++ b/src/vs/platform/environment/node/argvHelper.ts @@ -52,7 +52,7 @@ export function parseMainProcessArgv(processArgv: string[]): NativeParsedArgs { } // If called from CLI, don't report warnings as they are already reported. - let reportWarnings = !process.env['VSCODE_CLI']; + const reportWarnings = !isLaunchedFromCli(process.env); return parseAndValidate(args, reportWarnings); } @@ -60,7 +60,7 @@ export function parseMainProcessArgv(processArgv: string[]): NativeParsedArgs { * Use this to parse raw code CLI process.argv such as: `Electron cli.js . --verbose --wait` */ export function parseCLIProcessArgv(processArgv: string[]): NativeParsedArgs { - let [, , ...args] = processArgv; // remove the first non-option argument: it's always the app location + const [, , ...args] = processArgv; // remove the first non-option argument: it's always the app location return parseAndValidate(args, true); } @@ -78,3 +78,7 @@ export function addArg(argv: string[], ...args: string[]): string[] { return argv; } + +export function isLaunchedFromCli(env: NodeJS.ProcessEnv): boolean { + return env['VSCODE_CLI'] === '1'; +} diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 80f68fb1d..cef263622 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as os from 'os'; import { IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import * as paths from 'vs/base/node/paths'; -import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as resources from 'vs/base/common/resources'; import { memoize } from 'vs/base/common/decorators'; @@ -204,12 +204,11 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get disableTelemetry(): boolean { return !!this._args['disable-telemetry']; } constructor(protected _args: NativeParsedArgs) { - if (!process.env['VSCODE_LOGS']) { + if (!_args.logsPath) { const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''); - process.env['VSCODE_LOGS'] = path.join(this.userDataPath, 'logs', key); + _args.logsPath = path.join(this.userDataPath, 'logs', key); } - - this.logsPath = process.env['VSCODE_LOGS']!; + this.logsPath = _args.logsPath; } } @@ -246,5 +245,5 @@ export function parsePathArg(arg: string | undefined, process: NodeJS.Process): } export function parseUserDataDir(args: NativeParsedArgs, process: NodeJS.Process): string { - return parsePathArg(args['user-data-dir'], process) || path.resolve(paths.getDefaultUserDataPath(process.platform)); + return parsePathArg(args['user-data-dir'], process) || path.resolve(paths.getDefaultUserDataPath()); } diff --git a/src/vs/code/test/node/argv.test.ts b/src/vs/platform/environment/test/node/argv.test.ts similarity index 99% rename from src/vs/code/test/node/argv.test.ts rename to src/vs/platform/environment/test/node/argv.test.ts index 79ce7d224..61a4b4f2e 100644 --- a/src/vs/code/test/node/argv.test.ts +++ b/src/vs/platform/environment/test/node/argv.test.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { formatOptions, Option } from 'vs/platform/environment/node/argv'; import { addArg } from 'vs/platform/environment/node/argvHelper'; diff --git a/src/vs/platform/environment/test/node/nativeModules.test.ts b/src/vs/platform/environment/test/node/nativeModules.test.ts new file mode 100644 index 000000000..b64282f62 --- /dev/null +++ b/src/vs/platform/environment/test/node/nativeModules.test.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { isMacintosh, isWindows } from 'vs/base/common/platform'; + +function testErrorMessage(module: string): string { + return `Unable to load "${module}" dependency. It was probably not compiled for the right operating system architecture or had missing build tools.`; +} + +suite('Native Modules (all platforms)', () => { + + test('native-is-elevated', async () => { + const isElevated = await import('native-is-elevated'); + assert.ok(typeof isElevated === 'function', testErrorMessage('native-is-elevated ')); + }); + + test('native-keymap', async () => { + const keyMap = await import('native-keymap'); + assert.ok(typeof keyMap.getCurrentKeyboardLayout === 'function', testErrorMessage('native-keymap')); + }); + + test('native-watchdog', async () => { + const watchDog = await import('native-watchdog'); + assert.ok(typeof watchDog.start === 'function', testErrorMessage('native-watchdog')); + }); + + test('node-pty', async () => { + const nodePty = await import('node-pty'); + assert.ok(typeof nodePty.spawn === 'function', testErrorMessage('node-pty')); + }); + + test('spdlog', async () => { + const spdlog = await import('spdlog'); + assert.ok(typeof spdlog.createRotatingLogger === 'function', testErrorMessage('spdlog')); + }); + + test('v8-inspect-profiler', async () => { + const profiler = await import('v8-inspect-profiler'); + assert.ok(typeof profiler.startProfiling === 'function', testErrorMessage('v8-inspect-profiler')); + }); + + test('vscode-nsfw', async () => { + const nsfWatcher = await import('vscode-nsfw'); + assert.ok(typeof nsfWatcher === 'function', testErrorMessage('vscode-nsfw')); + }); + + test('vscode-sqlite3', async () => { + const sqlite3 = await import('vscode-sqlite3'); + assert.ok(typeof sqlite3.Database === 'function', testErrorMessage('vscode-sqlite3')); + }); +}); + +(!isMacintosh ? suite.skip : suite)('Native Modules (macOS)', () => { + + test('chokidar (fsevents)', async () => { + const chokidar = await import('chokidar'); + const watcher = chokidar.watch(__dirname); + assert.ok(watcher.options.useFsEvents, testErrorMessage('chokidar (fsevents)')); + + return watcher.close(); + }); +}); + +(!isWindows ? suite.skip : suite)('Native Modules (Windows)', () => { + + test('windows-mutex', async () => { + const mutex = await import('windows-mutex'); + assert.ok(mutex && typeof mutex.isActive === 'function', testErrorMessage('windows-mutex')); + assert.ok(typeof mutex.isActive === 'function', testErrorMessage('windows-mutex')); + }); + + test('windows-foreground-love', async () => { + const foregroundLove = await import('windows-foreground-love'); + assert.ok(typeof foregroundLove.allowSetForegroundWindow === 'function', testErrorMessage('windows-foreground-love')); + }); + + test('windows-process-tree', async () => { + const processTree = await import('windows-process-tree'); + assert.ok(typeof processTree.getProcessTree === 'function', testErrorMessage('windows-process-tree')); + }); + + test('vscode-windows-registry', async () => { + const windowsRegistry = await import('vscode-windows-registry'); + assert.ok(typeof windowsRegistry.GetStringRegKey === 'function', testErrorMessage('vscode-windows-registry')); + }); + + test('vscode-windows-ca-certs', async () => { + // @ts-ignore Windows only + const windowsCerts = await import('vscode-windows-ca-certs'); + const store = windowsCerts(); + assert.ok(windowsCerts, testErrorMessage('vscode-windows-ca-certs')); + let certCount = 0; + try { + while (store.next()) { + certCount++; + } + } finally { + store.done(); + } + assert(certCount > 0); + }); +}); diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index afb3cf6e6..7b0a60eeb 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -7,7 +7,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionIdentifier, IGlobalExtensionEnablementService, DISABLED_EXTENSIONS_STORAGE_PATH } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { isUndefinedOrNull } from 'vs/base/common/types'; export class GlobalExtensionEnablementService extends Disposable implements IGlobalExtensionEnablementService { @@ -96,7 +96,7 @@ export class StorageManager extends Disposable { constructor(private storageService: IStorageService) { super(); - this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + this._register(storageService.onDidChangeValue(e => this.onDidStorageChange(e))); } get(key: string, scope: StorageScope): IExtensionIdentifier[] { @@ -127,14 +127,14 @@ export class StorageManager extends Disposable { } } - private onDidStorageChange(workspaceStorageChangeEvent: IWorkspaceStorageChangeEvent): void { - if (workspaceStorageChangeEvent.scope === StorageScope.GLOBAL) { - if (!isUndefinedOrNull(this.storage[workspaceStorageChangeEvent.key])) { - const newValue = this._get(workspaceStorageChangeEvent.key, workspaceStorageChangeEvent.scope); - if (newValue !== this.storage[workspaceStorageChangeEvent.key]) { - const oldValues = this.get(workspaceStorageChangeEvent.key, workspaceStorageChangeEvent.scope); - delete this.storage[workspaceStorageChangeEvent.key]; - const newValues = this.get(workspaceStorageChangeEvent.key, workspaceStorageChangeEvent.scope); + private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void { + if (storageChangeEvent.scope === StorageScope.GLOBAL) { + if (!isUndefinedOrNull(this.storage[storageChangeEvent.key])) { + const newValue = this._get(storageChangeEvent.key, storageChangeEvent.scope); + if (newValue !== this.storage[storageChangeEvent.key]) { + const oldValues = this.get(storageChangeEvent.key, storageChangeEvent.scope); + delete this.storage[storageChangeEvent.key]; + const newValues = this.get(storageChangeEvent.key, storageChangeEvent.scope); const added = oldValues.filter(oldValue => !newValues.some(newValue => areSameExtensions(oldValue, newValue))); const removed = newValues.filter(newValue => !oldValues.some(oldValue => areSameExtensions(oldValue, newValue))); if (added.length || removed.length) { @@ -151,7 +151,8 @@ export class StorageManager extends Disposable { private _set(key: string, value: string | undefined, scope: StorageScope): void { if (value) { - this.storageService.store(key, value, scope); + // Enablement state is synced separately through extensions + this.storageService.store(key, value, scope, StorageTarget.MACHINE); } else { this.storageService.remove(key, scope); } diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index b9e1f47e8..3f0dd835c 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -19,7 +19,7 @@ import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { joinPath } from 'vs/base/common/resources'; @@ -803,7 +803,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { export async function resolveMarketplaceHeaders(version: string, environmentService: IEnvironmentService, fileService: IFileService, storageService: { get: (key: string, scope: StorageScope) => string | undefined, - store: (key: string, value: string, scope: StorageScope) => void + store: (key: string, value: string, scope: StorageScope, target: StorageTarget) => void } | undefined): Promise<{ [key: string]: string; }> { const headers: IHeaders = { 'X-Market-Client-Id': `VSCode ${version}`, diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 0c20952bb..0e46a92d6 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -201,7 +201,8 @@ export class ExtensionManagementError extends Error { } } -export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean }; +export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean, donotIncludePackAndDependencies?: boolean }; +export type UninstallOptions = { donotIncludePack?: boolean, donotCheckDependents?: boolean }; export const IExtensionManagementService = createDecorator('extensionManagementService'); export interface IExtensionManagementService { @@ -218,7 +219,7 @@ export interface IExtensionManagementService { install(vsix: URI, options?: InstallOptions): Promise; canInstall(extension: IGalleryExtension): Promise; installFromGallery(extension: IGalleryExtension, options?: InstallOptions): Promise; - uninstall(extension: ILocalExtension, force?: boolean): Promise; + uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise; reinstallFromGallery(extension: ILocalExtension): Promise; getInstalled(type?: ExtensionType): Promise; getExtensionsReport(): Promise; diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index da3540454..75422f963 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -4,12 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension, IExtensionTipsService, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { Event } from 'vs/base/common/event'; +import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension, IExtensionTipsService, InstallOptions, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { Emitter, Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IURITransformer, DefaultURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; import { cloneAndChange } from 'vs/base/common/objects'; import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { Disposable } from 'vs/base/common/lifecycle'; function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI { return URI.revive(transformer ? transformer.transformIncoming(uri) : uri); @@ -77,18 +78,31 @@ export class ExtensionManagementChannel implements IServerChannel { } } -export class ExtensionManagementChannelClient implements IExtensionManagementService { +export class ExtensionManagementChannelClient extends Disposable implements IExtensionManagementService { declare readonly _serviceBrand: undefined; + private readonly _onInstallExtension = this._register(new Emitter()); + readonly onInstallExtension = this._onInstallExtension.event; + + private readonly _onDidInstallExtension = this._register(new Emitter()); + readonly onDidInstallExtension = this._onDidInstallExtension.event; + + private readonly _onUninstallExtension = this._register(new Emitter()); + readonly onUninstallExtension = this._onUninstallExtension.event; + + private readonly _onDidUninstallExtension = this._register(new Emitter()); + readonly onDidUninstallExtension = this._onDidUninstallExtension.event; + constructor( private readonly channel: IChannel, - ) { } - - get onInstallExtension(): Event { return this.channel.listen('onInstallExtension'); } - get onDidInstallExtension(): Event { return Event.map(this.channel.listen('onDidInstallExtension'), i => ({ ...i, local: i.local ? transformIncomingExtension(i.local, null) : i.local })); } - get onUninstallExtension(): Event { return this.channel.listen('onUninstallExtension'); } - get onDidUninstallExtension(): Event { return this.channel.listen('onDidUninstallExtension'); } + ) { + super(); + this._register(this.channel.listen('onInstallExtension')(e => this._onInstallExtension.fire(e))); + this._register(this.channel.listen('onDidInstallExtension')(e => this._onDidInstallExtension.fire({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local }))); + this._register(this.channel.listen('onUninstallExtension')(e => this._onUninstallExtension.fire(e))); + this._register(this.channel.listen('onDidUninstallExtension')(e => this._onDidUninstallExtension.fire(e))); + } zip(extension: ILocalExtension): Promise { return Promise.resolve(this.channel.call('zip', [extension]).then(result => URI.revive(result))); @@ -114,8 +128,8 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer return Promise.resolve(this.channel.call('installFromGallery', [extension, installOptions])).then(local => transformIncomingExtension(local, null)); } - uninstall(extension: ILocalExtension, force = false): Promise { - return Promise.resolve(this.channel.call('uninstall', [extension!, force])); + uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { + return Promise.resolve(this.channel.call('uninstall', [extension!, options])); } reinstallFromGallery(extension: ILocalExtension): Promise { diff --git a/src/vs/platform/extensionManagement/common/extensionUrlTrust.ts b/src/vs/platform/extensionManagement/common/extensionUrlTrust.ts new file mode 100644 index 000000000..b26395646 --- /dev/null +++ b/src/vs/platform/extensionManagement/common/extensionUrlTrust.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IExtensionUrlTrustService = createDecorator('extensionUrlTrustService'); + +export interface IExtensionUrlTrustService { + readonly _serviceBrand: undefined; + isExtensionUrlTrusted(extensionId: string, url: string): Promise; +} diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts index 4072c4edf..7d17d9ef3 100644 --- a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts +++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts @@ -20,7 +20,7 @@ import { disposableTimeout, timeout } from 'vs/base/common/async'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { localize } from 'vs/nls'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; type ExeExtensionRecommendationsClassification = { extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; @@ -241,7 +241,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService { } private updateLastPromptedMediumExeTime(value: number): void { - this.storageService.store(lastPromptedMediumImpExeTimeStorageKey, value, StorageScope.GLOBAL); + this.storageService.store(lastPromptedMediumImpExeTimeStorageKey, value, StorageScope.GLOBAL, StorageTarget.MACHINE); } private getPromptedExecutableTips(): IStringDictionary { @@ -251,7 +251,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService { private addToRecommendedExecutables(exeName: string, tips: IExecutableBasedExtensionTip[]) { const promptedExecutableTips = this.getPromptedExecutableTips(); promptedExecutableTips[exeName] = tips.map(({ extensionId }) => extensionId.toLowerCase()); - this.storageService.store(promptedExecutableTipsStorageKey, JSON.stringify(promptedExecutableTips), StorageScope.GLOBAL); + this.storageService.store(promptedExecutableTipsStorageKey, JSON.stringify(promptedExecutableTips), StorageScope.GLOBAL, StorageTarget.USER); } private groupByInstalled(recommendationsToSuggest: string[], local: ILocalExtension[]): { installed: string[], uninstalled: string[] } { diff --git a/src/vs/platform/extensionManagement/node/extensionDownloader.ts b/src/vs/platform/extensionManagement/node/extensionDownloader.ts index f11e0174b..822dd47b1 100644 --- a/src/vs/platform/extensionManagement/node/extensionDownloader.ts +++ b/src/vs/platform/extensionManagement/node/extensionDownloader.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; +import { rename } from 'vs/base/node/pfs'; import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -13,6 +14,7 @@ import { ExtensionIdentifierWithVersion, groupByExtension } from 'vs/platform/ex import { ILogService } from 'vs/platform/log/common/log'; import { generateUuid } from 'vs/base/common/uuid'; import * as semver from 'vs/base/common/semver/semver'; +import { isWindows } from 'vs/base/common/platform'; const ExtensionIdVersionRegex = /^([^.]+\..+)-(\d+\.\d+\.\d+)$/; @@ -36,8 +38,21 @@ export class ExtensionsDownloader extends Disposable { async downloadExtension(extension: IGalleryExtension, operation: InstallOperation): Promise { await this.cleanUpPromise; - const location = joinPath(this.extensionsDownloadDir, this.getName(extension)); - await this.download(extension, location, operation); + const vsixName = this.getName(extension); + const location = joinPath(this.extensionsDownloadDir, vsixName); + + // Download only if vsix does not exist + if (!await this.fileService.exists(location)) { + // Download to temporary location first only if vsix does not exist + const tempLocation = joinPath(this.extensionsDownloadDir, `.${vsixName}`); + if (!await this.fileService.exists(tempLocation)) { + await this.extensionGalleryService.download(extension, tempLocation, operation); + } + + // Rename temp location to original + await this.rename(tempLocation, location, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */); + } + return location; } @@ -45,9 +60,15 @@ export class ExtensionsDownloader extends Disposable { // noop as caching is enabled always } - private async download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise { - if (!await this.fileService.exists(location)) { - await this.extensionGalleryService.download(extension, location, operation); + private async rename(from: URI, to: URI, retryUntil: number): Promise { + try { + await rename(from.fsPath, to.fsPath); + } catch (error) { + if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) { + this.logService.info(`Failed renaming ${from} to ${to} with 'EPERM' error. Trying again...`); + return this.rename(from, to, retryUntil); + } + throw error; } } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 11135f9eb..af1092c14 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -20,7 +20,8 @@ import { INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, ExtensionManagementError, - InstallOptions + InstallOptions, + UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions, getGalleryExtensionId, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -297,11 +298,13 @@ export class ExtensionManagementService extends Disposable implements IExtension try { await this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)); } catch (error) { /* Ignore */ } - try { - await this.installDependenciesAndPackExtensions(local, existingExtension, options); - } catch (error) { - try { await this.uninstall(local); } catch (error) { /* Ignore */ } - throw error; + if (!options.donotIncludePackAndDependencies) { + try { + await this.installDependenciesAndPackExtensions(local, existingExtension, options); + } catch (error) { + try { await this.uninstall(local); } catch (error) { /* Ignore */ } + throw error; + } } if (existingExtension && semver.neq(existingExtension.manifest.version, extension.version)) { @@ -471,7 +474,7 @@ export class ExtensionManagementService extends Disposable implements IExtension await Promise.all(extensionsToUninstall.map(local => this.uninstall(local))); } - async uninstall(extension: ILocalExtension): Promise { + async uninstall(extension: ILocalExtension, options: UninstallOptions = {}): Promise { this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id); const installed = await this.getInstalled(ExtensionType.User); const extensionToUninstall = installed.find(e => areSameExtensions(e.identifier, extension.identifier)); @@ -480,7 +483,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } try { - await this.checkForDependenciesAndUninstall(extensionToUninstall, installed); + await this.checkForDependenciesAndUninstall(extensionToUninstall, installed, options); } catch (error) { throw this.joinErrors(error); } @@ -533,15 +536,11 @@ export class ExtensionManagementService extends Disposable implements IExtension }, new Error('')); } - private async checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[]): Promise { + private async checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], options: UninstallOptions): Promise { try { await this.preUninstallExtension(extension); - const packedExtensions = this.getAllPackExtensionsToUninstall(extension, installed); - if (packedExtensions.length) { - await this.uninstallExtensions(extension, packedExtensions, installed); - } else { - await this.uninstallExtensions(extension, [], installed); - } + const packedExtensions = options.donotIncludePack ? [] : this.getAllPackExtensionsToUninstall(extension, installed); + await this.uninstallExtensions(extension, packedExtensions, installed, options); } catch (error) { await this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL)); throw error; @@ -549,10 +548,12 @@ export class ExtensionManagementService extends Disposable implements IExtension await this.postUninstallExtension(extension); } - private async uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[]): Promise { + private async uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[], options: UninstallOptions): Promise { const extensionsToUninstall = [extension, ...otherExtensionsToUninstall]; - for (const e of extensionsToUninstall) { - this.checkForDependents(e, extensionsToUninstall, installed, extension); + if (!options.donotCheckDependents) { + for (const e of extensionsToUninstall) { + this.checkForDependents(e, extensionsToUninstall, installed, extension); + } } await Promise.all([this.uninstallExtension(extension), ...otherExtensionsToUninstall.map(d => this.doUninstall(d))]); } diff --git a/src/vs/platform/extensionManagement/node/extensionUrlTrustService.ts b/src/vs/platform/extensionManagement/node/extensionUrlTrustService.ts new file mode 100644 index 000000000..8b638a63f --- /dev/null +++ b/src/vs/platform/extensionManagement/node/extensionUrlTrustService.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as crypto from 'crypto'; +import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; + +export class ExtensionUrlTrustService implements IExtensionUrlTrustService { + + declare readonly _serviceBrand: undefined; + + private trustedExtensionUrlPublicKeys = new Map(); + + constructor( + @IProductService private readonly productService: IProductService, + @ILogService private readonly logService: ILogService + ) { } + + async isExtensionUrlTrusted(extensionId: string, url: string): Promise { + if (!this.productService.trustedExtensionUrlPublicKeys) { + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'There are no configured trusted keys'); + return false; + } + + const match = /^(.*)#([^#]+)$/.exec(url); + + if (!match) { + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Uri has no fragment', url); + return false; + } + + const [, originalUrl, fragment] = match; + + let keys = this.trustedExtensionUrlPublicKeys.get(extensionId); + + if (!keys) { + keys = this.productService.trustedExtensionUrlPublicKeys[extensionId]; + + if (!keys || keys.length === 0) { + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Extension doesn\'t have any trusted keys', extensionId); + return false; + } + + this.trustedExtensionUrlPublicKeys.set(extensionId, [...keys]); + } + + const fragmentBuffer = Buffer.from(decodeURIComponent(fragment), 'base64'); + + if (fragmentBuffer.length <= 6) { + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Uri fragment is not a signature', url); + return false; + } + + const timestampBuffer = fragmentBuffer.slice(0, 6); + const timestamp = fragmentBuffer.readUIntBE(0, 6); + const diff = Date.now() - timestamp; + + if (diff < 0 || diff > 3_600_000) { // 1 hour + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Signed uri has expired', url); + return false; + } + + const signatureBuffer = fragmentBuffer.slice(6); + const verify = crypto.createVerify('SHA256'); + verify.write(timestampBuffer); + verify.write(Buffer.from(originalUrl)); + verify.end(); + + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + + if (key === null) { // failed to be parsed before + continue; + } else if (typeof key === 'string') { // needs to be parsed + try { + key = crypto.createPublicKey({ key: Buffer.from(key, 'base64'), format: 'der', type: 'spki' }); + keys[i] = key; + } catch (err) { + this.logService.warn('ExtensionUrlTrustService#isExtensionUrlTrusted', `Failed to parse trusted extension uri public key #${i + 1} for ${extensionId}:`, err); + keys[i] = null; + continue; + } + } + + if (verify.verify(key, signatureBuffer)) { + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Signed uri is valid', url); + return true; + } + } + + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Signed uri could not be verified', url); + return false; + } +} diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts index aee65f8ed..adce023b9 100644 --- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts +++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts @@ -49,7 +49,7 @@ export class ExtensionsScanner extends Disposable { ) { super(); this.systemExtensionsPath = environmentService.builtinExtensionsPath; - this.extensionsPath = environmentService.extensionsPath!; + this.extensionsPath = environmentService.extensionsPath; this.uninstalledPath = path.join(this.extensionsPath, '.obsolete'); this.uninstalledFileLimiter = new Queue(); } diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index b36a5aac6..23001b2a2 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -180,6 +180,7 @@ export class FileService extends Disposable implements IFileService { private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise; private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise { const provider = await this.withProvider(resource); + const isPathCaseSensitive = this.isPathCaseSensitive(provider); const resolveTo = options?.resolveTo; const resolveSingleChildDescendants = options?.resolveSingleChildDescendants; @@ -193,7 +194,7 @@ export class FileService extends Disposable implements IFileService { // lazy trie to check for recursive resolving if (!trie) { - trie = TernarySearchTree.forUris(); + trie = TernarySearchTree.forUris(() => !isPathCaseSensitive); trie.set(resource, true); if (isNonEmptyArray(resolveTo)) { resolveTo.forEach(uri => trie!.set(uri, true)); diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 2d2cdcfee..4ae154345 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -521,19 +521,19 @@ export class FileChangesEvent { switch (change.type) { case FileChangeType.ADDED: if (!this.added) { - this.added = TernarySearchTree.forUris(this.ignorePathCasing); + this.added = TernarySearchTree.forUris(() => this.ignorePathCasing); } this.added.set(change.resource, change); break; case FileChangeType.UPDATED: if (!this.updated) { - this.updated = TernarySearchTree.forUris(this.ignorePathCasing); + this.updated = TernarySearchTree.forUris(() => this.ignorePathCasing); } this.updated.set(change.resource, change); break; case FileChangeType.DELETED: if (!this.deleted) { - this.deleted = TernarySearchTree.forUris(this.ignorePathCasing); + this.deleted = TernarySearchTree.forUris(() => this.ignorePathCasing); } this.deleted.set(change.resource, change); break; @@ -971,33 +971,33 @@ export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096; /** * Helper to format a raw byte size into a human readable label. */ -export class BinarySize { +export class ByteSize { static readonly KB = 1024; - static readonly MB = BinarySize.KB * BinarySize.KB; - static readonly GB = BinarySize.MB * BinarySize.KB; - static readonly TB = BinarySize.GB * BinarySize.KB; + static readonly MB = ByteSize.KB * ByteSize.KB; + static readonly GB = ByteSize.MB * ByteSize.KB; + static readonly TB = ByteSize.GB * ByteSize.KB; static formatSize(size: number): string { if (!isNumber(size)) { size = 0; } - if (size < BinarySize.KB) { + if (size < ByteSize.KB) { return localize('sizeB', "{0}B", size.toFixed(0)); } - if (size < BinarySize.MB) { - return localize('sizeKB', "{0}KB", (size / BinarySize.KB).toFixed(2)); + if (size < ByteSize.MB) { + return localize('sizeKB', "{0}KB", (size / ByteSize.KB).toFixed(2)); } - if (size < BinarySize.GB) { - return localize('sizeMB', "{0}MB", (size / BinarySize.MB).toFixed(2)); + if (size < ByteSize.GB) { + return localize('sizeMB', "{0}MB", (size / ByteSize.MB).toFixed(2)); } - if (size < BinarySize.TB) { - return localize('sizeGB', "{0}GB", (size / BinarySize.GB).toFixed(2)); + if (size < ByteSize.TB) { + return localize('sizeGB', "{0}GB", (size / ByteSize.GB).toFixed(2)); } - return localize('sizeTB', "{0}TB", (size / BinarySize.TB).toFixed(2)); + return localize('sizeTB', "{0}TB", (size / ByteSize.TB).toFixed(2)); } } diff --git a/src/vs/platform/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts b/src/vs/platform/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts index de85fd770..9ad083fc9 100644 --- a/src/vs/platform/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts +++ b/src/vs/platform/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts @@ -5,23 +5,27 @@ import * as assert from 'assert'; import * as platform from 'vs/base/common/platform'; -import { NsfwWatcherService } from 'vs/platform/files/node/watcher/nsfw/nsfwWatcherService'; import { IWatcherRequest } from 'vs/platform/files/node/watcher/nsfw/watcher'; -class TestNsfwWatcherService extends NsfwWatcherService { +suite('NSFW Watcher Service', async () => { - normalizeRoots(roots: string[]): string[] { + // Load `nsfwWatcherService` within the suite to prevent all tests + // from failing to start if `vscode-nsfw` was not properly installed + const { NsfwWatcherService } = await import('vs/platform/files/node/watcher/nsfw/nsfwWatcherService'); - // Work with strings as paths to simplify testing - const requests: IWatcherRequest[] = roots.map(r => { - return { path: r, excludes: [] }; - }); + class TestNsfwWatcherService extends NsfwWatcherService { - return this._normalizeRoots(requests).map(r => r.path); + normalizeRoots(roots: string[]): string[] { + + // Work with strings as paths to simplify testing + const requests: IWatcherRequest[] = roots.map(r => { + return { path: r, excludes: [] }; + }); + + return this._normalizeRoots(requests).map(r => r.path); + } } -} -suite('NSFW Watcher Service', () => { suite('_normalizeRoots', () => { test('should not impacts roots that don\'t overlap', () => { const service = new TestNsfwWatcherService(); diff --git a/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts index 1f88cadc0..895e5dfa9 100644 --- a/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts +++ b/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts @@ -142,7 +142,7 @@ export class ChokidarWatcherService extends Disposable implements IWatcherServic } if (this.verboseLogging) { - this.log(`Start watching with chockidar: ${realBasePath}, excludes: ${excludes.join(',')}, usePolling: ${usePolling ? 'true, interval ' + pollingInterval : 'false'}`); + this.log(`Start watching with chokidar: ${realBasePath}, excludes: ${excludes.join(',')}, usePolling: ${usePolling ? 'true, interval ' + pollingInterval : 'false'}`); } let chokidarWatcher: chokidar.FSWatcher | null = chokidar.watch(realBasePath, watcherOpts); diff --git a/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts b/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts index 4fe0f39ca..4c780ec6b 100644 --- a/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts +++ b/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts @@ -5,32 +5,36 @@ import * as assert from 'assert'; import * as platform from 'vs/base/common/platform'; -import { normalizeRoots } from 'vs/platform/files/node/watcher/unix/chokidarWatcherService'; import { IWatcherRequest } from 'vs/platform/files/node/watcher/unix/watcher'; -function newRequest(basePath: string, ignored: string[] = []): IWatcherRequest { - return { path: basePath, excludes: ignored }; -} +suite('Chokidar normalizeRoots', async () => { -function assertNormalizedRootPath(inputPaths: string[], expectedPaths: string[]) { - const requests = inputPaths.map(path => newRequest(path)); - const actual = normalizeRoots(requests); - assert.deepEqual(Object.keys(actual).sort(), expectedPaths); -} + // Load `chokidarWatcherService` within the suite to prevent all tests + // from failing to start if `chokidar` was not properly installed + const { normalizeRoots } = await import('vs/platform/files/node/watcher/unix/chokidarWatcherService'); -function assertNormalizedRequests(inputRequests: IWatcherRequest[], expectedRequests: { [path: string]: IWatcherRequest[] }) { - const actual = normalizeRoots(inputRequests); - const actualPath = Object.keys(actual).sort(); - const expectedPaths = Object.keys(expectedRequests).sort(); - assert.deepEqual(actualPath, expectedPaths); - for (let path of actualPath) { - let a = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path)); - let e = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path)); - assert.deepEqual(a, e); + function newRequest(basePath: string, ignored: string[] = []): IWatcherRequest { + return { path: basePath, excludes: ignored }; + } + + function assertNormalizedRootPath(inputPaths: string[], expectedPaths: string[]) { + const requests = inputPaths.map(path => newRequest(path)); + const actual = normalizeRoots(requests); + assert.deepEqual(Object.keys(actual).sort(), expectedPaths); + } + + function assertNormalizedRequests(inputRequests: IWatcherRequest[], expectedRequests: { [path: string]: IWatcherRequest[] }) { + const actual = normalizeRoots(inputRequests); + const actualPath = Object.keys(actual).sort(); + const expectedPaths = Object.keys(expectedRequests).sort(); + assert.deepEqual(actualPath, expectedPaths); + for (let path of actualPath) { + let a = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path)); + let e = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path)); + assert.deepEqual(a, e); + } } -} -suite('Chokidar normalizeRoots', () => { test('should not impacts roots that don\'t overlap', () => { if (platform.isWindows) { assertNormalizedRootPath(['C:\\a'], ['C:\\a']); diff --git a/src/vs/platform/issue/common/issue.ts b/src/vs/platform/issue/common/issue.ts index 9cbaae735..e981a34d5 100644 --- a/src/vs/platform/issue/common/issue.ts +++ b/src/vs/platform/issue/common/issue.ts @@ -55,6 +55,7 @@ export interface IssueReporterData extends WindowData { enabledExtensions: IssueReporterExtensionData[]; issueType?: IssueType; extensionId?: string; + experiments?: string; readonly issueTitle?: string; readonly issueBody?: string; } diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index ca72fa628..da98e2f94 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -55,7 +55,7 @@ export class IssueMainService implements ICommonIssueService { .then(result => { const [info, remoteData] = result; this.diagnosticsService.getSystemInfo(info, remoteData).then(msg => { - event.sender.send('vscode:issueSystemInfoResponse', msg); + this.safeSend(event, 'vscode:issueSystemInfoResponse', msg); }); }); }); @@ -86,7 +86,7 @@ export class IssueMainService implements ICommonIssueService { this.logService.error(`Listing processes failed: ${e}`); } - event.sender.send('vscode:listProcessesResponse', processes); + this.safeSend(event, 'vscode:listProcessesResponse', processes); }); ipcMain.on('vscode:issueReporterClipboard', (event: IpcMainEvent) => { @@ -102,14 +102,14 @@ export class IssueMainService implements ICommonIssueService { if (this._issueWindow) { this.dialogMainService.showMessageBox(messageOptions, this._issueWindow) .then(result => { - event.sender.send('vscode:issueReporterClipboardResponse', result.response === 0); + this.safeSend(event, 'vscode:issueReporterClipboardResponse', result.response === 0); }); } }); ipcMain.on('vscode:issuePerformanceInfoRequest', (event: IpcMainEvent) => { this.getPerformanceInfo().then(msg => { - event.sender.send('vscode:issuePerformanceInfoResponse', msg); + this.safeSend(event, 'vscode:issuePerformanceInfoResponse', msg); }); }); @@ -174,11 +174,17 @@ export class IssueMainService implements ICommonIssueService { ipcMain.on('vscode:windowsInfoRequest', (event: IpcMainEvent) => { this.launchMainService.getMainProcessInfo().then(info => { - event.sender.send('vscode:windowsInfoResponse', info.windows); + this.safeSend(event, 'vscode:windowsInfoResponse', info.windows); }); }); } + private safeSend(event: IpcMainEvent, channel: string, ...args: unknown[]): void { + if (!event.sender.isDestroyed()) { + event.sender.send(channel, ...args); + } + } + openReporter(data: IssueReporterData): Promise { return new Promise(_ => { if (!this._issueWindow) { @@ -203,18 +209,8 @@ export class IssueMainService implements ICommonIssueService { spellcheck: false, nativeWindowOpen: true, zoomFactor: zoomLevelToZoomFactor(data.zoomLevel), - ...this.environmentService.sandbox ? - - // Sandbox - { - sandbox: true, - contextIsolation: true - } : - - // No Sandbox - { - nodeIntegration: true - } + sandbox: true, + contextIsolation: true } }); @@ -269,18 +265,8 @@ export class IssueMainService implements ICommonIssueService { spellcheck: false, nativeWindowOpen: true, zoomFactor: zoomLevelToZoomFactor(data.zoomLevel), - ...this.environmentService.sandbox ? - - // Sandbox - { - sandbox: true, - contextIsolation: true - } : - - // No Sandbox - { - nodeIntegration: true - } + sandbox: true, + contextIsolation: true } }); @@ -288,7 +274,6 @@ export class IssueMainService implements ICommonIssueService { const windowConfiguration = { appRoot: this.environmentService.appRoot, - nodeCachedDataDir: this.environmentService.nodeCachedDataDir, windowId: this._processExplorerWindow.id, userEnv: this.userEnv, machineId: this.machineId, @@ -416,7 +401,6 @@ export class IssueMainService implements ICommonIssueService { const windowConfiguration = { appRoot: this.environmentService.appRoot, - nodeCachedDataDir: this.environmentService.nodeCachedDataDir, windowId: this._issueWindow.id, machineId: this.machineId, userEnv: this.userEnv, @@ -452,7 +436,7 @@ function toWindowUrl(modulePathToHtml: string, windowConfiguration: T): strin } return FileAccess - .asBrowserUri(modulePathToHtml, require) + ._asCodeFileUri(modulePathToHtml, require) .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }) .toString(true); } diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 43a109d67..3582ce6d6 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -383,14 +383,9 @@ function printWhenExplanation(when: ContextKeyExpression | undefined): string { } function printSourceExplanation(kb: ResolvedKeybindingItem): string { - if (kb.isDefault) { - if (kb.extensionId) { - return `built-in extension ${kb.extensionId}`; - } - return `built-in`; - } - if (kb.extensionId) { - return `user extension ${kb.extensionId}`; - } - return `user`; + return ( + kb.extensionId + ? (kb.isBuiltinExtension ? `built-in extension ${kb.extensionId}` : `user extension ${kb.extensionId}`) + : (kb.isDefault ? `built-in` : `user`) + ); } diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index b4210f9e5..2bf215e08 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -17,6 +17,7 @@ export interface IKeybindingItem { weight1: number; weight2: number; extensionId: string | null; + isBuiltinExtension: boolean; } export interface IKeybindings { @@ -53,6 +54,7 @@ export interface IKeybindingRule2 { weight: number; when: ContextKeyExpression | undefined; extensionId?: string; + isBuiltinExtension?: boolean; } export const enum KeybindingWeight { @@ -164,7 +166,8 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { when: rule.when, weight1: rule.weight, weight2: 0, - extensionId: rule.extensionId || null + extensionId: rule.extensionId || null, + isBuiltinExtension: rule.isBuiltinExtension || false }; } } @@ -223,7 +226,8 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { when: when, weight1: weight1, weight2: weight2, - extensionId: null + extensionId: null, + isBuiltinExtension: false }); this._cachedMergedKeybindings = null; } diff --git a/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts b/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts index 0410c4698..6d2df1597 100644 --- a/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts +++ b/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts @@ -18,8 +18,9 @@ export class ResolvedKeybindingItem { public readonly when: ContextKeyExpression | undefined; public readonly isDefault: boolean; public readonly extensionId: string | null; + public readonly isBuiltinExtension: boolean; - constructor(resolvedKeybinding: ResolvedKeybinding | undefined, command: string | null, commandArgs: any, when: ContextKeyExpression | undefined, isDefault: boolean, extensionId: string | null) { + constructor(resolvedKeybinding: ResolvedKeybinding | undefined, command: string | null, commandArgs: any, when: ContextKeyExpression | undefined, isDefault: boolean, extensionId: string | null, isBuiltinExtension: boolean) { this.resolvedKeybinding = resolvedKeybinding; this.keypressParts = resolvedKeybinding ? removeElementsAfterNulls(resolvedKeybinding.getDispatchParts()) : []; this.bubble = (command ? command.charCodeAt(0) === CharCode.Caret : false); @@ -28,6 +29,7 @@ export class ResolvedKeybindingItem { this.when = when; this.isDefault = isDefault; this.extensionId = extensionId; + this.isBuiltinExtension = isBuiltinExtension; } } diff --git a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index b9af02e74..cbbb54344 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -192,7 +192,8 @@ suite('AbstractKeybindingService', () => { null, when, true, - null + null, + false ); } diff --git a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts index 0723eb637..2820e0ea4 100644 --- a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts @@ -28,7 +28,8 @@ suite('KeybindingResolver', () => { commandArgs, when, isDefault, - null + null, + false ); } diff --git a/src/vs/workbench/services/keybinding/common/dispatchConfig.ts b/src/vs/platform/keyboardLayout/common/dispatchConfig.ts similarity index 100% rename from src/vs/workbench/services/keybinding/common/dispatchConfig.ts rename to src/vs/platform/keyboardLayout/common/dispatchConfig.ts diff --git a/src/vs/platform/keyboardLayout/common/keyboardLayout.ts b/src/vs/platform/keyboardLayout/common/keyboardLayout.ts new file mode 100644 index 000000000..db0975fe9 --- /dev/null +++ b/src/vs/platform/keyboardLayout/common/keyboardLayout.ts @@ -0,0 +1,260 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Event } from 'vs/base/common/event'; +import { ScanCode, ScanCodeUtils } from 'vs/base/common/scanCode'; +import { IKeyboardMapper } from 'vs/platform/keyboardLayout/common/keyboardMapper'; +import { DispatchConfig } from 'vs/platform/keyboardLayout/common/dispatchConfig'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; + +export const IKeyboardLayoutService = createDecorator('keyboardLayoutService'); + +export interface IWindowsKeyMapping { + vkey: string; + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} +export interface IWindowsKeyboardMapping { + [code: string]: IWindowsKeyMapping; +} +export interface ILinuxKeyMapping { + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} +export interface ILinuxKeyboardMapping { + [code: string]: ILinuxKeyMapping; +} +export interface IMacKeyMapping { + value: string; + valueIsDeadKey: boolean; + withShift: string; + withShiftIsDeadKey: boolean; + withAltGr: string; + withAltGrIsDeadKey: boolean; + withShiftAltGr: string; + withShiftAltGrIsDeadKey: boolean; +} +export interface IMacKeyboardMapping { + [code: string]: IMacKeyMapping; +} + +export type IMacLinuxKeyMapping = IMacKeyMapping | ILinuxKeyMapping; +export type IMacLinuxKeyboardMapping = IMacKeyboardMapping | ILinuxKeyboardMapping; +export type IKeyboardMapping = IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping; + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "text": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface IWindowsKeyboardLayoutInfo { + name: string; + id: string; + text: string; +} + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "model" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "layout": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "variant": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "options": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "rules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface ILinuxKeyboardLayoutInfo { + model: string; + layout: string; + variant: string; + options: string; + rules: string; +} + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "lang": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "localizedName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface IMacKeyboardLayoutInfo { + id: string; + lang: string; + localizedName?: string; +} + +export type IKeyboardLayoutInfo = (IWindowsKeyboardLayoutInfo | ILinuxKeyboardLayoutInfo | IMacKeyboardLayoutInfo) & { isUserKeyboardLayout?: boolean; isUSStandard?: true }; + +export interface IKeyboardLayoutService { + readonly _serviceBrand: undefined; + + readonly onDidChangeKeyboardLayout: Event; + + getRawKeyboardMapping(): IKeyboardMapping | null; + getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null; + getAllKeyboardLayouts(): IKeyboardLayoutInfo[]; + getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper; + validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void; +} + +export function areKeyboardLayoutsEqual(a: IKeyboardLayoutInfo | null, b: IKeyboardLayoutInfo | null): boolean { + if (!a || !b) { + return false; + } + + if ((a).name && (b).name && (a).name === (b).name) { + return true; + } + + if ((a).id && (b).id && (a).id === (b).id) { + return true; + } + + if ((a).model && + (b).model && + (a).model === (b).model && + (a).layout === (b).layout + ) { + return true; + } + + return false; +} + +export function parseKeyboardLayoutDescription(layout: IKeyboardLayoutInfo | null): { label: string, description: string } { + if (!layout) { + return { label: '', description: '' }; + } + + if ((layout).name) { + // windows + let windowsLayout = layout; + return { + label: windowsLayout.text, + description: '' + }; + } + + if ((layout).id) { + let macLayout = layout; + if (macLayout.localizedName) { + return { + label: macLayout.localizedName, + description: '' + }; + } + + if (/^com\.apple\.keylayout\./.test(macLayout.id)) { + return { + label: macLayout.id.replace(/^com\.apple\.keylayout\./, '').replace(/-/, ' '), + description: '' + }; + } + if (/^.*inputmethod\./.test(macLayout.id)) { + return { + label: macLayout.id.replace(/^.*inputmethod\./, '').replace(/[-\.]/, ' '), + description: `Input Method (${macLayout.lang})` + }; + } + + return { + label: macLayout.lang, + description: '' + }; + } + + let linuxLayout = layout; + + return { + label: linuxLayout.layout, + description: '' + }; +} + +export function getKeyboardLayoutId(layout: IKeyboardLayoutInfo): string { + if ((layout).name) { + return (layout).name; + } + + if ((layout).id) { + return (layout).id; + } + + return (layout).layout; +} + +function windowsKeyMappingEquals(a: IWindowsKeyMapping, b: IWindowsKeyMapping): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return ( + a.vkey === b.vkey + && a.value === b.value + && a.withShift === b.withShift + && a.withAltGr === b.withAltGr + && a.withShiftAltGr === b.withShiftAltGr + ); +} + +export function windowsKeyboardMappingEquals(a: IWindowsKeyboardMapping | null, b: IWindowsKeyboardMapping | null): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + for (let scanCode = 0; scanCode < ScanCode.MAX_VALUE; scanCode++) { + const strScanCode = ScanCodeUtils.toString(scanCode); + const aEntry = a[strScanCode]; + const bEntry = b[strScanCode]; + if (!windowsKeyMappingEquals(aEntry, bEntry)) { + return false; + } + } + return true; +} + +function macLinuxKeyMappingEquals(a: IMacLinuxKeyMapping, b: IMacLinuxKeyMapping): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return ( + a.value === b.value + && a.withShift === b.withShift + && a.withAltGr === b.withAltGr + && a.withShiftAltGr === b.withShiftAltGr + ); +} + +export function macLinuxKeyboardMappingEquals(a: IMacLinuxKeyboardMapping | null, b: IMacLinuxKeyboardMapping | null): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + for (let scanCode = 0; scanCode < ScanCode.MAX_VALUE; scanCode++) { + const strScanCode = ScanCodeUtils.toString(scanCode); + const aEntry = a[strScanCode]; + const bEntry = b[strScanCode]; + if (!macLinuxKeyMappingEquals(aEntry, bEntry)) { + return false; + } + } + return true; +} diff --git a/src/vs/platform/keyboardLayout/common/keyboardLayoutMainService.ts b/src/vs/platform/keyboardLayout/common/keyboardLayoutMainService.ts new file mode 100644 index 000000000..4b6d3d468 --- /dev/null +++ b/src/vs/platform/keyboardLayout/common/keyboardLayoutMainService.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IKeyboardLayoutInfo, IKeyboardMapping } from 'vs/platform/keyboardLayout/common/keyboardLayout'; +import { Event } from 'vs/base/common/event'; + +export interface IKeyboardLayoutData { + keyboardLayoutInfo: IKeyboardLayoutInfo; + keyboardMapping: IKeyboardMapping; +} + +export interface IKeyboardLayoutMainService { + readonly _serviceBrand: undefined; + readonly onDidChangeKeyboardLayout: Event; + getKeyboardLayoutData(): Promise; +} diff --git a/src/vs/workbench/services/keybinding/common/keyboardMapper.ts b/src/vs/platform/keyboardLayout/common/keyboardMapper.ts similarity index 100% rename from src/vs/workbench/services/keybinding/common/keyboardMapper.ts rename to src/vs/platform/keyboardLayout/common/keyboardMapper.ts diff --git a/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts new file mode 100644 index 000000000..f28a15dca --- /dev/null +++ b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IKeyboardLayoutData, IKeyboardLayoutMainService as ICommonKeyboardLayoutMainService } from 'vs/platform/keyboardLayout/common/keyboardLayoutMainService'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as nativeKeymap from 'native-keymap'; + +export const IKeyboardLayoutMainService = createDecorator('keyboardLayoutMainService'); + +export interface IKeyboardLayoutMainService extends ICommonKeyboardLayoutMainService { } + +export class KeyboardLayoutMainService extends Disposable implements ICommonKeyboardLayoutMainService { + + declare readonly _serviceBrand: undefined; + + private readonly _onDidChangeKeyboardLayout = this._register(new Emitter()); + readonly onDidChangeKeyboardLayout = this._onDidChangeKeyboardLayout.event; + + private _initPromise: Promise | null; + private _keyboardLayoutData: IKeyboardLayoutData | null; + + constructor() { + super(); + this._initPromise = null; + this._keyboardLayoutData = null; + } + + private _initialize(): Promise { + if (!this._initPromise) { + this._initPromise = this._doInitialize(); + } + return this._initPromise; + } + + private async _doInitialize(): Promise { + const nativeKeymapMod = await import('native-keymap'); + + this._keyboardLayoutData = readKeyboardLayoutData(nativeKeymapMod); + nativeKeymapMod.onDidChangeKeyboardLayout(() => { + this._keyboardLayoutData = readKeyboardLayoutData(nativeKeymapMod); + this._onDidChangeKeyboardLayout.fire(this._keyboardLayoutData); + }); + } + + public async getKeyboardLayoutData(): Promise { + await this._initialize(); + return this._keyboardLayoutData!; + } +} + +function readKeyboardLayoutData(nativeKeymapMod: typeof nativeKeymap): IKeyboardLayoutData { + const keyboardMapping = nativeKeymapMod.getKeyMap(); + const keyboardLayoutInfo = nativeKeymapMod.getCurrentKeyboardLayout(); + return { keyboardMapping, keyboardLayoutInfo }; +} diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index e646cc854..9b3d65663 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -19,6 +19,8 @@ import { BrowserWindow, ipcMain, Event as IpcEvent, app } from 'electron'; import { coalesce } from 'vs/base/common/arrays'; import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/node/launch'; +import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const ID = 'launchMainService'; export const ILaunchMainService = createDecorator(ID); @@ -33,14 +35,14 @@ export interface IRemoteDiagnosticOptions { includeWorkspaceMetadata?: boolean; } -function parseOpenUrl(args: NativeParsedArgs): URI[] { +function parseOpenUrl(args: NativeParsedArgs): { uri: URI, url: string }[] { if (args['open-url'] && args._urls && args._urls.length > 0) { // --open-url must contain -- followed by the url(s) // process.argv is used over args._ as args._ are resolved to file paths at this point return coalesce(args._urls .map(url => { try { - return URI.parse(url); + return { uri: URI.parse(url), url }; } catch (err) { return null; } @@ -99,8 +101,8 @@ export class LaunchMainService implements ILaunchMainService { // Make sure a window is open, ready to receive the url event whenWindowReady.then(() => { - for (const url of urlsToOpen) { - this.urlService.open(url); + for (const { uri, url } of urlsToOpen) { + this.urlService.open(uri, { originalUrl: url }); } }); } @@ -112,7 +114,7 @@ export class LaunchMainService implements ILaunchMainService { } private startOpenWindow(args: NativeParsedArgs, userEnv: IProcessEnvironment): Promise { - const context = !!userEnv['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; + const context = isLaunchedFromCli(userEnv) ? OpenContext.CLI : OpenContext.DESKTOP; let usedWindows: ICodeWindow[] = []; const waitMarkerFileURI = args.wait && args.waitMarkerFilePath ? URI.file(args.waitMarkerFilePath) : undefined; @@ -138,7 +140,7 @@ export class LaunchMainService implements ILaunchMainService { // Otherwise check for settings else { - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); const openWithoutArgumentsInNewWindowConfig = windowConfig?.openWithoutArgumentsInNewWindow || 'default' /* default */; switch (openWithoutArgumentsInNewWindowConfig) { case 'on': @@ -247,7 +249,7 @@ export class LaunchMainService implements ILaunchMainService { folders: options.includeWorkspaceMetadata ? this.getFolderURIs(window) : undefined }; - window.sendWhenReady('vscode:getDiagnosticInfo', { replyChannel, args }); + window.sendWhenReady('vscode:getDiagnosticInfo', CancellationToken.None, { replyChannel, args }); ipcMain.once(replyChannel, (_: IpcEvent, data: IRemoteDiagnosticInfo) => { // No data is returned if getting the connection fails. diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index 48ad05b7b..62db3f751 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcMain as ipc, app, BrowserWindow } from 'electron'; +import { ipcMain, app, BrowserWindow } from 'electron'; import { ILogService } from 'vs/platform/log/common/log'; import { IStateService } from 'vs/platform/state/node/state'; import { Event, Emitter } from 'vs/base/common/event'; @@ -371,7 +371,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe // Only reload when the window has not vetoed this const veto = await this.unload(window, UnloadReason.RELOAD); if (!veto) { - window.reload(undefined, cli); + window.reload(cli); } } @@ -437,11 +437,11 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe const okChannel = `vscode:ok${oneTimeEventToken}`; const cancelChannel = `vscode:cancel${oneTimeEventToken}`; - ipc.once(okChannel, () => { + ipcMain.once(okChannel, () => { resolve(false); // no veto }); - ipc.once(cancelChannel, () => { + ipcMain.once(cancelChannel, () => { resolve(true); // veto }); @@ -468,7 +468,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe const oneTimeEventToken = this.oneTimeListenerTokenGenerator++; const replyChannel = `vscode:reply${oneTimeEventToken}`; - ipc.once(replyChannel, () => resolve()); + ipcMain.once(replyChannel, () => resolve()); window.send('vscode:onWillUnload', { replyChannel, reason }); }); diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index b1a865528..b553a0581 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -118,14 +118,15 @@ function createScopedContextKeyService(contextKeyService: IContextKeyService, wi return result; } -export const multiSelectModifierSettingKey = 'workbench.list.multiSelectModifier'; -export const openModeSettingKey = 'workbench.list.openMode'; -export const horizontalScrollingKey = 'workbench.list.horizontalScrolling'; -export const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation'; -export const automaticKeyboardNavigationSettingKey = 'workbench.list.automaticKeyboardNavigation'; +const multiSelectModifierSettingKey = 'workbench.list.multiSelectModifier'; +const openModeSettingKey = 'workbench.list.openMode'; +const horizontalScrollingKey = 'workbench.list.horizontalScrolling'; +const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation'; +const automaticKeyboardNavigationSettingKey = 'workbench.list.automaticKeyboardNavigation'; const treeIndentKey = 'workbench.tree.indent'; const treeRenderIndentGuidesKey = 'workbench.tree.renderIndentGuides'; const listSmoothScrolling = 'workbench.list.smoothScrolling'; +const treeExpandMode = 'workbench.tree.expandMode'; function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean { return configurationService.getValue(multiSelectModifierSettingKey) === 'alt'; @@ -444,11 +445,11 @@ abstract class ResourceNavigator extends Disposable { private readonly openOnFocus: boolean; private openOnSingleClick: boolean; - private readonly _onDidOpen = new Emitter>(); - readonly onDidOpen: Event> = this._onDidOpen.event; + private readonly _onDidOpen = this._register(new Emitter>()); + readonly onDidOpen: Event> = this._onDidOpen.event; constructor( - private readonly widget: ListWidget, + protected readonly widget: ListWidget, options?: IResourceNavigatorOptions ) { super(); @@ -456,8 +457,8 @@ abstract class ResourceNavigator extends Disposable { this.openOnFocus = options?.openOnFocus ?? false; this._register(Event.filter(this.widget.onDidChangeSelection, e => e.browserEvent instanceof KeyboardEvent)(e => this.onSelectionFromKeyboard(e))); - this._register(this.widget.onPointer((e: { browserEvent: MouseEvent }) => this.onPointer(e.browserEvent))); - this._register(this.widget.onMouseDblClick((e: { browserEvent: MouseEvent }) => this.onMouseDblClick(e.browserEvent))); + this._register(this.widget.onPointer((e: { browserEvent: MouseEvent, element: T | undefined }) => this.onPointer(e.element, e.browserEvent))); + this._register(this.widget.onMouseDblClick((e: { browserEvent: MouseEvent, element: T | undefined }) => this.onMouseDblClick(e.element, e.browserEvent))); if (this.openOnFocus) { this._register(Event.filter(this.widget.onDidChangeFocus, e => e.browserEvent instanceof KeyboardEvent)(e => this.onFocusFromKeyboard(e))); @@ -478,10 +479,10 @@ abstract class ResourceNavigator extends Disposable { this.widget.setSelection(focus, event.browserEvent); const preserveFocus = typeof (event.browserEvent as SelectionKeyboardEvent).preserveFocus === 'boolean' ? (event.browserEvent as SelectionKeyboardEvent).preserveFocus! : true; - const pinned = false; + const pinned = !preserveFocus; const sideBySide = false; - this._open(preserveFocus, pinned, sideBySide, event.browserEvent); + this._open(this.getSelectedElement(), preserveFocus, pinned, sideBySide, event.browserEvent); } private onSelectionFromKeyboard(event: ITreeEvent): void { @@ -490,13 +491,13 @@ abstract class ResourceNavigator extends Disposable { } const preserveFocus = typeof (event.browserEvent as SelectionKeyboardEvent).preserveFocus === 'boolean' ? (event.browserEvent as SelectionKeyboardEvent).preserveFocus! : true; - const pinned = false; + const pinned = !preserveFocus; const sideBySide = false; - this._open(preserveFocus, pinned, sideBySide, event.browserEvent); + this._open(this.getSelectedElement(), preserveFocus, pinned, sideBySide, event.browserEvent); } - private onPointer(browserEvent: MouseEvent): void { + private onPointer(element: T | undefined, browserEvent: MouseEvent): void { if (!this.openOnSingleClick) { return; } @@ -512,10 +513,10 @@ abstract class ResourceNavigator extends Disposable { const pinned = isMiddleClick; const sideBySide = browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey; - this._open(preserveFocus, pinned, sideBySide, browserEvent); + this._open(element, preserveFocus, pinned, sideBySide, browserEvent); } - private onMouseDblClick(browserEvent?: MouseEvent): void { + private onMouseDblClick(element: T | undefined, browserEvent?: MouseEvent): void { if (!browserEvent) { return; } @@ -524,10 +525,14 @@ abstract class ResourceNavigator extends Disposable { const pinned = true; const sideBySide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey); - this._open(preserveFocus, pinned, sideBySide, browserEvent); + this._open(element, preserveFocus, pinned, sideBySide, browserEvent); } - private _open(preserveFocus: boolean, pinned: boolean, sideBySide: boolean, browserEvent?: UIEvent): void { + private _open(element: T | undefined, preserveFocus: boolean, pinned: boolean, sideBySide: boolean, browserEvent?: UIEvent): void { + if (!element) { + return; + } + this._onDidOpen.fire({ editorOptions: { preserveFocus, @@ -535,27 +540,39 @@ abstract class ResourceNavigator extends Disposable { revealIfVisible: true }, sideBySide, - element: this.widget.getSelection()[0], + element, browserEvent }); } + + abstract getSelectedElement(): T | undefined; } -export class ListResourceNavigator extends ResourceNavigator { +export class ListResourceNavigator extends ResourceNavigator { + constructor( - list: List | PagedList, + protected readonly widget: List | PagedList, options?: IResourceNavigatorOptions ) { - super(list, options); + super(widget, options); + } + + getSelectedElement(): T | undefined { + return this.widget.getSelectedElements()[0]; } } class TreeResourceNavigator extends ResourceNavigator { + constructor( - tree: ObjectTree | CompressibleObjectTree | DataTree | AsyncDataTree | CompressibleAsyncDataTree, + protected readonly widget: ObjectTree | CompressibleObjectTree | DataTree | AsyncDataTree | CompressibleAsyncDataTree, options: IResourceNavigatorOptions ) { - super(tree, options); + super(widget, options); + } + + getSelectedElement(): T | undefined { + return this.widget.getSelection()[0] ?? undefined; } } @@ -590,7 +607,7 @@ export class WorkbenchObjectTree, TFilterData = void> private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } - get onDidOpen(): Event> { return this.internals.onDidOpen; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -626,7 +643,7 @@ export class WorkbenchCompressibleObjectTree, TFilter private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } - get onDidOpen(): Event> { return this.internals.onDidOpen; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -670,7 +687,7 @@ export class WorkbenchDataTree extends DataTree; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } - get onDidOpen(): Event> { return this.internals.onDidOpen; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -715,7 +732,7 @@ export class WorkbenchAsyncDataTree extends Async private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } - get onDidOpen(): Event> { return this.internals.onDidOpen; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -757,7 +774,7 @@ export class WorkbenchCompressibleAsyncDataTree e private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } - get onDidOpen(): Event> { return this.internals.onDidOpen; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -831,7 +848,8 @@ function workbenchTreeDataPreamble(treeExpandMode) === 'doubleClick') } as TOptions }; } @@ -847,7 +865,7 @@ class WorkbenchTreeInternals { private styler: IDisposable | undefined; private navigator: TreeResourceNavigator; - get onDidOpen(): Event> { return this.navigator.onDidOpen; } + get onDidOpen(): Event> { return this.navigator.onDidOpen; } constructor( private tree: WorkbenchObjectTree | WorkbenchCompressibleObjectTree | WorkbenchDataTree | WorkbenchAsyncDataTree | WorkbenchCompressibleAsyncDataTree, @@ -933,6 +951,9 @@ class WorkbenchTreeInternals { if (e.affectsConfiguration(openModeSettingKey)) { newOptions = { ...newOptions, expandOnlyOnDoubleClick: configurationService.getValue(openModeSettingKey) === 'doubleClick' }; } + if (e.affectsConfiguration(treeExpandMode) && options.expandOnlyOnTwistieClick === undefined) { + newOptions = { ...newOptions, expandOnlyOnTwistieClick: configurationService.getValue<'singleClick' | 'doubleClick'>(treeExpandMode) === 'doubleClick' }; + } if (Object.keys(newOptions).length > 0) { tree.updateOptions(newOptions); } @@ -1036,6 +1057,12 @@ configurationRegistry.registerConfiguration({ 'type': 'boolean', 'default': true, markdownDescription: localize('automatic keyboard navigation setting', "Controls whether keyboard navigation in lists and trees is automatically triggered simply by typing. If set to `false`, keyboard navigation is only triggered when executing the `list.toggleKeyboardNavigation` command, for which you can assign a keyboard shortcut.") + }, + [treeExpandMode]: { + type: 'string', + enum: ['singleClick', 'doubleClick'], + default: 'singleClick', + description: localize('expand mode', "Controls how tree folders are expanded when clicking the folder names."), } } }); diff --git a/src/vs/platform/log/common/fileLogService.ts b/src/vs/platform/log/common/fileLogService.ts index a6318446b..7f2665b51 100644 --- a/src/vs/platform/log/common/fileLogService.ts +++ b/src/vs/platform/log/common/fileLogService.ts @@ -5,7 +5,7 @@ import { ILogService, LogLevel, AbstractLogService, ILoggerService, ILogger } from 'vs/platform/log/common/log'; import { URI } from 'vs/base/common/uri'; -import { FileOperationError, FileOperationResult, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; +import { ByteSize, FileOperationError, FileOperationResult, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; import { Queue } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { dirname, joinPath, basename } from 'vs/base/common/resources'; @@ -13,7 +13,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; -const MAX_FILE_SIZE = 1024 * 1024 * 5; +const MAX_FILE_SIZE = 5 * ByteSize.MB; export class FileLogService extends AbstractLogService implements ILogService { diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index add6b7666..67462e8f9 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -419,23 +419,43 @@ export function getLogLevel(environmentService: IEnvironmentService): LogLevel { return LogLevel.Trace; } if (typeof environmentService.logLevel === 'string') { - const logLevel = environmentService.logLevel.toLowerCase(); - switch (logLevel) { - case 'trace': - return LogLevel.Trace; - case 'debug': - return LogLevel.Debug; - case 'info': - return LogLevel.Info; - case 'warn': - return LogLevel.Warning; - case 'error': - return LogLevel.Error; - case 'critical': - return LogLevel.Critical; - case 'off': - return LogLevel.Off; + const logLevel = parseLogLevel(environmentService.logLevel.toLowerCase()); + if (logLevel !== undefined) { + return logLevel; } } return DEFAULT_LOG_LEVEL; } + +export function parseLogLevel(logLevel: string): LogLevel | undefined { + switch (logLevel) { + case 'trace': + return LogLevel.Trace; + case 'debug': + return LogLevel.Debug; + case 'info': + return LogLevel.Info; + case 'warn': + return LogLevel.Warning; + case 'error': + return LogLevel.Error; + case 'critical': + return LogLevel.Critical; + case 'off': + return LogLevel.Off; + } + return undefined; +} + +export function LogLevelToString(logLevel: LogLevel): string { + switch (logLevel) { + case LogLevel.Trace: return 'trace'; + case LogLevel.Debug: return 'debug'; + case LogLevel.Info: return 'info'; + case LogLevel.Warning: return 'warn'; + case LogLevel.Error: return 'error'; + case LogLevel.Critical: return 'critical'; + case LogLevel.Off: return 'off'; + } +} + diff --git a/src/vs/platform/log/node/spdlogService.ts b/src/vs/platform/log/node/spdlogService.ts index c32ab07af..b011f2423 100644 --- a/src/vs/platform/log/node/spdlogService.ts +++ b/src/vs/platform/log/node/spdlogService.ts @@ -6,6 +6,7 @@ import * as path from 'vs/base/common/path'; import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log'; import * as spdlog from 'spdlog'; +import { ByteSize } from 'vs/platform/files/common/files'; async function createSpdLogLogger(processName: string, logsFolder: string): Promise { // Do not crash if spdlog cannot be loaded @@ -13,7 +14,7 @@ async function createSpdLogLogger(processName: string, logsFolder: string): Prom const _spdlog = await import('spdlog'); _spdlog.setAsyncMode(8192, 500); const logfilePath = path.join(logsFolder, `${processName}.log`); - return _spdlog.createRotatingLoggerAsync(processName, logfilePath, 1024 * 1024 * 5, 6); + return _spdlog.createRotatingLoggerAsync(processName, logfilePath, 5 * ByteSize.MB, 6); } catch (e) { console.error(e); } diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 4ecb37251..3039b01a1 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -24,6 +24,7 @@ import { IStateService } from 'vs/platform/state/node/state'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService'; +import { CancellationToken } from 'vs/base/common/cancellation'; const telemetryFrom = 'menu'; @@ -82,7 +83,7 @@ export class Menubar { this.menubarMenus = Object.create(null); this.keybindings = Object.create(null); - if (isMacintosh || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + if (isMacintosh || getTitleBarStyle(this.configurationService) === 'native') { this.restoreCachedMenubarData(); } @@ -416,7 +417,7 @@ export class Menubar { private shouldDrawMenu(menuId: string): boolean { // We need to draw an empty menu to override the electron default - if (!isMacintosh && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if (!isMacintosh && getTitleBarStyle(this.configurationService) === 'custom') { return false; } @@ -754,10 +755,10 @@ export class Menubar { if (invocation.type === 'commandId') { const runActionPayload: INativeRunActionInWindowRequest = { id: invocation.commandId, from: 'menu' }; - activeWindow.sendWhenReady('vscode:runAction', runActionPayload); + activeWindow.sendWhenReady('vscode:runAction', CancellationToken.None, runActionPayload); } else { const runKeybindingPayload: INativeRunKeybindingInWindowRequest = { userSettingsLabel: invocation.userSettingsLabel }; - activeWindow.sendWhenReady('vscode:runKeybinding', runKeybindingPayload); + activeWindow.sendWhenReady('vscode:runKeybinding', CancellationToken.None, runKeybindingPayload); } } else { this.logService.trace('menubar#runActionInRenderer: no active window found', invocation); diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index c71a369e9..e24efa83c 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -10,7 +10,7 @@ import { OpenContext } from 'vs/platform/windows/node/window'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions, IColorScheme } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; -import { isMacintosh, isWindows, isRootUser, isLinux } from 'vs/base/common/platform'; +import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform'; import { ICommonNativeHostService, IOSProperties, IOSStatistics } from 'vs/platform/native/common/native'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; @@ -364,7 +364,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain if (isWindows) { isAdmin = (await import('native-is-elevated'))(); } else { - isAdmin = isRootUser(); + isAdmin = process.getuid() === 0; } return isAdmin; @@ -550,7 +550,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async reload(windowId: number | undefined, options?: { disableExtensions?: boolean }): Promise { const window = this.windowById(windowId); if (window) { - return this.lifecycleMainService.reload(window, options?.disableExtensions ? { _: [], 'disable-extensions': true } : undefined); + return this.lifecycleMainService.reload(window, options?.disableExtensions !== undefined ? { _: [], 'disable-extensions': options?.disableExtensions } : undefined); } } @@ -615,11 +615,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain const window = this.windowById(windowId); if (window) { const contents = window.win.webContents; - if (isMacintosh && window.hasHiddenTitleBarStyle && !window.isFullScreen && !contents.isDevToolsOpened()) { - contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647 - } else { - contents.toggleDevTools(); - } + contents.toggleDevTools(); } } diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index cc3cb1077..74c263638 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -114,6 +114,8 @@ export interface INotificationActions { /** * Primary actions show up as buttons as part of the message and will close * the notification once clicked. + * + * Pass `ActionWithMenuAction` for an action that has additional menu actions. */ readonly primary?: ReadonlyArray; @@ -209,19 +211,13 @@ export interface INotificationHandle { close(): void; } -export interface IPromptChoice { +interface IBasePromptChoice { /** * Label to show for the choice to the user. */ readonly label: string; - /** - * Primary choices show up as buttons in the notification below the message. - * Secondary choices show up under the gear icon in the header of the notification. - */ - readonly isSecondary?: boolean; - /** * Whether to keep the notification open after the choice was selected * by the user. By default, will close the notification upon click. @@ -234,6 +230,28 @@ export interface IPromptChoice { run: () => void; } +export interface IPromptChoice extends IBasePromptChoice { + + /** + * Primary choices show up as buttons in the notification below the message. + * Secondary choices show up under the gear icon in the header of the notification. + */ + readonly isSecondary?: boolean; +} + +export interface IPromptChoiceWithMenu extends IPromptChoice { + + /** + * Additional choices those will be shown in the dropdown menu for this choice. + */ + readonly menu: IBasePromptChoice[]; + + /** + * Menu is not supported on secondary choices + */ + readonly isSecondary: false | undefined; +} + export interface IPromptOptions extends INotificationProperties { /** @@ -327,7 +345,7 @@ export interface INotificationService { * * @returns a handle on the notification to e.g. hide it or update message, buttons, etc. */ - prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle; + prompt(severity: Severity, message: string, choices: (IPromptChoice | IPromptChoiceWithMenu)[], options?: IPromptOptions): INotificationHandle; /** * Shows a status message in the status area with the provided text. diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 977cb79b8..d82651f93 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -7,6 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { equalsIgnoreCase, startsWithIgnoreCase } from 'vs/base/common/strings'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; export const IOpenerService = createDecorator('openerService'); @@ -18,6 +19,11 @@ type OpenInternalOptions = { */ readonly openToSide?: boolean; + /** + * Extra editor options to apply in case an editor is used to open. + */ + readonly editorOptions?: IEditorOptions; + /** * Signals that the editor to open was triggered through a user * action, such as keyboard or mouse usage. diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 2bea85740..56fbc68ad 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -20,7 +20,7 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire ! // Running out of sources if (Object.keys(product).length === 0) { Object.assign(product, { - version: '1.51.0-dev', + version: '1.52.0-dev', nameShort: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev', nameLong: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev', applicationName: 'code-oss', @@ -32,7 +32,6 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire ! extensionAllowedProposedApi: [ 'ms-vscode.vscode-js-profile-flame', 'ms-vscode.vscode-js-profile-table', - 'ms-vscode.references-view', 'ms-vscode.github-browser' ], }); diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 333e5b24b..74a8c744a 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -80,6 +80,7 @@ export interface IProductConfiguration { readonly remoteExtensionTips?: { [remoteName: string]: IRemoteExtensionTip; }; readonly extensionKeywords?: { [extension: string]: readonly string[]; }; readonly keymapExtensionTips?: readonly string[]; + readonly trustedExtensionUrlPublicKeys?: { [id: string]: string[]; }; readonly crashReporter?: { readonly companyName: string; diff --git a/src/vs/platform/quickinput/browser/commandsQuickAccess.ts b/src/vs/platform/quickinput/browser/commandsQuickAccess.ts index 18b7dc26e..a1d27c5ac 100644 --- a/src/vs/platform/quickinput/browser/commandsQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/commandsQuickAccess.ts @@ -11,7 +11,7 @@ import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecyc import { or, matchesPrefix, matchesWords, matchesContiguousSubString } from 'vs/base/common/filters'; import { withNullAsUndefined } from 'vs/base/common/types'; import { LRUCache } from 'vs/base/common/map'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -21,7 +21,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; export interface ICommandQuickPick extends IPickerQuickAccessItem { commandId: string; @@ -204,14 +203,9 @@ export class CommandsHistory extends Disposable { constructor( @IStorageService private readonly storageService: IStorageService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: CommandsHistory.PREF_KEY_CACHE, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: CommandsHistory.PREF_KEY_COUNTER, version: 1 }); - this.updateConfiguration(); this.load(); @@ -279,8 +273,8 @@ export class CommandsHistory extends Disposable { const serializedCache: ISerializedCommandHistory = { usesLRU: true, entries: [] }; CommandsHistory.cache.forEach((value, key) => serializedCache.entries.push({ key, value })); - storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache), StorageScope.GLOBAL); - storageService.store(CommandsHistory.PREF_KEY_COUNTER, CommandsHistory.counter, StorageScope.GLOBAL); + storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache), StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(CommandsHistory.PREF_KEY_COUNTER, CommandsHistory.counter, StorageScope.GLOBAL, StorageTarget.USER); } static getConfiguredCommandHistoryLength(configurationService: IConfigurationService): number { diff --git a/src/vs/platform/remote/common/tunnel.ts b/src/vs/platform/remote/common/tunnel.ts index 52dfacbc4..980c3203a 100644 --- a/src/vs/platform/remote/common/tunnel.ts +++ b/src/vs/platform/remote/common/tunnel.ts @@ -26,8 +26,12 @@ export interface TunnelOptions { label?: string; } +export interface TunnelCreationOptions { + elevationRequired?: boolean; +} + export interface ITunnelProvider { - forwardPort(tunnelOptions: TunnelOptions): Promise | undefined; + forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise | undefined; } export interface ITunnelService { @@ -56,8 +60,14 @@ export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address: }; } +export const LOCALHOST_ADDRESSES = ['localhost', '127.0.0.1', '0:0:0:0:0:0:0:1', '::1']; export function isLocalhost(host: string): boolean { - return host === 'localhost' || host === '127.0.0.1'; + return LOCALHOST_ADDRESSES.indexOf(host) >= 0; +} + +export const ALL_INTERFACES_ADDRESSES = ['0.0.0.0', '0:0:0:0:0:0:0:0', '::']; +export function isAllInterfaces(host: string): boolean { + return ALL_INTERFACES_ADDRESSES.indexOf(host) >= 0; } function getOtherLocalhost(host: string): string | undefined { @@ -198,6 +208,10 @@ export abstract class AbstractTunnelService implements ITunnelService { } protected abstract retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number): Promise | undefined; + + protected isPortPrivileged(port: number): boolean { + return port < 1024; + } } export class TunnelService extends AbstractTunnelService { @@ -209,7 +223,10 @@ export class TunnelService extends AbstractTunnelService { } if (this._tunnelProvider) { - const tunnel = this._tunnelProvider.forwardPort({ remoteAddress: { host: remoteHost, port: remotePort } }); + const preferredLocalPort = localPort === undefined ? remotePort : localPort; + const tunnelOptions = { remoteAddress: { host: remoteHost, port: remotePort }, localAddressPort: localPort }; + const creationInfo = { elevationRequired: this.isPortPrivileged(preferredLocalPort) }; + const tunnel = this._tunnelProvider.forwardPort(tunnelOptions, creationInfo); if (tunnel) { this.addTunnelToMap(remoteHost, remotePort, tunnel); } diff --git a/src/vs/platform/remote/node/tunnelService.ts b/src/vs/platform/remote/node/tunnelService.ts index 0a27aabb5..19ec1efd6 100644 --- a/src/vs/platform/remote/node/tunnelService.ts +++ b/src/vs/platform/remote/node/tunnelService.ts @@ -146,7 +146,10 @@ export class TunnelService extends AbstractTunnelService { } if (this._tunnelProvider) { - const tunnel = this._tunnelProvider.forwardPort({ remoteAddress: { host: remoteHost, port: remotePort }, localAddressPort: localPort }); + const preferredLocalPort = localPort === undefined ? remotePort : localPort; + const creationInfo = { elevationRequired: this.isPortPrivileged(preferredLocalPort) }; + const tunnelOptions = { remoteAddress: { host: remoteHost, port: remotePort }, localAddressPort: localPort }; + const tunnel = this._tunnelProvider.forwardPort(tunnelOptions, creationInfo); if (tunnel) { this.addTunnelToMap(remoteHost, remotePort, tunnel); } diff --git a/src/vs/platform/serviceMachineId/common/serviceMachineId.ts b/src/vs/platform/serviceMachineId/common/serviceMachineId.ts index 97abad373..c760a13bb 100644 --- a/src/vs/platform/serviceMachineId/common/serviceMachineId.ts +++ b/src/vs/platform/serviceMachineId/common/serviceMachineId.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { IFileService } from 'vs/platform/files/common/files'; -import { StorageScope } from 'vs/platform/storage/common/storage'; +import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isUUID, generateUuid } from 'vs/base/common/uuid'; import { VSBuffer } from 'vs/base/common/buffer'; export async function getServiceMachineId(environmentService: IEnvironmentService, fileService: IFileService, storageService: { get: (key: string, scope: StorageScope, fallbackValue?: string | undefined) => string | undefined, - store: (key: string, value: string, scope: StorageScope) => void + store: (key: string, value: string, scope: StorageScope, target: StorageTarget) => void } | undefined): Promise { let uuid: string | null = storageService ? storageService.get('storage.serviceMachineId', StorageScope.GLOBAL) || null : null; if (uuid) { @@ -34,7 +34,7 @@ export async function getServiceMachineId(environmentService: IEnvironmentServic } } if (storageService) { - storageService.store('storage.serviceMachineId', uuid, StorageScope.GLOBAL); + storageService.store('storage.serviceMachineId', uuid, StorageScope.GLOBAL, StorageTarget.MACHINE); } return uuid; } diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index ab3fd347b..c667dd88f 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -5,7 +5,7 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage, IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { StorageScope, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; @@ -16,15 +16,7 @@ import { runWhenIdle, RunOnceScheduler } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; -export class BrowserStorageService extends Disposable implements IStorageService { - - declare readonly _serviceBrand: undefined; - - private readonly _onDidChangeStorage = this._register(new Emitter()); - readonly onDidChangeStorage = this._onDidChangeStorage.event; - - private readonly _onWillSaveState = this._register(new Emitter()); - readonly onWillSaveState = this._onWillSaveState.event; +export class BrowserStorageService extends AbstractStorageService { private globalStorage: IStorage | undefined; private workspaceStorage: IStorage | undefined; @@ -40,6 +32,10 @@ export class BrowserStorageService extends Disposable implements IStorageService private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 5000 /* every 5s */)); private runWhenIdleDisposable: IDisposable | undefined = undefined; + get hasPendingUpdate(): boolean { + return (!!this.globalStorageDatabase && this.globalStorageDatabase.hasPendingUpdate) || (!!this.workspaceStorageDatabase && this.workspaceStorageDatabase.hasPendingUpdate); + } + constructor( @IEnvironmentService private readonly environmentService: IEnvironmentService, @IFileService private readonly fileService: IFileService @@ -66,13 +62,13 @@ export class BrowserStorageService extends Disposable implements IStorageService this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, false /* do not watch for external changes */, this.fileService)); this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); - this._register(this.workspaceStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.WORKSPACE }))); + this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); // Global Storage this.globalStorageFile = joinPath(stateRoot, 'global.json'); this.globalStorageDatabase = this._register(new FileStorageDatabase(this.globalStorageFile, true /* watch for external changes */, this.fileService)); this.globalStorage = this._register(new Storage(this.globalStorageDatabase)); - this._register(this.globalStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL }))); + this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); // Init both await Promise.all([ @@ -122,11 +118,11 @@ export class BrowserStorageService extends Disposable implements IStorageService return this.getStorage(scope).getNumber(key, fallbackValue); } - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { + protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { this.getStorage(scope).set(key, value); } - remove(key: string, scope: StorageScope): void { + protected doRemove(key: string, scope: StorageScope): void { this.getStorage(scope).delete(key); } @@ -149,6 +145,13 @@ export class BrowserStorageService extends Disposable implements IStorageService throw new Error('Migrating storage is currently unsupported in Web'); } + protected async doFlush(): Promise { + await Promise.all([ + this.getStorage(StorageScope.GLOBAL).whenFlushed(), + this.getStorage(StorageScope.WORKSPACE).whenFlushed() + ]); + } + private doFlushWhenIdle(): void { // Dispose any previous idle runner @@ -175,14 +178,6 @@ export class BrowserStorageService extends Disposable implements IStorageService }); } - get hasPendingUpdate(): boolean { - return (!!this.globalStorageDatabase && this.globalStorageDatabase.hasPendingUpdate) || (!!this.workspaceStorageDatabase && this.workspaceStorageDatabase.hasPendingUpdate); - } - - flush(): void { - this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); - } - close(): void { // We explicitly do not close our DBs because writing data onBeforeUnload() // can result in unexpected results. Namely, it seems that - even though this @@ -195,10 +190,6 @@ export class BrowserStorageService extends Disposable implements IStorageService this.dispose(); } - isNew(scope: StorageScope): boolean { - return this.getBoolean(IS_NEW_KEY, scope) === true; - } - dispose(): void { dispose(this.runWhenIdleDisposable); this.runWhenIdleDisposable = undefined; diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 6611f1dae..3d176f5b6 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -4,18 +4,27 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Event, Emitter, PauseableEmitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; export const IS_NEW_KEY = '__$__isNewStorageMarker'; +const TARGET_KEY = '__$__targetStorageMarker'; export const IStorageService = createDecorator('storageService'); export enum WillSaveStateReason { - NONE = 0, - SHUTDOWN = 1 + + /** + * No specific reason to save state. + */ + NONE, + + /** + * A hint that the workbench is about to shutdown. + */ + SHUTDOWN } export interface IWillSaveStateEvent { @@ -29,7 +38,12 @@ export interface IStorageService { /** * Emitted whenever data is updated or deleted. */ - readonly onDidChangeStorage: Event; + readonly onDidChangeValue: Event; + + /** + * Emitted whenever target of a storage entry changes. + */ + readonly onDidChangeTarget: Event; /** * Emitted when the storage is about to persist. This is the right time @@ -48,44 +62,49 @@ export interface IStorageService { /** * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. + * the provided `defaultValue` if the element is `null` or `undefined`. * - * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. + * @param scope allows to define the scope of the storage operation + * to either the current workspace only or all workspaces. */ get(key: string, scope: StorageScope, fallbackValue: string): string; get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined; /** * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a boolean. + * the provided `defaultValue` if the element is `null` or `undefined`. + * The element will be converted to a `boolean`. * - * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. + * @param scope allows to define the scope of the storage operation + * to either the current workspace only or all workspaces. */ getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined; /** * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a number using parseInt with a base of 10. + * the provided `defaultValue` if the element is `null` or `undefined`. + * The element will be converted to a `number` using `parseInt` with a + * base of `10`. * - * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. + * @param scope allows to define the scope of the storage operation + * to either the current workspace only or all workspaces. */ getNumber(key: string, scope: StorageScope, fallbackValue: number): number; getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; /** - * Store a value under the given key to storage. The value will be converted to a string. - * Storing either undefined or null will remove the entry under the key. + * Store a value under the given key to storage. The value will be + * converted to a `string`. Storing either `undefined` or `null` will + * remove the entry under the key. * - * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. + * @param scope allows to define the scope of the storage operation + * to either the current workspace only or all workspaces. + * + * @param target allows to define the target of the storage operation + * to either the current machine or user. */ - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void; + store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): void; /** * Delete an element stored under the provided key from storage. @@ -95,6 +114,22 @@ export interface IStorageService { */ remove(key: string, scope: StorageScope): void; + /** + * Returns all the keys used in the storage for the provided `scope` + * and `target`. + * + * Note: this will NOT return all keys stored in the storage layer. + * Some keys may not have an associated `StorageTarget` and thus + * will be excluded from the results. + * + * @param scope allows to define the scope for the keys + * to either the current workspace only or all workspaces. + * + * @param target allows to define the target for the keys + * to either the current machine or user. + */ + keys(scope: StorageScope, target: StorageTarget): string[]; + /** * Log the contents of the storage to the console. */ @@ -108,16 +143,18 @@ export interface IStorageService { /** * Whether the storage for the given scope was created during this session or * existed before. - * */ isNew(scope: StorageScope): boolean; /** * Allows to flush state, e.g. in cases where a shutdown is - * imminent. This will send out the onWillSaveState to ask + * imminent. This will send out the `onWillSaveState` to ask * everyone for latest state. + * + * @returns a `Promise` that can be awaited on when all updates + * to the underlying storage have been flushed. */ - flush(): void; + flush(): Promise; } export const enum StorageScope { @@ -133,21 +170,247 @@ export const enum StorageScope { WORKSPACE } -export interface IWorkspaceStorageChangeEvent { +export const enum StorageTarget { + + /** + * The stored data is user specific and applies across machines. + */ + USER, + + /** + * The stored data is machine specific. + */ + MACHINE +} + +export interface IStorageValueChangeEvent { + + /** + * The scope for the storage entry that changed + * or was removed. + */ + readonly scope: StorageScope; + + /** + * The `key` of the storage entry that was changed + * or was removed. + */ readonly key: string; + + /** + * The `target` can be `undefined` if a key is being + * removed. + */ + readonly target: StorageTarget | undefined; +} + +export interface IStorageTargetChangeEvent { + + /** + * The scope for the target that changed. Listeners + * should use `keys(scope, target)` to get an updated + * list of keys for the given `scope` and `target`. + */ readonly scope: StorageScope; } -export class InMemoryStorageService extends Disposable implements IStorageService { +interface IKeyTargets { + [key: string]: StorageTarget +} + +export abstract class AbstractStorageService extends Disposable implements IStorageService { declare readonly _serviceBrand: undefined; - private readonly _onDidChangeStorage = this._register(new Emitter()); - readonly onDidChangeStorage = this._onDidChangeStorage.event; + private readonly _onDidChangeValue = this._register(new PauseableEmitter()); + readonly onDidChangeValue = this._onDidChangeValue.event; - protected readonly _onWillSaveState = this._register(new Emitter()); + private readonly _onDidChangeTarget = this._register(new PauseableEmitter()); + readonly onDidChangeTarget = this._onDidChangeTarget.event; + + private readonly _onWillSaveState = this._register(new Emitter()); readonly onWillSaveState = this._onWillSaveState.event; + protected emitDidChangeValue(scope: StorageScope, key: string): void { + + // Specially handle `TARGET_KEY` + if (key === TARGET_KEY) { + + // Clear our cached version which is now out of date + if (scope === StorageScope.GLOBAL) { + this._globalKeyTargets = undefined; + } else if (scope === StorageScope.WORKSPACE) { + this._workspaceKeyTargets = undefined; + } + + // Emit as `didChangeTarget` event + this._onDidChangeTarget.fire({ scope }); + } + + // Emit any other key to outside + else { + this._onDidChangeValue.fire({ scope, key, target: this.getKeyTargets(scope)[key] }); + } + } + + protected emitWillSaveState(reason: WillSaveStateReason): void { + this._onWillSaveState.fire({ reason }); + } + + store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): void { + + // We remove the key for undefined/null values + if (isUndefinedOrNull(value)) { + this.remove(key, scope); + return; + } + + // Update our datastructures but send events only after + this.withPausedEmitters(() => { + + // Update key-target map + this.updateKeyTarget(key, scope, target); + + // Store actual value + this.doStore(key, value, scope); + }); + } + + remove(key: string, scope: StorageScope): void { + + // Update our datastructures but send events only after + this.withPausedEmitters(() => { + + // Update key-target map + this.updateKeyTarget(key, scope, undefined); + + // Remove actual key + this.doRemove(key, scope); + }); + } + + private withPausedEmitters(fn: Function): void { + + // Pause emitters + this._onDidChangeValue.pause(); + this._onDidChangeTarget.pause(); + + try { + fn(); + } finally { + + // Resume emitters + this._onDidChangeValue.resume(); + this._onDidChangeTarget.resume(); + } + } + + keys(scope: StorageScope, target: StorageTarget): string[] { + const keys: string[] = []; + + const keyTargets = this.getKeyTargets(scope); + for (const key of Object.keys(keyTargets)) { + const keyTarget = keyTargets[key]; + if (keyTarget === target) { + keys.push(key); + } + } + + return keys; + } + + private updateKeyTarget(key: string, scope: StorageScope, target: StorageTarget | undefined): void { + + // Add + const keyTargets = this.getKeyTargets(scope); + if (typeof target === 'number') { + if (keyTargets[key] !== target) { + keyTargets[key] = target; + this.doStore(TARGET_KEY, JSON.stringify(keyTargets), scope); + } + } + + // Remove + else { + if (typeof keyTargets[key] === 'number') { + delete keyTargets[key]; + this.doStore(TARGET_KEY, JSON.stringify(keyTargets), scope); + } + } + } + + private _workspaceKeyTargets: IKeyTargets | undefined = undefined; + private get workspaceKeyTargets(): IKeyTargets { + if (!this._workspaceKeyTargets) { + this._workspaceKeyTargets = this.loadKeyTargets(StorageScope.WORKSPACE); + } + + return this._workspaceKeyTargets; + } + + private _globalKeyTargets: IKeyTargets | undefined = undefined; + private get globalKeyTargets(): IKeyTargets { + if (!this._globalKeyTargets) { + this._globalKeyTargets = this.loadKeyTargets(StorageScope.GLOBAL); + } + + return this._globalKeyTargets; + } + + private getKeyTargets(scope: StorageScope): IKeyTargets { + return scope === StorageScope.GLOBAL ? this.globalKeyTargets : this.workspaceKeyTargets; + } + + private loadKeyTargets(scope: StorageScope): { [key: string]: StorageTarget } { + const keysRaw = this.get(TARGET_KEY, scope); + if (keysRaw) { + try { + return JSON.parse(keysRaw); + } catch (error) { + // Fail gracefully + } + } + + return Object.create(null); + } + + isNew(scope: StorageScope): boolean { + return this.getBoolean(IS_NEW_KEY, scope) === true; + } + + flush(): Promise { + + // Signal event to collect changes + this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); + + // Await flush + return this.doFlush(); + } + + // --- abstract + + abstract get(key: string, scope: StorageScope, fallbackValue: string): string; + abstract get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined; + + abstract getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; + abstract getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined; + + abstract getNumber(key: string, scope: StorageScope, fallbackValue: number): number; + abstract getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; + + protected abstract doStore(key: string, value: string | boolean | number, scope: StorageScope): void; + + protected abstract doRemove(key: string, scope: StorageScope): void; + + protected abstract doFlush(): Promise; + + abstract migrate(toWorkspace: IWorkspaceInitializationPayload): Promise; + + abstract logStorage(): void; +} + +export class InMemoryStorageService extends AbstractStorageService { + private readonly globalCache = new Map(); private readonly workspaceCache = new Map(); @@ -188,12 +451,7 @@ export class InMemoryStorageService extends Disposable implements IStorageServic return parseInt(value, 10); } - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise { - - // We remove the key for undefined/null values - if (isUndefinedOrNull(value)) { - return this.remove(key, scope); - } + protected doStore(key: string, value: string | boolean | number, scope: StorageScope): void { // Otherwise, convert to String and store const valueStr = String(value); @@ -201,28 +459,24 @@ export class InMemoryStorageService extends Disposable implements IStorageServic // Return early if value already set const currentValue = this.getCache(scope).get(key); if (currentValue === valueStr) { - return Promise.resolve(); + return; } // Update in cache this.getCache(scope).set(key, valueStr); // Events - this._onDidChangeStorage.fire({ scope, key }); - - return Promise.resolve(); + this.emitDidChangeValue(scope, key); } - remove(key: string, scope: StorageScope): Promise { + protected doRemove(key: string, scope: StorageScope): void { const wasDeleted = this.getCache(scope).delete(key); if (!wasDeleted) { - return Promise.resolve(); // Return early if value already deleted + return; // Return early if value already deleted } // Events - this._onDidChangeStorage.fire({ scope, key }); - - return Promise.resolve(); + this.emitDidChangeValue(scope, key); } logStorage(): void { @@ -233,13 +487,7 @@ export class InMemoryStorageService extends Disposable implements IStorageServic // not supported } - flush(): void { - this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); - } - - isNew(): boolean { - return true; // always new when in-memory - } + async doFlush(): Promise { } async close(): Promise { } } diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 096b9e234..97cb9b161 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage, IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { StorageScope, WillSaveStateReason, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; import { Storage, IStorageDatabase, IStorage, StorageHint } from 'vs/base/parts/storage/common/storage'; import { mark } from 'vs/base/common/performance'; @@ -17,19 +16,11 @@ import { IWorkspaceInitializationPayload, isWorkspaceIdentifier, isSingleFolderW import { assertIsDefined } from 'vs/base/common/types'; import { RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; -export class NativeStorageService extends Disposable implements IStorageService { - - declare readonly _serviceBrand: undefined; +export class NativeStorageService extends AbstractStorageService { private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb'; private static readonly WORKSPACE_META_NAME = 'workspace.json'; - private readonly _onDidChangeStorage = this._register(new Emitter()); - readonly onDidChangeStorage = this._onDidChangeStorage.event; - - private readonly _onWillSaveState = this._register(new Emitter()); - readonly onWillSaveState = this._onWillSaveState.event; - private readonly globalStorage = new Storage(this.globalStorageDatabase); private workspaceStoragePath: string | undefined; @@ -54,11 +45,7 @@ export class NativeStorageService extends Disposable implements IStorageService private registerListeners(): void { // Global Storage change events - this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL))); - } - - private handleDidChangeStorage(key: string, scope: StorageScope): void { - this._onDidChangeStorage.fire({ key, scope }); + this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); } initialize(payload?: IWorkspaceInitializationPayload): Promise { @@ -134,7 +121,7 @@ export class NativeStorageService extends Disposable implements IStorageService // Create new this.workspaceStoragePath = workspaceStoragePath; this.workspaceStorage = new Storage(new SQLiteStorageDatabase(workspaceStoragePath, { logging: workspaceLoggingOptions }), { hint }); - this.workspaceStorageListener = this.workspaceStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.WORKSPACE)); + this.workspaceStorageListener = this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)); return this.workspaceStorage; } @@ -201,11 +188,11 @@ export class NativeStorageService extends Disposable implements IStorageService return this.getStorage(scope).getNumber(key, fallbackValue); } - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { + protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { this.getStorage(scope).set(key, value); } - remove(key: string, scope: StorageScope): void { + protected doRemove(key: string, scope: StorageScope): void { this.getStorage(scope).delete(key); } @@ -213,6 +200,19 @@ export class NativeStorageService extends Disposable implements IStorageService return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage); } + protected async doFlush(): Promise { + const promises: Promise[] = []; + if (this.globalStorage) { + promises.push(this.globalStorage.whenFlushed()); + } + + if (this.workspaceStorage) { + promises.push(this.workspaceStorage.whenFlushed()); + } + + await Promise.all(promises); + } + private doFlushWhenIdle(): void { // Dispose any previous idle runner @@ -229,10 +229,6 @@ export class NativeStorageService extends Disposable implements IStorageService }); } - flush(): void { - this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); - } - async close(): Promise { // Stop periodic scheduler and idle runner as we now collect state normally @@ -241,7 +237,7 @@ export class NativeStorageService extends Disposable implements IStorageService this.runWhenIdleDisposable = undefined; // Signal as event so that clients can still store data - this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); + this.emitWillSaveState(WillSaveStateReason.SHUTDOWN); // Do it await Promise.all([ @@ -277,8 +273,4 @@ export class NativeStorageService extends Disposable implements IStorageService // Recreate and init workspace storage return this.createWorkspaceStorage(newWorkspaceStoragePath).init(); } - - isNew(scope: StorageScope): boolean { - return this.getBoolean(IS_NEW_KEY, scope) === true; - } } diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts new file mode 100644 index 000000000..886efb2f8 --- /dev/null +++ b/src/vs/platform/storage/test/common/storageService.test.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { strictEqual, ok, equal } from 'assert'; +import { StorageScope, InMemoryStorageService, StorageTarget, IStorageValueChangeEvent, IStorageTargetChangeEvent } from 'vs/platform/storage/common/storage'; + +suite('StorageService', function () { + + test('Get Data, Integer, Boolean (global, in-memory)', () => { + storeData(StorageScope.GLOBAL); + }); + + test('Get Data, Integer, Boolean (workspace, in-memory)', () => { + storeData(StorageScope.WORKSPACE); + }); + + function storeData(scope: StorageScope): void { + const storage = new InMemoryStorageService(); + + let storageValueChangeEvents: IStorageValueChangeEvent[] = []; + storage.onDidChangeValue(e => storageValueChangeEvents.push(e)); + + strictEqual(storage.get('test.get', scope, 'foobar'), 'foobar'); + strictEqual(storage.get('test.get', scope, ''), ''); + strictEqual(storage.getNumber('test.getNumber', scope, 5), 5); + strictEqual(storage.getNumber('test.getNumber', scope, 0), 0); + strictEqual(storage.getBoolean('test.getBoolean', scope, true), true); + strictEqual(storage.getBoolean('test.getBoolean', scope, false), false); + + storage.store('test.get', 'foobar', scope, StorageTarget.MACHINE); + strictEqual(storage.get('test.get', scope, (undefined)!), 'foobar'); + let storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.get'); + equal(storageValueChangeEvent?.scope, scope); + equal(storageValueChangeEvent?.key, 'test.get'); + storageValueChangeEvents = []; + + storage.store('test.get', '', scope, StorageTarget.MACHINE); + strictEqual(storage.get('test.get', scope, (undefined)!), ''); + storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.get'); + equal(storageValueChangeEvent!.scope, scope); + equal(storageValueChangeEvent!.key, 'test.get'); + + storage.store('test.getNumber', 5, scope, StorageTarget.MACHINE); + strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 5); + + storage.store('test.getNumber', 0, scope, StorageTarget.MACHINE); + strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 0); + + storage.store('test.getBoolean', true, scope, StorageTarget.MACHINE); + strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), true); + + storage.store('test.getBoolean', false, scope, StorageTarget.MACHINE); + strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), false); + + strictEqual(storage.get('test.getDefault', scope, 'getDefault'), 'getDefault'); + strictEqual(storage.getNumber('test.getNumberDefault', scope, 5), 5); + strictEqual(storage.getBoolean('test.getBooleanDefault', scope, true), true); + } + + test('Remove Data (global, in-memory)', () => { + removeData(StorageScope.GLOBAL); + }); + + test('Remove Data (workspace, in-memory)', () => { + removeData(StorageScope.WORKSPACE); + }); + + function removeData(scope: StorageScope): void { + const storage = new InMemoryStorageService(); + + let storageValueChangeEvents: IStorageValueChangeEvent[] = []; + storage.onDidChangeValue(e => storageValueChangeEvents.push(e)); + + storage.store('test.remove', 'foobar', scope, StorageTarget.MACHINE); + strictEqual('foobar', storage.get('test.remove', scope, (undefined)!)); + + storage.remove('test.remove', scope); + ok(!storage.get('test.remove', scope, (undefined)!)); + let storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.remove'); + equal(storageValueChangeEvent?.scope, scope); + equal(storageValueChangeEvent?.key, 'test.remove'); + } + + test('Keys (in-memory)', () => { + const storage = new InMemoryStorageService(); + + let storageTargetEvent: IStorageTargetChangeEvent | undefined = undefined; + storage.onDidChangeTarget(e => storageTargetEvent = e); + + let storageValueChangeEvent: IStorageValueChangeEvent | undefined = undefined; + storage.onDidChangeValue(e => storageValueChangeEvent = e); + + // Empty + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + strictEqual(storage.keys(scope, target).length, 0); + } + } + + // Add values + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + storageTargetEvent = Object.create(null); + storageValueChangeEvent = Object.create(null); + + storage.store('test.target1', 'value1', scope, target); + strictEqual(storage.keys(scope, target).length, 1); + equal(storageTargetEvent?.scope, scope); + equal(storageValueChangeEvent?.key, 'test.target1'); + equal(storageValueChangeEvent?.scope, scope); + equal(storageValueChangeEvent?.target, target); + + storageTargetEvent = undefined; + storageValueChangeEvent = Object.create(null); + + storage.store('test.target1', 'otherValue1', scope, target); + strictEqual(storage.keys(scope, target).length, 1); + equal(storageTargetEvent, undefined); + equal(storageValueChangeEvent?.key, 'test.target1'); + equal(storageValueChangeEvent?.scope, scope); + equal(storageValueChangeEvent?.target, target); + + storage.store('test.target2', 'value2', scope, target); + storage.store('test.target3', 'value3', scope, target); + + strictEqual(storage.keys(scope, target).length, 3); + } + } + + // Remove values + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + const keysLength = storage.keys(scope, target).length; + + storage.store('test.target4', 'value1', scope, target); + strictEqual(storage.keys(scope, target).length, keysLength + 1); + + storageTargetEvent = Object.create(null); + storageValueChangeEvent = Object.create(null); + + storage.remove('test.target4', scope); + strictEqual(storage.keys(scope, target).length, keysLength); + equal(storageTargetEvent?.scope, scope); + equal(storageValueChangeEvent?.key, 'test.target4'); + equal(storageValueChangeEvent?.scope, scope); + } + } + + // Remove all + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + const keys = storage.keys(scope, target); + + for (const key of keys) { + storage.remove(key, scope); + } + + strictEqual(storage.keys(scope, target).length, 0); + } + } + + // Adding undefined or null removes value + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + storage.store('test.target1', 'value1', scope, target); + strictEqual(storage.keys(scope, target).length, 1); + + storageTargetEvent = Object.create(null); + + storage.store('test.target1', undefined, scope, target); + strictEqual(storage.keys(scope, target).length, 0); + equal(storageTargetEvent?.scope, scope); + + storage.store('test.target1', '', scope, target); + strictEqual(storage.keys(scope, target).length, 1); + + storage.store('test.target1', null, scope, target); + strictEqual(storage.keys(scope, target).length, 0); + } + } + + // Target change + storageTargetEvent = undefined; + storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.USER); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + ok(!storageTargetEvent); // no change in target + }); +}); diff --git a/src/vs/platform/storage/test/node/storageService.test.ts b/src/vs/platform/storage/test/node/storageService.test.ts index a87802a84..e0a410ef6 100644 --- a/src/vs/platform/storage/test/node/storageService.test.ts +++ b/src/vs/platform/storage/test/node/storageService.test.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { strictEqual, ok, equal } from 'assert'; -import { StorageScope, InMemoryStorageService } from 'vs/platform/storage/common/storage'; +import { equal } from 'assert'; +import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { NativeStorageService } from 'vs/platform/storage/node/storageService'; import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'vs/base/common/path'; @@ -16,66 +16,7 @@ import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; import { URI } from 'vs/base/common/uri'; -suite('StorageService', function () { - - test('Remove Data (global, in-memory)', () => { - removeData(StorageScope.GLOBAL); - }); - - test('Remove Data (workspace, in-memory)', () => { - removeData(StorageScope.WORKSPACE); - }); - - function removeData(scope: StorageScope): void { - const storage = new InMemoryStorageService(); - - storage.store('test.remove', 'foobar', scope); - strictEqual('foobar', storage.get('test.remove', scope, (undefined)!)); - - storage.remove('test.remove', scope); - ok(!storage.get('test.remove', scope, (undefined)!)); - } - - test('Get Data, Integer, Boolean (global, in-memory)', () => { - storeData(StorageScope.GLOBAL); - }); - - test('Get Data, Integer, Boolean (workspace, in-memory)', () => { - storeData(StorageScope.WORKSPACE); - }); - - function storeData(scope: StorageScope): void { - const storage = new InMemoryStorageService(); - - strictEqual(storage.get('test.get', scope, 'foobar'), 'foobar'); - strictEqual(storage.get('test.get', scope, ''), ''); - strictEqual(storage.getNumber('test.getNumber', scope, 5), 5); - strictEqual(storage.getNumber('test.getNumber', scope, 0), 0); - strictEqual(storage.getBoolean('test.getBoolean', scope, true), true); - strictEqual(storage.getBoolean('test.getBoolean', scope, false), false); - - storage.store('test.get', 'foobar', scope); - strictEqual(storage.get('test.get', scope, (undefined)!), 'foobar'); - - storage.store('test.get', '', scope); - strictEqual(storage.get('test.get', scope, (undefined)!), ''); - - storage.store('test.getNumber', 5, scope); - strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 5); - - storage.store('test.getNumber', 0, scope); - strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 0); - - storage.store('test.getBoolean', true, scope); - strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), true); - - storage.store('test.getBoolean', false, scope); - strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), false); - - strictEqual(storage.get('test.getDefault', scope, 'getDefault'), 'getDefault'); - strictEqual(storage.getNumber('test.getNumberDefault', scope, 5), 5); - strictEqual(storage.getBoolean('test.getBooleanDefault', scope, true), true); - } +suite('NativeStorageService', function () { function uniqueStorageDir(): string { const id = generateUuid(); @@ -111,9 +52,13 @@ suite('StorageService', function () { const storage = new NativeStorageService(new InMemoryStorageDatabase(), new NullLogService(), new StorageTestEnvironmentService(URI.file(storageDir), storageDir)); await storage.initialize({ id: String(Date.now()) }); - storage.store('bar', 'foo', StorageScope.WORKSPACE); - storage.store('barNumber', 55, StorageScope.WORKSPACE); - storage.store('barBoolean', true, StorageScope.GLOBAL); + storage.store('bar', 'foo', StorageScope.WORKSPACE, StorageTarget.MACHINE); + storage.store('barNumber', 55, StorageScope.WORKSPACE, StorageTarget.MACHINE); + storage.store('barBoolean', true, StorageScope.GLOBAL, StorageTarget.MACHINE); + + equal(storage.get('bar', StorageScope.WORKSPACE), 'foo'); + equal(storage.getNumber('barNumber', StorageScope.WORKSPACE), 55); + equal(storage.getBoolean('barBoolean', StorageScope.GLOBAL), true); await storage.migrate({ id: String(Date.now() + 100) }); diff --git a/src/vs/platform/telemetry/browser/errorTelemetry.ts b/src/vs/platform/telemetry/browser/errorTelemetry.ts index 6ecc99a21..6ef338fd8 100644 --- a/src/vs/platform/telemetry/browser/errorTelemetry.ts +++ b/src/vs/platform/telemetry/browser/errorTelemetry.ts @@ -5,7 +5,7 @@ import { toDisposable } from 'vs/base/common/lifecycle'; import { globals } from 'vs/base/common/platform'; -import BaseErrorTelemetry, { ErrorEvent } from '../common/errorTelemetry'; +import BaseErrorTelemetry, { ErrorEvent } from 'vs/platform/telemetry/common/errorTelemetry'; export default class ErrorTelemetry extends BaseErrorTelemetry { protected installErrorListeners(): void { diff --git a/src/vs/platform/telemetry/node/appInsightsAppender.ts b/src/vs/platform/telemetry/node/appInsightsAppender.ts index b288c282a..0001e1772 100644 --- a/src/vs/platform/telemetry/node/appInsightsAppender.ts +++ b/src/vs/platform/telemetry/node/appInsightsAppender.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as appInsights from 'applicationinsights'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { mixin } from 'vs/base/common/objects'; import { ITelemetryAppender, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; -function getClient(aiKey: string): appInsights.TelemetryClient { - +async function getClient(aiKey: string): Promise { + const appInsights = await import('applicationinsights'); let client: appInsights.TelemetryClient; if (appInsights.defaultClient) { client = new appInsights.TelemetryClient(aiKey); @@ -36,7 +37,8 @@ function getClient(aiKey: string): appInsights.TelemetryClient { export class AppInsightsAppender implements ITelemetryAppender { - private _aiClient?: appInsights.TelemetryClient; + private _aiClient: string | appInsights.TelemetryClient | undefined; + private _asyncAIClient: Promise | null; constructor( private _eventPrefix: string, @@ -47,11 +49,37 @@ export class AppInsightsAppender implements ITelemetryAppender { this._defaultData = Object.create(null); } - if (typeof aiKeyOrClientFactory === 'string') { - this._aiClient = getClient(aiKeyOrClientFactory); - } else if (typeof aiKeyOrClientFactory === 'function') { + if (typeof aiKeyOrClientFactory === 'function') { this._aiClient = aiKeyOrClientFactory(); + } else { + this._aiClient = aiKeyOrClientFactory; } + this._asyncAIClient = null; + } + + private _withAIClient(callback: (aiClient: appInsights.TelemetryClient) => void): void { + if (!this._aiClient) { + return; + } + + if (typeof this._aiClient !== 'string') { + callback(this._aiClient); + return; + } + + if (!this._asyncAIClient) { + this._asyncAIClient = getClient(this._aiClient); + } + + this._asyncAIClient.then( + (aiClient) => { + callback(aiClient); + }, + (err) => { + onUnexpectedError(err); + console.error(err); + } + ); } log(eventName: string, data?: any): void { @@ -61,22 +89,24 @@ export class AppInsightsAppender implements ITelemetryAppender { data = mixin(data, this._defaultData); data = validateTelemetryData(data); - this._aiClient.trackEvent({ + this._withAIClient((aiClient) => aiClient.trackEvent({ name: this._eventPrefix + '/' + eventName, properties: data.properties, measurements: data.measurements - }); + })); } flush(): Promise { if (this._aiClient) { return new Promise(resolve => { - this._aiClient!.flush({ - callback: () => { - // all data flushed - this._aiClient = undefined; - resolve(undefined); - } + this._withAIClient((aiClient) => { + aiClient.flush({ + callback: () => { + // all data flushed + this._aiClient = undefined; + resolve(undefined); + } + }); }); }); } diff --git a/src/vs/platform/telemetry/node/errorTelemetry.ts b/src/vs/platform/telemetry/node/errorTelemetry.ts index 60f1633ee..562eb44b9 100644 --- a/src/vs/platform/telemetry/node/errorTelemetry.ts +++ b/src/vs/platform/telemetry/node/errorTelemetry.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; -import BaseErrorTelemetry from '../common/errorTelemetry'; +import BaseErrorTelemetry from 'vs/platform/telemetry/common/errorTelemetry'; export default class ErrorTelemetry extends BaseErrorTelemetry { protected installErrorListeners(): void { diff --git a/src/vs/platform/theme/browser/checkbox.ts b/src/vs/platform/theme/browser/checkbox.ts deleted file mode 100644 index c479126ee..000000000 --- a/src/vs/platform/theme/browser/checkbox.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CheckboxActionViewItem } from 'vs/base/browser/ui/checkbox/checkbox'; -import { IAction } from 'vs/base/common/actions'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { attachCheckboxStyler } from 'vs/platform/theme/common/styler'; -import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; - -export class ThemableCheckboxActionViewItem extends CheckboxActionViewItem { - - constructor(context: any, action: IAction, options: IBaseActionViewItemOptions | undefined, private readonly themeService: IThemeService) { - super(context, action, options); - } - - render(container: HTMLElement): void { - super.render(container); - if (this.checkbox) { - this.disposables.add(attachCheckboxStyler(this.checkbox, this.themeService)); - } - } - -} - diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index c9de28ba8..b5239353a 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -196,7 +196,7 @@ export const textBlockQuoteBorder = registerColor('textBlockQuote.border', { lig export const textCodeBlockBackground = registerColor('textCodeBlock.background', { light: '#dcdcdc66', dark: '#0a0a0a66', hc: Color.black }, nls.localize('textCodeBlockBackground', "Background color for code blocks in text.")); // ----- widgets -export const widgetShadow = registerColor('widget.shadow', { dark: '#000000', light: '#A8A8A8', hc: null }, nls.localize('widgetShadow', 'Shadow color of widgets such as find/replace inside the editor.')); +export const widgetShadow = registerColor('widget.shadow', { dark: transparent(Color.black, .36), light: transparent(Color.black, .16), hc: null }, nls.localize('widgetShadow', 'Shadow color of widgets such as find/replace inside the editor.')); export const inputBackground = registerColor('input.background', { dark: '#3C3C3C', light: Color.white, hc: Color.black }, nls.localize('inputBoxBackground', "Input box background.")); export const inputForeground = registerColor('input.foreground', { dark: foreground, light: foreground, hc: foreground }, nls.localize('inputBoxForeground', "Input box foreground.")); @@ -243,18 +243,23 @@ export const scrollbarSliderActiveBackground = registerColor('scrollbarSlider.ac export const progressBarBackground = registerColor('progressBar.background', { dark: Color.fromHex('#0E70C0'), light: Color.fromHex('#0E70C0'), hc: contrastBorder }, nls.localize('progressBarBackground', "Background color of the progress bar that can show for long running operations.")); +export const editorErrorBackground = registerColor('editorError.background', { dark: null, light: null, hc: null }, nls.localize('editorError.background', 'Background color of error text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorErrorForeground = registerColor('editorError.foreground', { dark: '#F48771', light: '#E51400', hc: null }, nls.localize('editorError.foreground', 'Foreground color of error squigglies in the editor.')); export const editorErrorBorder = registerColor('editorError.border', { dark: null, light: null, hc: Color.fromHex('#E47777').transparent(0.8) }, nls.localize('errorBorder', 'Border color of error boxes in the editor.')); +export const editorWarningBackground = registerColor('editorWarning.background', { dark: null, light: null, hc: null }, nls.localize('editorWarning.background', 'Background color of warning text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorWarningForeground = registerColor('editorWarning.foreground', { dark: '#CCA700', light: '#E9A700', hc: null }, nls.localize('editorWarning.foreground', 'Foreground color of warning squigglies in the editor.')); export const editorWarningBorder = registerColor('editorWarning.border', { dark: null, light: null, hc: Color.fromHex('#FFCC00').transparent(0.8) }, nls.localize('warningBorder', 'Border color of warning boxes in the editor.')); +export const editorInfoBackground = registerColor('editorInfo.background', { dark: null, light: null, hc: null }, nls.localize('editorInfo.background', 'Background color of info text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorInfoForeground = registerColor('editorInfo.foreground', { dark: '#75BEFF', light: '#75BEFF', hc: null }, nls.localize('editorInfo.foreground', 'Foreground color of info squigglies in the editor.')); export const editorInfoBorder = registerColor('editorInfo.border', { dark: null, light: null, hc: Color.fromHex('#75BEFF').transparent(0.8) }, nls.localize('infoBorder', 'Border color of info boxes in the editor.')); export const editorHintForeground = registerColor('editorHint.foreground', { dark: Color.fromHex('#eeeeee').transparent(0.7), light: '#6c6c6c', hc: null }, nls.localize('editorHint.foreground', 'Foreground color of hint squigglies in the editor.')); export const editorHintBorder = registerColor('editorHint.border', { dark: null, light: null, hc: Color.fromHex('#eeeeee').transparent(0.8) }, nls.localize('hintBorder', 'Border color of hint boxes in the editor.')); +export const sashHoverBorder = registerColor('sash.hoverBorder', { dark: null, light: null, hc: null }, nls.localize('sashActiveBorder', "Border color of active sashes.")); + /** * Editor background color. * Because of bug https://monacotools.visualstudio.com/DefaultCollection/Monaco/_workitems/edit/13254 @@ -353,7 +358,7 @@ export const diffDiagonalFill = registerColor('diffEditor.diagonalFill', { dark: */ export const listFocusBackground = registerColor('list.focusBackground', { dark: '#062F4A', light: '#D6EBFF', hc: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusForeground = registerColor('list.focusForeground', { dark: null, light: null, hc: null }, nls.localize('listFocusForeground', "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); -export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#0074E8', hc: null }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#0060C0', hc: null }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionForeground = registerColor('list.activeSelectionForeground', { dark: Color.white, light: Color.white, hc: null }, nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#37373D', light: '#E4E6F1', hc: null }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionForeground = registerColor('list.inactiveSelectionForeground', { dark: null, light: null, hc: null }, nls.localize('listInactiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); diff --git a/src/vs/platform/theme/common/iconRegistry.ts b/src/vs/platform/theme/common/iconRegistry.ts index da0df3677..a19a41a84 100644 --- a/src/vs/platform/theme/common/iconRegistry.ts +++ b/src/vs/platform/theme/common/iconRegistry.ts @@ -12,6 +12,7 @@ import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/plat import { RunOnceScheduler } from 'vs/base/common/async'; import * as Codicons from 'vs/base/common/codicons'; + // ------ API types @@ -29,14 +30,14 @@ export interface IconDefinition { export interface IconContribution { id: string; - description: string; + description: string | undefined; deprecationMessage?: string; defaults: IconDefaults; } export interface IIconRegistry { - readonly onDidChangeSchema: Event; + readonly onDidChange: Event; /** * Register a icon to the registry. @@ -44,7 +45,7 @@ export interface IIconRegistry { * @param defaults The default values * @description the description */ - registerIcon(id: string, defaults: IconDefaults, description: string): ThemeIcon; + registerIcon(id: string, defaults: IconDefaults, description?: string): ThemeIcon; /** * Register a icon to the registry. @@ -71,12 +72,17 @@ export interface IIconRegistry { */ getIconReferenceSchema(): IJSONSchema; + /** + * The CSS for all icons + */ + getCSS(): string; + } class IconRegistry implements IIconRegistry { - private readonly _onDidChangeSchema = new Emitter(); - readonly onDidChangeSchema: Event = this._onDidChangeSchema.event; + private readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; private iconsById: { [key: string]: IconContribution }; private iconSchema: IJSONSchema & { properties: IJSONSchemaMap } = { @@ -101,8 +107,18 @@ class IconRegistry implements IIconRegistry { } public registerIcon(id: string, defaults: IconDefaults, description?: string, deprecationMessage?: string): ThemeIcon { - if (!description) { - description = localize('icon.defaultDescription', 'Icon with identifier \'{0}\'', id); + const existing = this.iconsById[id]; + if (existing) { + if (description && !existing.description) { + existing.description = description; + this.iconSchema.properties[id].markdownDescription = `${description} $(${id})`; + const enumIndex = this.iconReferenceSchema.enum.indexOf(id); + if (enumIndex !== -1) { + this.iconReferenceSchema.enumDescriptions[enumIndex] = description; + } + this._onDidChange.fire(); + } + return existing; } let iconContribution: IconContribution = { id, description, defaults, deprecationMessage }; this.iconsById[id] = iconContribution; @@ -110,12 +126,14 @@ class IconRegistry implements IIconRegistry { if (deprecationMessage) { propertySchema.deprecationMessage = deprecationMessage; } - propertySchema.markdownDescription = `${description}: $(${id})`; + if (description) { + propertySchema.markdownDescription = `${description}: $(${id})`; + } this.iconSchema.properties[id] = propertySchema; this.iconReferenceSchema.enum.push(id); - this.iconReferenceSchema.enumDescriptions.push(description); + this.iconReferenceSchema.enumDescriptions.push(description || ''); - this._onDidChangeSchema.fire(); + this._onDidChange.fire(); return { id }; } @@ -128,7 +146,7 @@ class IconRegistry implements IIconRegistry { this.iconReferenceSchema.enum.splice(index, 1); this.iconReferenceSchema.enumDescriptions.splice(index, 1); } - this._onDidChangeSchema.fire(); + this._onDidChange.fire(); } public getIcons(): IconContribution[] { @@ -147,6 +165,29 @@ class IconRegistry implements IIconRegistry { return this.iconReferenceSchema; } + public getCSS() { + const rules = []; + for (let id in this.iconsById) { + const rule = this.formatRule(id); + if (rule) { + rules.push(rule); + } + } + return rules.join('\n'); + } + + private formatRule(id: string): string | undefined { + let definition = this.iconsById[id].defaults; + while (ThemeIcon.isThemeIcon(definition)) { + const c = this.iconsById[definition.id]; + if (!c) { + return undefined; + } + definition = c.defaults; + } + return `.codicon-${id}:before { content: '${definition.character}'; }`; + } + public toString() { const sorter = (i1: IconContribution, i2: IconContribution) => { const isThemeIcon1 = ThemeIcon.isThemeIcon(i1.defaults); @@ -169,7 +210,7 @@ class IconRegistry implements IIconRegistry { const contributions = Object.keys(this.iconsById).map(key => this.iconsById[key]); for (const i of contributions.sort(sorter)) { - reference.push(`||${i.id}|${ThemeIcon.isThemeIcon(i.defaults) ? i.defaults.id : ''}|`); + reference.push(`||${i.id}|${ThemeIcon.isThemeIcon(i.defaults) ? i.defaults.id : ''}|${i.description || ''}|`); if (!ThemeIcon.isThemeIcon((i.defaults))) { docCss.push(`.codicon-${i.id}:before { content: "${i.defaults.character}" }`); @@ -183,7 +224,7 @@ class IconRegistry implements IIconRegistry { const iconRegistry = new IconRegistry(); platform.Registry.add(Extensions.IconContribution, iconRegistry); -export function registerIcon(id: string, defaults: IconDefaults, description?: string, deprecationMessage?: string): ThemeIcon { +export function registerIcon(id: string, defaults: IconDefaults, description: string, deprecationMessage?: string): ThemeIcon { return iconRegistry.registerIcon(id, defaults, description, deprecationMessage); } @@ -193,20 +234,19 @@ export function getIconRegistry(): IIconRegistry { function initialize() { for (const icon of Codicons.iconRegistry.all) { - registerIcon(icon.id, icon.definition); + iconRegistry.registerIcon(icon.id, icon.definition, icon.description); } - Codicons.iconRegistry.onDidRegister(icon => registerIcon(icon.id, icon.definition)); + Codicons.iconRegistry.onDidRegister(icon => iconRegistry.registerIcon(icon.id, icon.definition, icon.description)); } initialize(); - export const iconsSchemaId = 'vscode://schemas/icons'; let schemaRegistry = platform.Registry.as(JSONExtensions.JSONContribution); schemaRegistry.registerSchema(iconsSchemaId, iconRegistry.getIconSchema()); const delayer = new RunOnceScheduler(() => schemaRegistry.notifySchemaChanged(iconsSchemaId), 200); -iconRegistry.onDidChangeSchema(() => { +iconRegistry.onDidChange(() => { if (!delayer.isScheduled()) { delayer.schedule(); } @@ -214,3 +254,11 @@ iconRegistry.onDidChangeSchema(() => { //setTimeout(_ => console.log(iconRegistry.toString()), 5000); + + +// common icons + +export const widgetClose = registerIcon('widget-close', Codicons.Codicon.close, localize('widgetClose', 'Icon for the close action in widgets.')); + +export const gotoPreviousLocation = registerIcon('goto-previous-location', Codicons.Codicon.arrowUp, localize('previousChangeIcon', 'Icon for goto previous editor location.')); +export const gotoNextLocation = registerIcon('goto-next-location', Codicons.Codicon.arrowDown, localize('nextChangeIcon', 'Icon for goto next editor location.')); diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 0ae9c30fe..f82c45767 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -11,6 +11,7 @@ import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { Event, Emitter } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { CSSIcon } from 'vs/base/common/codicons'; export const IThemeService = createDecorator('themeService'); @@ -18,6 +19,12 @@ export interface ThemeColor { id: string; } +export namespace ThemeColor { + export function isThemeColor(obj: any): obj is ThemeColor { + return obj && typeof obj === 'object' && typeof (obj).id === 'string'; + } +} + export function themeColorFromId(id: ColorIdentifier) { return { id }; } @@ -29,8 +36,8 @@ export interface ThemeIcon { } export namespace ThemeIcon { - export function isThemeIcon(obj: any): obj is ThemeIcon | { id: string } { - return obj && typeof obj === 'object' && typeof (obj).id === 'string'; + export function isThemeIcon(obj: any): obj is ThemeIcon { + return obj && typeof obj === 'object' && typeof (obj).id === 'string' && (typeof (obj).color === 'undefined' || ThemeColor.isThemeColor((obj).color)); } const _regexFromString = /^\$\(([a-z.]+\/)?([a-z-~]+)\)$/i; @@ -41,26 +48,67 @@ export namespace ThemeIcon { return undefined; } let [, owner, name] = match; - if (!owner) { - owner = `codicon/`; + if (!owner || owner === 'codicon/') { + return { id: name }; } return { id: owner + name }; } + export function modify(icon: ThemeIcon, modifier: 'disabled' | 'spin' | undefined): ThemeIcon { + let id = icon.id; + const tildeIndex = id.lastIndexOf('~'); + if (tildeIndex !== -1) { + id = id.substring(0, tildeIndex); + } + if (modifier) { + id = `${id}~${modifier}`; + } + return { id }; + } + + export function isEqual(ti1: ThemeIcon, ti2: ThemeIcon): boolean { + return ti1.id === ti2.id && ti1.color?.id === ti2.color?.id; + } + const _regexAsClassName = /^(codicon\/)?([a-z-]+)(~[a-z]+)?$/i; - export function asClassName(icon: ThemeIcon): string | undefined { - // todo@martin,joh -> this should go into the ThemeService + export function asClassNameArray(icon: ThemeIcon): string[] { const match = _regexAsClassName.exec(icon.id); if (!match) { - return undefined; + return ['codicon', 'codicon-error']; } let [, , name, modifier] = match; - let className = `codicon codicon-${name}`; + let className = `codicon-${name}`; if (modifier) { - className += ` ${modifier.substr(1)}`; + return ['codicon', className, modifier.substr(1)]; } - return className; + return ['codicon', className]; + } + + + export function asClassName(icon: ThemeIcon): string { + return asClassNameArray(icon).join(' '); + } + + export function asCSSSelector(icon: ThemeIcon): string { + return '.' + asClassNameArray(icon).join('.'); + } + + export function asCSSIcon(icon: ThemeIcon): CSSIcon { + return { + classNames: asClassName(icon) + }; + } + + export function asCodiconLabel(icon: ThemeIcon): string { + return '$(' + icon.id + ')'; + } + + export function revive(icon: any): ThemeIcon | undefined { + if (ThemeIcon.isThemeIcon(icon)) { + return { id: icon.id, color: icon.color ? { id: icon.color.id } : undefined }; + } + return undefined; } } diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index ab705dbca..cf7a8df0f 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -522,8 +522,9 @@ function createDefaultTokenClassificationRegistry(): TokenClassificationRegistry registerTokenType('typeParameter', nls.localize('typeParameter', "Style for type parameters."), [['entity.name.type.parameter']]); registerTokenType('function', nls.localize('function', "Style for functions"), [['entity.name.function'], ['support.function']]); - registerTokenType('member', nls.localize('member', "Style for member"), [['entity.name.function.member'], ['support.function']]); - registerTokenType('macro', nls.localize('macro', "Style for macros."), [['entity.name.other.preprocessor.macro']]); + registerTokenType('member', nls.localize('member', "Style for member functions"), [], 'method', 'Deprecated use `method` instead'); + registerTokenType('method', nls.localize('method', "Style for method (member functions)"), [['entity.name.function.member'], ['support.function']]); + registerTokenType('macro', nls.localize('macro', "Style for macros."), [['entity.name.function.preprocessor']]); registerTokenType('variable', nls.localize('variable', "Style for variables."), [['variable.other.readwrite'], ['entity.name.variable']]); registerTokenType('parameter', nls.localize('parameter', "Style for parameters."), [['variable.parameter']]); diff --git a/src/vs/platform/theme/electron-main/themeMainService.ts b/src/vs/platform/theme/electron-main/themeMainService.ts index 7bbacdb3d..1d07cf7bf 100644 --- a/src/vs/platform/theme/electron-main/themeMainService.ts +++ b/src/vs/platform/theme/electron-main/themeMainService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { isWindows, isMacintosh } from 'vs/base/common/platform'; -import { ipcMain as ipc, nativeTheme } from 'electron'; +import { ipcMain, nativeTheme } from 'electron'; import { IStateService } from 'vs/platform/state/node/state'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -28,7 +28,7 @@ export class ThemeMainService implements IThemeMainService { declare readonly _serviceBrand: undefined; constructor(@IStateService private stateService: IStateService) { - ipc.on('vscode:changeColorTheme', (e: Event, windowId: number, broadcast: string) => { + ipcMain.on('vscode:changeColorTheme', (e: Event, windowId: number, broadcast: string) => { // Theme changes if (typeof broadcast === 'string') { this.storeBackgroundColor(JSON.parse(broadcast)); diff --git a/src/vs/platform/undoRedo/common/undoRedo.ts b/src/vs/platform/undoRedo/common/undoRedo.ts index c70e013dd..021003bab 100644 --- a/src/vs/platform/undoRedo/common/undoRedo.ts +++ b/src/vs/platform/undoRedo/common/undoRedo.ts @@ -81,6 +81,27 @@ export class UndoRedoGroup { public static None = new UndoRedoGroup(); } +export class UndoRedoSource { + private static _ID = 0; + + public readonly id: number; + private order: number; + + constructor() { + this.id = UndoRedoSource._ID++; + this.order = 1; + } + + public nextOrder(): number { + if (this.id === 0) { + return 0; + } + return this.order++; + } + + public static None = new UndoRedoSource(); +} + export interface IUndoRedoService { readonly _serviceBrand: undefined; @@ -100,7 +121,7 @@ export interface IUndoRedoService { * Add a new element to the `undo` stack. * This will destroy the `redo` stack. */ - pushElement(element: IUndoRedoElement, group?: UndoRedoGroup): void; + pushElement(element: IUndoRedoElement, group?: UndoRedoGroup, source?: UndoRedoSource): void; /** * Get the last pushed element for a resource. @@ -133,9 +154,9 @@ export interface IUndoRedoService { */ restoreSnapshot(snapshot: ResourceEditStackSnapshot): void; - canUndo(resource: URI): boolean; - undo(resource: URI): Promise | void; + canUndo(resource: URI | UndoRedoSource): boolean; + undo(resource: URI | UndoRedoSource): Promise | void; - canRedo(resource: URI): boolean; - redo(resource: URI): Promise | void; + canRedo(resource: URI | UndoRedoSource): boolean; + redo(resource: URI | UndoRedoSource): Promise | void; } diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index 2a5bdb84d..2fb802fbc 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IUndoRedoService, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements, ResourceEditStackSnapshot, UriComparisonKeyComputer, IResourceUndoRedoElement, UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoService, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements, ResourceEditStackSnapshot, UriComparisonKeyComputer, IResourceUndoRedoElement, UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; import { onUnexpectedError } from 'vs/base/common/errors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -34,9 +34,11 @@ class ResourceStackElement { public readonly strResources: string[]; public readonly groupId: number; public readonly groupOrder: number; + public readonly sourceId: number; + public readonly sourceOrder: number; public isValid: boolean; - constructor(actual: IUndoRedoElement, resourceLabel: string, strResource: string, groupId: number, groupOrder: number) { + constructor(actual: IUndoRedoElement, resourceLabel: string, strResource: string, groupId: number, groupOrder: number, sourceId: number, sourceOrder: number) { this.actual = actual; this.label = actual.label; this.resourceLabel = resourceLabel; @@ -45,6 +47,8 @@ class ResourceStackElement { this.strResources = [this.strResource]; this.groupId = groupId; this.groupOrder = groupOrder; + this.sourceId = sourceId; + this.sourceOrder = sourceOrder; this.isValid = true; } @@ -130,16 +134,20 @@ class WorkspaceStackElement { public readonly strResources: string[]; public readonly groupId: number; public readonly groupOrder: number; + public readonly sourceId: number; + public readonly sourceOrder: number; public removedResources: RemovedResources | null; public invalidatedResources: RemovedResources | null; - constructor(actual: IWorkspaceUndoRedoElement, resourceLabels: string[], strResources: string[], groupId: number, groupOrder: number) { + constructor(actual: IWorkspaceUndoRedoElement, resourceLabels: string[], strResources: string[], groupId: number, groupOrder: number, sourceId: number, sourceOrder: number) { this.actual = actual; this.label = actual.label; this.resourceLabels = resourceLabels; this.strResources = strResources; this.groupId = groupId; this.groupOrder = groupOrder; + this.sourceId = sourceId; + this.sourceOrder = sourceOrder; this.removedResources = null; this.invalidatedResources = null; } @@ -490,11 +498,11 @@ export class UndoRedoService implements IUndoRedoService { console.log(str.join('\n')); } - public pushElement(element: IUndoRedoElement, group: UndoRedoGroup = UndoRedoGroup.None): void { + public pushElement(element: IUndoRedoElement, group: UndoRedoGroup = UndoRedoGroup.None, source: UndoRedoSource = UndoRedoSource.None): void { if (element.type === UndoRedoElementType.Resource) { const resourceLabel = getResourceLabel(element.resource); const strResource = this.getUriComparisonKey(element.resource); - this._pushElement(new ResourceStackElement(element, resourceLabel, strResource, group.id, group.nextOrder())); + this._pushElement(new ResourceStackElement(element, resourceLabel, strResource, group.id, group.nextOrder(), source.id, source.nextOrder())); } else { const seen = new Set(); const resourceLabels: string[] = []; @@ -512,9 +520,9 @@ export class UndoRedoService implements IUndoRedoService { } if (resourceLabels.length === 1) { - this._pushElement(new ResourceStackElement(element, resourceLabels[0], strResources[0], group.id, group.nextOrder())); + this._pushElement(new ResourceStackElement(element, resourceLabels[0], strResources[0], group.id, group.nextOrder(), source.id, source.nextOrder())); } else { - this._pushElement(new WorkspaceStackElement(element, resourceLabels, strResources, group.id, group.nextOrder())); + this._pushElement(new WorkspaceStackElement(element, resourceLabels, strResources, group.id, group.nextOrder(), source.id, source.nextOrder())); } } if (DEBUG) { @@ -558,7 +566,7 @@ export class UndoRedoService implements IUndoRedoService { for (const _element of individualArr) { const resourceLabel = getResourceLabel(_element.resource); const strResource = this.getUriComparisonKey(_element.resource); - const element = new ResourceStackElement(_element, resourceLabel, strResource, 0, 0); + const element = new ResourceStackElement(_element, resourceLabel, strResource, 0, 0, 0, 0); individualMap.set(element.strResource, element); } @@ -577,7 +585,7 @@ export class UndoRedoService implements IUndoRedoService { for (const _element of individualArr) { const resourceLabel = getResourceLabel(_element.resource); const strResource = this.getUriComparisonKey(_element.resource); - const element = new ResourceStackElement(_element, resourceLabel, strResource, 0, 0); + const element = new ResourceStackElement(_element, resourceLabel, strResource, 0, 0, 0, 0); individualMap.set(element.strResource, element); } @@ -657,8 +665,37 @@ export class UndoRedoService implements IUndoRedoService { return { past: [], future: [] }; } - public canUndo(resource: URI): boolean { - const strResource = this.getUriComparisonKey(resource); + private _findClosestUndoElementWithSource(sourceId: number): [StackElement | null, string | null] { + if (!sourceId) { + return [null, null]; + } + + // find an element with the sourceId and with the highest sourceOrder ready to be undone + let matchedElement: StackElement | null = null; + let matchedStrResource: string | null = null; + + for (const [strResource, editStack] of this._editStacks) { + const candidate = editStack.getClosestPastElement(); + if (!candidate) { + continue; + } + if (candidate.sourceId === sourceId) { + if (!matchedElement || candidate.sourceOrder > matchedElement.sourceOrder) { + matchedElement = candidate; + matchedStrResource = strResource; + } + } + } + + return [matchedElement, matchedStrResource]; + } + + public canUndo(resourceOrSource: URI | UndoRedoSource): boolean { + if (resourceOrSource instanceof UndoRedoSource) { + const [, matchedStrResource] = this._findClosestUndoElementWithSource(resourceOrSource.id); + return matchedStrResource ? true : false; + } + const strResource = this.getUriComparisonKey(resourceOrSource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; return editStack.hasPastElements(); @@ -774,7 +811,7 @@ export class UndoRedoService implements IUndoRedoService { if (element.canSplit()) { this._splitPastWorkspaceElement(element, ignoreResources); this._notificationService.info(message); - return new WorkspaceVerificationError(this.undo(strResource)); + return new WorkspaceVerificationError(this._undo(strResource)); } else { // Cannot safely split this workspace element => flush all undo/redo stacks for (const strResource of element.strResources) { @@ -922,7 +959,7 @@ export class UndoRedoService implements IUndoRedoService { if (result.choice === 1) { // choice: undo this file this._splitPastWorkspaceElement(element, null); - return this.undo(strResource); + return this._undo(strResource); } // choice: undo in all files @@ -1007,12 +1044,22 @@ export class UndoRedoService implements IUndoRedoService { const [, matchedStrResource] = this._findClosestUndoElementInGroup(groupId); if (matchedStrResource) { - return this.undo(matchedStrResource); + return this._undo(matchedStrResource); } } - public undo(resource: URI | string): Promise | void { - const strResource = typeof resource === 'string' ? resource : this.getUriComparisonKey(resource); + public undo(resourceOrSource: URI | UndoRedoSource): Promise | void { + if (resourceOrSource instanceof UndoRedoSource) { + const [, matchedStrResource] = this._findClosestUndoElementWithSource(resourceOrSource.id); + return matchedStrResource ? this._undo(matchedStrResource, resourceOrSource.id) : undefined; + } + if (typeof resourceOrSource === 'string') { + return this._undo(resourceOrSource); + } + return this._undo(this.getUriComparisonKey(resourceOrSource)); + } + + private _undo(strResource: string, sourceId: number = 0): Promise | void { if (!this._editStacks.has(strResource)) { return; } @@ -1028,10 +1075,15 @@ export class UndoRedoService implements IUndoRedoService { const [matchedElement, matchedStrResource] = this._findClosestUndoElementInGroup(element.groupId); if (element !== matchedElement && matchedStrResource) { // there is an element in the same group that should be undone before this one - return this.undo(matchedStrResource); + return this._undo(matchedStrResource); } } + if (element.sourceId !== sourceId) { + // Hit a different source, prompt for confirmation + return this._confirmDifferentSourceAndContinueUndo(strResource, element); + } + try { if (element.type === UndoRedoElementType.Workspace) { return this._workspaceUndo(strResource, element); @@ -1045,8 +1097,59 @@ export class UndoRedoService implements IUndoRedoService { } } - public canRedo(resource: URI): boolean { - const strResource = this.getUriComparisonKey(resource); + private async _confirmDifferentSourceAndContinueUndo(strResource: string, element: StackElement): Promise { + const result = await this._dialogService.show( + Severity.Info, + nls.localize('confirmDifferentSource', "Would you like to undo '{0}'?", element.label), + [ + nls.localize('confirmDifferentSource.ok', "Undo"), + nls.localize('cancel', "Cancel"), + ], + { + cancelId: 1 + } + ); + + if (result.choice === 1) { + // choice: cancel + return; + } + + // choice: undo + return this._undo(strResource, element.sourceId); + } + + private _findClosestRedoElementWithSource(sourceId: number): [StackElement | null, string | null] { + if (!sourceId) { + return [null, null]; + } + + // find an element with sourceId and with the lowest sourceOrder ready to be redone + let matchedElement: StackElement | null = null; + let matchedStrResource: string | null = null; + + for (const [strResource, editStack] of this._editStacks) { + const candidate = editStack.getClosestFutureElement(); + if (!candidate) { + continue; + } + if (candidate.sourceId === sourceId) { + if (!matchedElement || candidate.sourceOrder < matchedElement.sourceOrder) { + matchedElement = candidate; + matchedStrResource = strResource; + } + } + } + + return [matchedElement, matchedStrResource]; + } + + public canRedo(resourceOrSource: URI | UndoRedoSource): boolean { + if (resourceOrSource instanceof UndoRedoSource) { + const [, matchedStrResource] = this._findClosestRedoElementWithSource(resourceOrSource.id); + return matchedStrResource ? true : false; + } + const strResource = this.getUriComparisonKey(resourceOrSource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; return editStack.hasFutureElements(); @@ -1058,7 +1161,7 @@ export class UndoRedoService implements IUndoRedoService { if (element.canSplit()) { this._splitFutureWorkspaceElement(element, ignoreResources); this._notificationService.info(message); - return new WorkspaceVerificationError(this.redo(strResource)); + return new WorkspaceVerificationError(this._redo(strResource)); } else { // Cannot safely split this workspace element => flush all undo/redo stacks for (const strResource of element.strResources) { @@ -1230,12 +1333,22 @@ export class UndoRedoService implements IUndoRedoService { const [, matchedStrResource] = this._findClosestRedoElementInGroup(groupId); if (matchedStrResource) { - return this.redo(matchedStrResource); + return this._redo(matchedStrResource); } } - public redo(resource: URI | string): Promise | void { - const strResource = typeof resource === 'string' ? resource : this.getUriComparisonKey(resource); + public redo(resourceOrSource: URI | UndoRedoSource | string): Promise | void { + if (resourceOrSource instanceof UndoRedoSource) { + const [, matchedStrResource] = this._findClosestRedoElementWithSource(resourceOrSource.id); + return matchedStrResource ? this._redo(matchedStrResource) : undefined; + } + if (typeof resourceOrSource === 'string') { + return this._redo(resourceOrSource); + } + return this._redo(this.getUriComparisonKey(resourceOrSource)); + } + + private _redo(strResource: string): Promise | void { if (!this._editStacks.has(strResource)) { return; } @@ -1251,7 +1364,7 @@ export class UndoRedoService implements IUndoRedoService { const [matchedElement, matchedStrResource] = this._findClosestRedoElementInGroup(element.groupId); if (element !== matchedElement && matchedStrResource) { // there is an element in the same group that should be redone before this one - return this.redo(matchedStrResource); + return this._redo(matchedStrResource); } } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index ce1033b12..2e18cc8cd 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -56,7 +56,8 @@ export class DarwinUpdateService extends AbstractUpdateService { } protected buildUpdateFeedUrl(quality: string): string | undefined { - const url = createUpdateURL('darwin', quality); + const assetID = process.arch === 'x64' ? 'darwin' : 'darwin-arm64'; + const url = createUpdateURL(assetID, quality); try { electron.autoUpdater.setFeedURL({ url }); } catch (e) { diff --git a/src/vs/platform/url/common/url.ts b/src/vs/platform/url/common/url.ts index 2069fc48e..5202226dd 100644 --- a/src/vs/platform/url/common/url.ts +++ b/src/vs/platform/url/common/url.ts @@ -18,6 +18,8 @@ export interface IOpenURLOptions { * might be shown to the user. */ trusted?: boolean; + + originalUrl?: string; } export interface IURLHandler { diff --git a/src/vs/platform/url/common/urlIpc.ts b/src/vs/platform/url/common/urlIpc.ts index 08d19ffd5..52fad0e24 100644 --- a/src/vs/platform/url/common/urlIpc.ts +++ b/src/vs/platform/url/common/urlIpc.ts @@ -19,7 +19,7 @@ export class URLHandlerChannel implements IServerChannel { call(_: unknown, command: string, arg?: any): Promise { switch (command) { - case 'handleURL': return this.handler.handleURL(URI.revive(arg)); + case 'handleURL': return this.handler.handleURL(URI.revive(arg[0]), arg[1]); } throw new Error(`Call not found: ${command}`); @@ -31,7 +31,7 @@ export class URLHandlerChannelClient implements IURLHandler { constructor(private channel: IChannel) { } handleURL(uri: URI, options?: IOpenURLOptions): Promise { - return this.channel.call('handleURL', uri.toJSON()); + return this.channel.call('handleURL', [uri.toJSON(), options]); } } diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts index efc15e029..50ca2ea24 100644 --- a/src/vs/platform/url/electron-main/electronUrlListener.ts +++ b/src/vs/platform/url/electron-main/electronUrlListener.ts @@ -34,13 +34,13 @@ function uriFromRawUrl(url: string): URI | null { */ export class ElectronURLListener { - private uris: URI[] = []; + private uris: { uri: URI, url: string }[] = []; private retryCount = 0; private flushDisposable: IDisposable = Disposable.None; private disposables = new DisposableStore(); constructor( - initialUrisToHandle: URI[], + initialUrisToHandle: { uri: URI, url: string }[], private readonly urlService: IURLService, windowsMainService: IWindowsMainService, environmentService: IEnvironmentMainService @@ -64,8 +64,15 @@ export class ElectronURLListener { return url; }); - const onOpenUrl = Event.filter(Event.map(onOpenElectronUrl, uriFromRawUrl), (uri): uri is URI => !!uri); - onOpenUrl(this.urlService.open, this.urlService, this.disposables); + this.disposables.add(onOpenElectronUrl(url => { + const uri = uriFromRawUrl(url); + + if (!uri) { + return; + } + + this.urlService.open(uri, { originalUrl: url }); + })); // Send initial links to the window once it has loaded const isWindowReady = windowsMainService.getWindows() @@ -84,13 +91,13 @@ export class ElectronURLListener { return; } - const uris: URI[] = []; + const uris: { uri: URI, url: string }[] = []; - for (const uri of this.uris) { - const handled = await this.urlService.open(uri); + for (const obj of this.uris) { + const handled = await this.urlService.open(obj.uri, { originalUrl: obj.url }); if (!handled) { - uris.push(uri); + uris.push(obj); } } diff --git a/src/vs/platform/userDataSync/common/extensionsStorageSync.ts b/src/vs/platform/userDataSync/common/extensionsStorageSync.ts new file mode 100644 index 000000000..37846e053 --- /dev/null +++ b/src/vs/platform/userDataSync/common/extensionsStorageSync.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; + +export interface IExtensionIdWithVersion { + id: string; + version: string; +} + +export const IExtensionsStorageSyncService = createDecorator('IExtensionsStorageSyncService'); + +export interface IExtensionsStorageSyncService { + + _serviceBrand: any; + + readonly onDidChangeExtensionsStorage: Event; + setKeysForSync(extensionIdWithVersion: IExtensionIdWithVersion, keys: string[]): void; + getKeysForSync(extensionIdWithVersion: IExtensionIdWithVersion): string[] | undefined; + +} + +const EXTENSION_KEYS_ID_VERSION_REGEX = /^extensionKeys\/([^.]+\..+)@(\d+\.\d+\.\d+(-.*)?)$/; + +export class ExtensionsStorageSyncService extends Disposable implements IExtensionsStorageSyncService { + + declare readonly _serviceBrand: undefined; + + private static toKey(extension: IExtensionIdWithVersion): string { + return `extensionKeys/${extension.id}@${extension.version}`; + } + + private static fromKey(key: string): IExtensionIdWithVersion | undefined { + const matches = EXTENSION_KEYS_ID_VERSION_REGEX.exec(key); + if (matches && matches[1]) { + return { id: matches[1], version: matches[2] }; + } + return undefined; + } + + private readonly _onDidChangeExtensionsStorage = this._register(new Emitter()); + readonly onDidChangeExtensionsStorage = this._onDidChangeExtensionsStorage.event; + + private readonly extensionsWithKeysForSync = new Set(); + + constructor( + @IStorageService private readonly storageService: IStorageService, + ) { + super(); + this.initialize(); + this._register(this.storageService.onDidChangeValue(e => this.onDidChangeStorageValue(e))); + } + + private initialize(): void { + const keys = this.storageService.keys(StorageScope.GLOBAL, StorageTarget.MACHINE); + for (const key of keys) { + const extensionIdWithVersion = ExtensionsStorageSyncService.fromKey(key); + if (extensionIdWithVersion) { + this.extensionsWithKeysForSync.add(extensionIdWithVersion.id.toLowerCase()); + } + } + } + + private onDidChangeStorageValue(e: IStorageValueChangeEvent): void { + if (e.scope !== StorageScope.GLOBAL) { + return; + } + + // State of extension with keys for sync has changed + if (this.extensionsWithKeysForSync.has(e.key.toLowerCase())) { + this._onDidChangeExtensionsStorage.fire(); + return; + } + + // Keys for sync of an extension has changed + const extensionIdWithVersion = ExtensionsStorageSyncService.fromKey(e.key); + if (extensionIdWithVersion) { + this.extensionsWithKeysForSync.add(extensionIdWithVersion.id.toLowerCase()); + this._onDidChangeExtensionsStorage.fire(); + return; + } + } + + setKeysForSync(extensionIdWithVersion: IExtensionIdWithVersion, keys: string[]): void { + this.storageService.store(ExtensionsStorageSyncService.toKey(extensionIdWithVersion), JSON.stringify(keys), StorageScope.GLOBAL, StorageTarget.MACHINE); + } + + getKeysForSync(extensionIdWithVersion: IExtensionIdWithVersion): string[] | undefined { + const keysForSyncValue = this.storageService.get(ExtensionsStorageSyncService.toKey(extensionIdWithVersion), StorageScope.GLOBAL); + return keysForSyncValue ? JSON.parse(keysForSyncValue) : undefined; + } +} diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index f34669f83..e716e1355 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -21,12 +21,12 @@ import { URI } from 'vs/base/common/uri'; import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; import { compare } from 'vs/base/common/strings'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { getErrorMessage } from 'vs/base/common/errors'; import { forEach, IStringDictionary } from 'vs/base/common/collections'; +import { IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; interface IExtensionResourceMergeResult extends IAcceptResult { readonly added: ISyncExtension[]; @@ -105,7 +105,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @ITelemetryService telemetryService: ITelemetryService, - @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, + @IExtensionsStorageSyncService private readonly extensionsStorageSyncService: IExtensionsStorageSyncService, ) { super(SyncResource.Extensions, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); this._register( @@ -114,16 +114,14 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse Event.filter(this.extensionManagementService.onDidInstallExtension, (e => !!e.gallery)), Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error)), this.extensionEnablementService.onDidChangeEnablement, - this.storageKeysSyncRegistryService.onDidChangeExtensionStorageKeys, - Event.filter(this.storageService.onDidChangeStorage, e => e.scope === StorageScope.GLOBAL - && this.storageKeysSyncRegistryService.extensionsStorageKeys.some(([extensionIdentifier]) => areSameExtensions(extensionIdentifier, { id: e.key })))), + this.extensionsStorageSyncService.onDidChangeExtensionsStorage), () => undefined, 500)(() => this.triggerLocalChange())); } protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise { const remoteExtensions: ISyncExtension[] | null = remoteUserData.syncData ? await parseAndMigrateExtensions(remoteUserData.syncData, this.extensionManagementService) : null; - const skippedExtensions: ISyncExtension[] = lastSyncUserData ? lastSyncUserData.skippedExtensions || [] : []; - const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData ? await parseAndMigrateExtensions(lastSyncUserData.syncData!, this.extensionManagementService) : null; + const skippedExtensions: ISyncExtension[] = lastSyncUserData?.skippedExtensions || []; + const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData?.syncData ? await parseAndMigrateExtensions(lastSyncUserData.syncData, this.extensionManagementService) : null; const installedExtensions = await this.extensionManagementService.getInstalled(); const localExtensions = this.getLocalExtensions(installedExtensions); @@ -352,7 +350,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse const extensionsToRemove = installedExtensions.filter(({ identifier, isBuiltin }) => !isBuiltin && removed.some(r => areSameExtensions(identifier, r))); await Promise.all(extensionsToRemove.map(async extensionToRemove => { this.logService.trace(`${this.syncResourceLogLabel}: Uninstalling local extension...`, extensionToRemove.identifier.id); - await this.extensionManagementService.uninstall(extensionToRemove); + await this.extensionManagementService.uninstall(extensionToRemove, { donotIncludePack: true, donotCheckDependents: true }); this.logService.info(`${this.syncResourceLogLabel}: Uninstalled local extension.`, extensionToRemove.identifier.id); removeFromSkipped.push(extensionToRemove.identifier); })); @@ -365,9 +363,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse // Builtin Extension Sync: Enablement & State if (installedExtension && installedExtension.isBuiltin) { if (e.state && installedExtension.manifest.version === e.version) { - const extensionState = JSON.parse(this.storageService.get(e.identifier.id, StorageScope.GLOBAL) || '{}'); - forEach(e.state, ({ key, value }) => extensionState[key] = value); - this.storageService.store(e.identifier.id, JSON.stringify(extensionState), StorageScope.GLOBAL); + this.updateExtensionState(e.state, e.identifier.id, installedExtension.manifest.version); } if (e.disabled) { this.logService.trace(`${this.syncResourceLogLabel}: Disabling extension...`, e.identifier.id); @@ -393,9 +389,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse (installedExtension ? installedExtension.manifest.version === e.version /* Installed and has same version */ : !!extension /* Installable */) ) { - const extensionState = JSON.parse(this.storageService.get(e.identifier.id, StorageScope.GLOBAL) || '{}'); - forEach(e.state, ({ key, value }) => extensionState[key] = value); - this.storageService.store(e.identifier.id, JSON.stringify(extensionState), StorageScope.GLOBAL); + this.updateExtensionState(e.state, e.identifier.id, installedExtension?.manifest.version); } if (extension) { @@ -413,7 +407,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse // Install only if the extension does not exist if (!installedExtension) { this.logService.trace(`${this.syncResourceLogLabel}: Installing extension...`, e.identifier.id, extension.version); - await this.extensionManagementService.installFromGallery(extension, { isMachineScoped: false } /* pass options to prevent install and sync dialog in web */); + await this.extensionManagementService.installFromGallery(extension, { isMachineScoped: false, donotIncludePackAndDependencies: true } /* pass options to prevent install and sync dialog in web */); this.logService.info(`${this.syncResourceLogLabel}: Installed extension.`, e.identifier.id, extension.version); removeFromSkipped.push(extension.identifier); } @@ -442,6 +436,17 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse return newSkippedExtensions; } + private updateExtensionState(state: IStringDictionary, id: string, version?: string): void { + const extensionState = JSON.parse(this.storageService.get(id, StorageScope.GLOBAL) || '{}'); + const keys = version ? this.extensionsStorageSyncService.getKeysForSync({ id, version }) : undefined; + if (keys) { + keys.forEach(key => extensionState[key] = state[key]); + } else { + forEach(state, ({ key, value }) => extensionState[key] = value); + } + this.storageService.store(id, JSON.stringify(extensionState), StorageScope.GLOBAL, StorageTarget.MACHINE); + } + private parseExtensions(syncData: ISyncData): ISyncExtension[] { return JSON.parse(syncData.content); } @@ -457,10 +462,10 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse if (!isBuiltin) { syncExntesion.installed = true; } - const keys = this.storageKeysSyncRegistryService.getExtensioStorageKeys({ id: identifier.id, version: manifest.version }); - if (keys) { - const extensionStorageValue = this.storageService.get(identifier.id, StorageScope.GLOBAL) || '{}'; - try { + try { + const keys = this.extensionsStorageSyncService.getKeysForSync({ id: identifier.id, version: manifest.version }); + if (keys) { + const extensionStorageValue = this.storageService.get(identifier.id, StorageScope.GLOBAL) || '{}'; const extensionStorageState = JSON.parse(extensionStorageValue); syncExntesion.state = Object.keys(extensionStorageState).reduce((state: IStringDictionary, key) => { if (keys.includes(key)) { @@ -468,9 +473,9 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse } return state; }, {}); - } catch (error) { - this.logService.info(`${this.syncResourceLogLabel}: Error while parsing extension state`, getErrorMessage(error)); } + } catch (error) { + this.logService.info(`${this.syncResourceLogLabel}: Error while parsing extension state`, getErrorMessage(error)); } return syncExntesion; }); @@ -499,6 +504,11 @@ export class ExtensionsInitializer extends AbstractInitializer { return; } + await this.initializeRemoteExtensions(remoteExtensions); + } + + protected async initializeRemoteExtensions(remoteExtensions: ISyncExtension[]): Promise { + const newlyEnabledExtensions: ILocalExtension[] = []; const installedExtensions = await this.extensionManagementService.getInstalled(); const newExtensionsToSync = new Map(); const installedExtensionsToSync: ISyncExtension[] = []; @@ -528,10 +538,13 @@ export class ExtensionsInitializer extends AbstractInitializer { try { const extensionToSync = newExtensionsToSync.get(galleryExtension.identifier.id.toLowerCase())!; if (extensionToSync.state) { - this.storageService.store(extensionToSync.identifier.id, JSON.stringify(extensionToSync.state), StorageScope.GLOBAL); + this.storageService.store(extensionToSync.identifier.id, JSON.stringify(extensionToSync.state), StorageScope.GLOBAL, StorageTarget.MACHINE); } this.logService.trace(`Installing extension...`, galleryExtension.identifier.id); - await this.extensionManagementService.installFromGallery(galleryExtension, { isMachineScoped: false } /* pass options to prevent install and sync dialog in web */); + const local = await this.extensionManagementService.installFromGallery(galleryExtension, { isMachineScoped: false } /* pass options to prevent install and sync dialog in web */); + if (!toDisable.some(identifier => areSameExtensions(identifier, galleryExtension.identifier))) { + newlyEnabledExtensions.push(local); + } this.logService.info(`Installed extension.`, galleryExtension.identifier.id); } catch (error) { this.logService.error(error); @@ -551,11 +564,11 @@ export class ExtensionsInitializer extends AbstractInitializer { if (extensionToSync.state) { const extensionState = JSON.parse(this.storageService.get(extensionToSync.identifier.id, StorageScope.GLOBAL) || '{}'); forEach(extensionToSync.state, ({ key, value }) => extensionState[key] = value); - this.storageService.store(extensionToSync.identifier.id, JSON.stringify(extensionState), StorageScope.GLOBAL); + this.storageService.store(extensionToSync.identifier.id, JSON.stringify(extensionState), StorageScope.GLOBAL, StorageTarget.MACHINE); } } + + return newlyEnabledExtensions; } } - - diff --git a/src/vs/platform/userDataSync/common/globalStateMerge.ts b/src/vs/platform/userDataSync/common/globalStateMerge.ts index 8af4c4de7..e519854a9 100644 --- a/src/vs/platform/userDataSync/common/globalStateMerge.ts +++ b/src/vs/platform/userDataSync/common/globalStateMerge.ts @@ -6,24 +6,22 @@ import * as objects from 'vs/base/common/objects'; import { IStorageValue } from 'vs/platform/userDataSync/common/userDataSync'; import { IStringDictionary } from 'vs/base/common/collections'; -import { IStorageKey } from 'vs/platform/userDataSync/common/storageKeys'; import { ILogService } from 'vs/platform/log/common/log'; export interface IMergeResult { local: { added: IStringDictionary, removed: string[], updated: IStringDictionary }; remote: IStringDictionary | null; - skipped: string[]; } -export function merge(localStorage: IStringDictionary, remoteStorage: IStringDictionary | null, baseStorage: IStringDictionary | null, storageKeys: ReadonlyArray, previouslySkipped: string[], logService: ILogService): IMergeResult { +export function merge(localStorage: IStringDictionary, remoteStorage: IStringDictionary | null, baseStorage: IStringDictionary | null, storageKeys: { machine: ReadonlyArray, unregistered: ReadonlyArray }, logService: ILogService): IMergeResult { if (!remoteStorage) { - return { remote: Object.keys(localStorage).length > 0 ? localStorage : null, local: { added: {}, removed: [], updated: {} }, skipped: [] }; + return { remote: Object.keys(localStorage).length > 0 ? localStorage : null, local: { added: {}, removed: [], updated: {} } }; } const localToRemote = compare(localStorage, remoteStorage); if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { // No changes found between local and remote. - return { remote: null, local: { added: {}, removed: [], updated: {} }, skipped: [] }; + return { remote: null, local: { added: {}, removed: [], updated: {} } }; } const baseToRemote = baseStorage ? compare(baseStorage, remoteStorage) : { added: Object.keys(remoteStorage).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; @@ -31,26 +29,48 @@ export function merge(localStorage: IStringDictionary, remoteStor const local: { added: IStringDictionary, removed: string[], updated: IStringDictionary } = { added: {}, removed: [], updated: {} }; const remote: IStringDictionary = objects.deepClone(remoteStorage); - const skipped: string[] = []; + + // Added in local + for (const key of baseToLocal.added.values()) { + // Skip if local was not synced before and remote also has the key + // In this case, remote gets precedence + if (!baseStorage && baseToRemote.added.has(key)) { + continue; + } else { + remote[key] = localStorage[key]; + } + } + + // Updated in local + for (const key of baseToLocal.updated.values()) { + remote[key] = localStorage[key]; + } + + // Removed in local + for (const key of baseToLocal.removed.values()) { + // Do not remove from remote if key is not registered. + if (storageKeys.unregistered.includes(key)) { + continue; + } + delete remote[key]; + } // Added in remote for (const key of baseToRemote.added.values()) { const remoteValue = remoteStorage[key]; - const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; - if (!storageKey) { - skipped.push(key); - logService.trace(`GlobalState: Skipped adding ${key} in local storage as it is not registered.`); + if (storageKeys.machine.includes(key)) { + logService.info(`GlobalState: Skipped adding ${key} in local storage because it is declared as machine scoped.`); continue; } - if (storageKey.version !== remoteValue.version) { - logService.info(`GlobalState: Skipped adding ${key} in local storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`); + // Skip if the value is also added in local from the time it is last synced + if (baseStorage && baseToLocal.added.has(key)) { continue; } const localValue = localStorage[key]; if (localValue && localValue.value === remoteValue.value) { continue; } - if (baseToLocal.added.has(key)) { + if (localValue) { local.updated[key] = remoteValue; } else { local.added[key] = remoteValue; @@ -60,14 +80,12 @@ export function merge(localStorage: IStringDictionary, remoteStor // Updated in Remote for (const key of baseToRemote.updated.values()) { const remoteValue = remoteStorage[key]; - const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; - if (!storageKey) { - skipped.push(key); - logService.trace(`GlobalState: Skipped updating ${key} in local storage as is not registered.`); + if (storageKeys.machine.includes(key)) { + logService.info(`GlobalState: Skipped updating ${key} in local storage because it is declared as machine scoped.`); continue; } - if (storageKey.version !== remoteValue.version) { - logService.info(`GlobalState: Skipped updating ${key} in local storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`); + // Skip if the value is also updated or removed in local + if (baseToLocal.updated.has(key) || baseToLocal.removed.has(key)) { continue; } const localValue = localStorage[key]; @@ -79,67 +97,18 @@ export function merge(localStorage: IStringDictionary, remoteStor // Removed in remote for (const key of baseToRemote.removed.values()) { - const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; - if (!storageKey) { - logService.trace(`GlobalState: Skipped removing ${key} in local storage. It is not registered to sync.`); + if (storageKeys.machine.includes(key)) { + logService.trace(`GlobalState: Skipped removing ${key} in local storage because it is declared as machine scoped.`); + continue; + } + // Skip if the value is also updated or removed in local + if (baseToLocal.updated.has(key) || baseToLocal.removed.has(key)) { continue; } local.removed.push(key); } - // Added in local - for (const key of baseToLocal.added.values()) { - if (!baseToRemote.added.has(key)) { - remote[key] = localStorage[key]; - } - } - - // Updated in local - for (const key of baseToLocal.updated.values()) { - if (baseToRemote.updated.has(key) || baseToRemote.removed.has(key)) { - continue; - } - const remoteValue = remote[key]; - const localValue = localStorage[key]; - if (localValue.version < remoteValue.version) { - logService.info(`GlobalState: Skipped updating ${key} in remote storage. Local version '${localValue.version}' and remote version '${remoteValue.version} are not same.`); - continue; - } - remote[key] = localValue; - } - - // Removed in local - for (const key of baseToLocal.removed.values()) { - // do not remove from remote if it is updated in remote - if (baseToRemote.updated.has(key)) { - continue; - } - - const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; - // do not remove from remote if storage key is not found - if (!storageKey) { - skipped.push(key); - logService.trace(`GlobalState: Skipped removing ${key} in remote storage. It is not registered to sync.`); - continue; - } - - const remoteValue = remote[key]; - // do not remove from remote if local data version is old - if (storageKey.version < remoteValue.version) { - logService.info(`GlobalState: Skipped updating ${key} in remote storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`); - continue; - } - - // add to local if it was skipped before - if (previouslySkipped.indexOf(key) !== -1) { - local.added[key] = remote[key]; - continue; - } - - delete remote[key]; - } - - return { local, remote: areSame(remote, remoteStorage) ? null : remote, skipped }; + return { local, remote: areSame(remote, remoteStorage) ? null : remote }; } function compare(from: IStringDictionary, to: IStringDictionary): { added: Set, removed: Set, updated: Set } { diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index c1f4daa7b..7c69ad463 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -21,29 +21,34 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { URI } from 'vs/base/common/uri'; import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IStorageKeysSyncRegistryService, IStorageKey } from 'vs/platform/userDataSync/common/storageKeys'; -import { equals } from 'vs/base/common/arrays'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { CancellationToken } from 'vs/base/common/cancellation'; const argvStoragePrefx = 'globalState.argv.'; const argvProperties: string[] = ['locale']; +type StorageKeys = { machine: string[], user: string[], unregistered: string[] }; + interface IGlobalStateResourceMergeResult extends IAcceptResult { readonly local: { added: IStringDictionary, removed: string[], updated: IStringDictionary }; readonly remote: IStringDictionary | null; } export interface IGlobalStateResourcePreview extends IResourcePreview { - readonly skippedStorageKeys: string[]; readonly localUserData: IGlobalState; readonly previewResult: IGlobalStateResourceMergeResult; + readonly storageKeys: StorageKeys; } -interface ILastSyncUserData extends IRemoteUserData { - skippedStorageKeys: string[] | undefined; -} - +/** + * Synchronises global state that includes + * - Global storage with user scope + * - Locale from argv properties + * + * Global storage is synced without checking version just like other resources (settings, keybindings). + * If there is a change in format of the value of a storage key which requires migration then + * Owner of that key should remove that key from user scope and replace that with new user scoped key. + */ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser { private static readonly GLOBAL_STATE_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'globalState', path: `/globalState.json` }); @@ -63,23 +68,22 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @IStorageService private readonly storageService: IStorageService, - @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { super(SyncResource.GlobalState, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); - this._register(this.fileService.watch(this.extUri.dirname(this.environmentService.argvResource))); + this._register(fileService.watch(this.extUri.dirname(this.environmentService.argvResource))); this._register( Event.any( /* Locale change */ - Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource)), - /* Storage change */ - Event.filter(this.storageService.onDidChangeStorage, e => storageKeysSyncRegistryService.storageKeys.some(({ key }) => e.key === key)), - /* Storage key registered */ - this.storageKeysSyncRegistryService.onDidChangeStorageKeys + Event.filter(fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource)), + /* Global storage with user target has changed */ + Event.filter(storageService.onDidChangeValue, e => e.scope === StorageScope.GLOBAL && e.target !== undefined ? e.target === StorageTarget.USER : storageService.keys(StorageScope.GLOBAL, StorageTarget.USER).includes(e.key)), + /* Storage key target has changed */ + this.storageService.onDidChangeTarget )((() => this.triggerLocalChange())) ); } - protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null, token: CancellationToken): Promise { + protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { const remoteGlobalState: IGlobalState = remoteUserData.syncData ? JSON.parse(remoteUserData.syncData.content) : null; const lastSyncGlobalState: IGlobalState | null = lastSyncUserData && lastSyncUserData.syncData ? JSON.parse(lastSyncUserData.syncData.content) : null; @@ -91,7 +95,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs this.logService.trace(`${this.syncResourceLogLabel}: Remote ui state does not exist. Synchronizing ui state for the first time.`); } - const { local, remote, skipped } = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); + const storageKeys = this.getStorageKeys(lastSyncGlobalState); + const { local, remote } = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, storageKeys, this.logService); const previewResult: IGlobalStateResourceMergeResult = { content: null, local, @@ -101,7 +106,6 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs }; return [{ - skippedStorageKeys: skipped, localResource: this.localResource, localContent: this.format(localGloablState), localUserData: localGloablState, @@ -112,6 +116,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs localChange: previewResult.localChange, remoteChange: previewResult.remoteChange, acceptedResource: this.acceptedResource, + storageKeys }]; } @@ -152,7 +157,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs private async acceptRemote(resourcePreview: IGlobalStateResourcePreview): Promise { if (resourcePreview.remoteContent !== null) { const remoteGlobalState: IGlobalState = JSON.parse(resourcePreview.remoteContent); - const { local, remote } = merge(resourcePreview.localUserData.storage, remoteGlobalState.storage, null, this.getSyncStorageKeys(), resourcePreview.skippedStorageKeys, this.logService); + const { local, remote } = merge(resourcePreview.localUserData.storage, remoteGlobalState.storage, null, resourcePreview.storageKeys, this.logService); return { content: resourcePreview.remoteContent, local, @@ -171,8 +176,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs } } - protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null, resourcePreviews: [IGlobalStateResourcePreview, IGlobalStateResourceMergeResult][], force: boolean): Promise { - let { localUserData, skippedStorageKeys } = resourcePreviews[0][0]; + protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: [IGlobalStateResourcePreview, IGlobalStateResourceMergeResult][], force: boolean): Promise { + let { localUserData } = resourcePreviews[0][0]; let { local, remote, localChange, remoteChange } = resourcePreviews[0][1]; if (localChange === Change.None && remoteChange === Change.None) { @@ -195,10 +200,10 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs this.logService.info(`${this.syncResourceLogLabel}: Updated remote ui state`); } - if (lastSyncUserData?.ref !== remoteUserData.ref || !equals(lastSyncUserData.skippedStorageKeys, skippedStorageKeys)) { + if (lastSyncUserData?.ref !== remoteUserData.ref) { // update last sync this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized ui state...`); - await this.updateLastSyncUserData(remoteUserData, { skippedStorageKeys }); + await this.updateLastSyncUserData(remoteUserData); this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized ui state`); } } @@ -267,10 +272,10 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs storage[`${argvStoragePrefx}${argvProperty}`] = { version: 1, value: argvValue[argvProperty] }; } } - for (const { key, version } of this.storageKeysSyncRegistryService.storageKeys) { + for (const key of this.storageService.keys(StorageScope.GLOBAL, StorageTarget.USER)) { const value = this.storageService.get(key, StorageScope.GLOBAL); if (value) { - storage[key] = { version, value }; + storage[key] = { version: 1, value }; } } return { storage }; @@ -317,7 +322,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs if (updatedStorageKeys.length) { this.logService.trace(`${this.syncResourceLogLabel}: Updating global state...`); for (const key of Object.keys(updatedStorage)) { - this.storageService.store(key, updatedStorage[key], StorageScope.GLOBAL); + this.storageService.store(key, updatedStorage[key], StorageScope.GLOBAL, StorageTarget.USER); } this.logService.info(`${this.syncResourceLogLabel}: Updated global state`, Object.keys(updatedStorage)); } @@ -336,8 +341,12 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs } } - private getSyncStorageKeys(): IStorageKey[] { - return [...this.storageKeysSyncRegistryService.storageKeys, ...argvProperties.map(argvProprety => ({ key: `${argvStoragePrefx}${argvProprety}`, version: 1 }))]; + private getStorageKeys(lastSyncGlobalState: IGlobalState | null): StorageKeys { + const user = this.storageService.keys(StorageScope.GLOBAL, StorageTarget.USER); + const machine = this.storageService.keys(StorageScope.GLOBAL, StorageTarget.MACHINE); + const registered = [...user, ...machine]; + const unregistered = lastSyncGlobalState?.storage ? Object.keys(lastSyncGlobalState.storage).filter(key => !key.startsWith(argvStoragePrefx) && !registered.includes(key) && this.storageService.get(key, StorageScope.GLOBAL) !== undefined) : []; + return { user, machine, unregistered }; } } @@ -385,7 +394,7 @@ export class GlobalStateInitializer extends AbstractInitializer { if (Object.keys(storage).length) { for (const key of Object.keys(storage)) { - this.storageService.store(key, storage[key], StorageScope.GLOBAL); + this.storageService.store(key, storage[key], StorageScope.GLOBAL, StorageTarget.USER); } } } diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 80f54b97a..7f9363670 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -23,6 +23,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { VSBuffer } from 'vs/base/common/buffer'; +import { Event } from 'vs/base/common/event'; interface ISyncContent { mac?: string; @@ -35,6 +36,10 @@ interface IKeybindingsResourcePreview extends IFileResourcePreview { previewResult: IMergeResult; } +interface ILastSyncUserData extends IRemoteUserData { + platformSpecific?: boolean; +} + export function getKeybindingsContentFromSyncContent(syncContent: string, platformSpecific: boolean): string | null { const parsed = JSON.parse(syncContent); if (!platformSpecific) { @@ -72,11 +77,12 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem @ITelemetryService telemetryService: ITelemetryService, ) { super(environmentService.keybindingsResource, SyncResource.Keybindings, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService); + this._register(Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('settingsSync.keybindingsPerPlatform'))(() => this.triggerLocalChange())); } - protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { + protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null, token: CancellationToken): Promise { const remoteContent = remoteUserData.syncData ? this.getKeybindingsContentFromSyncContent(remoteUserData.syncData.content) : null; - const lastSyncContent: string | null = lastSyncUserData && lastSyncUserData.syncData ? this.getKeybindingsContentFromSyncContent(lastSyncUserData.syncData.content) : null; + const lastSyncContent: string | null = lastSyncUserData ? this.getKeybindingsContentFromLastSyncUserData(lastSyncUserData) : null; // Get file content last to get the latest const fileContent = await this.getLocalFileContent(); @@ -204,7 +210,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem if (localChange !== Change.None) { this.logService.trace(`${this.syncResourceLogLabel}: Updating local keybindings...`); if (fileContent) { - await this.backupLocal(this.toSyncContent(fileContent.value.toString(), null)); + await this.backupLocal(this.toSyncContent(fileContent.value.toString())); } await this.updateLocalFileContent(content || '[]', fileContent, force); this.logService.info(`${this.syncResourceLogLabel}: Updated local keybindings`); @@ -212,7 +218,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem if (remoteChange !== Change.None) { this.logService.trace(`${this.syncResourceLogLabel}: Updating remote keybindings...`); - const remoteContents = this.toSyncContent(content || '[]', remoteUserData.syncData ? remoteUserData.syncData.content : null); + const remoteContents = this.toSyncContent(content || '[]', remoteUserData.syncData?.content); remoteUserData = await this.updateRemoteUserData(remoteContents, force ? null : remoteUserData.ref); this.logService.info(`${this.syncResourceLogLabel}: Updated remote keybindings`); } @@ -224,15 +230,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem if (lastSyncUserData?.ref !== remoteUserData.ref) { this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized keybindings...`); - const lastSyncContent = content !== null ? this.toSyncContent(content, null) : remoteUserData.syncData?.content; - await this.updateLastSyncUserData({ - ref: remoteUserData.ref, - syncData: lastSyncContent ? { - version: remoteUserData.syncData ? remoteUserData.syncData.version : this.version, - machineId: remoteUserData.syncData!.machineId, - content: lastSyncContent - } : null - }); + await this.updateLastSyncUserData(remoteUserData, { platformSpecific: this.syncKeybindingsPerPlatform() }); this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized keybindings`); } @@ -281,6 +279,19 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem return null; } + private getKeybindingsContentFromLastSyncUserData(lastSyncUserData: ILastSyncUserData): string | null { + if (!lastSyncUserData.syncData) { + return null; + } + + // Return null if there is a change in platform specific property from last time sync. + if (lastSyncUserData.platformSpecific !== undefined && lastSyncUserData.platformSpecific !== this.syncKeybindingsPerPlatform()) { + return null; + } + + return this.getKeybindingsContentFromSyncContent(lastSyncUserData.syncData.content); + } + private getKeybindingsContentFromSyncContent(syncContent: string): string | null { try { return getKeybindingsContentFromSyncContent(syncContent, this.syncKeybindingsPerPlatform()); @@ -290,7 +301,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem } } - private toSyncContent(keybindingsContent: string, syncContent: string | null): string { + private toSyncContent(keybindingsContent: string, syncContent?: string): string { let parsed: ISyncContent = {}; try { parsed = JSON.parse(syncContent || '{}'); diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 6b583f9c8..d4e31ee05 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -13,7 +13,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { localize } from 'vs/nls'; import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; import { updateIgnoredSettings, merge, getIgnoredSettings, isEmpty } from 'vs/platform/userDataSync/common/settingsMerge'; import { edit } from 'vs/platform/userDataSync/common/content'; @@ -198,6 +198,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement await this.backupLocal(JSON.stringify(this.toSettingsSyncContent(fileContent.value.toString()))); } await this.updateLocalFileContent(content, fileContent, force); + await this.configurationService.reloadConfiguration(ConfigurationTarget.USER_LOCAL); this.logService.info(`${this.syncResourceLogLabel}: Updated local settings`); } diff --git a/src/vs/platform/userDataSync/common/storageKeys.ts b/src/vs/platform/userDataSync/common/storageKeys.ts deleted file mode 100644 index 5ffe0786f..000000000 --- a/src/vs/platform/userDataSync/common/storageKeys.ts +++ /dev/null @@ -1,134 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export interface IStorageKey { - - readonly key: string; - readonly version: number; - -} - -export interface IExtensionIdWithVersion { - id: string; - version: string; -} - -export namespace ExtensionIdWithVersion { - - const EXTENSION_ID_VERSION_REGEX = /^([^.]+\..+)@(\d+\.\d+\.\d+(-.*)?)$/; - - export function toKey(extension: IExtensionIdWithVersion): string { - return `${extension.id}@${extension.version}`; - } - - export function fromKey(key: string): IExtensionIdWithVersion | undefined { - const matches = EXTENSION_ID_VERSION_REGEX.exec(key); - if (matches && matches[1]) { - return { id: matches[1], version: matches[2] }; - } - return undefined; - } -} - -export const IStorageKeysSyncRegistryService = createDecorator('IStorageKeysSyncRegistryService'); - -export interface IStorageKeysSyncRegistryService { - - _serviceBrand: any; - - /** - * All registered storage keys - */ - readonly storageKeys: ReadonlyArray; - - /** - * Event that is triggered when storage keys are changed - */ - readonly onDidChangeStorageKeys: Event>; - - /** - * Register a storage key that has to be synchronized during sync. - */ - registerStorageKey(key: IStorageKey): void; - - /** - * All registered extensions storage keys - */ - readonly extensionsStorageKeys: ReadonlyArray<[IExtensionIdentifierWithVersion, ReadonlyArray]>; - - /** - * Event that is triggered when extension storage keys are changed - */ - onDidChangeExtensionStorageKeys: Event<[IExtensionIdWithVersion, ReadonlyArray]>; - - /** - * Register storage keys that has to be synchronized for the given extension. - */ - registerExtensionStorageKeys(extension: IExtensionIdWithVersion, keys: string[]): void; - - /** - * Returns storage keys of the given extension that has to be synchronized. - */ - getExtensioStorageKeys(extension: IExtensionIdWithVersion): ReadonlyArray | undefined; -} - -export abstract class AbstractStorageKeysSyncRegistryService extends Disposable implements IStorageKeysSyncRegistryService { - - declare readonly _serviceBrand: undefined; - - protected readonly _storageKeys = new Map(); - get storageKeys(): ReadonlyArray { return [...this._storageKeys.values()]; } - - protected readonly _onDidChangeStorageKeys: Emitter> = this._register(new Emitter>()); - readonly onDidChangeStorageKeys = this._onDidChangeStorageKeys.event; - - protected readonly _extensionsStorageKeys = new Map(); - get extensionsStorageKeys() { - const result: [IExtensionIdWithVersion, ReadonlyArray][] = []; - this._extensionsStorageKeys.forEach((keys, extension) => result.push([ExtensionIdWithVersion.fromKey(extension)!, keys])); - return result; - } - protected readonly _onDidChangeExtensionStorageKeys = this._register(new Emitter<[IExtensionIdWithVersion, ReadonlyArray]>()); - readonly onDidChangeExtensionStorageKeys = this._onDidChangeExtensionStorageKeys.event; - - constructor() { - super(); - this._register(toDisposable(() => this._storageKeys.clear())); - } - - getExtensioStorageKeys(extension: IExtensionIdWithVersion): ReadonlyArray | undefined { - return this._extensionsStorageKeys.get(ExtensionIdWithVersion.toKey(extension)); - } - - protected updateExtensionStorageKeys(extension: IExtensionIdWithVersion, keys: string[]): void { - this._extensionsStorageKeys.set(ExtensionIdWithVersion.toKey(extension), keys); - this._onDidChangeExtensionStorageKeys.fire([extension, keys]); - } - - abstract registerStorageKey(key: IStorageKey): void; - abstract registerExtensionStorageKeys(extension: IExtensionIdWithVersion, keys: string[]): void; -} - -export class StorageKeysSyncRegistryService extends AbstractStorageKeysSyncRegistryService { - - _serviceBrand: any; - - registerStorageKey(storageKey: IStorageKey): void { - if (!this._storageKeys.has(storageKey.key)) { - this._storageKeys.set(storageKey.key, storageKey); - this._onDidChangeStorageKeys.fire(this.storageKeys); - } - } - - registerExtensionStorageKeys(extension: IExtensionIdWithVersion, keys: string[]): void { - this.updateExtensionStorageKeys(extension, keys); - } - -} diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index cb2342a2e..e1960cf0d 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -11,7 +11,7 @@ import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/use import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { localize } from 'vs/nls'; @@ -55,7 +55,7 @@ export class UserDataAutoSyncEnablementService extends Disposable implements _IU @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, ) { super(); - this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + this._register(storageService.onDidChangeValue(e => this.onDidStorageChange(e))); } isEnabled(defaultEnablement?: boolean): boolean { @@ -73,15 +73,15 @@ export class UserDataAutoSyncEnablementService extends Disposable implements _IU } setEnablement(enabled: boolean): void { - this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL); + this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL, StorageTarget.MACHINE); } - private onDidStorageChange(workspaceStorageChangeEvent: IWorkspaceStorageChangeEvent): void { - if (workspaceStorageChangeEvent.scope !== StorageScope.GLOBAL) { + private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void { + if (storageChangeEvent.scope !== StorageScope.GLOBAL) { return; } - if (enablementKey === workspaceStorageChangeEvent.key) { + if (enablementKey === storageChangeEvent.key) { this._onDidChangeEnablement.fire(this.isEnabled()); return; } @@ -110,7 +110,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto } private set syncUrl(syncUrl: URI | undefined) { if (syncUrl) { - this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.GLOBAL); + this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.GLOBAL, StorageTarget.MACHINE); } else { this.storageService.remove(storeUrlKey, StorageScope.GLOBAL); } @@ -325,7 +325,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto } private async disableMachineEventually(): Promise { - this.storageService.store(disableMachineEventuallyKey, true, StorageScope.GLOBAL); + this.storageService.store(disableMachineEventuallyKey, true, StorageScope.GLOBAL, StorageTarget.MACHINE); await timeout(1000 * 60 * 10); // Return if got stopped meanwhile. @@ -526,7 +526,7 @@ class AutoSync extends Disposable { // Update local session id if (manifest && manifest.session !== sessionId) { - this.storageService.store(sessionIdKey, manifest.session, StorageScope.GLOBAL); + this.storageService.store(sessionIdKey, manifest.session, StorageScope.GLOBAL, StorageTarget.MACHINE); } // Return if cancellation is requested diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 35a10cfd1..6d9d58d1b 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -220,7 +220,9 @@ export enum UserDataSyncErrorCode { TooManyRequestsAndRetryAfter = 'TooManyRequestsAndRetryAfter', /* 429 + Retry-After */ // Local Errors - ConnectionRefused = 'ConnectionRefused', + RequestFailed = 'RequestFailed', + RequestCanceled = 'RequestCanceled', + RequestTimeout = 'RequestTimeout', NoRef = 'NoRef', TurnedOff = 'TurnedOff', SessionExpired = 'SessionExpired', @@ -252,7 +254,7 @@ export class UserDataSyncError extends Error { } export class UserDataSyncStoreError extends UserDataSyncError { - constructor(message: string, code: UserDataSyncErrorCode, readonly operationId: string | undefined) { + constructor(message: string, readonly url: string, code: UserDataSyncErrorCode, readonly operationId: string | undefined) { super(message, code, undefined, operationId); } } diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index c9088b8d7..f67dfb1c0 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -9,11 +9,9 @@ import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncServic import { URI } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; -import { IStorageKeysSyncRegistryService, IStorageKey, IExtensionIdWithVersion, AbstractStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ILogService } from 'vs/platform/log/common/log'; import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; -import { IExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagement'; export class UserDataSyncChannel implements IServerChannel { @@ -175,68 +173,6 @@ export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService { } -type StorageKeysSyncRegistryServiceInitData = { storageKeys: ReadonlyArray, extensionsStorageKeys: ReadonlyArray<[IExtensionIdWithVersion, ReadonlyArray]> }; - -export class StorageKeysSyncRegistryChannel implements IServerChannel { - - constructor(private readonly service: IStorageKeysSyncRegistryService) { } - - listen(_: unknown, event: string): Event { - switch (event) { - case 'onDidChangeStorageKeys': return this.service.onDidChangeStorageKeys; - case 'onDidChangeExtensionStorageKeys': return this.service.onDidChangeExtensionStorageKeys; - } - throw new Error(`Event not found: ${event}`); - } - - call(context: any, command: string, args?: any): Promise { - switch (command) { - case '_getInitialData': return Promise.resolve({ storageKeys: this.service.storageKeys, extensionsStorageKeys: this.service.extensionsStorageKeys }); - case 'registerStorageKey': return Promise.resolve(this.service.registerStorageKey(args[0])); - case 'registerExtensionStorageKeys': return Promise.resolve(this.service.registerExtensionStorageKeys(args[0], args[1])); - } - throw new Error('Invalid call'); - } -} - -export class StorageKeysSyncRegistryChannelClient extends AbstractStorageKeysSyncRegistryService { - - declare readonly _serviceBrand: undefined; - - constructor(private readonly channel: IChannel) { - super(); - this.channel.call('_getInitialData').then(({ storageKeys, extensionsStorageKeys }) => { - this.updateStorageKeys(storageKeys); - this.updateExtensionsStorageKeys(extensionsStorageKeys); - this._register(this.channel.listen>('onDidChangeStorageKeys')(storageKeys => this.updateStorageKeys(storageKeys))); - this._register(this.channel.listen<[IExtensionIdentifierWithVersion, string[]]>('onDidChangeExtensionStorageKeys')(e => this.updateExtensionStorageKeys(e[0], e[1]))); - }); - } - - private async updateStorageKeys(storageKeys: ReadonlyArray): Promise { - this._storageKeys.clear(); - for (const storageKey of storageKeys) { - this._storageKeys.set(storageKey.key, storageKey); - } - this._onDidChangeStorageKeys.fire(this.storageKeys); - } - - private async updateExtensionsStorageKeys(extensionStorageKeys: ReadonlyArray<[IExtensionIdentifierWithVersion, ReadonlyArray]>): Promise { - for (const [extension, keys] of extensionStorageKeys) { - this.updateExtensionStorageKeys(extension, [...keys]); - } - } - - registerStorageKey(storageKey: IStorageKey): void { - this.channel.call('registerStorageKey', [storageKey]); - } - - registerExtensionStorageKeys(extension: IExtensionIdentifierWithVersion, keys: string[]): void { - this.channel.call('registerExtensionStorageKeys', [extension, keys]); - } - -} - export class UserDataSyncMachinesServiceChannel implements IServerChannel { constructor(private readonly service: IUserDataSyncMachinesService) { } diff --git a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts index 1b9d608b7..f50ad491a 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IUserDataSyncStoreService, IUserData, IUserDataSyncLogService, IUserDataManifest } from 'vs/platform/userDataSync/common/userDataSync'; import { localize } from 'vs/nls'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -103,7 +103,7 @@ export class UserDataSyncMachinesService extends Disposable implements IUserData machine.name = name; await this.writeMachinesData(machineData); if (machineData.machines.some(({ id }) => id === currentMachineId)) { - this.storageService.store(currentMachineNameKey, name, StorageScope.GLOBAL); + this.storageService.store(currentMachineNameKey, name, StorageScope.GLOBAL, StorageTarget.MACHINE); } } } diff --git a/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts index a84f21744..4fd2b3c33 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts @@ -6,7 +6,7 @@ import { IUserDataSyncResourceEnablementService, ALL_SYNC_RESOURCES, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; type SyncEnablementClassification = { @@ -28,7 +28,7 @@ export class UserDataSyncResourceEnablementService extends Disposable implements @ITelemetryService private readonly telemetryService: ITelemetryService, ) { super(); - this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + this._register(storageService.onDidChangeValue(e => this.onDidStorageChange(e))); } isResourceEnabled(resource: SyncResource): boolean { @@ -39,13 +39,13 @@ export class UserDataSyncResourceEnablementService extends Disposable implements if (this.isResourceEnabled(resource) !== enabled) { const resourceEnablementKey = getEnablementKey(resource); this.telemetryService.publicLog2<{ enabled: boolean }, SyncEnablementClassification>(resourceEnablementKey, { enabled }); - this.storageService.store(resourceEnablementKey, enabled, StorageScope.GLOBAL); + this.storageService.store(resourceEnablementKey, enabled, StorageScope.GLOBAL, StorageTarget.MACHINE); } } - private onDidStorageChange(workspaceStorageChangeEvent: IWorkspaceStorageChangeEvent): void { - if (workspaceStorageChangeEvent.scope === StorageScope.GLOBAL) { - const resourceKey = ALL_SYNC_RESOURCES.filter(resourceKey => getEnablementKey(resourceKey) === workspaceStorageChangeEvent.key)[0]; + private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void { + if (storageChangeEvent.scope === StorageScope.GLOBAL) { + const resourceKey = ALL_SYNC_RESOURCES.filter(resourceKey => getEnablementKey(resourceKey) === storageChangeEvent.key)[0]; if (resourceKey) { this._onDidChangeResourceEnablement.fire([resourceKey, this.isResourceEnabled(resourceKey)]); return; diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 50ea1f334..2cf4ac375 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -5,7 +5,7 @@ import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncErrorCode, - UserDataSyncError, ISyncResourceHandle, IUserDataManifest, ISyncTask, IResourcePreview, IManualSyncTask, ISyncResourcePreview, HEADER_EXECUTION_ID, MergeState, Change, IUserDataSyncStoreManagementService + UserDataSyncError, ISyncResourceHandle, IUserDataManifest, ISyncTask, IResourcePreview, IManualSyncTask, ISyncResourcePreview, HEADER_EXECUTION_ID, MergeState, Change, IUserDataSyncStoreManagementService, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -16,7 +16,7 @@ import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalS import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { equals } from 'vs/base/common/arrays'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; import { isEqual } from 'vs/base/common/resources'; @@ -30,6 +30,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; type SyncErrorClassification = { code: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; service: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + url?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; resource?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; executionId?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; @@ -118,9 +119,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } manifest = await this.userDataSyncStoreService.manifest(syncHeaders); } catch (error) { - error = UserDataSyncError.toUserDataSyncError(error); - this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); - throw error; + const userDataSyncError = UserDataSyncError.toUserDataSyncError(error); + this.reportUserDataSyncError(userDataSyncError, executionId); + throw userDataSyncError; } let executed = false; @@ -156,9 +157,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ try { manifest = await this.userDataSyncStoreService.manifest(syncHeaders); } catch (error) { - error = UserDataSyncError.toUserDataSyncError(error); - this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); - throw error; + const userDataSyncError = UserDataSyncError.toUserDataSyncError(error); + this.reportUserDataSyncError(userDataSyncError, executionId); + throw userDataSyncError; } return new ManualSyncTask(executionId, manifest, syncHeaders, this.synchronisers, this.logService); @@ -202,9 +203,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`); this.updateLastSyncTime(); } catch (error) { - error = UserDataSyncError.toUserDataSyncError(error); - this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); - throw error; + const userDataSyncError = UserDataSyncError.toUserDataSyncError(error); + this.reportUserDataSyncError(userDataSyncError, executionId); + throw userDataSyncError; } finally { this.updateStatus(); this._onSyncErrors.fire(this._syncErrors); @@ -365,7 +366,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ private updateLastSyncTime(): void { if (this.status === SyncStatus.Idle) { this._lastSyncTime = new Date().getTime(); - this.storageService.store(LAST_SYNC_TIME_KEY, this._lastSyncTime, StorageScope.GLOBAL); + this.storageService.store(LAST_SYNC_TIME_KEY, this._lastSyncTime, StorageScope.GLOBAL, StorageTarget.MACHINE); this._onDidChangeLastSyncTime.fire(this._lastSyncTime); } } @@ -390,6 +391,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.logService.error(`${source}: ${toErrorMessage(e)}`); } + private reportUserDataSyncError(userDataSyncError: UserDataSyncError, executionId: string) { + this.telemetryService.publicLog2<{ code: string, service: string, url?: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', + { code: userDataSyncError.code, url: userDataSyncError instanceof UserDataSyncStoreError ? userDataSyncError.url : undefined, resource: userDataSyncError.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); + } + private computeConflicts(): [SyncResource, IResourcePreview[]][] { return this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts) .map(s => ([s.resource, s.conflicts.map(toStrictResourcePreview)])); diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index fe6f311f9..409586895 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -14,13 +14,14 @@ import { IProductService, ConfigurationSyncStore } from 'vs/platform/product/com import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { generateUuid } from 'vs/base/common/uuid'; import { isWeb } from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; import { createCancelablePromise, timeout, CancelablePromise } from 'vs/base/common/async'; import { isString, isObject, isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; +import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; const SYNC_SERVICE_URL_TYPE = 'sync.store.url.type'; const SYNC_PREVIOUS_STORE = 'sync.previous.store'; @@ -67,10 +68,10 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa const syncStore = value as ConfigurationSyncStore; const canSwitch = !!syncStore.canSwitch && !configuredStore?.url; const type: UserDataSyncStoreType | undefined = canSwitch ? this.storageService.get(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL) as UserDataSyncStoreType : undefined; - const url = configuredStore?.url - || type === 'insiders' ? syncStore.insidersUrl - : type === 'stable' ? syncStore.stableUrl - : syncStore.url; + const url = configuredStore?.url || + (type === 'insiders' ? syncStore.insidersUrl + : type === 'stable' ? syncStore.stableUrl + : syncStore.url); return { url: URI.parse(url), type, @@ -111,7 +112,7 @@ export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStor const syncStore = this.productService[CONFIGURATION_SYNC_STORE_KEY]; if (syncStore) { - this.storageService.store(SYNC_PREVIOUS_STORE, JSON.stringify(syncStore), StorageScope.GLOBAL); + this.storageService.store(SYNC_PREVIOUS_STORE, JSON.stringify(syncStore), StorageScope.GLOBAL, StorageTarget.MACHINE); } else { this.storageService.remove(SYNC_PREVIOUS_STORE, StorageScope.GLOBAL); } @@ -122,7 +123,7 @@ export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStor if (type === this.userDataSyncStore.defaultType) { this.storageService.remove(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL); } else { - this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.GLOBAL); + this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.GLOBAL, StorageTarget.MACHINE); } this.updateUserDataSyncStore(); } @@ -207,7 +208,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } if (this._donotMakeRequestsUntil) { - this.storageService.store(DONOT_MAKE_REQUESTS_UNTIL_KEY, this._donotMakeRequestsUntil.getTime(), StorageScope.GLOBAL); + this.storageService.store(DONOT_MAKE_REQUESTS_UNTIL_KEY, this._donotMakeRequestsUntil.getTime(), StorageScope.GLOBAL, StorageTarget.MACHINE); this.resetDonotMakeRequestsUntilPromise = createCancelablePromise(token => timeout(this._donotMakeRequestsUntil!.getTime() - Date.now(), token).then(() => this.setDonotMakeRequestsUntil(undefined))); } else { this.storageService.remove(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.GLOBAL); @@ -225,7 +226,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const uri = joinPath(this.userDataSyncStoreUrl, 'resource', resource); const headers: IHeaders = {}; - const context = await this.request({ type: 'GET', url: uri.toString(), headers }, [], CancellationToken.None); + const context = await this.request(uri.toString(), { type: 'GET', headers }, [], CancellationToken.None); const result = await asJson<{ url: string, created: number }[]>(context) || []; return result.map(({ url, created }) => ({ ref: relativePath(uri, uri.with({ path: url }))!, created: created * 1000 /* Server returns in seconds */ })); @@ -240,7 +241,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const headers: IHeaders = {}; headers['Cache-Control'] = 'no-cache'; - const context = await this.request({ type: 'GET', url, headers }, [], CancellationToken.None); + const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None); const content = await asText(context); return content; } @@ -253,7 +254,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource).toString(); const headers: IHeaders = {}; - await this.request({ type: 'DELETE', url, headers }, [], CancellationToken.None); + await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None); } async read(resource: ServerResource, oldValue: IUserData | null, headers: IHeaders = {}): Promise { @@ -269,7 +270,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync headers['If-None-Match'] = oldValue.ref; } - const context = await this.request({ type: 'GET', url, headers }, [304], CancellationToken.None); + const context = await this.request(url, { type: 'GET', headers }, [304], CancellationToken.None); if (context.res.statusCode === 304) { // There is no new value. Hence return the old value. @@ -278,7 +279,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const ref = context.res.headers['etag']; if (!ref) { - throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, context.res.headers[HEADER_OPERATION_ID]); + throw new UserDataSyncStoreError('Server did not return the ref', url, UserDataSyncErrorCode.NoRef, context.res.headers[HEADER_OPERATION_ID]); } const content = await asText(context); return { ref, content }; @@ -296,11 +297,11 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync headers['If-Match'] = ref; } - const context = await this.request({ type: 'POST', url, data, headers }, [], CancellationToken.None); + const context = await this.request(url, { type: 'POST', data, headers }, [], CancellationToken.None); const newRef = context.res.headers['etag']; if (!newRef) { - throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, context.res.headers[HEADER_OPERATION_ID]); + throw new UserDataSyncStoreError('Server did not return the ref', url, UserDataSyncErrorCode.NoRef, context.res.headers[HEADER_OPERATION_ID]); } return newRef; } @@ -314,7 +315,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync headers = { ...headers }; headers['Content-Type'] = 'application/json'; - const context = await this.request({ type: 'GET', url, headers }, [], CancellationToken.None); + const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None); const manifest = await asJson(context); const currentSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.GLOBAL); @@ -331,7 +332,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync if (manifest) { // update session - this.storageService.store(USER_SESSION_ID_KEY, manifest.session, StorageScope.GLOBAL); + this.storageService.store(USER_SESSION_ID_KEY, manifest.session, StorageScope.GLOBAL, StorageTarget.MACHINE); } return manifest; @@ -345,7 +346,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const url = joinPath(this.userDataSyncStoreUrl, 'resource').toString(); const headers: IHeaders = { 'Content-Type': 'text/plain' }; - await this.request({ type: 'DELETE', url, headers }, [], CancellationToken.None); + await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None); // clear cached session. this.clearSession(); @@ -356,13 +357,13 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync this.storageService.remove(MACHINE_SESSION_ID_KEY, StorageScope.GLOBAL); } - private async request(options: IRequestOptions, successCodes: number[], token: CancellationToken): Promise { + private async request(url: string, options: IRequestOptions, successCodes: number[], token: CancellationToken): Promise { if (!this.authToken) { - throw new UserDataSyncStoreError('No Auth Token Available', UserDataSyncErrorCode.Unauthorized, undefined); + throw new UserDataSyncStoreError('No Auth Token Available', url, UserDataSyncErrorCode.Unauthorized, undefined); } if (this._donotMakeRequestsUntil && Date.now() < this._donotMakeRequestsUntil.getTime()) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too many requests (429).`, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter, undefined); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too many requests (429).`, url, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter, undefined); } this.setDonotMakeRequestsUntil(undefined); @@ -377,21 +378,23 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync // Add session headers this.addSessionHeaders(options.headers); - this.logService.trace('Sending request to server', { url: options.url, type: options.type, headers: { ...options.headers, ...{ authorization: undefined } } }); + this.logService.trace('Sending request to server', { url, type: options.type, headers: { ...options.headers, ...{ authorization: undefined } } }); let context; try { - context = await this.session.request(options, token); + context = await this.session.request(url, options, token); } catch (e) { if (!(e instanceof UserDataSyncStoreError)) { - e = new UserDataSyncStoreError(`Connection refused for the request '${options.url?.toString()}'.`, UserDataSyncErrorCode.ConnectionRefused, undefined); + const code = isPromiseCanceledError(e) ? UserDataSyncErrorCode.RequestCanceled + : getErrorMessage(e).startsWith('XHR timeout') ? UserDataSyncErrorCode.RequestTimeout : UserDataSyncErrorCode.RequestFailed; + e = new UserDataSyncStoreError(`Connection refused for the request '${url}'.`, url, code, undefined); } - this.logService.info('Request failed', options.url); + this.logService.info('Request failed', url); throw e; } const operationId = context.res.headers[HEADER_OPERATION_ID]; - const requestInfo = { url: options.url, status: context.res.statusCode, 'execution-id': options.headers[HEADER_EXECUTION_ID], 'operation-id': operationId }; + const requestInfo = { url, status: context.res.statusCode, 'execution-id': options.headers[HEADER_EXECUTION_ID], 'operation-id': operationId }; const isSuccess = isSuccessContext(context) || (context.res.statusCode && successCodes.indexOf(context.res.statusCode) !== -1); if (isSuccess) { this.logService.trace('Request succeeded', requestInfo); @@ -402,43 +405,43 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync if (context.res.statusCode === 401) { this.authToken = undefined; this._onTokenFailed.fire(); - throw new UserDataSyncStoreError(`Request '${options.url?.toString()}' failed because of Unauthorized (401).`, UserDataSyncErrorCode.Unauthorized, operationId); + throw new UserDataSyncStoreError(`Request '${url}' failed because of Unauthorized (401).`, url, UserDataSyncErrorCode.Unauthorized, operationId); } this._onTokenSucceed.fire(); if (context.res.statusCode === 409) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Conflict (409). There is new data for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.Conflict, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of Conflict (409). There is new data for this resource. Make the request again with latest data.`, url, UserDataSyncErrorCode.Conflict, operationId); } if (context.res.statusCode === 410) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because the requested resource is not longer available (410).`, UserDataSyncErrorCode.Gone, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because the requested resource is not longer available (410).`, url, UserDataSyncErrorCode.Gone, operationId); } if (context.res.statusCode === 412) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Precondition Failed (412). There is new data for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.PreconditionFailed, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of Precondition Failed (412). There is new data for this resource. Make the request again with latest data.`, url, UserDataSyncErrorCode.PreconditionFailed, operationId); } if (context.res.statusCode === 413) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too large payload (413).`, UserDataSyncErrorCode.TooLarge, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too large payload (413).`, url, UserDataSyncErrorCode.TooLarge, operationId); } if (context.res.statusCode === 426) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed with status Upgrade Required (426). Please upgrade the client and try again.`, UserDataSyncErrorCode.UpgradeRequired, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed with status Upgrade Required (426). Please upgrade the client and try again.`, url, UserDataSyncErrorCode.UpgradeRequired, operationId); } if (context.res.statusCode === 429) { const retryAfter = context.res.headers['retry-after']; if (retryAfter) { this.setDonotMakeRequestsUntil(new Date(Date.now() + (parseInt(retryAfter) * 1000))); - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too many requests (429).`, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too many requests (429).`, url, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter, operationId); } else { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too many requests (429).`, UserDataSyncErrorCode.TooManyRequests, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too many requests (429).`, url, UserDataSyncErrorCode.TooManyRequests, operationId); } } if (!isSuccess) { - throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, operationId); + throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, url, UserDataSyncErrorCode.Unknown, operationId); } return context; @@ -448,7 +451,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync let machineSessionId = this.storageService.get(MACHINE_SESSION_ID_KEY, StorageScope.GLOBAL); if (machineSessionId === undefined) { machineSessionId = generateUuid(); - this.storageService.store(MACHINE_SESSION_ID_KEY, machineSessionId, StorageScope.GLOBAL); + this.storageService.store(MACHINE_SESSION_ID_KEY, machineSessionId, StorageScope.GLOBAL, StorageTarget.MACHINE); } headers['X-Machine-Session-Id'] = machineSessionId; @@ -490,18 +493,20 @@ export class RequestsSession { private readonly logService: IUserDataSyncLogService, ) { } - request(options: IRequestOptions, token: CancellationToken): Promise { + request(url: string, options: IRequestOptions, token: CancellationToken): Promise { if (this.isExpired()) { this.reset(); } + options.url = url; + if (this.requests.length >= this.limit) { this.logService.info('Too many requests', ...this.requests); - throw new UserDataSyncStoreError(`Too many requests. Only ${this.limit} requests allowed in ${this.interval / (1000 * 60)} minutes.`, UserDataSyncErrorCode.LocalTooManyRequests, undefined); + throw new UserDataSyncStoreError(`Too many requests. Only ${this.limit} requests allowed in ${this.interval / (1000 * 60)} minutes.`, url, UserDataSyncErrorCode.LocalTooManyRequests, undefined); } this.startTime = this.startTime || new Date(); - this.requests.push(options.url!); + this.requests.push(url); return this.requestService.request(options, token); } diff --git a/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts b/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts index e89763a4d..27fb0a235 100644 --- a/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts @@ -9,11 +9,11 @@ import { NullLogService } from 'vs/platform/log/common/log'; suite('GlobalStateMerge', () => { - test('merge when local and remote are same with one value', async () => { + test('merge when local and remote are same with one value and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -21,11 +21,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when local and remote are same with multiple entries', async () => { + test('merge when local and remote are same with multiple entries and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; const remote = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -33,11 +33,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when local and remote are same with multiple entries in different order', async () => { + test('merge when local and remote are same with multiple entries in different order and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -50,7 +50,7 @@ suite('GlobalStateMerge', () => { const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; const base = { 'b': { version: 1, value: 'a' } }; - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, base, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -58,11 +58,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when a new entry is added to remote', async () => { + test('merge when a new entry is added to remote and local has not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, { 'b': { version: 1, value: 'b' } }); assert.deepEqual(actual.local.updated, {}); @@ -70,11 +70,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when multiple new entries are added to remote', async () => { + test('merge when multiple new entries are added to remote and local is not synced yet', async () => { const local = {}; const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }); assert.deepEqual(actual.local.updated, {}); @@ -86,7 +86,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, { 'b': { version: 1, value: 'b' } }); assert.deepEqual(actual.local.updated, {}); @@ -98,7 +98,7 @@ suite('GlobalStateMerge', () => { const local = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -110,7 +110,7 @@ suite('GlobalStateMerge', () => { const local = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; const remote = {}; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -122,7 +122,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'b' } }; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } }); @@ -134,7 +134,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; const remote = { 'a': { version: 1, value: 'd' }, 'c': { version: 1, value: 'c' } }; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, { 'c': { version: 1, value: 'c' } }); assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'd' } }); @@ -142,11 +142,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when new entries are added to local', async () => { + test('merge when new entries are added to local and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; const remote = { 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -158,7 +158,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' }, 'c': { version: 1, value: 'c' } }; const remote = { 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, remote, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -170,7 +170,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, remote, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -182,7 +182,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'b' } }; const remote = { 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, remote, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -194,7 +194,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'd' }, 'b': { version: 1, value: 'b' } }; const remote = { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' } }; - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, remote, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -202,11 +202,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, local); }); - test('merge when local and remote with one entry but different value', async () => { + test('merge when local and remote with one entry but different value and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'b' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } }); @@ -219,12 +219,12 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'd' } }; const remote = { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' } }; - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, base, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, { 'c': { version: 1, value: 'c' } }); assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, ['b']); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.local.removed, []); + assert.deepEqual(actual.remote, { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' }, 'b': { version: 1, value: 'd' } }); }); test('merge with single entry and local is empty', async () => { @@ -232,32 +232,32 @@ suite('GlobalStateMerge', () => { const local = {}; const remote = { 'a': { version: 1, value: 'b' } }; - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, base, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } }); + assert.deepEqual(actual.local.updated, {}); assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.remote, local); }); - test('merge when local and remote has moved forwareded with conflicts', async () => { + test('merge when local and remote has moved forward with conflicts', async () => { const base = { 'a': { version: 1, value: 'a' } }; const local = { 'a': { version: 1, value: 'd' } }; const remote = { 'a': { version: 1, value: 'b' } }; - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, base, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } }); + assert.deepEqual(actual.local.updated, {}); assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.remote, local); }); - test('merge when a new entry is added to remote but not a registered key', async () => { + test('merge when a new entry is added to remote but scoped to machine locally and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: ['b'], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -265,23 +265,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when a new entry is added to remote but different version', async () => { - const local = { 'a': { version: 1, value: 'a' } }; - const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); - }); - - test('merge when an entry is updated to remote but not a registered key', async () => { + test('merge when an entry is updated to remote but scoped to machine locally', async () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'b' } }; - const actual = merge(local, remote, local, [], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: ['a'], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -289,92 +277,43 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when a new entry is updated to remote but different version', async () => { + test('merge when a local value is removed and scoped to machine locally', async () => { + const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; + const local = { 'a': { version: 1, value: 'a' } }; + const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; + + const actual = merge(local, remote, base, { machine: ['b'], unregistered: [] }, new NullLogService()); + + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); + assert.deepEqual(actual.remote, local); + }); + + test('merge when local moved forwared by changing a key to machine scope', async () => { + const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; + const remote = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; + const local = { 'a': { version: 1, value: 'a' } }; + + const actual = merge(local, remote, base, { machine: ['b'], unregistered: [] }, new NullLogService()); + + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); + assert.deepEqual(actual.remote, local); + }); + + test('merge should not remove remote keys if not registered', async () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } }; + const base = { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' } }; + const remote = { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' } }; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, base, { machine: [], unregistered: ['c'] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); - }); - - test('merge when a local value is update with lower version', async () => { - const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'c' } }; - const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); - }); - - test('merge when a local value is update with higher version', async () => { - const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 2, value: 'c' } }; - const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 2 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, local); - }); - - test('merge when a local value is removed but not registered', async () => { - const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const local = { 'a': { version: 1, value: 'a' } }; - const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); - }); - - test('merge when a local value is removed with lower version', async () => { - const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const local = { 'a': { version: 1, value: 'a' } }; - const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); - }); - - test('merge when a local value is removed with higher version', async () => { - const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const local = { 'a': { version: 1, value: 'a' } }; - const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 2 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, local); - }); - - test('merge when a local value is not yet registered', async () => { - const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const local = { 'a': { version: 1, value: 'a' } }; - const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.remote, { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' }, 'c': { version: 1, value: 'c' } }); }); }); diff --git a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts index 2d5594c01..324469b7d 100644 --- a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts @@ -10,10 +10,9 @@ import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { IFileService } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalStateSync'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; suite('GlobalStateSync', () => { @@ -28,17 +27,11 @@ suite('GlobalStateSync', () => { setup(async () => { testClient = disposableStore.add(new UserDataSyncClient(server)); await testClient.setUp(true); - let storageKeysSyncRegistryService = testClient.instantiationService.get(IStorageKeysSyncRegistryService); - storageKeysSyncRegistryService.registerStorageKey({ key: 'a', version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: 'b', version: 1 }); testObject = (testClient.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.GlobalState) as GlobalStateSynchroniser; disposableStore.add(toDisposable(() => testClient.instantiationService.get(IUserDataSyncStoreService).clear())); client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); - storageKeysSyncRegistryService = client2.instantiationService.get(IStorageKeysSyncRegistryService); - storageKeysSyncRegistryService.registerStorageKey({ key: 'a', version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: 'b', version: 1 }); }); teardown(() => disposableStore.clear()); @@ -72,7 +65,7 @@ suite('GlobalStateSync', () => { test('when global state is created after first sync', async () => { await testObject.sync(await testClient.manifest()); - updateStorage('a', 'value1', testClient); + updateUserStorage('a', 'value1', testClient); let lastSyncUserData = await testObject.getLastSyncUserData(); const manifest = await testClient.manifest(); @@ -91,7 +84,8 @@ suite('GlobalStateSync', () => { }); test('first time sync - outgoing to server (no state)', async () => { - updateStorage('a', 'value1', testClient); + updateUserStorage('a', 'value1', testClient); + updateMachineStorage('b', 'value1', testClient); await updateLocale(testClient); await testObject.sync(await testClient.manifest()); @@ -105,7 +99,7 @@ suite('GlobalStateSync', () => { }); test('first time sync - incoming from server (no state)', async () => { - updateStorage('a', 'value1', client2); + updateUserStorage('a', 'value1', client2); await updateLocale(client2); await client2.sync(); @@ -118,10 +112,10 @@ suite('GlobalStateSync', () => { }); test('first time sync when storage exists', async () => { - updateStorage('a', 'value1', client2); + updateUserStorage('a', 'value1', client2); await client2.sync(); - updateStorage('b', 'value2', testClient); + updateUserStorage('b', 'value2', testClient); await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -136,10 +130,10 @@ suite('GlobalStateSync', () => { }); test('first time sync when storage exists - has conflicts', async () => { - updateStorage('a', 'value1', client2); + updateUserStorage('a', 'value1', client2); await client2.sync(); - updateStorage('a', 'value2', client2); + updateUserStorage('a', 'value2', client2); await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); @@ -154,10 +148,10 @@ suite('GlobalStateSync', () => { }); test('sync adding a storage value', async () => { - updateStorage('a', 'value1', testClient); + updateUserStorage('a', 'value1', testClient); await testObject.sync(await testClient.manifest()); - updateStorage('b', 'value2', testClient); + updateUserStorage('b', 'value2', testClient); await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -172,10 +166,10 @@ suite('GlobalStateSync', () => { }); test('sync updating a storage value', async () => { - updateStorage('a', 'value1', testClient); + updateUserStorage('a', 'value1', testClient); await testObject.sync(await testClient.manifest()); - updateStorage('a', 'value2', testClient); + updateUserStorage('a', 'value2', testClient); await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -189,8 +183,8 @@ suite('GlobalStateSync', () => { }); test('sync removing a storage value', async () => { - updateStorage('a', 'value1', testClient); - updateStorage('b', 'value2', testClient); + updateUserStorage('a', 'value1', testClient); + updateUserStorage('b', 'value2', testClient); await testObject.sync(await testClient.manifest()); removeStorage('b', testClient); @@ -218,9 +212,14 @@ suite('GlobalStateSync', () => { await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'en' }))); } - function updateStorage(key: string, value: string, client: UserDataSyncClient): void { + function updateUserStorage(key: string, value: string, client: UserDataSyncClient): void { const storageService = client.instantiationService.get(IStorageService); - storageService.store(key, value, StorageScope.GLOBAL); + storageService.store(key, value, StorageScope.GLOBAL, StorageTarget.USER); + } + + function updateMachineStorage(key: string, value: string, client: UserDataSyncClient): void { + const storageService = client.instantiationService.get(IStorageService); + storageService.store(key, value, StorageScope.GLOBAL, StorageTarget.MACHINE); } function removeStorage(key: string, client: UserDataSyncClient): void { diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 6e821b1ba..322046976 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -36,10 +36,10 @@ import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/plat import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; +import { ExtensionsStorageSyncService, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; export class UserDataSyncClient extends Disposable { @@ -104,9 +104,9 @@ export class UserDataSyncClient extends Disposable { this.instantiationService.stub(IUserDataSyncBackupStoreService, this.instantiationService.createInstance(UserDataSyncBackupStoreService)); this.instantiationService.stub(IUserDataSyncUtilService, new TestUserDataSyncUtilService()); this.instantiationService.stub(IUserDataSyncResourceEnablementService, this.instantiationService.createInstance(UserDataSyncResourceEnablementService)); - this.instantiationService.stub(IStorageKeysSyncRegistryService, this.instantiationService.createInstance(StorageKeysSyncRegistryService)); this.instantiationService.stub(IGlobalExtensionEnablementService, this.instantiationService.createInstance(GlobalExtensionEnablementService)); + this.instantiationService.stub(IExtensionsStorageSyncService, this.instantiationService.createInstance(ExtensionsStorageSyncService)); this.instantiationService.stub(IIgnoredExtensionsManagementService, this.instantiationService.createInstance(IgnoredExtensionsManagementService)); this.instantiationService.stub(IExtensionManagementService, >{ async getInstalled() { return []; }, diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts index 3277d22be..2c7d1734d 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts @@ -464,10 +464,10 @@ suite('UserDataSyncRequestsSession', () => { test('too many requests are thrown when limit exceeded', async () => { const testObject = new RequestsSession(1, 500, requestService, new NullLogService()); - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); try { - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); } catch (error) { assert.ok(error instanceof UserDataSyncStoreError); assert.equal((error).code, UserDataSyncErrorCode.LocalTooManyRequests); @@ -478,19 +478,19 @@ suite('UserDataSyncRequestsSession', () => { test('requests are handled after session is expired', async () => { const testObject = new RequestsSession(1, 500, requestService, new NullLogService()); - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); await timeout(600); - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); }); test('too many requests are thrown after session is expired', async () => { const testObject = new RequestsSession(1, 500, requestService, new NullLogService()); - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); await timeout(600); - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); try { - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); } catch (error) { assert.ok(error instanceof UserDataSyncStoreError); assert.equal((error).code, UserDataSyncErrorCode.LocalTooManyRequests); diff --git a/src/vs/platform/webview/common/webviewManagerService.ts b/src/vs/platform/webview/common/webviewManagerService.ts index 8963865f3..476201f8f 100644 --- a/src/vs/platform/webview/common/webviewManagerService.ts +++ b/src/vs/platform/webview/common/webviewManagerService.ts @@ -11,6 +11,14 @@ import { IWebviewPortMapping } from 'vs/platform/webview/common/webviewPortMappi export const IWebviewManagerService = createDecorator('webviewManagerService'); +export interface WebviewWebContentsId { + readonly webContentsId: number; +} + +export interface WebviewWindowId { + readonly windowId: number; +} + export interface IWebviewManagerService { _serviceBrand: unknown; @@ -20,7 +28,7 @@ export interface IWebviewManagerService { didLoadResource(requestId: number, content: VSBuffer | undefined): void; - setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise; + setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise; } export interface RegisterWebviewMetadata { diff --git a/src/vs/platform/webview/electron-main/webviewMainService.ts b/src/vs/platform/webview/electron-main/webviewMainService.ts index c3b49724a..03a6e306c 100644 --- a/src/vs/platform/webview/electron-main/webviewMainService.ts +++ b/src/vs/platform/webview/electron-main/webviewMainService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { webContents } from 'electron'; +import { WebContents, webContents } from 'electron'; import { VSBuffer } from 'vs/base/common/buffer'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { IRequestService } from 'vs/platform/request/common/request'; -import { IWebviewManagerService, RegisterWebviewMetadata } from 'vs/platform/webview/common/webviewManagerService'; +import { IWebviewManagerService, RegisterWebviewMetadata, WebviewWebContentsId, WebviewWindowId } from 'vs/platform/webview/common/webviewManagerService'; import { WebviewPortMappingProvider } from 'vs/platform/webview/electron-main/webviewPortMappingProvider'; import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; @@ -26,7 +26,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer @IFileService fileService: IFileService, @IRequestService requestService: IRequestService, @ITunnelService tunnelService: ITunnelService, - @IWindowsMainService windowsMainService: IWindowsMainService, + @IWindowsMainService private readonly windowsMainService: IWindowsMainService, ) { super(); this.protocolProvider = this._register(new WebviewProtocolProvider(fileService, requestService, windowsMainService)); @@ -70,11 +70,24 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer }); } - public async setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise { - const contents = webContents.fromId(webContentsId); - if (!contents) { - throw new Error(`Invalid webContentsId: ${webContentsId}`); + public async setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise { + let contents: WebContents | undefined; + + if (typeof (id as WebviewWindowId).windowId === 'number') { + const { windowId } = (id as WebviewWindowId); + const window = this.windowsMainService.getWindowById(windowId); + if (!window) { + throw new Error(`Invalid windowId: ${windowId}`); + } + contents = window.win.webContents; + } else { + const { webContentsId } = (id as WebviewWebContentsId); + contents = webContents.fromId(webContentsId); + if (!contents) { + throw new Error(`Invalid webContentsId: ${webContentsId}`); + } } + if (!contents.isDestroyed()) { contents.setIgnoreMenuShortcuts(enabled); } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 9d614c541..40c952136 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { isMacintosh, isLinux, isWeb, IProcessEnvironment } from 'vs/base/common/platform'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; @@ -85,8 +84,8 @@ export function isFileToOpen(uriToOpen: IWindowOpenable): uriToOpen is IFileToOp export type MenuBarVisibility = 'default' | 'visible' | 'toggle' | 'hidden' | 'compact'; -export function getMenuBarVisibility(configurationService: IConfigurationService, environment: IEnvironmentService, isExtensionDevelopment = environment.isExtensionDevelopment): MenuBarVisibility { - const titleBarStyle = getTitleBarStyle(configurationService, environment, isExtensionDevelopment); +export function getMenuBarVisibility(configurationService: IConfigurationService): MenuBarVisibility { + const titleBarStyle = getTitleBarStyle(configurationService); const menuBarVisibility = configurationService.getValue('window.menuBarVisibility'); if (titleBarStyle === 'native' && menuBarVisibility === 'compact') { @@ -104,7 +103,7 @@ export interface IWindowSettings { openFilesInNewWindow: 'on' | 'off' | 'default'; openFoldersInNewWindow: 'on' | 'off' | 'default'; openWithoutArgumentsInNewWindow: 'on' | 'off'; - restoreWindows: 'all' | 'folders' | 'one' | 'none'; + restoreWindows: 'preserve' | 'all' | 'folders' | 'one' | 'none'; restoreFullscreen: boolean; zoomLevel: number; titleBarStyle: 'native' | 'custom'; @@ -119,17 +118,12 @@ export interface IWindowSettings { enableExperimentalProxyLoginDialog: boolean; } -export function getTitleBarStyle(configurationService: IConfigurationService, environment: IEnvironmentService, isExtensionDevelopment = environment.isExtensionDevelopment): 'native' | 'custom' { +export function getTitleBarStyle(configurationService: IConfigurationService): 'native' | 'custom' { if (isWeb) { return 'custom'; } - const configuration = configurationService.getValue('window'); - - const isDev = !environment.isBuilt || isExtensionDevelopment; - if (isMacintosh && isDev) { - return 'native'; // not enabled when developing due to https://github.com/electron/electron/issues/3647 - } + const configuration = configurationService.getValue('window'); if (configuration) { const useNativeTabs = isMacintosh && configuration.nativeTabs === true; @@ -230,6 +224,10 @@ export interface IWindowConfiguration { filesToDiff?: IPath[]; } +export interface IOSConfiguration { + release: string; +} + export interface INativeWindowConfiguration extends IWindowConfiguration, NativeParsedArgs { mainPid: number; @@ -256,6 +254,8 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native userEnv: IProcessEnvironment; filesToWait?: IPathsToWaitFor; + + os: IOSConfiguration; } /** diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index e1f5be82b..83eccca73 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -12,8 +12,9 @@ import { IProcessEnvironment } from 'vs/base/common/platform'; import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; -import { Rectangle, BrowserWindow } from 'electron'; +import { Rectangle, BrowserWindow, WebContents } from 'electron'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface IWindowState { width?: number; @@ -33,6 +34,11 @@ export const enum WindowMode { export interface ICodeWindow extends IDisposable { + readonly onLoad: Event; + readonly onReady: Event; + readonly onClose: Event; + readonly onDestroy: Event; + readonly whenClosedOrLoaded: Promise; readonly id: number; @@ -59,7 +65,7 @@ export interface ICodeWindow extends IDisposable { addTabbedWindow(window: ICodeWindow): void; load(config: INativeWindowConfiguration, isReload?: boolean): void; - reload(configuration?: INativeWindowConfiguration, cli?: NativeParsedArgs): void; + reload(cli?: NativeParsedArgs): void; focus(options?: { force: boolean }): void; close(): void; @@ -67,7 +73,7 @@ export interface ICodeWindow extends IDisposable { getBounds(): Rectangle; send(channel: string, ...args: any[]): void; - sendWhenReady(channel: string, ...args: any[]): void; + sendWhenReady(channel: string, token: CancellationToken, ...args: any[]): void; readonly isFullScreen: boolean; toggleFullScreen(): void; @@ -113,6 +119,7 @@ export interface IWindowsMainService { getLastActiveWindow(): ICodeWindow | undefined; getWindowById(windowId: number): ICodeWindow | undefined; + getWindowByWebContents(webContents: WebContents): ICodeWindow | undefined; getWindows(): ICodeWindow[]; getWindowCount(): number; } diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index b87cc3744..6bcedb5c7 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -3,18 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; +import { statSync, unlink } from 'fs'; import { basename, normalize, join, posix } from 'vs/base/common/path'; import { localize } from 'vs/nls'; -import * as arrays from 'vs/base/common/arrays'; -import { mixin } from 'vs/base/common/objects'; +import { coalesce, distinct, firstOrDefault } from 'vs/base/common/arrays'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { IStateService } from 'vs/platform/state/node/state'; import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window'; -import { screen, BrowserWindow, MessageBoxOptions, Display, app } from 'electron'; +import { screen, BrowserWindow, MessageBoxOptions, Display, app, WebContents } from 'electron'; import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; @@ -40,6 +39,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { isWindowsDriveLetter, toSlashes, parseLineAndColumnAware } from 'vs/base/common/extpath'; import { CharCode } from 'vs/base/common/charCode'; import { getPathLabel } from 'vs/base/common/labels'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface IWindowState { workspace?: IWorkspaceIdentifier; @@ -59,7 +59,7 @@ interface INewWindowState extends ISingleWindowState { hasDefaultState?: boolean; } -type RestoreWindowsSetting = 'all' | 'folders' | 'one' | 'none'; +type RestoreWindowsSetting = 'preserve' | 'all' | 'folders' | 'one' | 'none'; interface IOpenBrowserWindowOptions { userEnv?: IProcessEnvironment; @@ -72,7 +72,7 @@ interface IOpenBrowserWindowOptions { initialStartup?: boolean; - fileInputs?: IFileInputs; + filesToOpen?: IFilesToOpen; forceNewWindow?: boolean; forceNewTabbedWindow?: boolean; @@ -87,7 +87,7 @@ interface IPathParseOptions { remoteAuthority?: string; } -interface IFileInputs { +interface IFilesToOpen { filesToOpenOrCreate: IPath[]; filesToDiff: IPath[]; filesToWait?: IPathsToWaitFor; @@ -307,7 +307,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic currentWindowsState.lastPluginDevelopmentHostWindow = this.toWindowState(extensionHostWindow); } - // 3.) All windows (except extension host) for N >= 2 to support restoreWindows: all or for auto update + // 3.) All windows (except extension host) for N >= 2 to support `restoreWindows: all` or for auto update // // Careful here: asking a window for its window state after it has been closed returns bogus values (width: 0, height: 0) // so if we ever want to persist the UI state of the last closed window (window count === 1), it has @@ -385,17 +385,17 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic this.logService.trace('windowsManager#open'); openConfig = this.validateOpenConfig(openConfig); - const pathsToOpen = this.getPathsToOpen(openConfig); - this.logService.trace('windowsManager#open pathsToOpen', pathsToOpen); - const foldersToAdd: IFolderPathToOpen[] = []; const foldersToOpen: IFolderPathToOpen[] = []; const workspacesToOpen: IWorkspacePathToOpen[] = []; const workspacesToRestore: IWorkspacePathToOpen[] = []; - const emptyToRestore: IEmptyWindowBackupInfo[] = []; // empty windows with backupPath + const emptyToRestore: IEmptyWindowBackupInfo[] = []; + let filesToOpen: IFilesToOpen | undefined; + let emptyToOpen = 0; - let emptyToOpen: number = 0; - let fileInputs: IFileInputs | undefined; // collect all file inputs + // Identify things to open from open config + const pathsToOpen = this.getPathsToOpen(openConfig); + this.logService.trace('windowsManager#open pathsToOpen', pathsToOpen); for (const path of pathsToOpen) { if (isFolderPathToOpen(path)) { if (openConfig.addMode) { @@ -408,10 +408,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } else if (isWorkspacePathToOpen(path)) { workspacesToOpen.push(path); } else if (path.fileUri) { - if (!fileInputs) { - fileInputs = { filesToOpenOrCreate: [], filesToDiff: [], remoteAuthority: path.remoteAuthority }; + if (!filesToOpen) { + filesToOpen = { filesToOpenOrCreate: [], filesToDiff: [], remoteAuthority: path.remoteAuthority }; } - fileInputs.filesToOpenOrCreate.push(path); + filesToOpen.filesToOpenOrCreate.push(path); } else if (path.backupPath) { emptyToRestore.push({ backupFolder: basename(path.backupPath), remoteAuthority: path.remoteAuthority }); } else { @@ -421,14 +421,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // When run with --diff, take the files to open as files to diff // if there are exactly two files provided. - if (fileInputs && openConfig.diffMode && fileInputs.filesToOpenOrCreate.length === 2) { - fileInputs.filesToDiff = fileInputs.filesToOpenOrCreate; - fileInputs.filesToOpenOrCreate = []; + if (openConfig.diffMode && filesToOpen?.filesToOpenOrCreate.length === 2) { + filesToOpen.filesToDiff = filesToOpen.filesToOpenOrCreate; + filesToOpen.filesToOpenOrCreate = []; } // When run with --wait, make sure we keep the paths to wait for - if (fileInputs && openConfig.waitMarkerFileURI) { - fileInputs.filesToWait = { paths: [...fileInputs.filesToDiff, ...fileInputs.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI }; + if (filesToOpen && openConfig.waitMarkerFileURI) { + filesToOpen.filesToWait = { paths: [...filesToOpen.filesToDiff, ...filesToOpen.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI }; } // @@ -447,52 +447,61 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Open based on config - const usedWindows = this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyToRestore, emptyToOpen, fileInputs, foldersToAdd); + const { windows: usedWindows, filesOpenedInWindow } = this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyToRestore, emptyToOpen, filesToOpen, foldersToAdd); this.logService.trace(`windowsManager#open used window count ${usedWindows.length} (workspacesToOpen: ${workspacesToOpen.length}, foldersToOpen: ${foldersToOpen.length}, emptyToRestore: ${emptyToRestore.length}, emptyToOpen: ${emptyToOpen})`); // Make sure to pass focus to the most relevant of the windows if we open multiple if (usedWindows.length > 1) { - const focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !openConfig.cli['file-uri'] && !openConfig.cli['folder-uri'] && !(openConfig.urisToOpen && openConfig.urisToOpen.length); - let focusLastOpened = true; - let focusLastWindow = true; - // 1.) focus last active window if we are not instructed to open any paths - if (focusLastActive) { - const lastActiveWindow = usedWindows.filter(window => this.windowsState.lastActiveWindow && window.backupPath === this.windowsState.lastActiveWindow.backupPath); - if (lastActiveWindow.length) { - lastActiveWindow[0].focus(); - focusLastOpened = false; - focusLastWindow = false; - } + // 1.) focus window we opened files in always with highest priority + if (filesOpenedInWindow) { + filesOpenedInWindow.focus(); } - // 2.) if instructed to open paths, focus last window which is not restored - if (focusLastOpened) { - for (let i = usedWindows.length - 1; i >= 0; i--) { - const usedWindow = usedWindows[i]; - if ( - (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => usedWindow.openedWorkspace && workspace.workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace - (usedWindow.backupPath && emptyToRestore.some(empty => usedWindow.backupPath && empty.backupFolder === basename(usedWindow.backupPath))) // skip over restored empty window - ) { - continue; + // Otherwise, find a good window based on open params + else { + const focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !openConfig.cli['file-uri'] && !openConfig.cli['folder-uri'] && !(openConfig.urisToOpen && openConfig.urisToOpen.length); + let focusLastOpened = true; + let focusLastWindow = true; + + // 2.) focus last active window if we are not instructed to open any paths + if (focusLastActive) { + const lastActiveWindow = usedWindows.filter(window => this.windowsState.lastActiveWindow && window.backupPath === this.windowsState.lastActiveWindow.backupPath); + if (lastActiveWindow.length) { + lastActiveWindow[0].focus(); + focusLastOpened = false; + focusLastWindow = false; } - - usedWindow.focus(); - focusLastWindow = false; - break; } - } - // 3.) finally, always ensure to have at least last used window focused - if (focusLastWindow) { - usedWindows[usedWindows.length - 1].focus(); + // 3.) if instructed to open paths, focus last window which is not restored + if (focusLastOpened) { + for (let i = usedWindows.length - 1; i >= 0; i--) { + const usedWindow = usedWindows[i]; + if ( + (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => usedWindow.openedWorkspace && workspace.workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace + (usedWindow.backupPath && emptyToRestore.some(empty => usedWindow.backupPath && empty.backupFolder === basename(usedWindow.backupPath))) // skip over restored empty window + ) { + continue; + } + + usedWindow.focus(); + focusLastWindow = false; + break; + } + } + + // 4.) finally, always ensure to have at least last used window focused + if (focusLastWindow) { + usedWindows[usedWindows.length - 1].focus(); + } } } // Remember in recent document list (unless this opens for extension development) // Also do not add paths when files are opened for diffing, only if opened individually - const isDiff = fileInputs && fileInputs.filesToDiff.length > 0; + const isDiff = filesToOpen && filesToOpen.filesToDiff.length > 0; if (!usedWindows.some(window => window.isExtensionDevelopmentHost) && !isDiff && !openConfig.noRecentEntry) { const recents: IRecent[] = []; for (let pathToOpen of pathsToOpen) { @@ -513,7 +522,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // process can continue. We do this by deleting the waitMarkerFilePath. const waitMarkerFileURI = openConfig.waitMarkerFileURI; if (openConfig.context === OpenContext.CLI && waitMarkerFileURI && usedWindows.length === 1 && usedWindows[0]) { - usedWindows[0].whenClosedOrLoaded.then(() => fs.unlink(waitMarkerFileURI.fsPath, _error => undefined)); + usedWindows[0].whenClosedOrLoaded.then(() => unlink(waitMarkerFileURI.fsPath, () => undefined)); } return usedWindows; @@ -535,10 +544,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic foldersToOpen: IFolderPathToOpen[], emptyToRestore: IEmptyWindowBackupInfo[], emptyToOpen: number, - fileInputs: IFileInputs | undefined, + filesToOpen: IFilesToOpen | undefined, foldersToAdd: IFolderPathToOpen[] - ) { + ): { windows: ICodeWindow[], filesOpenedInWindow: ICodeWindow | undefined } { const usedWindows: ICodeWindow[] = []; + let filesOpenedInWindow: ICodeWindow | undefined = undefined; // Settings can decide if files/folders open in new window or not let { openFolderInNewWindow, openFilesInNewWindow } = this.shouldOpenNewWindow(openConfig); @@ -554,13 +564,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Handle files to open/diff or to create when we dont open a folder and we do not restore any folder/untitled from hot-exit const potentialWindowsCount = foldersToOpen.length + workspacesToOpen.length + emptyToRestore.length; - if (potentialWindowsCount === 0 && fileInputs) { + if (potentialWindowsCount === 0 && filesToOpen) { // Find suitable window or folder path to open files in - const fileToCheck = fileInputs.filesToOpenOrCreate[0] || fileInputs.filesToDiff[0]; + const fileToCheck = filesToOpen.filesToOpenOrCreate[0] || filesToOpen.filesToDiff[0]; // only look at the windows with correct authority - const windows = WindowsMainService.WINDOWS.filter(window => fileInputs && window.remoteAuthority === fileInputs.remoteAuthority); + const windows = WindowsMainService.WINDOWS.filter(window => filesToOpen && window.remoteAuthority === filesToOpen.remoteAuthority); const bestWindowOrFolder = findBestWindowOrFolderForFile({ windows, @@ -587,46 +597,52 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic else { // Do open files - usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, fileInputs)); + const window = this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, filesToOpen); + usedWindows.push(window); - // Reset these because we handled them - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + filesToOpen = undefined; + filesOpenedInWindow = window; } } // Finally, if no window or folder is found, just open the files in an empty window else { - usedWindows.push(this.openInBrowserWindow({ + const window = this.openInBrowserWindow({ userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, - fileInputs, + filesToOpen, forceNewWindow: true, - remoteAuthority: fileInputs.remoteAuthority, + remoteAuthority: filesToOpen.remoteAuthority, forceNewTabbedWindow: openConfig.forceNewTabbedWindow - })); + }); + usedWindows.push(window); - // Reset these because we handled them - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + filesToOpen = undefined; + filesOpenedInWindow = window; } } // Handle workspaces to open (instructed and to restore) - const allWorkspacesToOpen = arrays.distinct(workspacesToOpen, workspace => workspace.workspace.id); // prevent duplicates + const allWorkspacesToOpen = distinct(workspacesToOpen, workspace => workspace.workspace.id); // prevent duplicates if (allWorkspacesToOpen.length > 0) { // Check for existing instances - const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, workspaceToOpen.workspace))); + const windowsOnWorkspace = coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, workspaceToOpen.workspace))); if (windowsOnWorkspace.length > 0) { const windowOnWorkspace = windowsOnWorkspace[0]; - const fileInputsForWindow = (fileInputs?.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === windowOnWorkspace.remoteAuthority) ? filesToOpen : undefined; // Do open files - usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, fileInputsForWindow)); + const window = this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, filesToOpenInWindow); + usedWindows.push(window); - // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpenInWindow) { + filesToOpen = undefined; + filesOpenedInWindow = window; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -639,14 +655,16 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } const remoteAuthority = workspaceToOpen.remoteAuthority; - const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined; // Do open folder - usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, workspaceToOpen, openFolderInNewWindow, fileInputsForWindow)); + const window = this.doOpenFolderOrWorkspace(openConfig, workspaceToOpen, openFolderInNewWindow, filesToOpenInWindow); + usedWindows.push(window); - // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpenInWindow) { + filesToOpen = undefined; + filesOpenedInWindow = window; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -654,21 +672,23 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Handle folders to open (instructed and to restore) - const allFoldersToOpen = arrays.distinct(foldersToOpen, folder => extUriBiasedIgnorePathCase.getComparisonKey(folder.folderUri)); // prevent duplicates + const allFoldersToOpen = distinct(foldersToOpen, folder => extUriBiasedIgnorePathCase.getComparisonKey(folder.folderUri)); // prevent duplicates if (allFoldersToOpen.length > 0) { // Check for existing instances - const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, folderToOpen.folderUri))); + const windowsOnFolderPath = coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, folderToOpen.folderUri))); if (windowsOnFolderPath.length > 0) { const windowOnFolderPath = windowsOnFolderPath[0]; - const fileInputsForWindow = fileInputs?.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : undefined; + const filesToOpenInWindow = filesToOpen?.remoteAuthority === windowOnFolderPath.remoteAuthority ? filesToOpen : undefined; // Do open files - usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, fileInputsForWindow)); + const window = this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, filesToOpenInWindow); + usedWindows.push(window); - // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpenInWindow) { + filesToOpen = undefined; + filesOpenedInWindow = window; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -682,14 +702,16 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } const remoteAuthority = folderToOpen.remoteAuthority; - const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined; // Do open folder - usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, folderToOpen, openFolderInNewWindow, fileInputsForWindow)); + const window = this.doOpenFolderOrWorkspace(openConfig, folderToOpen, openFolderInNewWindow, filesToOpenInWindow); + usedWindows.push(window); - // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpenInWindow) { + filesToOpen = undefined; + filesOpenedInWindow = window; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -697,26 +719,28 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Handle empty to restore - const allEmptyToRestore = arrays.distinct(emptyToRestore, info => info.backupFolder); // prevent duplicates + const allEmptyToRestore = distinct(emptyToRestore, info => info.backupFolder); // prevent duplicates if (allEmptyToRestore.length > 0) { allEmptyToRestore.forEach(emptyWindowBackupInfo => { const remoteAuthority = emptyWindowBackupInfo.remoteAuthority; - const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined; - usedWindows.push(this.openInBrowserWindow({ + const window = this.openInBrowserWindow({ userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, - fileInputs: fileInputsForWindow, + filesToOpen: filesToOpenInWindow, remoteAuthority, forceNewWindow: true, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, emptyWindowBackupInfo - })); + }); + usedWindows.push(window); - // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpenInWindow) { + filesToOpen = undefined; + filesOpenedInWindow = window; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -724,42 +748,48 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Handle empty to open (only if no other window opened) - if (usedWindows.length === 0 || fileInputs) { - if (fileInputs && !emptyToOpen) { + if (usedWindows.length === 0 || filesToOpen) { + if (filesToOpen && !emptyToOpen) { emptyToOpen++; } - const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined); + const remoteAuthority = filesToOpen ? filesToOpen.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined); for (let i = 0; i < emptyToOpen; i++) { - usedWindows.push(this.doOpenEmpty(openConfig, openFolderInNewWindow, remoteAuthority, fileInputs)); + const window = this.doOpenEmpty(openConfig, openFolderInNewWindow, remoteAuthority, filesToOpen); + usedWindows.push(window); - // Reset these because we handled them - fileInputs = undefined; - openFolderInNewWindow = true; // any other window to open must open in new window then + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpen) { + filesToOpen = undefined; + filesOpenedInWindow = window; + } + + // any other window to open must open in new window then + openFolderInNewWindow = true; } } - return arrays.distinct(usedWindows); + return { windows: distinct(usedWindows), filesOpenedInWindow }; } - private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, fileInputs?: IFileInputs): ICodeWindow { + private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, filesToOpen?: IFilesToOpen): ICodeWindow { this.logService.trace('windowsManager#doOpenFilesInExistingWindow'); window.focus(); // make sure window has focus const params: { filesToOpenOrCreate?: IPath[], filesToDiff?: IPath[], filesToWait?: IPathsToWaitFor, termProgram?: string } = {}; - if (fileInputs) { - params.filesToOpenOrCreate = fileInputs.filesToOpenOrCreate; - params.filesToDiff = fileInputs.filesToDiff; - params.filesToWait = fileInputs.filesToWait; + if (filesToOpen) { + params.filesToOpenOrCreate = filesToOpen.filesToOpenOrCreate; + params.filesToDiff = filesToOpen.filesToDiff; + params.filesToWait = filesToOpen.filesToWait; } if (configuration.userEnv) { params.termProgram = configuration.userEnv['TERM_PROGRAM']; } - window.sendWhenReady('vscode:openFiles', params); + window.sendWhenReady('vscode:openFiles', CancellationToken.None, params); return window; } @@ -768,12 +798,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic window.focus(); // make sure window has focus const request: IAddFoldersRequest = { foldersToAdd }; - window.sendWhenReady('vscode:addFolders', request); + window.sendWhenReady('vscode:addFolders', CancellationToken.None, request); return window; } - private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, fileInputs: IFileInputs | undefined, windowToUse?: ICodeWindow): ICodeWindow { + private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, filesToOpen: IFilesToOpen | undefined, windowToUse?: ICodeWindow): ICodeWindow { if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') { windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/97172 } @@ -785,12 +815,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic remoteAuthority, forceNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, - fileInputs, + filesToOpen, windowToUse }); } - private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, forceNewWindow: boolean, fileInputs: IFileInputs | undefined, windowToUse?: ICodeWindow): ICodeWindow { + private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, forceNewWindow: boolean, filesToOpen: IFilesToOpen | undefined, windowToUse?: ICodeWindow): ICodeWindow { if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') { windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/49587 } @@ -801,7 +831,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic initialStartup: openConfig.initialStartup, workspace: folderOrWorkspace.workspace, folderUri: folderOrWorkspace.folderUri, - fileInputs, + filesToOpen, remoteAuthority: folderOrWorkspace.remoteAuthority, forceNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, @@ -812,6 +842,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private getPathsToOpen(openConfig: IOpenConfiguration): IPathToOpen[] { let windowsToOpen: IPathToOpen[]; let isCommandLineOrAPICall = false; + let restoredWindows = false; // Extract paths: from API if (openConfig.urisToOpen && openConfig.urisToOpen.length > 0) { @@ -833,11 +864,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Extract windows: from previous session else { windowsToOpen = this.doGetWindowsFromLastSession(); + restoredWindows = true; } // Convert multiple folders into workspace (if opened via API or CLI) // This will ensure to open these folders in one window instead of multiple - // If we are in addMode, we should not do this because in that case all + // If we are in `addMode`, we should not do this because in that case all // folders should be added to the existing window. if (!openConfig.addMode && isCommandLineOrAPICall) { const foldersToOpen = windowsToOpen.filter(path => !!path.folderUri); @@ -853,6 +885,15 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } } + // Check for `window.startup` setting to include all windows + // from the previous session if this is the initial startup and we have + // not restored windows already otherwise. + // Use `unshift` to ensure any new window to open comes last + // for proper focus treatment. + if (openConfig.initialStartup && !restoredWindows && this.configurationService.getValue('window')?.restoreWindows === 'preserve') { + windowsToOpen.unshift(...this.doGetWindowsFromLastSession().filter(window => window.workspace || window.folderUri || window.backupPath)); + } + return windowsToOpen; } @@ -914,7 +955,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } } - // file uris const fileUris = cli['file-uri']; if (fileUris) { @@ -947,9 +987,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } private doGetWindowsFromLastSession(): IPathToOpen[] { - const restoreWindows = this.getRestoreWindowsSetting(); + const restoreWindowsSetting = this.getRestoreWindowsSetting(); - switch (restoreWindows) { + switch (restoreWindowsSetting) { // none: we always open an empty window case 'none': @@ -960,9 +1000,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // folders: restore last opened folders only case 'one': case 'all': + case 'preserve': case 'folders': + + // Collect previously opened windows const openedWindows: IWindowState[] = []; - if (restoreWindows !== 'one') { + if (restoreWindowsSetting !== 'one') { openedWindows.push(...this.windowsState.openedWindows); } if (this.windowsState.lastActiveWindow) { @@ -971,17 +1014,25 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const windowsToOpen: IPathToOpen[] = []; for (const openedWindow of openedWindows) { - if (openedWindow.workspace) { // Workspaces + + // Workspaces + if (openedWindow.workspace) { const pathToOpen = this.parseUri({ workspaceUri: openedWindow.workspace.configPath }, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen?.workspace) { windowsToOpen.push(pathToOpen); } - } else if (openedWindow.folderUri) { // Folders + } + + // Folders + else if (openedWindow.folderUri) { const pathToOpen = this.parseUri({ folderUri: openedWindow.folderUri }, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen?.folderUri) { windowsToOpen.push(pathToOpen); } - } else if (restoreWindows !== 'folders' && openedWindow.backupPath) { // Empty window, potentially editors open to be restored + } + + // Empty window, potentially editors open to be restored + else if (restoreWindowsSetting !== 'folders' && openedWindow.backupPath) { windowsToOpen.push({ backupPath: openedWindow.backupPath, remoteAuthority: openedWindow.remoteAuthority }); } } @@ -1002,10 +1053,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic if (this.lifecycleMainService.wasRestarted) { restoreWindows = 'all'; // always reopen all windows when an update was applied } else { - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); restoreWindows = windowConfig?.restoreWindows || 'all'; // by default restore all windows - if (!['all', 'folders', 'one', 'none'].includes(restoreWindows)) { + if (!['preserve', 'all', 'folders', 'one', 'none'].includes(restoreWindows)) { restoreWindows = 'all'; // by default restore all windows } } @@ -1142,7 +1193,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic let candidate = normalize(anyPath); try { - const candidateStat = fs.statSync(candidate); + const candidateStat = statSync(candidate); if (candidateStat.isFile()) { // Workspace (unless disabled via flag) @@ -1197,7 +1248,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private shouldOpenNewWindow(openConfig: IOpenConfiguration): { openFolderInNewWindow: boolean; openFilesInNewWindow: boolean; } { // let the user settings override how folders are open in a new window or same window unless we are forced - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); const openFolderInNewWindowConfig = windowConfig?.openFoldersInNewWindow || 'default' /* default */; const openFilesInNewWindowConfig = windowConfig?.openFilesInNewWindow || 'off' /* default */; @@ -1348,7 +1399,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow { // Build INativeWindowConfiguration from config and options - const configuration: INativeWindowConfiguration = mixin({}, options.cli); // inherit all properties from CLI + const configuration = { ...options.cli } as INativeWindowConfiguration; configuration.appRoot = this.environmentService.appRoot; configuration.machineId = this.machineId; configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir; @@ -1360,11 +1411,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic configuration.folderUri = options.folderUri; configuration.remoteAuthority = options.remoteAuthority; - const fileInputs = options.fileInputs; - if (fileInputs) { - configuration.filesToOpenOrCreate = fileInputs.filesToOpenOrCreate; - configuration.filesToDiff = fileInputs.filesToDiff; - configuration.filesToWait = fileInputs.filesToWait; + const filesToOpen = options.filesToOpen; + if (filesToOpen) { + configuration.filesToOpenOrCreate = filesToOpen.filesToOpenOrCreate; + configuration.filesToDiff = filesToOpen.filesToDiff; + configuration.filesToWait = filesToOpen.filesToWait; } // if we know the backup folder upfront (for empty windows to restore), we can set it @@ -1385,18 +1436,18 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // New window if (!window) { - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); const state = this.getNewWindowState(configuration); // Window state is not from a previous session: only allow fullscreen if we inherit it or user wants fullscreen let allowFullscreen: boolean; if (state.hasDefaultState) { - allowFullscreen = (windowConfig?.newWindowDimensions && ['fullscreen', 'inherit', 'offset'].indexOf(windowConfig.newWindowDimensions) >= 0); + allowFullscreen = !!(windowConfig?.newWindowDimensions && ['fullscreen', 'inherit', 'offset'].indexOf(windowConfig.newWindowDimensions) >= 0); } // Window state is from a previous session: only allow fullscreen when we got updated or user wants to restore else { - allowFullscreen = this.lifecycleMainService.wasRestarted || windowConfig?.restoreFullscreen; + allowFullscreen = !!(this.lifecycleMainService.wasRestarted || windowConfig?.restoreFullscreen); if (allowFullscreen && isMacintosh && WindowsMainService.WINDOWS.some(win => win.isFullScreen)) { // macOS: Electron does not allow to restore multiple windows in @@ -1581,7 +1632,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic state.y = Math.round(displayToUse.bounds.y + (displayToUse.bounds.height / 2) - (state.height! / 2)); // Check for newWindowDimensions setting and adjust accordingly - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); let ensureNoOverlap = true; if (windowConfig?.newWindowDimensions) { if (windowConfig.newWindowDimensions === 'maximized') { @@ -1659,7 +1710,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const focusedWindow = this.getFocusedWindow() || this.getLastActiveWindow(); if (focusedWindow) { - focusedWindow.sendWhenReady(channel, ...args); + focusedWindow.sendWhenReady(channel, CancellationToken.None, ...args); } } @@ -1669,14 +1720,23 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic continue; // do not send if we are instructed to ignore it } - window.sendWhenReady(channel, payload); + window.sendWhenReady(channel, CancellationToken.None, payload); } } getWindowById(windowId: number): ICodeWindow | undefined { const res = WindowsMainService.WINDOWS.filter(window => window.id === windowId); - return arrays.firstOrDefault(res); + return firstOrDefault(res); + } + + getWindowByWebContents(webContents: WebContents): ICodeWindow | undefined { + const browserWindow = BrowserWindow.fromWebContents(webContents); + if (!browserWindow) { + return undefined; + } + + return this.getWindowById(browserWindow.id); } getWindows(): ICodeWindow[] { diff --git a/src/vs/code/test/electron-main/windowsStateStorage.test.ts b/src/vs/platform/windows/test/electron-main/windowsStateStorage.test.ts similarity index 100% rename from src/vs/code/test/electron-main/windowsStateStorage.test.ts rename to src/vs/platform/windows/test/electron-main/windowsStateStorage.test.ts diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index c0c34d74f..72ca615f9 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -144,13 +144,14 @@ export interface IWorkspaceFolder extends IWorkspaceFolderData { export class Workspace implements IWorkspace { - private _foldersMap: TernarySearchTree = TernarySearchTree.forUris(); + private _foldersMap: TernarySearchTree = TernarySearchTree.forUris(this._ignorePathCasing); private _folders!: WorkspaceFolder[]; constructor( private _id: string, - folders: WorkspaceFolder[] = [], - private _configuration: URI | null = null + folders: WorkspaceFolder[], + private _configuration: URI | null, + private _ignorePathCasing: (key: URI) => boolean, ) { this.folders = folders; } @@ -158,6 +159,7 @@ export class Workspace implements IWorkspace { update(workspace: Workspace) { this._id = workspace.id; this._configuration = workspace.configuration; + this._ignorePathCasing = workspace._ignorePathCasing; this.folders = workspace.folders; } @@ -195,7 +197,7 @@ export class Workspace implements IWorkspace { } private updateFoldersMap(): void { - this._foldersMap = TernarySearchTree.forUris(); + this._foldersMap = TernarySearchTree.forUris(this._ignorePathCasing); for (const folder of this.folders) { this._foldersMap.set(folder.uri, folder); } diff --git a/src/vs/platform/workspace/test/common/testWorkspace.ts b/src/vs/platform/workspace/test/common/testWorkspace.ts index 75762fed4..0cdb093ab 100644 --- a/src/vs/platform/workspace/test/common/testWorkspace.ts +++ b/src/vs/platform/workspace/test/common/testWorkspace.ts @@ -4,8 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { Workspace, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { isWindows } from 'vs/base/common/platform'; +import { Workspace as BaseWorkspace, toWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { isLinux, isWindows } from 'vs/base/common/platform'; + +export class Workspace extends BaseWorkspace { + constructor( + id: string, + folders: WorkspaceFolder[] = [], + configuration: URI | null = null, + ignorePathCasing: (key: URI) => boolean = () => !isLinux + ) { + super(id, folders, configuration, ignorePathCasing); + } +} const wsUri = URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace'); export const TestWorkspace = testWorkspace(wsUri); diff --git a/src/vs/platform/workspace/test/common/workspace.test.ts b/src/vs/platform/workspace/test/common/workspace.test.ts index 10e257276..1d3f78f7b 100644 --- a/src/vs/platform/workspace/test/common/workspace.test.ts +++ b/src/vs/platform/workspace/test/common/workspace.test.ts @@ -8,7 +8,7 @@ import * as path from 'vs/base/common/path'; import { Workspace, toWorkspaceFolders, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; import { IRawFileWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; -import { isWindows } from 'vs/base/common/platform'; +import { isLinux, isWindows } from 'vs/base/common/platform'; suite('Workspace', () => { @@ -27,7 +27,7 @@ suite('Workspace', () => { test('getFolder returns the folder with given uri', () => { const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 2 }); - let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), expected, new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), expected, new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })], null, () => !isLinux); const actual = testObject.getFolder(expected.uri); @@ -36,7 +36,7 @@ suite('Workspace', () => { test('getFolder returns the folder if the uri is sub', () => { const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 0 }); - let testObject = new Workspace('', [expected, new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 1 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); + let testObject = new Workspace('', [expected, new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 1 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })], null, () => !isLinux); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'test/a'))); @@ -45,7 +45,7 @@ suite('Workspace', () => { test('getFolder returns the closest folder if the uri is sub', () => { const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 2 }); - let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected], null, () => !isLinux); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'test/a'))); @@ -54,7 +54,7 @@ suite('Workspace', () => { test('getFolder returns the folder even if the uri has query path', () => { const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 2 }); - let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected], null, () => !isLinux); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'test/a')).with({ query: 'somequery' })); @@ -62,7 +62,7 @@ suite('Workspace', () => { }); test('getFolder returns null if the uri is not sub', () => { - let testObject = new Workspace('', [new WorkspaceFolder({ uri: testFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 })]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: testFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 })], null, () => !isLinux); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'main/a'))); diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index b6321d734..7a5baa225 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -330,53 +330,57 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa }); // Recent Workspaces - if (this.getRecentlyOpened().workspaces.length > 0) { + try { + if (this.getRecentlyOpened().workspaces.length > 0) { - // The user might have meanwhile removed items from the jump list and we have to respect that - // so we need to update our list of recent paths with the choice of the user to not add them again - // Also: Windows will not show our custom category at all if there is any entry which was removed - // by the user! See https://github.com/microsoft/vscode/issues/15052 - let toRemove: URI[] = []; - for (let item of app.getJumpListSettings().removedItems) { - const args = item.args; - if (args) { - const match = /^--(folder|file)-uri\s+"([^"]+)"$/.exec(args); - if (match) { - toRemove.push(URI.parse(match[2])); + // The user might have meanwhile removed items from the jump list and we have to respect that + // so we need to update our list of recent paths with the choice of the user to not add them again + // Also: Windows will not show our custom category at all if there is any entry which was removed + // by the user! See https://github.com/microsoft/vscode/issues/15052 + let toRemove: URI[] = []; + for (let item of app.getJumpListSettings().removedItems) { + const args = item.args; + if (args) { + const match = /^--(folder|file)-uri\s+"([^"]+)"$/.exec(args); + if (match) { + toRemove.push(URI.parse(match[2])); + } } } + this.removeRecentlyOpened(toRemove); + + // Add entries + jumpList.push({ + type: 'custom', + name: nls.localize('recentFolders', "Recent Workspaces"), + items: arrays.coalesce(this.getRecentlyOpened().workspaces.slice(0, 7 /* limit number of entries here */).map(recent => { + const workspace = isRecentWorkspace(recent) ? recent.workspace : recent.folderUri; + const title = recent.label ? splitName(recent.label).name : this.getSimpleWorkspaceLabel(workspace, this.environmentService.untitledWorkspacesHome); + + let description; + let args; + if (isSingleFolderWorkspaceIdentifier(workspace)) { + description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), getPathLabel(dirname(workspace), this.environmentService)); + args = `--folder-uri "${workspace.toString()}"`; + } else { + description = nls.localize('workspaceDesc', "{0} {1}", getBaseLabel(workspace.configPath), getPathLabel(dirname(workspace.configPath), this.environmentService)); + args = `--file-uri "${workspace.configPath.toString()}"`; + } + + return { + type: 'task', + title, + description, + program: process.execPath, + args, + iconPath: 'explorer.exe', // simulate folder icon + iconIndex: 0 + }; + })) + }); } - this.removeRecentlyOpened(toRemove); - - // Add entries - jumpList.push({ - type: 'custom', - name: nls.localize('recentFolders', "Recent Workspaces"), - items: arrays.coalesce(this.getRecentlyOpened().workspaces.slice(0, 7 /* limit number of entries here */).map(recent => { - const workspace = isRecentWorkspace(recent) ? recent.workspace : recent.folderUri; - const title = recent.label ? splitName(recent.label).name : this.getSimpleWorkspaceLabel(workspace, this.environmentService.untitledWorkspacesHome); - - let description; - let args; - if (isSingleFolderWorkspaceIdentifier(workspace)) { - description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), getPathLabel(dirname(workspace), this.environmentService)); - args = `--folder-uri "${workspace.toString()}"`; - } else { - description = nls.localize('workspaceDesc', "{0} {1}", getBaseLabel(workspace.configPath), getPathLabel(dirname(workspace.configPath), this.environmentService)); - args = `--file-uri "${workspace.configPath.toString()}"`; - } - - return { - type: 'task', - title, - description, - program: process.execPath, - args, - iconPath: 'explorer.exe', // simulate folder icon - iconIndex: 0 - }; - })) - }); + } catch (error) { + this.logService.warn('updateWindowsJumpList#recentWorkspaces', error); // https://github.com/microsoft/vscode/issues/111177 } // Recent @@ -387,7 +391,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa try { app.setJumpList(jumpList); } catch (error) { - this.logService.warn('#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors + this.logService.warn('updateWindowsJumpList#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors } } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 99ba257db..e1c2dd30c 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -791,8 +791,8 @@ declare module 'vscode' { /** * A reference to a named icon. Currently, [File](#ThemeIcon.File), [Folder](#ThemeIcon.Folder), - * and [codicons](https://microsoft.github.io/vscode-codicons/dist/codicon.html) are supported. - * Using a theme icon is preferred over a custom icon as it gives theme authors the possibility to change the icons. + * and [ThemeIcon ids](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing) are supported. + * Using a theme icon is preferred over a custom icon as it gives product theme authors the possibility to change the icons. * * *Note* that theme icons can also be rendered inside labels and descriptions. Places that support theme icons spell this out * and they use the `$()`-syntax, for instance `quickPick.label = "Hello World $(globe)"`. @@ -809,7 +809,7 @@ declare module 'vscode' { static readonly Folder: ThemeIcon; /** - * The id of the icon. The available icons are listed in https://microsoft.github.io/vscode-codicons/dist/codicon.html. + * The id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. */ readonly id: string; @@ -820,7 +820,7 @@ declare module 'vscode' { /** * Creates a reference to a theme icon. - * @param id id of the icon. The available icons are listed in https://microsoft.github.io/vscode-codicons/dist/codicon.html. + * @param id id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. * @param color optional `ThemeColor` for the icon. The color is currently only used in [TreeItem](#TreeItem). */ constructor(id: string, color?: ThemeColor); @@ -1878,8 +1878,9 @@ declare module 'vscode' { /** * A relative pattern is a helper to construct glob patterns that are matched - * relatively to a base path. The base path can either be an absolute file path - * or a [workspace folder](#WorkspaceFolder). + * relatively to a base file path. The base path can either be an absolute file + * path as string or uri or a [workspace folder](#WorkspaceFolder), which is the + * preferred way of creating the relative pattern. */ export class RelativePattern { @@ -1898,14 +1899,28 @@ declare module 'vscode' { pattern: string; /** - * Creates a new relative pattern object with a base path and pattern to match. This pattern - * will be matched on file paths relative to the base path. + * Creates a new relative pattern object with a base file path and pattern to match. This pattern + * will be matched on file paths relative to the base. * - * @param base A base file path to which this pattern will be matched against relatively. - * @param pattern A file glob pattern like `*.{ts,js}` that will be matched on file paths - * relative to the base path. + * Example: + * ```ts + * const folder = vscode.workspace.workspaceFolders?.[0]; + * if (folder) { + * + * // Match any TypeScript file in the root of this workspace folder + * const pattern1 = new vscode.RelativePattern(folder, '*.ts'); + * + * // Match any TypeScript file in `someFolder` inside this workspace folder + * const pattern2 = new vscode.RelativePattern(folder, 'someFolder/*.ts'); + * } + * ``` + * + * @param base A base to which this pattern will be matched against relatively. It is recommended + * to pass in a [workspace folder](#WorkspaceFolder) if the pattern should match inside the workspace. + * Otherwise, a uri or string should only be used if the pattern is for a file path outside the workspace. + * @param pattern A file glob pattern like `*.{ts,js}` that will be matched on paths relative to the base. */ - constructor(base: WorkspaceFolder | string, pattern: string) + constructor(base: WorkspaceFolder | Uri | string, pattern: string) } /** @@ -4325,6 +4340,12 @@ declare module 'vscode' { * [Folding](https://code.visualstudio.com/docs/editor/codebasics#_folding) in the editor. */ export interface FoldingRangeProvider { + + /** + * An optional event to signal that the folding ranges from this provider have changed. + */ + onDidChangeFoldingRanges?: Event; + /** * Returns a list of folding ranges or null and undefined if the provider * does not want to participate or was cancelled. @@ -4520,6 +4541,50 @@ declare module 'vscode' { provideCallHierarchyOutgoingCalls(item: CallHierarchyItem, token: CancellationToken): ProviderResult; } + /** + * Represents a list of ranges that can be edited together along with a word pattern to describe valid range contents. + */ + export class LinkedEditingRanges { + /** + * Create a new linked editing ranges object. + * + * @param ranges A list of ranges that can be edited together + * @param wordPattern An optional word pattern that describes valid contents for the given ranges + */ + constructor(ranges: Range[], wordPattern?: RegExp); + + /** + * A list of ranges that can be edited together. The ranges must have + * identical length and text content. The ranges cannot overlap. + */ + readonly ranges: Range[]; + + /** + * An optional word pattern that describes valid contents for the given ranges. + * If no pattern is provided, the language configuration's word pattern will be used. + */ + readonly wordPattern?: RegExp; + } + + /** + * The linked editing range provider interface defines the contract between extensions and + * the linked editing feature. + */ + export interface LinkedEditingRangeProvider { + /** + * For a given position in a document, returns the range of the symbol at the position and all ranges + * that have the same content. A change to one of the ranges can be applied to all other ranges if the new content + * is valid. An optional word pattern can be returned with the result to describe valid contents. + * If no result-specific word pattern is provided, the word pattern from the language configuration is used. + * + * @param document The document in which the provider was invoked. + * @param position The position at which the provider was invoked. + * @param token A cancellation token. + * @return A list of ranges that can be edited together + */ + provideLinkedEditingRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + /** * A tuple of two characters, like a pair of * opening and closing brackets. @@ -5339,7 +5404,7 @@ declare module 'vscode' { * * `My text $(icon-name) contains icons like $(icon-name) this one.` * - * Where the icon-name is taken from the [codicon](https://microsoft.github.io/vscode-codicons/dist/codicon.html) icon set, e.g. + * Where the icon-name is taken from the ThemeIcon [icon set](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing), e.g. * `light-bulb`, `thumbsup`, `zap` etc. */ text: string; @@ -5525,6 +5590,72 @@ declare module 'vscode' { tooltip?: string; } + /** + * A file decoration represents metadata that can be rendered with a file. + */ + export class FileDecoration { + + /** + * A very short string that represents this decoration. + */ + badge?: string; + + /** + * A human-readable tooltip for this decoration. + */ + tooltip?: string; + + /** + * The color of this decoration. + */ + color?: ThemeColor; + + /** + * A flag expressing that this decoration should be + * propagated to its parents. + */ + propagate?: boolean; + + /** + * Creates a new decoration. + * + * @param badge A letter that represents the decoration. + * @param tooltip The tooltip of the decoration. + * @param color The color of the decoration. + */ + constructor(badge?: string, tooltip?: string, color?: ThemeColor); + } + + /** + * The decoration provider interfaces defines the contract between extensions and + * file decorations. + */ + export interface FileDecorationProvider { + + /** + * An optional event to signal that decorations for one or many files have changed. + * + * *Note* that this event should be used to propagate information about children. + * + * @see [EventEmitter](#EventEmitter) + */ + onDidChangeFileDecorations?: Event; + + /** + * Provide decorations for a given uri. + * + * *Note* that this function is only called when a file gets rendered in the UI. + * This means a decoration from a descendent that propagates upwards must be signaled + * to the editor via the [onDidChangeFileDecorations](#FileDecorationProvider.onDidChangeFileDecorations)-event. + * + * @param uri The uri of the file to provide a decoration for. + * @param token A cancellation token. + * @returns A decoration or a thenable that resolves to such. + */ + provideFileDecoration(uri: Uri, token: CancellationToken): ProviderResult; + } + + /** * In a remote window the extension kind describes if an extension * runs where the UI (window) runs or if an extension runs remotely. @@ -6180,14 +6311,13 @@ declare module 'vscode' { */ export class CustomExecution { /** - * Constructs a CustomExecution task object. The callback will be executed the task is run, at which point the + * Constructs a CustomExecution task object. The callback will be executed when the task is run, at which point the * extension should return the Pseudoterminal it will "run in". The task should wait to do further execution until * [Pseudoterminal.open](#Pseudoterminal.open) is called. Task cancellation should be handled using * [Pseudoterminal.close](#Pseudoterminal.close). When the task is complete fire * [Pseudoterminal.onDidClose](#Pseudoterminal.onDidClose). - * @param process The [Pseudoterminal](#Pseudoterminal) to be used by the task to display output. * @param callback The callback that will be called when the task is started by a user. Any ${} style variables that - * were in the task definition will be resolved and passed into the callback. + * were in the task definition will be resolved and passed into the callback as `resolvedDefinition`. */ constructor(callback: (resolvedDefinition: TaskDefinition) => Thenable); } @@ -6413,9 +6543,9 @@ declare module 'vscode' { readonly execution: TaskExecution; /** - * The process's exit code. + * The process's exit code. Will be `undefined` when the task is terminated. */ - readonly exitCode: number; + readonly exitCode: number | undefined; } export interface TaskFilter { @@ -6860,6 +6990,21 @@ declare module 'vscode' { * @param options Defines if existing files should be overwritten. */ copy(source: Uri, target: Uri, options?: { overwrite?: boolean }): Thenable; + + /** + * Check if a given file system supports writing files. + * + * Keep in mind that just because a file system supports writing, that does + * not mean that writes will always succeed. There may be permissions issues + * or other errors that prevent writing a file. + * + * @param scheme The scheme of the filesystem, for example `file` or `git`. + * + * @return `true` if the file system supports writing, `false` if it does not + * support writing (i.e. it is readonly), and `undefined` if VS Code does not + * know about the filesystem. + */ + isWritableFileSystem(scheme: string): boolean | undefined; } /** @@ -8524,6 +8669,14 @@ declare module 'vscode' { */ export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable; + /** + * Register a file decoration provider. + * + * @param provider A [FileDecorationProvider](#FileDecorationProvider). + * @return A [disposable](#Disposable) that unregisters the provider. + */ + export function registerFileDecorationProvider(provider: FileDecorationProvider): Disposable; + /** * The currently active color theme as configured in the settings. The active * theme can be changed via the `workbench.colorTheme` setting. @@ -8700,13 +8853,34 @@ declare module 'vscode' { * @return Parent of `element`. */ getParent?(element: T): ProviderResult; + + /** + * Called only on hover to resolve the [TreeItem](#TreeItem.tooltip) property if it is undefined. + * Only properties that were undefined can be resolved in `resolveTreeItem`. + * Functionality may be expanded later to include being called to resolve other missing + * properties on selection and/or on open. + * + * Will only ever be called once per TreeItem. + * + * onDidChangeTreeData should not be triggered from within resolveTreeItem. + * + * *Note* that this function is called when tree items are already showing in the UI. + * Because of that, no property that changes the presentation (label, description, command, etc.) + * can be changed. + * + * @param element The object associated with the TreeItem + * @param item Undefined properties of `item` should be set then `item` should be returned. + * @return The resolved tree item or a thenable that resolves to such. It is OK to return the given + * `item`. When no result is returned, the given `item` will be used. + */ + resolveTreeItem?(item: TreeItem, element: T): ProviderResult; } export class TreeItem { /** * A human-readable string describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). */ - label?: string; + label?: string | TreeItemLabel; /** * Optional id for the tree item that has to be unique across tree. The id is used to preserve the selection and expansion state of the tree item. @@ -8739,10 +8913,14 @@ declare module 'vscode' { /** * The tooltip text when you hover over this item. */ - tooltip?: string | undefined; + tooltip?: string | MarkdownString | undefined; /** * The [command](#Command) that should be executed when the tree item is selected. + * + * Please use `vscode.open` or `vscode.diff` as command IDs when the tree item is opening + * something in the editor. Using these commands ensures that the resulting editor will + * appear consistent with how other built-in trees open editors. */ command?: Command; @@ -8782,7 +8960,7 @@ declare module 'vscode' { * @param label A human-readable string describing this item * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) */ - constructor(label: string, collapsibleState?: TreeItemCollapsibleState); + constructor(label: string | TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); /** * @param resourceUri The [uri](#Uri) of the resource representing this item. @@ -8809,6 +8987,23 @@ declare module 'vscode' { Expanded = 2 } + /** + * Label describing the [Tree item](#TreeItem) + */ + export interface TreeItemLabel { + + /** + * A human-readable string describing the [Tree item](#TreeItem). + */ + label: string; + + /** + * Ranges in the label to highlight. A range is defined as a tuple of two number where the + * first is the inclusive start index and the second the exclusive end index + */ + highlights?: [number, number][]; + } + /** * Value-object describing what options a terminal should use. */ @@ -9924,6 +10119,8 @@ declare module 'vscode' { * flags to ignore certain kinds of events can be provided. To stop listening to events the watcher must be disposed. * * *Note* that only files within the current [workspace folders](#workspace.workspaceFolders) can be watched. + * *Note* that when watching for file changes such as '**​/*.js', notifications will not be sent when a parent folder is + * moved or deleted (this is a known limitation of the current implementation and may change in the future). * * @param globPattern A [glob pattern](#GlobPattern) that is applied to the absolute paths of created, changed, * and deleted files. Use a [relative pattern](#RelativePattern) to limit events to a certain [workspace folder](#WorkspaceFolder). @@ -10691,6 +10888,19 @@ declare module 'vscode' { */ export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyProvider): Disposable; + /** + * Register a linked editing range provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their [score](#languages.match) and the best-matching provider that has a result is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A linked editing range provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerLinkedEditingRangeProvider(selector: DocumentSelector, provider: LinkedEditingRangeProvider): Disposable; + /** * Set a [language configuration](#LanguageConfiguration) for a language. * @@ -10699,6 +10909,7 @@ declare module 'vscode' { * @return A [disposable](#Disposable) that unsets this configuration. */ export function setLanguageConfiguration(language: string, configuration: LanguageConfiguration): Disposable; + } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 4076358f4..19e8d8f10 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -223,6 +223,13 @@ declare module 'vscode' { } + export interface TunnelCreationOptions { + /** + * True when the local operating system will require elevation to use the requested local port. + */ + elevationRequired?: boolean; + } + export type ResolverResult = ResolvedAuthority & ResolvedOptions & TunnelInformation; export class RemoteAuthorityResolverError extends Error { @@ -238,8 +245,11 @@ declare module 'vscode' { * Can be optionally implemented if the extension can forward ports better than the core. * When not implemented, the core will use its default forwarding logic. * When implemented, the core will use this to forward ports. + * + * To enable the "Change Local Port" action on forwarded ports, make sure to set the `localAddress` of + * the returned `Tunnel` to a `{ port: number, host: string; }` and not a string. */ - tunnelFactory?: (tunnelOptions: TunnelOptions) => Thenable | undefined; + tunnelFactory?: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable | undefined; /** * Provides filtering for candidate ports. @@ -741,71 +751,6 @@ declare module 'vscode' { //#endregion - //#region file-decorations: https://github.com/microsoft/vscode/issues/54938 - - - export class FileDecoration { - - /** - * A very short string that represents this decoration. - */ - badge?: string; - - /** - * A human-readable tooltip for this decoration. - */ - tooltip?: string; - - /** - * The color of this decoration. - */ - color?: ThemeColor; - - /** - * A flag expressing that this decoration should be - * propagated to its parents. - */ - propagate?: boolean; - - /** - * Creates a new decoration. - * - * @param badge A letter that represents the decoration. - * @param tooltip The tooltip of the decoration. - * @param color The color of the decoration. - */ - constructor(badge?: string, tooltip?: string, color?: ThemeColor); - } - - /** - * The decoration provider interfaces defines the contract between extensions and - * file decorations. - */ - export interface FileDecorationProvider { - - /** - * An event to signal decorations for one or many files have changed. - * - * @see [EventEmitter](#EventEmitter - */ - onDidChange: Event; - - /** - * Provide decorations for a given uri. - * - * @param uri The uri of the file to provide a decoration for. - * @param token A cancellation token. - * @returns A decoration or a thenable that resolves to such. - */ - provideFileDecoration(uri: Uri, token: CancellationToken): ProviderResult; - } - - export namespace window { - export function registerDecorationProvider(provider: FileDecorationProvider): Disposable; - } - - //#endregion - //#region debug /** @@ -824,46 +769,9 @@ declare module 'vscode' { // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_Variable). } - // deprecated debug API - - export interface DebugConfigurationProvider { - /** - * Deprecated, use DebugAdapterDescriptorFactory.provideDebugAdapter instead. - * @deprecated Use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead - */ - debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; - } - //#endregion - //#region LogLevel: https://github.com/microsoft/vscode/issues/85992 - /** - * @deprecated DO NOT USE, will be removed - */ - export enum LogLevel { - Trace = 1, - Debug = 2, - Info = 3, - Warning = 4, - Error = 5, - Critical = 6, - Off = 7 - } - - export namespace env { - /** - * @deprecated DO NOT USE, will be removed - */ - export const logLevel: LogLevel; - - /** - * @deprecated DO NOT USE, will be removed - */ - export const onDidChangeLogLevel: Event; - } - - //#endregion //#region @joaomoreno: SCM validation @@ -1008,45 +916,8 @@ declare module 'vscode' { //#endregion //#region Tree View: https://github.com/microsoft/vscode/issues/61313 - /** - * Label describing the [Tree item](#TreeItem) - */ - export interface TreeItemLabel { - - /** - * A human-readable string describing the [Tree item](#TreeItem). - */ - label: string; - - /** - * Ranges in the label to highlight. A range is defined as a tuple of two number where the - * first is the inclusive start index and the second the exclusive end index - */ - highlights?: [number, number][]; - - } - - // https://github.com/microsoft/vscode/issues/100741 - export interface TreeDataProvider { - resolveTreeItem?(element: T, item: TreeItem2): TreeItem2 | Thenable; - } - - export class TreeItem2 extends TreeItem { - /** - * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). - */ - label?: string | TreeItemLabel | /* for compilation */ any; - - /** - * Content to be shown when you hover over the tree item. - */ - tooltip?: string | MarkdownString | /* for compilation */ any; - - /** - * @param label Label describing this item - * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) - */ - constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); + export interface TreeView extends Disposable { + reveal(element: T | undefined, options?: { select?: boolean, focus?: boolean, expand?: boolean | number }): Thenable; } //#endregion @@ -1112,45 +983,6 @@ declare module 'vscode' { //#endregion - //#region OnTypeRename: https://github.com/microsoft/vscode/issues/88424 - - /** - * The rename provider interface defines the contract between extensions and - * the live-rename feature. - */ - export interface OnTypeRenameProvider { - /** - * Provide a list of ranges that can be live renamed together. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param token A cancellation token. - * @return A list of ranges that can be live-renamed togehter. The ranges must have - * identical length and contain identical text content. The ranges cannot overlap. Optional a word pattern - * that overrides the word pattern defined when registering the provider. Live rename stops as soon as the renamed content - * no longer matches the word pattern. - */ - provideOnTypeRenameRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<{ ranges: Range[]; wordPattern?: RegExp; }>; - } - - namespace languages { - /** - * Register a rename provider that works on type. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and the best-matching provider is used. Failure - * of the selected provider will cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An on type rename provider. - * @param wordPattern Word pattern for this provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerOnTypeRenameProvider(selector: DocumentSelector, provider: OnTypeRenameProvider, wordPattern?: RegExp): Disposable; - } - - //#endregion - //#region Custom editor move https://github.com/microsoft/vscode/issues/86146 // TODO: Also for custom editor @@ -1403,6 +1235,12 @@ declare module 'vscode' { * The document's current run state */ runState?: NotebookRunState; + + /** + * Whether the document is trusted, default to true + * When false, insecure outputs like HTML, JavaScript, SVG will not be rendered. + */ + trusted?: boolean; } export interface NotebookDocumentContentOptions { @@ -1824,6 +1662,13 @@ declare module 'vscode' { dispose(): void; } + export interface NotebookDocumentShowOptions { + viewColumn?: ViewColumn; + preserveFocus?: boolean; + preview?: boolean; + selection?: NotebookCellRange; + } + export namespace notebook { export function registerNotebookContentProvider( @@ -1891,6 +1736,7 @@ declare module 'vscode' { export const onDidChangeActiveNotebookEditor: Event; export const onDidChangeNotebookEditorSelection: Event; export const onDidChangeNotebookEditorVisibleRanges: Event; + export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Promise; } //#endregion @@ -2138,36 +1984,338 @@ declare module 'vscode' { } //#endregion - //#region https://github.com/microsoft/vscode/issues/91697 - - export interface FileSystem { + //#region https://github.com/microsoft/vscode/issues/107467 + /* + General activation events: + - `onLanguage:*` most test extensions will want to activate when their + language is opened to provide code lenses. + - `onTests:*` new activation event very simiular to `workspaceContains`, + but only fired when the user wants to run tests or opens the test explorer. + */ + export namespace test { /** - * Check if a given file system supports writing files. - * - * Keep in mind that just because a file system supports writing, that does - * not mean that writes will always succeed. There may be permissions issues - * or other errors that prevent writing a file. - * - * @param scheme The scheme of the filesystem, for example `file` or `git`. - * - * @return `true` if the file system supports writing, `false` if it does not - * support writing (i.e. it is readonly), and `undefined` if VS Code does not - * know about the filesystem. + * Registers a provider that discovers tests for the given document + * selectors. It is activated when either tests need to be enumerated, or + * a document matching the selector is opened. */ - isWritableFileSystem(scheme: string): boolean | undefined; + export function registerTestProvider(testProvider: TestProvider): Disposable; + + /** + * Runs tests with the given options. If no options are given, then + * all tests are run. Returns the resulting test run. + */ + export function runTests(options: TestRunOptions): Thenable; + + /** + * Returns an observer that retrieves tests in the given workspace folder. + */ + export function createWorkspaceTestObserver(workspaceFolder: WorkspaceFolder): TestObserver; + + /** + * Returns an observer that retrieves tests in the given text document. + */ + export function createDocumentTestObserver(document: TextDocument): TestObserver; } + export interface TestObserver { + /** + * List of tests returned by test provider for files in the workspace. + */ + readonly tests: ReadonlyArray; + /** + * An event that fires when an existing test in the collection changes, or + * null if a top-level test was added or removed. When fired, the consumer + * should check the test item and all its children for changes. + */ + readonly onDidChangeTest: Event; + + /** + * An event the fires when all test providers have signalled that the tests + * the observer references have been discovered. Providers may continue to + * watch for changes and cause {@link onDidChangeTest} to fire as files + * change, until the observer is disposed. + * + * @todo as below + */ + readonly onDidDiscoverInitialTests: Event; + + /** + * Dispose of the observer, allowing VS Code to eventually tell test + * providers that they no longer need to update tests. + */ + dispose(): void; + } + + export interface TestChangeEvent { + /** + * List of all tests that are newly added. + */ + readonly added: ReadonlyArray; + + /** + * List of existing tests that have updated. + */ + readonly updated: ReadonlyArray; + + /** + * List of existing tests that have been removed. + */ + readonly removed: ReadonlyArray; + + /** + * Highest node in the test tree under which changes were made. This can + * be easily plugged into events like the TreeDataProvider update event. + */ + readonly commonChangeAncestor: TestItem | null; + } + + /** + * Tree of tests returned from the provide methods in the {@link TestProvider}. + */ + export interface TestHierarchy { + /** + * Root node for tests. The `testRoot` instance must not be replaced over + * the lifespan of the TestHierarchy, since you will need to reference it + * in `onDidChangeTest` when a test is added or removed. + */ + readonly root: T; + + /** + * An event that fires when an existing test under the `root` changes. + * This can be a result of a state change in a test run, a property update, + * or an update to its children. Changes made to tests will not be visible + * to {@link TestObserver} instances until this event is fired. + * + * This will signal a change recursively to all children of the given node. + * For example, firing the event with the {@link testRoot} will refresh + * all tests. + */ + readonly onDidChangeTest: Event; + + /** + * An event that should be fired when all tests that are currently defined + * have been discovered. The provider should continue to watch for changes + * and fire `onDidChangeTest` until the hierarchy is disposed. + * + * @todo can this be covered by existing progress apis? Or return a promise + */ + readonly onDidDiscoverInitialTests: Event; + + /** + * Dispose will be called when there are no longer observers interested + * in the hierarchy. + */ + dispose(): void; + } + + /** + * Discovers and provides tests. It's expected that the TestProvider will + * ambiently listen to {@link vscode.window.onDidChangeVisibleTextEditors} to + * provide test information about the open files for use in code lenses and + * other file-specific UI. + * + * Additionally, the UI may request it to discover tests for the workspace + * via `addWorkspaceTests`. + * + * @todo rename from provider + */ + export interface TestProvider { + /** + * Requests that tests be provided for the given workspace. This will + * generally be called when tests need to be enumerated for the + * workspace. + * + * It's guaranteed that this method will not be called again while + * there is a previous undisposed watcher for the given workspace folder. + */ + createWorkspaceTestHierarchy?(workspace: WorkspaceFolder): TestHierarchy; + + /** + * Requests that tests be provided for the given document. This will + * be called when tests need to be enumerated for a single open file, + * for instance by code lens UI. + */ + createDocumentTestHierarchy?(document: TextDocument): TestHierarchy; + + /** + * Starts a test run. This should cause {@link onDidChangeTest} to + * fire with update test states during the run. + * @todo this will eventually need to be able to return a summary report, coverage for example. + */ + runTests?(options: TestRunOptions, cancellationToken: CancellationToken): ProviderResult; + } + + /** + * Options given to `TestProvider.runTests` + */ + export interface TestRunOptions { + /** + * Array of specific tests to run. The {@link TestProvider.testRoot} may + * be provided as an indication to run all tests. + */ + tests: T[]; + + /** + * Whether or not tests in this run should be debugged. + */ + debug: boolean; + } + + /** + * A test item is an item shown in the "test explorer" view. It encompasses + * both a suite and a test, since they have almost or identical capabilities. + */ + export interface TestItem { + /** + * Display name describing the test case. + */ + label: string; + + /** + * Optional description that appears next to the label. + */ + description?: string; + + /** + * Whether this test item can be run individually, defaults to `true` + * if not provided. + * + * In some cases, like Go's tests, test can have children but these + * children cannot be run independently. + */ + runnable?: boolean; + + /** + * Whether this test item can be debugged. Defaults to `false` if not provided. + */ + debuggable?: boolean; + + /** + * VS Code location. + */ + location?: Location; + + /** + * Optional list of nested tests for this item. + */ + children?: TestItem[]; + + /** + * Test run state. Will generally be {@link TestRunState.Unset} by + * default. + */ + state: TestState; + } + + export enum TestRunState { + // Initial state + Unset = 0, + // Test is currently running + Running = 1, + // Test run has passed + Passed = 2, + // Test run has failed (on an assertion) + Failed = 3, + // Test run has been skipped + Skipped = 4, + // Test run failed for some other reason (compilation error, timeout, etc) + Errored = 5 + } + + /** + * TestState includes a test and its run state. This is included in the + * {@link TestItem} and is immutable; it should be replaced in th TestItem + * in order to update it. This allows consumers to quickly and easily check + * for changes via object identity. + */ + export class TestState { + /** + * Current state of the test. + */ + readonly runState: TestRunState; + + /** + * Optional duration of the test run, in milliseconds. + */ + readonly duration?: number; + + /** + * Associated test run message. Can, for example, contain assertion + * failure information if the test fails. + */ + readonly messages: ReadonlyArray>; + + /** + * @param state Run state to hold in the test state + * @param messages List of associated messages for the test + * @param duration Length of time the test run took, if appropriate. + */ + constructor(runState: TestRunState, messages?: TestMessage[], duration?: number); + } + + /** + * Represents the severity of test messages. + */ + export enum TestMessageSeverity { + Error = 0, + Warning = 1, + Information = 2, + Hint = 3 + } + + /** + * Message associated with the test state. Can be linked to a specific + * source range -- useful for assertion failures, for example. + */ + export interface TestMessage { + /** + * Human-readable message text to display. + */ + message: string | MarkdownString; + + /** + * Message severity. Defaults to "Error", if not provided. + */ + severity?: TestMessageSeverity; + + /** + * Expected test output. If given with `actual`, a diff view will be shown. + */ + expectedOutput?: string; + + /** + * Actual test output. If given with `actual`, a diff view will be shown. + */ + actualOutput?: string; + + /** + * Associated file location. + */ + location?: Location; + } //#endregion - //#region https://github.com/microsoft/vscode/issues/108929 FoldingRangeProvider.onDidChangeFoldingRanges @aeschli - export interface FoldingRangeProvider2 extends FoldingRangeProvider { + //#region Statusbar Item Background Color (https://github.com/microsoft/vscode/issues/110214) + + /** + * A status bar item is a status bar contribution that can + * show text and icons and run a command on click. + */ + export interface StatusBarItem { /** - * An optional event to signal that the folding ranges from this provider have changed. + * The background color for this entry. + * + * Note: only `new ThemeColor('statusBarItem.errorBackground')` is + * supported for now. More background colors may be supported in the + * future. + * + * Note: when a background color is set, the statusbar may override + * the `color` choice to ensure the entry is readable in all themes. */ - onDidChangeFoldingRanges?: Event; - + backgroundColor: ThemeColor | undefined; } + //#endregion } diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index a4df85236..3b4c8a66c 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -64,6 +64,7 @@ import './mainThreadLabelService'; import './mainThreadTunnelService'; import './mainThreadAuthentication'; import './mainThreadTimeline'; +import './mainThreadTesting'; import 'vs/workbench/api/common/apiCommands'; export class ExtensionPoints implements IWorkbenchContribution { diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 02240e28b..c39650404 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -10,11 +10,10 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { IAuthenticationService, AllowedExtension, readAllowedExtensions, getAuthenticationProviderActivationEvent } from 'vs/workbench/services/authentication/browser/authenticationService'; import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import Severity from 'vs/base/common/severity'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { fromNow } from 'vs/base/common/date'; import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -23,7 +22,7 @@ import { IEncryptionService } from 'vs/workbench/services/encryption/common/encr import { IProductService } from 'vs/platform/product/common/productService'; import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials'; -const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser', 'ms-vscode.github-browser']; +const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser', 'ms-vscode.github-browser', 'github.codespaces']; interface IAccountUsage { extensionId: string; @@ -70,7 +69,7 @@ function addAccountUsage(storageService: IStorageService, providerId: string, ac }); } - storageService.store(accountKey, JSON.stringify(usages), StorageScope.GLOBAL); + storageService.store(accountKey, JSON.stringify(usages), StorageScope.GLOBAL, StorageTarget.MACHINE); } export class MainThreadAuthenticationProvider extends Disposable { @@ -83,7 +82,6 @@ export class MainThreadAuthenticationProvider extends Disposable { public readonly label: string, public readonly supportsMultipleAccounts: boolean, private readonly notificationService: INotificationService, - private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, private readonly storageService: IStorageService, private readonly quickInputService: IQuickInputService, private readonly dialogService: IDialogService @@ -100,9 +98,15 @@ export class MainThreadAuthenticationProvider extends Disposable { } public manageTrustedExtensions(accountName: string) { + const allowedExtensions = readAllowedExtensions(this.storageService, this.id, accountName); + + if (!allowedExtensions.length) { + this.dialogService.show(Severity.Info, nls.localize('noTrustedExtensions', "This account has not been used by any extensions."), []); + return; + } + const quickPick = this.quickInputService.createQuickPick<{ label: string, description: string, extension: AllowedExtension }>(); quickPick.canSelectMany = true; - const allowedExtensions = readAllowedExtensions(this.storageService, this.id, accountName); const usages = readAccountUsages(this.storageService, this.id, accountName); const items = allowedExtensions.map(extension => { const usage = usages.find(usage => extension.id === usage.extensionId); @@ -122,7 +126,7 @@ export class MainThreadAuthenticationProvider extends Disposable { quickPick.onDidAccept(() => { const updatedAllowedList = quickPick.selectedItems.map(item => item.extension); - this.storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL); + this.storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL, StorageTarget.USER); quickPick.dispose(); }); @@ -153,8 +157,6 @@ export class MainThreadAuthenticationProvider extends Disposable { } else { this._accounts.set(session.account.label, [session.id]); } - - this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.label}`, version: 1 }); } async signOut(accountName: string): Promise { @@ -171,6 +173,7 @@ export class MainThreadAuthenticationProvider extends Disposable { if (result.confirmed) { sessionsForAccount?.forEach(sessionId => this.logout(sessionId)); removeAccountUsage(this.storageService, this.id, accountName); + this.storageService.remove(`${this.id}-${accountName}`, StorageScope.GLOBAL); } } @@ -220,7 +223,6 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu @IDialogService private readonly dialogService: IDialogService, @IStorageService private readonly storageService: IStorageService, @INotificationService private readonly notificationService: INotificationService, - @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IExtensionService private readonly extensionService: IExtensionService, @@ -259,7 +261,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } async $registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): Promise { - const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageKeysSyncRegistryService, this.storageService, this.quickInputService, this.dialogService); + const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageService, this.quickInputService, this.dialogService); await provider.initialize(); this.authenticationService.registerAuthenticationProvider(id, provider); } @@ -383,10 +385,10 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu const allowList = readAllowedExtensions(this.storageService, providerId, accountName); if (!allowList.find(allowed => allowed.id === extensionId)) { allowList.push({ id: extensionId, name: extensionName }); - this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); + this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER); } - this.storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL); + this.storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL, StorageTarget.MACHINE); quickPick.dispose(); resolve(session); @@ -435,7 +437,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu if (allow) { addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName); allowList.push({ id: extensionId, name: extensionName }); - this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); + this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER); } return allow; @@ -458,10 +460,11 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu const allowList = readAllowedExtensions(this.storageService, providerId, accountName); if (!allowList.find(allowed => allowed.id === extensionId)) { allowList.push({ id: extensionId, name: extensionName }); - this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); + this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER); } - this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL); + this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL, StorageTarget.MACHINE); + addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName); } private getFullKey(extensionId: string): string { diff --git a/src/vs/workbench/api/browser/mainThreadBulkEdits.ts b/src/vs/workbench/api/browser/mainThreadBulkEdits.ts index b6ee8cacf..70fb045d7 100644 --- a/src/vs/workbench/api/browser/mainThreadBulkEdits.ts +++ b/src/vs/workbench/api/browser/mainThreadBulkEdits.ts @@ -37,8 +37,14 @@ export class MainThreadBulkEdits implements MainThreadBulkEditsShape { dispose(): void { } - $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise { + $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto, undoRedoGroupId?: number): Promise { const edits = reviveWorkspaceEditDto2(dto); - return this._bulkEditService.apply(edits).then(() => true, _err => false); + return this._bulkEditService.apply(edits, { + // having a undoRedoGroupId means that this is a nested workspace edit, + // e.g one from a onWill-handler and for now we need to forcefully suppress + // refactor previewing, see: https://github.com/microsoft/vscode/issues/111873#issuecomment-738739852 + undoRedoGroupId, + suppressPreview: typeof undoRedoGroupId === 'number' ? true : undefined + }).then(() => true, _err => false); } } diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index 8ea573366..1256682d4 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -14,7 +14,7 @@ import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { isEqual } from 'vs/base/common/resources'; -// todo@joh move these things back into something like contrib/insets +// todo@jrieken move these things back into something like contrib/insets class EditorWebviewZone implements IViewZone { readonly domNode: HTMLElement; @@ -73,7 +73,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise { let editor: IActiveCodeEditor | undefined; - id = id.substr(0, id.indexOf(',')); //todo@joh HACK + id = id.substr(0, id.indexOf(',')); //todo@jrieken HACK for (const candidate of this._editorService.listCodeEditors()) { if (candidate.getId() === id && candidate.hasModel() && isEqual(candidate.getModel().uri, URI.revive(uri))) { diff --git a/src/vs/workbench/api/browser/mainThreadCommands.ts b/src/vs/workbench/api/browser/mainThreadCommands.ts index ff817619f..38f6ac669 100644 --- a/src/vs/workbench/api/browser/mainThreadCommands.ts +++ b/src/vs/workbench/api/browser/mainThreadCommands.ts @@ -34,23 +34,23 @@ export class MainThreadCommands implements MainThreadCommandsShape { this._generateCommandsDocumentationRegistration.dispose(); } - private _generateCommandsDocumentation(): Promise { - return this._proxy.$getContributedCommandHandlerDescriptions().then(result => { - // add local commands - const commands = CommandsRegistry.getCommands(); - for (const [id, command] of commands) { - if (command.description) { - result[id] = command.description; - } - } + private async _generateCommandsDocumentation(): Promise { + const result = await this._proxy.$getContributedCommandHandlerDescriptions(); - // print all as markdown - const all: string[] = []; - for (let id in result) { - all.push('`' + id + '` - ' + _generateMarkdown(result[id])); + // add local commands + const commands = CommandsRegistry.getCommands(); + for (const [id, command] of commands) { + if (command.description) { + result[id] = command.description; } - console.log(all.join('\n')); - }); + } + + // print all as markdown + const all: string[] = []; + for (let id in result) { + all.push('`' + id + '` - ' + _generateMarkdown(result[id])); + } + console.log(all.join('\n')); } $registerCommand(id: string): void { diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index b6b328412..8dba884cb 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -21,6 +21,8 @@ import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, V import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { localize } from 'vs/nls'; export class MainThreadCommentThread implements modes.CommentThread { @@ -351,6 +353,9 @@ export class MainThreadCommentController { } } + +const commentsViewIcon = registerIcon('comments-view-icon', Codicon.commentDiscussion, localize('commentsViewIcon', 'View icon of the comments view.')); + @extHostNamedCustomer(MainContext.MainThreadComments) export class MainThreadComments extends Disposable implements MainThreadCommentsShape { private readonly _proxy: ExtHostCommentsShape; @@ -472,7 +477,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: COMMENTS_VIEW_TITLE, hideIfEmpty: true, - icon: Codicon.commentDiscussion.classNames, + icon: commentsViewIcon, order: 10, }, ViewContainerLocation.Panel); @@ -482,7 +487,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments canToggleVisibility: false, ctorDescriptor: new SyncDescriptor(CommentsPanel), canMoveView: true, - containerIcon: Codicon.commentDiscussion.classNames, + containerIcon: commentsViewIcon, focusCommand: { id: 'workbench.action.focusCommentsPanel' } diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 410b6b061..bcf31a1a7 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -23,8 +23,7 @@ import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/comm import { MainThreadWebviewPanels } from 'vs/workbench/api/browser/mainThreadWebviewPanels'; import { MainThreadWebviews, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; -import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; -import { IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; +import { editorGroupToViewColumn, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { CustomDocumentBackupData } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory'; import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; @@ -604,6 +603,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod this._proxy.$backup(this._editorResource.toJSON(), this.viewType, token))); this._hotExitState = pendingState; + token.onCancellationRequested(() => { + pendingState.operation.cancel(); + }); + try { const backupId = await pendingState.operation; // Make sure state has not changed in the meantime diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 54fd5da63..f1906f16a 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -82,7 +82,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb // RPC methods (MainThreadDebugServiceShape) public $registerDebugTypes(debugTypes: string[]) { - this._toDispose.add(this.debugService.getConfigurationManager().registerDebugAdapterFactory(debugTypes, this)); + this._toDispose.add(this.debugService.getAdapterManager().registerDebugAdapterFactory(debugTypes, this)); } public $startBreakpointEvents(): void { @@ -155,7 +155,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return Promise.resolve(); } - public $registerDebugConfigurationProvider(debugType: string, providerTriggerKind: DebugConfigurationProviderTriggerKind, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, hasProvideDebugAdapter: boolean, handle: number): Promise { + public $registerDebugConfigurationProvider(debugType: string, providerTriggerKind: DebugConfigurationProviderTriggerKind, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, handle: number): Promise { const provider = { type: debugType, @@ -176,12 +176,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return this._proxy.$resolveDebugConfigurationWithSubstitutedVariables(handle, folder, config, token); }; } - if (hasProvideDebugAdapter) { - console.info('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.'); - provider.debugAdapterExecutable = (folder) => { - return this._proxy.$legacyDebugAdapterExecutable(handle, folder); - }; - } this._debugConfigurationProviders.set(handle, provider); this._toDispose.add(this.debugService.getConfigurationManager().registerDebugConfigurationProvider(provider)); @@ -205,7 +199,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb } }; this._debugAdapterDescriptorFactories.set(handle, provider); - this._toDispose.add(this.debugService.getConfigurationManager().registerDebugAdapterDescriptorFactory(provider)); + this._toDispose.add(this.debugService.getAdapterManager().registerDebugAdapterDescriptorFactory(provider)); return Promise.resolve(undefined); } @@ -214,7 +208,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb const provider = this._debugAdapterDescriptorFactories.get(handle); if (provider) { this._debugAdapterDescriptorFactories.delete(handle); - this.debugService.getConfigurationManager().unregisterDebugAdapterDescriptorFactory(provider); + this.debugService.getAdapterManager().unregisterDebugAdapterDescriptorFactory(provider); } } diff --git a/src/vs/workbench/api/browser/mainThreadDialogs.ts b/src/vs/workbench/api/browser/mainThreadDialogs.ts index ff3b66e93..afa4240f3 100644 --- a/src/vs/workbench/api/browser/mainThreadDialogs.ts +++ b/src/vs/workbench/api/browser/mainThreadDialogs.ts @@ -23,12 +23,20 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { // } - $showOpenDialog(options?: MainThreadDialogOpenOptions): Promise { - return Promise.resolve(this._fileDialogService.showOpenDialog(MainThreadDialogs._convertOpenOptions(options))); + async $showOpenDialog(options?: MainThreadDialogOpenOptions): Promise { + const convertedOptions = MainThreadDialogs._convertOpenOptions(options); + if (!convertedOptions.defaultUri) { + convertedOptions.defaultUri = await this._fileDialogService.defaultFilePath(); + } + return Promise.resolve(this._fileDialogService.showOpenDialog(convertedOptions)); } - $showSaveDialog(options?: MainThreadDialogSaveOptions): Promise { - return Promise.resolve(this._fileDialogService.showSaveDialog(MainThreadDialogs._convertSaveOptions(options))); + async $showSaveDialog(options?: MainThreadDialogSaveOptions): Promise { + const convertedOptions = MainThreadDialogs._convertSaveOptions(options); + if (!convertedOptions.defaultUri) { + convertedOptions.defaultUri = await this._fileDialogService.defaultFilePath(); + } + return Promise.resolve(this._fileDialogService.showSaveDialog(convertedOptions)); } private static _convertOpenOptions(options?: MainThreadDialogOpenOptions): IOpenDialogOptions { @@ -38,7 +46,8 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { canSelectFolders: options?.canSelectFolders, canSelectMany: options?.canSelectMany, defaultUri: options?.defaultUri ? URI.revive(options.defaultUri) : undefined, - title: options?.title || undefined + title: options?.title || undefined, + availableFileSystems: [] }; if (options?.filters) { result.filters = []; diff --git a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts index c92a58c27..242521214 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts @@ -19,9 +19,8 @@ import { MainThreadDocuments } from 'vs/workbench/api/browser/mainThreadDocument import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor'; import { MainThreadTextEditors } from 'vs/workbench/api/browser/mainThreadEditors'; import { ExtHostContext, ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IExtHostContext, IModelAddedData, ITextEditorAddData, MainContext } from 'vs/workbench/api/common/extHost.protocol'; -import { EditorViewColumn, editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; -import { IEditorPane } from 'vs/workbench/common/editor'; +import { editorGroupToViewColumn, EditorGroupColumn, IEditorPane } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -31,41 +30,8 @@ import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/commo import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { diffSets, diffMaps } from 'vs/base/common/collections'; -namespace delta { - - export function ofSets(before: Set, after: Set): { removed: T[], added: T[] } { - const removed: T[] = []; - const added: T[] = []; - for (let element of before) { - if (!after.has(element)) { - removed.push(element); - } - } - for (let element of after) { - if (!before.has(element)) { - added.push(element); - } - } - return { removed, added }; - } - - export function ofMaps(before: Map, after: Map): { removed: V[], added: V[] } { - const removed: V[] = []; - const added: V[] = []; - for (let [index, value] of before) { - if (!after.has(index)) { - removed.push(value); - } - } - for (let [index, value] of after) { - if (!before.has(index)) { - added.push(value); - } - } - return { removed, added }; - } -} class TextEditorSnapshot { @@ -118,8 +84,8 @@ class DocumentAndEditorState { undefined, after.activeEditor ); } - const documentDelta = delta.ofSets(before.documents, after.documents); - const editorDelta = delta.ofMaps(before.textEditors, after.textEditors); + const documentDelta = diffSets(before.documents, after.documents); + const editorDelta = diffMaps(before.textEditors, after.textEditors); const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined; const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined; @@ -443,7 +409,7 @@ export class MainThreadDocumentsAndEditors { }; } - private _findEditorPosition(editor: MainThreadTextEditor): EditorViewColumn | undefined { + private _findEditorPosition(editor: MainThreadTextEditor): EditorGroupColumn | undefined { for (const editorPane of this._editorService.visibleEditorPanes) { if (editor.matches(editorPane)) { return editorGroupToViewColumn(this._editorGroupService, editorPane.group); diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index 738493d39..1723f6328 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -14,18 +14,14 @@ import { ISelection } from 'vs/editor/common/core/selection'; import { IDecorationOptions, IDecorationRenderOptions, ILineChange } from 'vs/editor/common/editorCommon'; import { ISingleEditOperation } from 'vs/editor/common/model'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IEditorOptions, ITextEditorOptions, IResourceEditorInput, EditorActivation } from 'vs/platform/editor/common/editor'; +import { ITextEditorOptions, IResourceEditorInput, EditorActivation } from 'vs/platform/editor/common/editor'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors'; import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor'; import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType, IWorkspaceEditDto, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; -import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; +import { editorGroupToViewColumn, EditorGroupColumn, viewColumnToEditorGroup } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { revive } from 'vs/base/common/marshalling'; @@ -161,7 +157,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { return this._documentsAndEditors.findTextEditorIdFor(editor); } - async $tryShowEditor(id: string, position?: EditorViewColumn): Promise { + async $tryShowEditor(id: string, position?: EditorGroupColumn): Promise { const mainThreadEditor = this._documentsAndEditors.getEditor(id); if (mainThreadEditor) { const model = mainThreadEditor.getModel(); @@ -297,59 +293,6 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // --- commands -CommandsRegistry.registerCommand('_workbench.open', async function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorViewColumn, string?]) { - const editorService = accessor.get(IEditorService); - const editorGroupService = accessor.get(IEditorGroupsService); - const openerService = accessor.get(IOpenerService); - - const [resource, options, position, label] = args; - - if (options || typeof position === 'number') { - // use editor options or editor view column as a hint to use the editor service for opening - await editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, position)); - return; - } - - if (resource && resource.scheme === 'command') { - // do not allow to execute commands from here - return; - - } - // finally, delegate to opener service - await openerService.open(resource); -}); - -CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, ITextEditorOptions | undefined, EditorViewColumn | undefined]) => { - const editorService = accessor.get(IEditorService); - const editorGroupsService = accessor.get(IEditorGroupsService); - const configurationService = accessor.get(IConfigurationService); - const quickInputService = accessor.get(IQuickInputService); - - const [resource, id, options, position] = args; - - const group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, position)) ?? editorGroupsService.activeGroup; - const textOptions: ITextEditorOptions = options ? { ...options, override: false } : { override: false }; - - const input = editorService.createEditorInput({ resource }); - return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService); -}); - - -CommandsRegistry.registerCommand('_workbench.diff', async function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorViewColumn]) { - const editorService = accessor.get(IEditorService); - const editorGroupService = accessor.get(IEditorGroupsService); - - let [leftResource, rightResource, label, description, options, position] = args; - - if (!options || typeof options !== 'object') { - options = { - preserveFocus: false - }; - } - - await editorService.openEditor({ leftResource, rightResource, label, description, options }, viewColumnToEditorGroup(editorGroupService, position)); -}); - CommandsRegistry.registerCommand('_workbench.revertAllDirty', async function (accessor: ServicesAccessor) { const environmentService = accessor.get(IEnvironmentService); if (!environmentService.extensionTestsLocationURI) { diff --git a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts index ca2c751ed..81905c6b1 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts @@ -54,11 +54,13 @@ export class MainThreadFileSystemEventService { // BEFORE file operation - workingCopyFileService.addFileOperationParticipant({ - participate: (files, operation, progress, timeout, token) => { - return proxy.$onWillRunFileOperation(operation, files, timeout, token); + this._listener.add(workingCopyFileService.addFileOperationParticipant({ + participate: async (files, operation, undoRedoGroupId, isUndoing, _progress, timeout, token) => { + if (!isUndoing) { + return proxy.$onWillRunFileOperation(operation, files, undoRedoGroupId, timeout, token); + } } - }); + })); // AFTER file operation this._listener.add(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => proxy.$onDidRunFileOperation(e.operation, e.files))); @@ -75,7 +77,7 @@ Registry.as(Extensions.Configuration).registerConfigurat properties: { 'files.participants.timeout': { type: 'number', - default: 5000, + default: 60000, markdownDescription: localize('files.participants.timeout', "Timeout in milliseconds after which file participants for create, rename, and delete are cancelled. Use `0` to disable participants."), } } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 64a3f19a3..ddea8e0e9 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -165,16 +165,15 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerCodeLensSupport(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void { const provider = { - provideCodeLenses: (model: ITextModel, token: CancellationToken): Promise => { - return this._proxy.$provideCodeLenses(handle, model.uri, token).then(listDto => { - if (!listDto) { - return undefined; - } - return { - lenses: listDto.lenses, - dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId) - }; - }); + provideCodeLenses: async (model: ITextModel, token: CancellationToken): Promise => { + const listDto = await this._proxy.$provideCodeLenses(handle, model.uri, token); + if (!listDto) { + return undefined; + } + return { + lenses: listDto.lenses, + dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId) + }; }, resolveCodeLens: (_model: ITextModel, codeLens: modes.CodeLens, token: CancellationToken): Promise => { return this._proxy.$resolveCodeLens(handle, codeLens, token); @@ -261,14 +260,12 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha })); } - // --- on type rename + // --- linked editing - $registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[], wordPattern?: IRegExpDto): void { - const revivedWordPattern = wordPattern ? MainThreadLanguageFeatures._reviveRegExp(wordPattern) : undefined; - this._registrations.set(handle, modes.OnTypeRenameProviderRegistry.register(selector, { - wordPattern: revivedWordPattern, - provideOnTypeRenameRanges: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: RegExp; } | undefined> => { - const res = await this._proxy.$provideOnTypeRenameRanges(handle, model.uri, position, token); + $registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void { + this._registrations.set(handle, modes.LinkedEditingRangeProviderRegistry.register(selector, { + provideLinkedEditingRanges: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise => { + const res = await this._proxy.$provideLinkedEditingRanges(handle, model.uri, position, token); if (res) { return { ranges: res.ranges, diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 93215c290..cbf34422d 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -5,6 +5,7 @@ import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { diffMaps, diffSets } from 'vs/base/common/collections'; import { Emitter } from 'vs/base/common/event'; import { IRelativePattern } from 'vs/base/common/glob'; import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; @@ -14,8 +15,11 @@ import { IExtUri } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { ILogService } from 'vs/platform/log/common/log'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { viewColumnToEditorGroup } from 'vs/workbench/common/editor'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; @@ -23,44 +27,14 @@ import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/com import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookEditorModel, INotebookExclusiveDocumentFilter, NotebookCellOutputsSplice, NotebookCellsChangeType, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; +import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; +import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; class DocumentAndEditorState { - static ofSets(before: Set, after: Set): { removed: T[], added: T[] } { - const removed: T[] = []; - const added: T[] = []; - before.forEach(element => { - if (!after.has(element)) { - removed.push(element); - } - }); - after.forEach(element => { - if (!before.has(element)) { - added.push(element); - } - }); - return { removed, added }; - } - - static ofMaps(before: Map, after: Map): { removed: V[], added: V[] } { - const removed: V[] = []; - const added: V[] = []; - before.forEach((value, index) => { - if (!after.has(index)) { - removed.push(value); - } - }); - after.forEach((value, index) => { - if (!before.has(index)) { - added.push(value); - } - }); - return { removed, added }; - } - static compute(before: DocumentAndEditorState | undefined, after: DocumentAndEditorState): INotebookDocumentsAndEditorsDelta { if (!before) { const apiEditors = []; @@ -75,8 +49,8 @@ class DocumentAndEditorState { visibleEditors: [...after.visibleEditors].map(editor => editor[0]) }; } - const documentDelta = DocumentAndEditorState.ofSets(before.documents, after.documents); - const editorDelta = DocumentAndEditorState.ofMaps(before.textEditors, after.textEditors); + const documentDelta = diffSets(before.documents, after.documents); + const editorDelta = diffMaps(before.textEditors, after.textEditors); const addedAPIEditors = editorDelta.added.map(add => ({ id: add.getId(), documentUri: add.uri!, @@ -89,7 +63,7 @@ class DocumentAndEditorState { // const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined; const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined; - const visibleEditorDelta = DocumentAndEditorState.ofMaps(before.visibleEditors, after.visibleEditors); + const visibleEditorDelta = diffMaps(before.visibleEditors, after.visibleEditors); return { addedDocuments: documentDelta.added.map((e: NotebookTextModel): INotebookModelAddedData => { @@ -152,7 +126,10 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo @INotebookService private _notebookService: INotebookService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorService private readonly editorService: IEditorService, + @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService, + @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, + @IQuickInputService private readonly quickInputService: IQuickInputService, @ILogService private readonly logService: ILogService, @INotebookCellStatusBarService private readonly cellStatusBarService: INotebookCellStatusBarService, @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, @@ -171,7 +148,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo if (!textModel) { return false; } - this._notebookService.transformEditsOutputs(textModel, cellEdits); return textModel.applyEdits(modelVersionId, cellEdits, true, undefined, () => undefined, undefined); } @@ -413,9 +389,9 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const editors = new Map(); this._notebookService.listNotebookEditors().forEach(editor => { - if (editor.hasModel()) { + if (editor.textModel) { editors.set(editor.getId(), editor); - documentEditorsMap.set(editor.textModel!.uri.toString(), editor); + documentEditorsMap.set(editor.textModel.uri.toString(), editor); } }); @@ -434,7 +410,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo documents.add(document); }); - if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.hasModel()) { + if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.textModel) { activeEditor = focusedNotebookEditor.getId(); } @@ -479,8 +455,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const edits: ICellEditOperation[] = [ { editType: CellEditType.Replace, index: 0, count: mainthreadTextModel.cells.length, cells: data.cells } ]; - - this._notebookService.transformEditsOutputs(mainthreadTextModel, edits); await new Promise(resolve => { DOM.scheduleAtNextAnimationFrame(() => { const ret = mainthreadTextModel!.applyEdits(mainthreadTextModel!.versionId, edits, true, undefined, () => undefined, undefined); @@ -606,7 +580,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return; } - this._notebookService.transformSpliceOutputs(textModel, splices); const cell = textModel.cells.find(cell => cell.handle === cellHandle); if (!cell) { @@ -662,8 +635,11 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const editor = this._notebookService.listNotebookEditors().find(editor => editor.getId() === id); if (editor && editor.isNotebookEditor) { const notebookEditor = editor as INotebookEditor; + if (!notebookEditor.hasModel()) { + return; + } const viewModel = notebookEditor.viewModel; - const cell = viewModel?.viewCells[range.start]; + const cell = viewModel.viewCells[range.start]; if (!cell) { return; } @@ -726,6 +702,51 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return uri; } + + async $tryShowNotebookDocument(resource: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise { + const editorOptions: ITextEditorOptions = { + preserveFocus: options.preserveFocus, + pinned: options.pinned, + // selection: options.selection, + // preserve pre 1.38 behaviour to not make group active when preserveFocus: true + // but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633 + activation: options.preserveFocus ? EditorActivation.RESTORE : undefined, + override: false, + }; + + const columnArg = viewColumnToEditorGroup(this._editorGroupService, options.position); + + let group: IEditorGroup | undefined = undefined; + + if (columnArg === SIDE_GROUP) { + const direction = preferredSideBySideGroupDirection(this.configurationService); + + let neighbourGroup = this.editorGroupsService.findGroup({ direction }); + if (!neighbourGroup) { + neighbourGroup = this.editorGroupsService.addGroup(this.editorGroupsService.activeGroup, direction); + } + group = neighbourGroup; + } else { + group = this.editorGroupsService.getGroup(viewColumnToEditorGroup(this.editorGroupsService, columnArg)) ?? this.editorGroupsService.activeGroup; + } + + const input = this.editorService.createEditorInput({ resource: URI.revive(resource), options: editorOptions }); + + // TODO: handle options.selection + const editorPane = await openEditorWith(input, viewType, options, group, this.editorService, this.configurationService, this.quickInputService); + const notebookEditor = (editorPane as unknown as { isNotebookEditor?: boolean })?.isNotebookEditor ? (editorPane!.getControl() as INotebookEditor) : undefined; + + if (notebookEditor) { + if (notebookEditor.viewModel && options.selection && notebookEditor.viewModel.viewCells[options.selection.start]) { + const focusedCell = notebookEditor.viewModel.viewCells[options.selection.start]; + notebookEditor.revealInCenterIfOutsideViewport(focusedCell); + notebookEditor.selectElement(focusedCell); + } + return notebookEditor.getId(); + } else { + throw new Error(`Notebook Editor creation failure for documenet ${resource}`); + } + } } diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 32dbe8049..8f1e25ebb 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -68,7 +68,8 @@ class MainThreadSCMResource implements ISCMResource { readonly sourceUri: URI, readonly resourceGroup: ISCMResourceGroup, readonly decorations: ISCMResourceDecorations, - readonly contextValue: string | undefined + readonly contextValue: string | undefined, + readonly command: Command | undefined ) { } open(preserveFocus: boolean): Promise { @@ -201,7 +202,7 @@ class MainThreadSCMProvider implements ISCMProvider { for (const [start, deleteCount, rawResources] of groupSlices) { const resources = rawResources.map(rawResource => { - const [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue] = rawResource; + const [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue, command] = rawResource; const icon = icons[0]; const iconDark = icons[1] || icon; const decorations = { @@ -220,7 +221,8 @@ class MainThreadSCMProvider implements ISCMProvider { URI.revive(sourceUri), group, decorations, - contextValue || undefined + contextValue || undefined, + command ); }); diff --git a/src/vs/workbench/api/browser/mainThreadStatusBar.ts b/src/vs/workbench/api/browser/mainThreadStatusBar.ts index d50bc8f08..a847df3bd 100644 --- a/src/vs/workbench/api/browser/mainThreadStatusBar.ts +++ b/src/vs/workbench/api/browser/mainThreadStatusBar.ts @@ -27,7 +27,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape { this.entries.clear(); } - $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void { + $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void { // if there are icons in the text use the tooltip for the aria label let ariaLabel: string; let role: string | undefined = undefined; @@ -37,7 +37,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape { } else { ariaLabel = text ? text.replace(MainThreadStatusBar.CODICON_REGEXP, (_match, codiconName) => codiconName) : ''; } - const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel, role }; + const entry: IStatusbarEntry = { text, tooltip, command, color, backgroundColor, ariaLabel, role }; if (typeof priority === 'undefined') { priority = 0; diff --git a/src/vs/workbench/api/browser/mainThreadStorage.ts b/src/vs/workbench/api/browser/mainThreadStorage.ts index 57abf0e86..5cb658e91 100644 --- a/src/vs/workbench/api/browser/mainThreadStorage.ts +++ b/src/vs/workbench/api/browser/mainThreadStorage.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { MainThreadStorageShape, MainContext, IExtHostContext, ExtHostStorageShape, ExtHostContext } from '../common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IExtensionIdWithVersion, IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IExtensionIdWithVersion, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; @extHostNamedCustomer(MainContext.MainThreadStorage) export class MainThreadStorage implements MainThreadStorageShape { private readonly _storageService: IStorageService; - private readonly _storageKeysSyncRegistryService: IStorageKeysSyncRegistryService; + private readonly _extensionsStorageSyncService: IExtensionsStorageSyncService; private readonly _proxy: ExtHostStorageShape; private readonly _storageListener: IDisposable; private readonly _sharedStorageKeysToWatch: Map = new Map(); @@ -21,13 +21,13 @@ export class MainThreadStorage implements MainThreadStorageShape { constructor( extHostContext: IExtHostContext, @IStorageService storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, + @IExtensionsStorageSyncService extensionsStorageSyncService: IExtensionsStorageSyncService, ) { this._storageService = storageService; - this._storageKeysSyncRegistryService = storageKeysSyncRegistryService; + this._extensionsStorageSyncService = extensionsStorageSyncService; this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostStorage); - this._storageListener = this._storageService.onDidChangeStorage(e => { + this._storageListener = this._storageService.onDidChangeValue(e => { const shared = e.scope === StorageScope.GLOBAL; if (shared && this._sharedStorageKeysToWatch.has(e.key)) { try { @@ -66,7 +66,8 @@ export class MainThreadStorage implements MainThreadStorageShape { let jsonValue: string; try { jsonValue = JSON.stringify(value); - this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE); + // Extension state is synced separately through extensions + this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE, StorageTarget.MACHINE); } catch (err) { return Promise.reject(err); } @@ -74,6 +75,6 @@ export class MainThreadStorage implements MainThreadStorageShape { } $registerExtensionStorageKeysToSync(extension: IExtensionIdWithVersion, keys: string[]): void { - this._storageKeysSyncRegistryService.registerExtensionStorageKeys(extension, keys); + this._extensionsStorageSyncService.setKeysForSync(extension, keys); } } diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index 391cd0cef..2f4cb073c 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -53,7 +53,7 @@ namespace TaskProcessStartedDTO { } namespace TaskProcessEndedDTO { - export function from(value: TaskExecution, exitCode: number): TaskProcessEndedDTO { + export function from(value: TaskExecution, exitCode: number | undefined): TaskProcessEndedDTO { return { id: value.id, exitCode @@ -429,7 +429,7 @@ export class MainThreadTask implements MainThreadTaskShape { } else if (event.kind === TaskEventKind.ProcessStarted) { this._proxy.$onDidStartTaskProcess(TaskProcessStartedDTO.from(task.getTaskExecution(), event.processId!)); } else if (event.kind === TaskEventKind.ProcessEnded) { - this._proxy.$onDidEndTaskProcess(TaskProcessEndedDTO.from(task.getTaskExecution(), event.exitCode!)); + this._proxy.$onDidEndTaskProcess(TaskProcessEndedDTO.from(task.getTaskExecution(), event.exitCode)); } else if (event.kind === TaskEventKind.End) { this._proxy.$OnDidEndTask(TaskExecutionDTO.from(task.getTaskExecution())); } diff --git a/src/vs/workbench/api/browser/mainThreadTesting.ts b/src/vs/workbench/api/browser/mainThreadTesting.ts new file mode 100644 index 000000000..52185f8dd --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadTesting.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { getTestSubscriptionKey, RunTestsRequest, RunTestsResult, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol'; +import { URI, UriComponents } from 'vs/base/common/uri'; + +@extHostNamedCustomer(MainContext.MainThreadTesting) +export class MainThreadTesting extends Disposable implements MainThreadTestingShape { + private readonly proxy: ExtHostTestingShape; + private readonly testSubscriptions = new Map(); + + constructor( + extHostContext: IExtHostContext, + @ITestService private readonly testService: ITestService, + ) { + super(); + this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostTesting); + this._register(this.testService.onShouldSubscribe(args => this.proxy.$subscribeToTests(args.resource, args.uri))); + this._register(this.testService.onShouldUnsubscribe(args => this.proxy.$unsubscribeFromTests(args.resource, args.uri))); + } + + /** + * @inheritdoc + */ + public $registerTestProvider(id: string) { + this.testService.registerTestController(id, { + runTests: req => this.proxy.$runTestsForProvider(req), + }); + } + + /** + * @inheritdoc + */ + public $unregisterTestProvider(id: string) { + this.testService.unregisterTestController(id); + } + + /** + * @inheritdoc + */ + $subscribeToDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void { + const uri = URI.revive(uriComponents); + const disposable = this.testService.subscribeToDiffs(resource, uri, + diff => this.proxy.$acceptDiff(resource, uriComponents, diff)); + this.testSubscriptions.set(getTestSubscriptionKey(resource, uri), disposable); + } + + /** + * @inheritdoc + */ + public $unsubscribeFromDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void { + const key = getTestSubscriptionKey(resource, URI.revive(uriComponents)); + this.testSubscriptions.get(key)?.dispose(); + this.testSubscriptions.delete(key); + } + + /** + * @inheritdoc + */ + public $publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void { + this.testService.publishDiff(resource, URI.revive(uri), diff); + } + + public $runTests(req: RunTestsRequest): Promise { + return this.testService.runTests(req); + } + + public dispose() { + // no-op + } +} diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 7d0d71cc4..8e32acd7f 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -52,14 +52,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie }); } - $reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise { - this.logService.trace('MainThreadTreeViews#$reveal', treeViewId, item, parentChain, options); + $reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise { + this.logService.trace('MainThreadTreeViews#$reveal', treeViewId, itemInfo?.item, itemInfo?.parentChain, options); return this.viewsService.openView(treeViewId, options.focus) .then(() => { const viewer = this.getTreeView(treeViewId); - if (viewer) { - return this.reveal(viewer, this._dataProviders.get(treeViewId)!, item, parentChain, options); + if (viewer && itemInfo) { + return this.reveal(viewer, this._dataProviders.get(treeViewId)!, itemInfo.item, itemInfo.parentChain, options); } return undefined; }); @@ -235,10 +235,14 @@ class TreeViewDataProvider implements ITreeViewDataProvider { private updateTreeItem(current: ITreeItem, treeItem: ITreeItem): void { treeItem.children = treeItem.children ? treeItem.children : undefined; if (current) { - const properties = distinct([...Object.keys(current), ...Object.keys(treeItem)]); + const properties = distinct([...Object.keys(current instanceof ResolvableTreeItem ? current.asTreeItem() : current), + ...Object.keys(treeItem)]); for (const property of properties) { (current)[property] = (treeItem)[property]; } + if (current instanceof ResolvableTreeItem) { + current.resetResolve(); + } } } } diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index 2030fe217..ac9b83562 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -6,8 +6,8 @@ import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { IRemoteExplorerService, MakeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService'; -import { ITunnelProvider, ITunnelService, TunnelOptions } from 'vs/platform/remote/common/tunnel'; +import { IRemoteExplorerService, makeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; @@ -26,8 +26,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun this._register(tunnelService.onTunnelClosed(() => this._proxy.$onDidTunnelsChange())); } - async $openTunnel(tunnelOptions: TunnelOptions): Promise { - const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remoteAddress, tunnelOptions.localAddressPort, tunnelOptions.label); + async $openTunnel(tunnelOptions: TunnelOptions, source: string): Promise { + const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remoteAddress, tunnelOptions.localAddressPort, tunnelOptions.label, source); if (tunnel) { return TunnelDto.fromServiceTunnel(tunnel); } @@ -47,8 +47,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun }); } - async $registerCandidateFinder(): Promise { - this.remoteExplorerService.registerCandidateFinder(() => this._proxy.$findCandidatePorts()); + async $onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise { + this.remoteExplorerService.onFoundNewCandidates(candidates); } async $tunnelServiceReady(): Promise { @@ -57,19 +57,17 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun async $setTunnelProvider(): Promise { const tunnelProvider: ITunnelProvider = { - forwardPort: (tunnelOptions: TunnelOptions) => { - const forward = this._proxy.$forwardPort(tunnelOptions); + forwardPort: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => { + const forward = this._proxy.$forwardPort(tunnelOptions, tunnelCreationOptions); if (forward) { return forward.then(tunnel => { return { tunnelRemotePort: tunnel.remoteAddress.port, tunnelRemoteHost: tunnel.remoteAddress.host, - localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : MakeAddress(tunnel.localAddress.host, tunnel.localAddress.port), + localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : makeAddress(tunnel.localAddress.host, tunnel.localAddress.port), tunnelLocalPort: typeof tunnel.localAddress !== 'string' ? tunnel.localAddress.port : undefined, - dispose: (silent: boolean) => { - if (!silent) { - this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port }); - } + dispose: (silent?: boolean) => { + this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port }, silent); } }; }); @@ -80,22 +78,6 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun this.tunnelService.setTunnelProvider(tunnelProvider); } - async $setCandidateFilter(): Promise { - this._register(this.remoteExplorerService.setCandidateFilter(async (candidates: { host: string, port: number, detail: string }[]): Promise<{ host: string, port: number, detail: string }[]> => { - const filters: boolean[] = await this._proxy.$filterCandidates(candidates); - const filteredCandidates: { host: string, port: number, detail: string }[] = []; - if (filters.length !== candidates.length) { - return candidates; - } - for (let i = 0; i < candidates.length; i++) { - if (filters[i]) { - filteredCandidates.push(candidates[i]); - } - } - return filteredCandidates; - })); - } - dispose(): void { } diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index 561e3c219..6428e6f48 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -9,8 +9,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { MainThreadWebviews, reviveWebviewExtension, reviveWebviewOptions } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; -import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; -import { IEditorInput } from 'vs/workbench/common/editor'; +import { editorGroupToViewColumn, EditorGroupColumn, IEditorInput, viewColumnToEditorGroup } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; @@ -150,7 +149,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc handle: extHostProtocol.WebviewHandle, viewType: string, title: string, - showOptions: { viewColumn?: EditorViewColumn, preserveFocus?: boolean; }, + showOptions: { viewColumn?: EditorGroupColumn, preserveFocus?: boolean; }, options: WebviewInputOptions ): void { const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null); diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index d6a1d810a..6be78f4ec 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -8,8 +8,8 @@ import { forEach } from 'vs/base/common/collections'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import * as resources from 'vs/base/common/resources'; import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation } from 'vs/workbench/common/views'; -import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; +import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation, testViewIcon } from 'vs/workbench/common/views'; +import { CustomTreeView, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { coalesce, } from 'vs/base/common/arrays'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -30,9 +30,8 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES } import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { Codicon } from 'vs/base/common/codicons'; -import { CustomTreeView } from 'vs/workbench/contrib/views/browser/treeView'; import { WebviewViewPane } from 'vs/workbench/contrib/webviewView/browser/webviewViewPane'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export interface IUserFriendlyViewsContainerDescriptor { id: string; @@ -321,7 +320,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { private registerTestViewContainer(): void { const title = localize('test', "Test"); - const icon = Codicon.beaker.classNames; + const icon = testViewIcon; this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, undefined, ViewContainerLocation.Sidebar); } @@ -356,7 +355,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution { private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[], location: ViewContainerLocation): number { containers.forEach(descriptor => { - const icon = resources.joinPath(extension.extensionLocation, descriptor.icon); + const themeIcon = ThemeIcon.fromString(descriptor.icon); + + const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon); const id = `workbench.view.extension.${descriptor.id}`; const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, extension.identifier, location); @@ -376,7 +377,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return order; } - private registerCustomViewContainer(id: string, title: string, icon: URI | string, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer { + private registerCustomViewContainer(id: string, title: string, icon: URI | ThemeIcon, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer { let viewContainer = this.viewContainersRegistry.get(id); if (!viewContainer) { @@ -470,7 +471,11 @@ class ViewsExtensionHandler implements IWorkbenchContribution { ? container.viewOrderDelegate.getOrder(item.group) : undefined; - const icon = item.icon ? resources.joinPath(extension.description.extensionLocation, item.icon) : undefined; + let icon: ThemeIcon | URI | undefined; + if (typeof item.icon === 'string') { + icon = ThemeIcon.fromString(item.icon) || resources.joinPath(extension.description.extensionLocation, item.icon); + } + const initialVisibility = this.convertInitialVisibility(item.visibility); const type = this.getViewType(item.type); diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index eaf1c90ec..7670549c6 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -3,20 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type * as vscode from 'vscode'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands'; -import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; -import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; -import { IWorkspacesService, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces'; -import { Schemas } from 'vs/base/common/network'; +import { IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; +import { IWorkspacesService, IRecent } from 'vs/platform/workspaces/common/workspaces'; import { ILogService } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IViewDescriptorService, IViewsService, ViewVisibilityState } from 'vs/workbench/common/views'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; // ----------------------------------------------------------------- // The following commands are registered on both sides separately. @@ -35,41 +31,6 @@ function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) => }; } -interface IOpenFolderAPICommandOptions { - forceNewWindow?: boolean; - forceReuseWindow?: boolean; - noRecentEntry?: boolean; -} - -export class OpenFolderAPICommand { - public static readonly ID = 'vscode.openFolder'; - public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Promise; - public static execute(executor: ICommandsExecutor, uri?: URI, options?: IOpenFolderAPICommandOptions): Promise; - public static execute(executor: ICommandsExecutor, uri?: URI, arg: boolean | IOpenFolderAPICommandOptions = {}): Promise { - if (typeof arg === 'boolean') { - arg = { forceNewWindow: arg }; - } - if (!uri) { - return executor.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg.forceNewWindow }); - } - const options: IOpenWindowOptions = { forceNewWindow: arg.forceNewWindow, forceReuseWindow: arg.forceReuseWindow, noRecentEntry: arg.noRecentEntry }; - uri = URI.revive(uri); - const uriToOpen: IWindowOpenable = (hasWorkspaceFileExtension(uri) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri }; - return executor.executeCommand('_files.windowOpen', [uriToOpen], options); - } -} -CommandsRegistry.registerCommand({ - id: OpenFolderAPICommand.ID, - handler: adjustHandler(OpenFolderAPICommand.execute), - description: { - description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.', - args: [ - { name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || value instanceof URI }, - { name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Wheter the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' } - ] - } -}); - interface INewWindowAPICommandOptions { reuseWindow?: boolean; remoteAuthority?: string; @@ -96,67 +57,6 @@ CommandsRegistry.registerCommand({ } }); -export class DiffAPICommand { - public static readonly ID = 'vscode.diff'; - public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: typeConverters.TextEditorOpenOptions): Promise { - return executor.executeCommand('_workbench.diff', [ - left, right, - label, - undefined, - typeConverters.TextEditorOpenOptions.from(options), - options ? typeConverters.ViewColumn.from(options.viewColumn) : undefined - ]); - } -} -CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute)); - -export class OpenAPICommand { - public static readonly ID = 'vscode.open'; - public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | typeConverters.TextEditorOpenOptions, label?: string): Promise { - let options: ITextEditorOptions | undefined; - let position: EditorViewColumn | undefined; - - if (columnOrOptions) { - if (typeof columnOrOptions === 'number') { - position = typeConverters.ViewColumn.from(columnOrOptions); - } else { - options = typeConverters.TextEditorOpenOptions.from(columnOrOptions); - position = typeConverters.ViewColumn.from(columnOrOptions.viewColumn); - } - } - - return executor.executeCommand('_workbench.open', [ - resource, - options, - position, - label - ]); - } -} -CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute)); - -export class OpenWithAPICommand { - public static readonly ID = 'vscode.openWith'; - public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, columnOrOptions?: vscode.ViewColumn | typeConverters.TextEditorOpenOptions): Promise { - let options: ITextEditorOptions | undefined; - let position: EditorViewColumn | undefined; - - if (typeof columnOrOptions === 'number') { - position = typeConverters.ViewColumn.from(columnOrOptions); - } else if (typeof columnOrOptions !== 'undefined') { - options = typeConverters.TextEditorOpenOptions.from(columnOrOptions); - } - - return executor.executeCommand('_workbench.openWith', [ - resource, - viewType, - options, - position - ]); - } -} -CommandsRegistry.registerCommand(OpenWithAPICommand.ID, adjustHandler(OpenWithAPICommand.execute)); - CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, uri: URI) { const workspacesService = accessor.get(IWorkspacesService); return workspacesService.removeRecentlyOpened([uri]); @@ -219,38 +119,6 @@ CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function return workspacesService.getRecentlyOpened(); }); -export class SetEditorLayoutAPICommand { - public static readonly ID = 'vscode.setEditorLayout'; - public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Promise { - return executor.executeCommand('layoutEditorGroups', layout); - } -} -CommandsRegistry.registerCommand({ - id: SetEditorLayoutAPICommand.ID, - handler: adjustHandler(SetEditorLayoutAPICommand.execute), - description: { - description: 'Set Editor Layout', - args: [{ - name: 'args', - schema: { - 'type': 'object', - 'required': ['groups'], - 'properties': { - 'orientation': { - 'type': 'number', - 'default': 0, - 'enum': [0, 1] - }, - 'groups': { - '$ref': '#/definitions/editorGroupsSchema', // defined in keybindingService.ts ... - 'default': [{}, {}], - } - } - } - }] - } -}); - CommandsRegistry.registerCommand('_extensionTests.setLogLevel', function (accessor: ServicesAccessor, level: number) { const logService = accessor.get(ILogService); const environmentService = accessor.get(IEnvironmentService); @@ -260,6 +128,13 @@ CommandsRegistry.registerCommand('_extensionTests.setLogLevel', function (access } }); +CommandsRegistry.registerCommand('_workbench.openExternal', function (accessor: ServicesAccessor, uri: UriComponents, options: { allowTunneling?: boolean }) { + // TODO: discuss martin, ben where to put this + const openerService = accessor.get(IOpenerService); + openerService.open(URI.revive(uri), options); +}); + + CommandsRegistry.registerCommand('_extensionTests.getLogLevel', function (accessor: ServicesAccessor) { const logService = accessor.get(ILogService); @@ -304,3 +179,31 @@ CommandsRegistry.registerCommand({ args: [] } }); + + +// ----------------------------------------------------------------- +// The following commands are registered on the renderer but as API +// command. DO NOT USE this unless you have understood what this +// means +// ----------------------------------------------------------------- + + +class OpenAPICommand { + public static readonly ID = 'vscode.open'; + public static execute(executor: ICommandsExecutor, resource: URI): Promise { + + return executor.executeCommand('_workbench.open', resource); + } +} +CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute)); + +class DiffAPICommand { + public static readonly ID = 'vscode.diff'; + public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: typeConverters.TextEditorOpenOptions): Promise { + return executor.executeCommand('_workbench.diff', [ + left, right, + label, + ]); + } +} +CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute)); diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index 83c0ab29e..aab5275fd 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -28,6 +28,10 @@ const configurationEntrySchema: IJSONSchema = { properties: { description: nls.localize('vscode.extension.contributes.configuration.properties', 'Description of the configuration properties.'), type: 'object', + propertyNames: { + pattern: '\\S+', + patternErrorMessage: nls.localize('vscode.extension.contributes.configuration.property.empty', 'Property should not be empty.'), + }, additionalProperties: { anyOf: [ { $ref: 'http://json-schema.org/draft-07/schema#' }, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 284c6aff8..fa880fcfc 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -51,7 +51,7 @@ import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { originalFSPath } from 'vs/base/common/resources'; import { values } from 'vs/base/common/collections'; @@ -81,6 +81,7 @@ import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEdito import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels'; import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits'; import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; +import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -152,6 +153,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostWebviewPanels = rpcProtocol.set(ExtHostContext.ExtHostWebviewPanels, new ExtHostWebviewPanels(rpcProtocol, extHostWebviews, extHostWorkspace)); const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews, extHostWebviewPanels)); const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews)); + const extHostTesting = rpcProtocol.set(ExtHostContext.ExtHostTesting, new ExtHostTesting(rpcProtocol, extHostDocumentsAndEditors, extHostWorkspace)); // Check that no named customers are missing const expected: ProxyIdentifier[] = values(ExtHostContext); @@ -201,40 +203,50 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I })(); const authentication: typeof vscode.authentication = { - registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable { - return extHostAuthentication.registerAuthenticationProvider(provider); - }, - get onDidChangeAuthenticationProviders(): Event { - return extHostAuthentication.onDidChangeAuthenticationProviders; - }, - getProviderIds(): Thenable> { - return extHostAuthentication.getProviderIds(); - }, - get providerIds(): string[] { - return extHostAuthentication.providerIds; - }, - get providers(): ReadonlyArray { - return extHostAuthentication.providers; - }, getSession(providerId: string, scopes: string[], options?: vscode.AuthenticationGetSessionOptions) { return extHostAuthentication.getSession(extension, providerId, scopes, options as any); }, - logout(providerId: string, sessionId: string): Thenable { - return extHostAuthentication.logout(providerId, sessionId); - }, get onDidChangeSessions(): Event { return extHostAuthentication.onDidChangeSessions; }, + registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable { + checkProposedApiEnabled(extension); + return extHostAuthentication.registerAuthenticationProvider(provider); + }, + get onDidChangeAuthenticationProviders(): Event { + checkProposedApiEnabled(extension); + return extHostAuthentication.onDidChangeAuthenticationProviders; + }, + getProviderIds(): Thenable> { + checkProposedApiEnabled(extension); + return extHostAuthentication.getProviderIds(); + }, + get providerIds(): string[] { + checkProposedApiEnabled(extension); + return extHostAuthentication.providerIds; + }, + get providers(): ReadonlyArray { + checkProposedApiEnabled(extension); + return extHostAuthentication.providers; + }, + logout(providerId: string, sessionId: string): Thenable { + checkProposedApiEnabled(extension); + return extHostAuthentication.logout(providerId, sessionId); + }, getPassword(key: string): Thenable { + checkProposedApiEnabled(extension); return extHostAuthentication.getPassword(extension, key); }, setPassword(key: string, value: string): Thenable { + checkProposedApiEnabled(extension); return extHostAuthentication.setPassword(extension, key, value); }, deletePassword(key: string): Thenable { + checkProposedApiEnabled(extension); return extHostAuthentication.deletePassword(extension, key); }, get onDidChangePassword(): Event { + checkProposedApiEnabled(extension); return extHostAuthentication.onDidChangePassword; } }; @@ -293,14 +305,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get appName() { return initData.environment.appName; }, get appRoot() { return initData.environment.appRoot?.fsPath ?? ''; }, get uriScheme() { return initData.environment.appUriScheme; }, - get logLevel() { - checkProposedApiEnabled(extension); - return typeConverters.LogLevel.to(extHostLogService.getLevel()); - }, - get onDidChangeLogLevel() { - checkProposedApiEnabled(extension); - return Event.map(extHostLogService.onDidChangeLogLevel, l => typeConverters.LogLevel.to(l)); - }, get clipboard(): vscode.Clipboard { return extHostClipboard; }, @@ -333,6 +337,25 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ? extHostTypes.ExtensionKind.Workspace : extHostTypes.ExtensionKind.UI; + const test: typeof vscode.test = { + registerTestProvider(provider) { + checkProposedApiEnabled(extension); + return extHostTesting.registerTestProvider(provider); + }, + createDocumentTestObserver(document) { + checkProposedApiEnabled(extension); + return extHostTesting.createTextDocumentTestObserver(document); + }, + createWorkspaceTestObserver(workspaceFolder) { + checkProposedApiEnabled(extension); + return extHostTesting.createWorkspaceTestObserver(workspaceFolder); + }, + runTests(provider) { + checkProposedApiEnabled(extension); + return extHostTesting.runTests(provider); + }, + }; + // namespace: extensions const extensions: typeof vscode.extensions = { getExtension(extensionId: string): Extension | undefined { @@ -397,9 +420,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); }, - registerOnTypeRenameProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider, stopPattern?: RegExp): vscode.Disposable { - checkProposedApiEnabled(extension); - return extHostLanguageFeatures.registerOnTypeRenameProvider(extension, checkSelector(selector), provider, stopPattern); + registerLinkedEditingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.LinkedEditingRangeProvider): vscode.Disposable { + return extHostLanguageFeatures.registerLinkedEditingRangeProvider(extension, checkSelector(selector), provider); }, registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable { return extHostLanguageFeatures.registerReferenceProvider(extension, checkSelector(selector), provider); @@ -569,7 +591,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I priority = priority; } - return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority, accessibilityInformation); + return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority, accessibilityInformation, extension); }, setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): vscode.Disposable { return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable); @@ -617,9 +639,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => { return extHostCustomEditors.registerCustomEditorProvider(extension, viewType, provider, options); }, - registerDecorationProvider(provider: vscode.FileDecorationProvider) { - checkProposedApiEnabled(extension); - return extHostDecorations.registerDecorationProvider(provider, extension.identifier); + registerFileDecorationProvider(provider: vscode.FileDecorationProvider) { + return extHostDecorations.registerFileDecorationProvider(provider, extension.identifier); }, registerUriHandler(handler: vscode.UriHandler) { return extHostUrls.registerUriHandler(extension.identifier, handler); @@ -667,6 +688,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostNotebook.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables); }, + showNotebookDocument(document, options?) { + checkProposedApiEnabled(extension); + return extHostNotebook.showNotebookDocument(document, options); + } }; // namespace: workspace @@ -835,7 +860,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, openTunnel: (forward: vscode.TunnelOptions) => { checkProposedApiEnabled(extension); - return extHostTunnelService.openTunnel(forward).then(value => { + return extHostTunnelService.openTunnel(extension, forward).then(value => { if (!value) { throw new Error('cannot open tunnel'); } @@ -870,14 +895,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } }; - const comment: typeof vscode.comments = { + // namespace: comments + const comments: typeof vscode.comments = { createCommentController(id: string, label: string) { return extHostComment.createCommentController(extension, id, label); } }; - const comments = comment; - // namespace: debug const debug: typeof vscode.debug = { get activeDebugSession() { @@ -1014,6 +1038,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebook.registerNotebookKernelProvider(extension, selector, provider); }, createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType { + checkProposedApiEnabled(extension); return extHostNotebook.createNotebookEditorDecorationType(options); }, get activeNotebookEditor(): vscode.NotebookEditor | undefined { @@ -1067,40 +1092,46 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // namespaces authentication, commands, + comments, debug, env, extensions, languages, - scm, - comment, - comments, - tasks, notebook, + scm, + tasks, + test, window, workspace, // types Breakpoint: extHostTypes.Breakpoint, + CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall, + CallHierarchyItem: extHostTypes.CallHierarchyItem, + CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall, CancellationTokenSource: CancellationTokenSource, CodeAction: extHostTypes.CodeAction, CodeActionKind: extHostTypes.CodeActionKind, CodeActionTrigger: extHostTypes.CodeActionTrigger, CodeLens: extHostTypes.CodeLens, - CodeInset: extHostTypes.CodeInset, Color: extHostTypes.Color, ColorInformation: extHostTypes.ColorInformation, ColorPresentation: extHostTypes.ColorPresentation, - CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState, + ColorThemeKind: extHostTypes.ColorThemeKind, CommentMode: extHostTypes.CommentMode, + CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState, CompletionItem: extHostTypes.CompletionItem, CompletionItemKind: extHostTypes.CompletionItemKind, CompletionItemTag: extHostTypes.CompletionItemTag, CompletionList: extHostTypes.CompletionList, CompletionTriggerKind: extHostTypes.CompletionTriggerKind, ConfigurationTarget: extHostTypes.ConfigurationTarget, + CustomExecution: extHostTypes.CustomExecution, DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable, - DebugAdapterServer: extHostTypes.DebugAdapterServer, - DebugAdapterNamedPipeServer: extHostTypes.DebugAdapterNamedPipeServer, DebugAdapterInlineImplementation: extHostTypes.DebugAdapterInlineImplementation, + DebugAdapterNamedPipeServer: extHostTypes.DebugAdapterNamedPipeServer, + DebugAdapterServer: extHostTypes.DebugAdapterServer, + DebugConfigurationProviderTriggerKind: extHostTypes.DebugConfigurationProviderTriggerKind, + DebugConsoleMode: extHostTypes.DebugConsoleMode, DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior, Diagnostic: extHostTypes.Diagnostic, DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation, @@ -1117,9 +1148,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I EventEmitter: Emitter, ExtensionKind: extHostTypes.ExtensionKind, ExtensionMode: extHostTypes.ExtensionMode, - ExtensionRuntime: extHostTypes.ExtensionRuntime, - CustomExecution: extHostTypes.CustomExecution, FileChangeType: extHostTypes.FileChangeType, + FileDecoration: extHostTypes.FileDecoration, FileSystemError: extHostTypes.FileSystemError, FileType: files.FileType, FoldingRange: extHostTypes.FoldingRange, @@ -1128,7 +1158,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I Hover: extHostTypes.Hover, IndentAction: languageConfiguration.IndentAction, Location: extHostTypes.Location, - LogLevel: extHostTypes.LogLevel, MarkdownString: extHostTypes.MarkdownString, OverviewRulerLane: OverviewRulerLane, ParameterInformation: extHostTypes.ParameterInformation, @@ -1138,23 +1167,20 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I QuickInputButtons: extHostTypes.QuickInputButtons, Range: extHostTypes.Range, RelativePattern: extHostTypes.RelativePattern, - ResolvedAuthority: extHostTypes.ResolvedAuthority, - RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError, - SemanticTokensLegend: extHostTypes.SemanticTokensLegend, - SemanticTokensBuilder: extHostTypes.SemanticTokensBuilder, - SemanticTokens: extHostTypes.SemanticTokens, - SemanticTokensEdits: extHostTypes.SemanticTokensEdits, - SemanticTokensEdit: extHostTypes.SemanticTokensEdit, Selection: extHostTypes.Selection, SelectionRange: extHostTypes.SelectionRange, + SemanticTokens: extHostTypes.SemanticTokens, + SemanticTokensBuilder: extHostTypes.SemanticTokensBuilder, + SemanticTokensEdit: extHostTypes.SemanticTokensEdit, + SemanticTokensEdits: extHostTypes.SemanticTokensEdits, + SemanticTokensLegend: extHostTypes.SemanticTokensLegend, ShellExecution: extHostTypes.ShellExecution, ShellQuoting: extHostTypes.ShellQuoting, - SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind, SignatureHelp: extHostTypes.SignatureHelp, + SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind, SignatureInformation: extHostTypes.SignatureInformation, SnippetString: extHostTypes.SnippetString, SourceBreakpoint: extHostTypes.SourceBreakpoint, - SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType, StandardTokenType: extHostTypes.StandardTokenType, StatusBarAlignment: extHostTypes.StatusBarAlignment, SymbolInformation: extHostTypes.SymbolInformation, @@ -1174,29 +1200,80 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ThemeColor: extHostTypes.ThemeColor, ThemeIcon: extHostTypes.ThemeIcon, TreeItem: extHostTypes.TreeItem, - TreeItem2: extHostTypes.TreeItem, TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState, + UIKind: UIKind, Uri: URI, ViewColumn: extHostTypes.ViewColumn, WorkspaceEdit: extHostTypes.WorkspaceEdit, - // proposed - CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall, - CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall, - CallHierarchyItem: extHostTypes.CallHierarchyItem, - DebugConsoleMode: extHostTypes.DebugConsoleMode, - DebugConfigurationProviderTriggerKind: extHostTypes.DebugConfigurationProviderTriggerKind, - FileDecoration: extHostTypes.FileDecoration, - UIKind: UIKind, - ColorThemeKind: extHostTypes.ColorThemeKind, - TimelineItem: extHostTypes.TimelineItem, - CellKind: extHostTypes.CellKind, - CellOutputKind: extHostTypes.CellOutputKind, - NotebookCellRunState: extHostTypes.NotebookCellRunState, - NotebookRunState: extHostTypes.NotebookRunState, - NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment, - NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType, - NotebookCellOutput: extHostTypes.NotebookCellOutput, - NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem, + // proposed api types + get RemoteAuthorityResolverError() { + // checkProposedApiEnabled(extension); + return extHostTypes.RemoteAuthorityResolverError; + }, + get ResolvedAuthority() { + // checkProposedApiEnabled(extension); + return extHostTypes.ResolvedAuthority; + }, + get SourceControlInputBoxValidationType() { + // checkProposedApiEnabled(extension); + return extHostTypes.SourceControlInputBoxValidationType; + }, + get ExtensionRuntime() { + // checkProposedApiEnabled(extension); + return extHostTypes.ExtensionRuntime; + }, + get TimelineItem() { + // checkProposedApiEnabled(extension); + return extHostTypes.TimelineItem; + }, + get CellKind() { + // checkProposedApiEnabled(extension); + return extHostTypes.CellKind; + }, + get CellOutputKind() { + // checkProposedApiEnabled(extension); + return extHostTypes.CellOutputKind; + }, + get NotebookCellRunState() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookCellRunState; + }, + get NotebookRunState() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookRunState; + }, + get NotebookCellStatusBarAlignment() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookCellStatusBarAlignment; + }, + get NotebookEditorRevealType() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookEditorRevealType; + }, + get NotebookCellOutput() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookCellOutput; + }, + get NotebookCellOutputItem() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookCellOutputItem; + }, + get LinkedEditingRanges() { + // checkProposedApiEnabled(extension); + return extHostTypes.LinkedEditingRanges; + }, + get TestRunState() { + // checkProposedApiEnabled(extension); + return extHostTypes.TestRunState; + }, + get TestMessageSeverity() { + // checkProposedApiEnabled(extension); + return extHostTypes.TestMessageSeverity; + }, + get TestState() { + // checkProposedApiEnabled(extension); + return extHostTypes.TestState; + }, }; }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 77ef65778..b97a962f6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -36,7 +36,6 @@ import * as statusbar from 'vs/workbench/services/statusbar/common/statusbar'; import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import * as tasks from 'vs/workbench/api/common/shared/tasks'; import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views'; import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug'; @@ -45,10 +44,10 @@ import { ITerminalDimensions, IShellLaunchConfig, ITerminalLaunchError } from 'v import { ActivationKind, ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import * as search from 'vs/workbench/services/search/common/search'; -import { SaveReason } from 'vs/workbench/common/editor'; +import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; -import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; +import { TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -57,7 +56,8 @@ import { Dto } from 'vs/base/common/types'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes'; import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; -import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/storageKeys'; +import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync'; +import { RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -263,21 +263,21 @@ export interface IApplyEditsOptions extends IUndoStopOptions { } export interface ITextDocumentShowOptions { - position?: EditorViewColumn; + position?: EditorGroupColumn; preserveFocus?: boolean; pinned?: boolean; selection?: IRange; } export interface MainThreadBulkEditsShape extends IDisposable { - $tryApplyWorkspaceEdit(workspaceEditDto: IWorkspaceEditDto): Promise; + $tryApplyWorkspaceEdit(workspaceEditDto: IWorkspaceEditDto, undoRedoGroupId?: number): Promise; } export interface MainThreadTextEditorsShape extends IDisposable { $tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise; $registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void; $removeTextEditorDecorationType(key: string): void; - $tryShowEditor(id: string, position: EditorViewColumn): Promise; + $tryShowEditor(id: string, position: EditorGroupColumn): Promise; $tryHideEditor(id: string): Promise; $trySetOptions(id: string, options: ITextEditorConfigurationUpdate): Promise; $trySetDecorations(id: string, key: string, ranges: editorCommon.IDecorationOptions[]): Promise; @@ -292,7 +292,7 @@ export interface MainThreadTextEditorsShape extends IDisposable { export interface MainThreadTreeViewsShape extends IDisposable { $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): void; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem; }): Promise; - $reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise; + $reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise; $setMessage(treeViewId: string, message: string): void; $setTitle(treeViewId: string, title: string, description: string | undefined): void; } @@ -384,7 +384,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void; - $registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[], stopPattern: IRegExpDto | undefined): void; + $registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string, supportsResolve: boolean): void; $registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void; @@ -565,7 +565,7 @@ export interface MainThreadQuickOpenShape extends IDisposable { } export interface MainThreadStatusBarShape extends IDisposable { - $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: ICommandDto | undefined, color: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void; + $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: ICommandDto | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void; $dispose(id: number): void; } @@ -597,7 +597,7 @@ export interface ExtHostEditorInsetsShape { export type WebviewHandle = string; export interface WebviewPanelShowOptions { - readonly viewColumn?: EditorViewColumn; + readonly viewColumn?: EditorGroupColumn; readonly preserveFocus?: boolean; } @@ -661,7 +661,7 @@ export interface WebviewPanelViewStateData { [handle: string]: { readonly active: boolean; readonly visible: boolean; - readonly position: EditorViewColumn; + readonly position: EditorGroupColumn; }; } @@ -673,11 +673,11 @@ export interface ExtHostWebviewsShape { export interface ExtHostWebviewPanelsShape { $onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void; $onDidDisposeWebviewPanel(handle: WebviewHandle): Promise; - $deserializeWebviewPanel(newWebviewHandle: WebviewHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; + $deserializeWebviewPanel(newWebviewHandle: WebviewHandle, viewType: string, title: string, state: any, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; } export interface ExtHostCustomEditorsShape { - $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise; + $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewHandle, viewType: string, title: string, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise; $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>; $disposeCustomDocument(resource: UriComponents, viewType: string): Promise; @@ -741,6 +741,13 @@ export enum NotebookEditorRevealType { InCenterIfOutsideViewport = 2, } +export interface INotebookDocumentShowOptions { + position?: EditorGroupColumn; + preserveFocus?: boolean; + pinned?: boolean; + selection?: ICellRange; +} + export type INotebookCellStatusBarEntryDto = Dto; export interface MainThreadNotebookShape extends IDisposable { @@ -760,6 +767,7 @@ export interface MainThreadNotebookShape extends IDisposable { $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise; $setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise; $tryOpenDocument(uriComponents: UriComponents, viewType?: string): Promise; + $tryShowNotebookDocument(uriComponents: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise; $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise; $registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void; $removeNotebookEditorDecorationType(key: string): void; @@ -866,7 +874,8 @@ export type SCMRawResource = [ string /*tooltip*/, boolean /*strike through*/, boolean /*faded*/, - string /*context value*/ + string /*context value*/, + ICommandDto | undefined /*command*/ ]; export type SCMRawResourceSplice = [ @@ -920,7 +929,7 @@ export interface MainThreadDebugServiceShape extends IDisposable { $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void; $acceptDAError(handle: number, name: string, message: string, stack: string | undefined): void; $acceptDAExit(handle: number, code: number | undefined, signal: string | undefined): void; - $registerDebugConfigurationProvider(type: string, triggerKind: DebugConfigurationProviderTriggerKind, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, hasProvideDaMethod: boolean, handle: number): Promise; + $registerDebugConfigurationProvider(type: string, triggerKind: DebugConfigurationProviderTriggerKind, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, handle: number): Promise; $registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise; $unregisterDebugConfigurationProvider(handle: number): void; $unregisterDebugAdapterDescriptorFactory(handle: number): void; @@ -946,13 +955,12 @@ export interface MainThreadWindowShape extends IDisposable { } export interface MainThreadTunnelServiceShape extends IDisposable { - $openTunnel(tunnelOptions: TunnelOptions): Promise; + $openTunnel(tunnelOptions: TunnelOptions, source: string | undefined): Promise; $closeTunnel(remote: { host: string, port: number }): Promise; $getTunnels(): Promise; - $registerCandidateFinder(): Promise; $setTunnelProvider(): Promise; - $setCandidateFilter(): Promise; $tunnelServiceReady(): Promise; + $onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise; } export interface MainThreadTimelineShape extends IDisposable { @@ -1006,10 +1014,10 @@ export interface ITextEditorAddData { options: IResolvedTextEditorConfiguration; selections: ISelection[]; visibleRanges: IRange[]; - editorPosition: EditorViewColumn | undefined; + editorPosition: EditorGroupColumn | undefined; } export interface ITextEditorPositionData { - [id: string]: EditorViewColumn; + [id: string]: EditorGroupColumn; } export interface IEditorPropertiesChangeData { options: IResolvedTextEditorConfiguration | null; @@ -1139,7 +1147,7 @@ export interface SourceTargetPair { export interface ExtHostFileSystemEventServiceShape { $onFileEvent(events: FileSystemEvents): void; - $onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise; + $onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise; $onDidRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[]): void; } @@ -1307,7 +1315,7 @@ export interface IWorkspaceCellEditDto { export interface IWorkspaceEditDto { edits: Array; - // todo@joh reject should go into rename + // todo@jrieken reject should go into rename rejectReason?: string; } @@ -1395,6 +1403,11 @@ export interface ILanguageWordDefinitionDto { regexFlags: string } +export interface ILinkedEditingRangesDto { + ranges: IRange[]; + wordPattern?: IRegExpDto; +} + export interface ExtHostLanguageFeaturesShape { $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise; $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise; @@ -1407,7 +1420,7 @@ export interface ExtHostLanguageFeaturesShape { $provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; - $provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: IRegExpDto; } | undefined>; + $provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise; $provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise; $resolveCodeAction(handle: number, id: ChainedCacheId, token: CancellationToken): Promise; @@ -1602,7 +1615,6 @@ export interface ExtHostDebugServiceShape { $resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise; $resolveDebugConfigurationWithSubstitutedVariables(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise; $provideDebugConfigurations(handle: number, folder: UriComponents | undefined, token: CancellationToken): Promise; - $legacyDebugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Promise; // TODO@AW legacy $provideDebugAdapter(handle: number, session: IDebugSessionDto): Promise; $acceptDebugSessionStarted(session: IDebugSessionDto): void; $acceptDebugSessionTerminated(session: IDebugSessionDto): void; @@ -1709,7 +1721,6 @@ export interface ExtHostNotebookShape { $resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise; $cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise; - $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise; $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise; $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise; $backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise; @@ -1738,10 +1749,8 @@ export interface MainThreadThemingShape extends IDisposable { } export interface ExtHostTunnelServiceShape { - $findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]>; - $filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise; - $forwardPort(tunnelOptions: TunnelOptions): Promise | undefined; - $closeTunnel(remote: { host: string, port: number }): Promise; + $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise | undefined; + $closeTunnel(remote: { host: string, port: number }, silent?: boolean): Promise; $onDidTunnelsChange(): Promise; } @@ -1749,6 +1758,28 @@ export interface ExtHostTimelineShape { $getTimeline(source: string, uri: UriComponents, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions): Promise; } +export const enum ExtHostTestingResource { + Workspace, + TextDocument +} + +export interface ExtHostTestingShape { + $runTestsForProvider(req: RunTestForProviderRequest): Promise; + $subscribeToTests(resource: ExtHostTestingResource, uri: UriComponents): void; + $unsubscribeFromTests(resource: ExtHostTestingResource, uri: UriComponents): void; + + $acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void; +} + +export interface MainThreadTestingShape { + $registerTestProvider(id: string): void; + $unregisterTestProvider(id: string): void; + $subscribeToDiffs(resource: ExtHostTestingResource, uri: UriComponents): void; + $unsubscribeFromDiffs(resource: ExtHostTestingResource, uri: UriComponents): void; + $publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void; + $runTests(req: RunTestsRequest): Promise; +} + // --- proxy identifiers export const MainContext = { @@ -1798,7 +1829,8 @@ export const MainContext = { MainThreadNotebook: createMainId('MainThreadNotebook'), MainThreadTheming: createMainId('MainThreadTheming'), MainThreadTunnelService: createMainId('MainThreadTunnelService'), - MainThreadTimeline: createMainId('MainThreadTimeline') + MainThreadTimeline: createMainId('MainThreadTimeline'), + MainThreadTesting: createMainId('MainThreadTesting'), }; export const ExtHostContext = { @@ -1841,5 +1873,6 @@ export const ExtHostContext = { ExtHostTheming: createMainId('ExtHostTheming'), ExtHostTunnelService: createMainId('ExtHostTunnelService'), ExtHostAuthentication: createMainId('ExtHostAuthentication'), - ExtHostTimeline: createMainId('ExtHostTimeline') + ExtHostTimeline: createMainId('ExtHostTimeline'), + ExtHostTesting: createMainId('ExtHostTesting'), }; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 26300e195..08b2543a4 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import type * as vscode from 'vscode'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as types from 'vs/workbench/api/common/extHostTypes'; @@ -12,77 +12,17 @@ import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto, IIncomingCallD import * as modes from 'vs/editor/common/modes'; import * as search from 'vs/workbench/contrib/search/common/search'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; -import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; +import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { CustomCodeAction } from 'vs/workbench/api/common/extHostLanguageFeatures'; -import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand, OpenIssueReporter, OpenIssueReporterArgs } from './apiCommands'; -import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { ICommandsExecutor, RemoveFromRecentlyOpenedAPICommand, OpenIssueReporter, OpenIssueReporterArgs } from './apiCommands'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { IRange } from 'vs/editor/common/core/range'; import { IPosition } from 'vs/editor/common/core/position'; import { TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; //#region --- NEW world -export class ApiCommandArgument { - - static readonly Uri = new ApiCommandArgument('uri', 'Uri of a text document', v => URI.isUri(v), v => v); - static readonly Position = new ApiCommandArgument('position', 'A position in a text document', v => types.Position.isPosition(v), typeConverters.Position.from); - static readonly Range = new ApiCommandArgument('range', 'A range in a text document', v => types.Range.isRange(v), typeConverters.Range.from); - - static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof types.CallHierarchyItem, typeConverters.CallHierarchyItem.to); - - constructor( - readonly name: string, - readonly description: string, - readonly validate: (v: V) => boolean, - readonly convert: (v: V) => O - ) { } -} - -export class ApiCommandResult { - - constructor( - readonly description: string, - readonly convert: (v: V, apiArgs: any[]) => O - ) { } -} - -export class ApiCommand { - - constructor( - readonly id: string, - readonly internalId: string, - readonly description: string, - readonly args: ApiCommandArgument[], - readonly result: ApiCommandResult - ) { } - - register(commands: ExtHostCommands): IDisposable { - - return commands.registerCommand(false, this.id, async (...apiArgs) => { - - const internalArgs = this.args.map((arg, i) => { - if (!arg.validate(apiArgs[i])) { - throw new Error(`Invalid argument '${arg.name}' when running '${this.id}', receieved: ${apiArgs[i]}`); - } - return arg.convert(apiArgs[i]); - }); - - const internalResult = await commands.executeCommand(this.internalId, ...internalArgs); - return this.result.convert(internalResult, apiArgs); - }, undefined, this._getCommandHandlerDesc()); - } - - private _getCommandHandlerDesc(): ICommandHandlerDescription { - return { - description: this.description, - args: this.args, - returns: this.result.description - }; - } -} - - const newCommands: ApiCommand[] = [ // -- document highlights new ApiCommand( @@ -189,7 +129,7 @@ const newCommands: ApiCommand[] = [ // -- symbol search new ApiCommand( 'vscode.executeWorkspaceSymbolProvider', '_executeWorkspaceSymbolProvider', 'Execute all workspace symbol providers.', - [new ApiCommandArgument('query', 'Search string', v => typeof v === 'string', v => v)], + [ApiCommandArgument.String.with('query', 'Search string')], new ApiCommandResult<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][], types.SymbolInformation[]>('A promise that resolves to an array of SymbolInformation-instances.', value => { const result: types.SymbolInformation[] = []; if (Array.isArray(value)) { @@ -219,7 +159,7 @@ const newCommands: ApiCommand[] = [ // --- rename new ApiCommand( 'vscode.executeDocumentRenameProvider', '_executeDocumentRenameProvider', 'Execute rename provider.', - [ApiCommandArgument.Uri, ApiCommandArgument.Position, new ApiCommandArgument('newName', 'The new symbol name', v => typeof v === 'string', v => v)], + [ApiCommandArgument.Uri, ApiCommandArgument.Position, ApiCommandArgument.String.with('newName', 'The new symbol name')], new ApiCommandResult('A promise that resolves to a WorkspaceEdit.', value => { if (!value) { return undefined; @@ -233,9 +173,169 @@ const newCommands: ApiCommand[] = [ // --- links new ApiCommand( 'vscode.executeLinkProvider', '_executeLinkProvider', 'Execute document link provider.', - [ApiCommandArgument.Uri, new ApiCommandArgument('linkResolveCount', '(optional) Number of links that should be resolved, only when links are unresolved.', v => typeof v === 'number' || typeof v === 'undefined', v => v)], + [ApiCommandArgument.Uri, ApiCommandArgument.Number.with('linkResolveCount', 'Number of links that should be resolved, only when links are unresolved.').optional()], new ApiCommandResult('A promise that resolves to an array of DocumentLink-instances.', value => value.map(typeConverters.DocumentLink.to)) - ) + ), + // --- completions + new ApiCommand( + 'vscode.executeCompletionItemProvider', '_executeCompletionItemProvider', 'Execute completion item provider.', + [ + ApiCommandArgument.Uri, + ApiCommandArgument.Position, + ApiCommandArgument.String.with('triggerCharacter', 'Trigger completion when the user types the character, like `,` or `(`').optional(), + ApiCommandArgument.Number.with('itemResolveCount', 'Number of completions to resolve (too large numbers slow down completions)').optional() + ], + new ApiCommandResult('A promise that resolves to a CompletionList-instance.', (value, _args, converter) => { + if (!value) { + return new types.CompletionList([]); + } + const items = value.suggestions.map(suggestion => typeConverters.CompletionItem.to(suggestion, converter)); + return new types.CompletionList(items, value.incomplete); + }) + ), + // --- signature help + new ApiCommand( + 'vscode.executeSignatureHelpProvider', '_executeSignatureHelpProvider', 'Execute signature help provider.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position, ApiCommandArgument.String.with('triggerCharacter', 'Trigger signature help when the user types the character, like `,` or `(`').optional()], + new ApiCommandResult('A promise that resolves to SignatureHelp.', value => { + if (value) { + return typeConverters.SignatureHelp.to(value); + } + return undefined; + }) + ), + // --- code lens + new ApiCommand( + 'vscode.executeCodeLensProvider', '_executeCodeLensProvider', 'Execute code lens provider.', + [ApiCommandArgument.Uri, ApiCommandArgument.Number.with('itemResolveCount', 'Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)').optional()], + new ApiCommandResult('A promise that resolves to an array of CodeLens-instances.', (value, _args, converter) => { + return tryMapWith(item => { + return new types.CodeLens(typeConverters.Range.to(item.range), item.command && converter.fromInternal(item.command)); + })(value); + }) + ), + // --- code actions + new ApiCommand( + 'vscode.executeCodeActionProvider', '_executeCodeActionProvider', 'Execute code action provider.', + [ + ApiCommandArgument.Uri, + new ApiCommandArgument('rangeOrSelection', 'Range in a text document. Some refactoring provider requires Selection object.', v => types.Range.isRange(v), v => types.Selection.isSelection(v) ? typeConverters.Selection.from(v) : typeConverters.Range.from(v)), + ApiCommandArgument.String.with('kind', 'Code action kind to return code actions for').optional(), + ApiCommandArgument.Number.with('itemResolveCount', 'Number of code actions to resolve (too large numbers slow down code actions)').optional() + ], + new ApiCommandResult('A promise that resolves to an array of Command-instances.', (value, _args, converter) => { + return tryMapWith((codeAction) => { + if (codeAction._isSynthetic) { + if (!codeAction.command) { + throw new Error('Synthetic code actions must have a command'); + } + return converter.fromInternal(codeAction.command); + } else { + const ret = new types.CodeAction( + codeAction.title, + codeAction.kind ? new types.CodeActionKind(codeAction.kind) : undefined + ); + if (codeAction.edit) { + ret.edit = typeConverters.WorkspaceEdit.to(codeAction.edit); + } + if (codeAction.command) { + ret.command = converter.fromInternal(codeAction.command); + } + ret.isPreferred = codeAction.isPreferred; + return ret; + } + })(value); + }) + ), + // --- colors + new ApiCommand( + 'vscode.executeDocumentColorProvider', '_executeDocumentColorProvider', 'Execute document color provider.', + [ApiCommandArgument.Uri], + new ApiCommandResult('A promise that resolves to an array of ColorInformation objects.', result => { + if (result) { + return result.map(ci => new types.ColorInformation(typeConverters.Range.to(ci.range), typeConverters.Color.to(ci.color))); + } + return []; + }) + ), + new ApiCommand( + 'vscode.executeColorPresentationProvider', '_executeColorPresentationProvider', 'Execute color presentation provider.', + [ + new ApiCommandArgument('color', 'The color to show and insert', v => v instanceof types.Color, typeConverters.Color.from), + new ApiCommandArgument<{ uri: URI, range: types.Range; }, { uri: URI, range: IRange; }>('context', 'Context object with uri and range', _v => true, v => ({ uri: v.uri, range: typeConverters.Range.from(v.range) })), + ], + new ApiCommandResult('A promise that resolves to an array of ColorPresentation objects.', result => { + if (result) { + return result.map(typeConverters.ColorPresentation.to); + } + return []; + }) + ), + // --- notebooks + new ApiCommand( + 'vscode.resolveNotebookContentProviders', '_resolveNotebookContentProvider', 'Resolve Notebook Content Providers', + [ + new ApiCommandArgument('viewType', '', v => typeof v === 'string', v => v), + new ApiCommandArgument('displayName', '', v => typeof v === 'string', v => v), + new ApiCommandArgument('options', '', v => typeof v === 'object', v => v), + ], + new ApiCommandResult<{ + viewType: string; + displayName: string; + options: { transientOutputs: boolean; transientMetadata: TransientMetadata }; + filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[] + }[], { + viewType: string; + displayName: string; + filenamePattern: vscode.NotebookFilenamePattern[]; + options: vscode.NotebookDocumentContentOptions; + }[] | undefined>('A promise that resolves to an array of NotebookContentProvider static info objects.', tryMapWith(item => { + return { + viewType: item.viewType, + displayName: item.displayName, + options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata }, + filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern)) + }; + })) + ), + // --- open'ish commands + new ApiCommand( + 'vscode.open', '_workbench.open', 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.', + [ + ApiCommandArgument.Uri, + new ApiCommandArgument('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', + v => v === undefined || typeof v === 'number' || typeof v === 'object', + v => !v ? v : typeof v === 'number' ? [v, undefined] : [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)] + ).optional(), + ApiCommandArgument.String.with('label', '').optional() + ], + ApiCommandResult.Void + ), + new ApiCommand( + 'vscode.openWith', '_workbench.openWith', 'Opens the provided resource with a specific editor.', + [ + ApiCommandArgument.Uri.with('resource', 'Resource to open'), + ApiCommandArgument.String.with('viewId', 'Custom editor view id or \'default\' to use VS Code\'s default editor'), + new ApiCommandArgument('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', + v => v === undefined || typeof v === 'number' || typeof v === 'object', + v => !v ? v : typeof v === 'number' ? [v, undefined] : [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)], + ).optional() + ], + ApiCommandResult.Void + ), + new ApiCommand( + 'vscode.diff', '_workbench.diff', 'Opens the provided resources in the diff editor to compare their contents.', + [ + ApiCommandArgument.Uri.with('left', 'Left-hand side resource of the diff editor'), + ApiCommandArgument.Uri.with('right', 'Rigth-hand side resource of the diff editor'), + ApiCommandArgument.String.with('title', 'Human readable title for the diff editor').optional(), + new ApiCommandArgument('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', + v => v === undefined || typeof v === 'object', + v => v && [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)] + ).optional(), + ], + ApiCommandResult.Void + ), ]; //#endregion @@ -246,7 +346,7 @@ const newCommands: ApiCommand[] = [ export class ExtHostApiCommands { static register(commands: ExtHostCommands) { - newCommands.forEach(command => command.register(commands)); + newCommands.forEach(commands.registerApiCommand, commands); return new ExtHostApiCommands(commands).registerCommands(); } @@ -258,66 +358,10 @@ export class ExtHostApiCommands { } registerCommands() { - this._register('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider, { - description: 'Execute signature help provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position in a text document', constraint: types.Position }, - { name: 'triggerCharacter', description: '(optional) Trigger signature help when the user types the character, like `,` or `(`', constraint: (value: any) => value === undefined || typeof value === 'string' } - ], - returns: 'A promise that resolves to SignatureHelp.' - }); - this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, { - description: 'Execute completion item provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position in a text document', constraint: types.Position }, - { name: 'triggerCharacter', description: '(optional) Trigger completion when the user types the character, like `,` or `(`', constraint: (value: any) => value === undefined || typeof value === 'string' }, - { name: 'itemResolveCount', description: '(optional) Number of completions to resolve (too large numbers slow down completions)', constraint: (value: any) => value === undefined || typeof value === 'number' } - ], - returns: 'A promise that resolves to a CompletionList-instance.' - }); - this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider, { - description: 'Execute code action provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'rangeOrSelection', description: 'Range in a text document. Some refactoring provider requires Selection object.', constraint: types.Range }, - { name: 'kind', description: '(optional) Code action kind to return code actions for', constraint: (value: any) => !value || typeof value.value === 'string' }, - { name: 'itemResolveCount', description: '(optional) Number of code actions to resolve (too large numbers slow down code actions)', constraint: (value: any) => value === undefined || typeof value === 'number' } - ], - returns: 'A promise that resolves to an array of Command-instances.' - }); - this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider, { - description: 'Execute CodeLens provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'itemResolveCount', description: '(optional) Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)', constraint: (value: any) => value === undefined || typeof value === 'number' } - ], - returns: 'A promise that resolves to an array of CodeLens-instances.' - }); - this._register('vscode.executeDocumentColorProvider', this._executeDocumentColorProvider, { - description: 'Execute document color provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - ], - returns: 'A promise that resolves to an array of ColorInformation objects.' - }); - this._register('vscode.executeColorPresentationProvider', this._executeColorPresentationProvider, { - description: 'Execute color presentation provider.', - args: [ - { name: 'color', description: 'The color to show and insert', constraint: types.Color }, - { name: 'context', description: 'Context object with uri and range' } - ], - returns: 'A promise that resolves to an array of ColorPresentation objects.' - }); - this._register('vscode.resolveNotebookContentProviders', this._resolveNotebookContentProviders, { - description: 'Resolve Notebook Content Providers', - args: [], - returns: 'A promise that resolves to an array of NotebookContentProvider static info objects.' - }); + // ----------------------------------------------------------------- // The following commands are registered on both sides separately. @@ -333,32 +377,6 @@ export class ExtHostApiCommands { }; }; - this._register(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute), { - description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.', - args: [ - { name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || URI.isUri(value) }, - { name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Whether the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' } - ] - }); - - this._register(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute), { - description: 'Opens the provided resources in the diff editor to compare their contents.', - args: [ - { name: 'left', description: 'Left-hand side resource of the diff editor', constraint: URI }, - { name: 'right', description: 'Right-hand side resource of the diff editor', constraint: URI }, - { name: 'title', description: '(optional) Human readable title for the diff editor', constraint: (v: any) => v === undefined || typeof v === 'string' }, - { name: 'options', description: '(optional) Editor options, see vscode.TextDocumentShowOptions' } - ] - }); - - this._register(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute), { - description: 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.', - args: [ - { name: 'resource', description: 'Resource to open', constraint: URI }, - { name: 'columnOrOptions', description: '(optional) Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', constraint: (v: any) => v === undefined || typeof v === 'number' || typeof v === 'object' } - ] - }); - this._register(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute), { description: 'Removes an entry with the given path from the recently opened list.', args: [ @@ -366,13 +384,6 @@ export class ExtHostApiCommands { ] }); - this._register(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute), { - description: 'Sets the editor layout. The layout is described as object with an initial (optional) orientation (0 = horizontal, 1 = vertical) and an array of editor groups within. Each editor group can have a size and another array of editor groups that will be laid out orthogonal to the orientation. If editor group sizes are provided, their sum must be 1 to be applied per row or column. Example for a 2x2 grid: `{ orientation: 0, groups: [{ groups: [{}, {}], size: 0.5 }, { groups: [{}, {}], size: 0.5 }] }`', - args: [ - { name: 'layout', description: 'The editor layout to set.', constraint: (value: EditorGroupLayout) => typeof value === 'object' && Array.isArray(value.groups) } - ] - }); - this._register(OpenIssueReporter.ID, adjustHandler(OpenIssueReporter.execute), { description: 'Opens the issue reporter with the provided extension id as the selected source', args: [ @@ -383,132 +394,16 @@ export class ExtHostApiCommands { // --- command impl + /** + * @deprecated use the ApiCommand instead + */ private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void { const disposable = this._commands.registerCommand(false, id, handler, this, description); this._disposables.add(disposable); } - private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position), - triggerCharacter - }; - return this._commands.executeCommand('_executeSignatureHelpProvider', args).then(value => { - if (value) { - return typeConverters.SignatureHelp.to(value); - } - return undefined; - }); - } - - private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string, maxItemsToResolve: number): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position), - triggerCharacter, - maxItemsToResolve - }; - return this._commands.executeCommand('_executeCompletionItemProvider', args).then(result => { - if (result) { - const items = result.suggestions.map(suggestion => typeConverters.CompletionItem.to(suggestion, this._commands.converter)); - return new types.CompletionList(items, result.incomplete); - } - return undefined; - }); - } - - private _executeDocumentColorProvider(resource: URI): Promise { - const args = { - resource - }; - return this._commands.executeCommand('_executeDocumentColorProvider', args).then(result => { - if (result) { - return result.map(ci => ({ range: typeConverters.Range.to(ci.range), color: typeConverters.Color.to(ci.color) })); - } - return []; - }); - } - - private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range; }): Promise { - const args = { - resource: context.uri, - color: typeConverters.Color.from(color), - range: typeConverters.Range.from(context.range), - }; - return this._commands.executeCommand('_executeColorPresentationProvider', args).then(result => { - if (result) { - return result.map(typeConverters.ColorPresentation.to); - } - return []; - }); - } - private _executeCodeActionProvider(resource: URI, rangeOrSelection: types.Range | types.Selection, kind?: string, itemResolveCount?: number): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> { - const args = { - resource, - rangeOrSelection: types.Selection.isSelection(rangeOrSelection) - ? typeConverters.Selection.from(rangeOrSelection) - : typeConverters.Range.from(rangeOrSelection), - kind, - itemResolveCount, - }; - return this._commands.executeCommand('_executeCodeActionProvider', args) - .then(tryMapWith(codeAction => { - if (codeAction._isSynthetic) { - if (!codeAction.command) { - throw new Error('Synthetic code actions must have a command'); - } - return this._commands.converter.fromInternal(codeAction.command); - } else { - const ret = new types.CodeAction( - codeAction.title, - codeAction.kind ? new types.CodeActionKind(codeAction.kind) : undefined - ); - if (codeAction.edit) { - ret.edit = typeConverters.WorkspaceEdit.to(codeAction.edit); - } - if (codeAction.command) { - ret.command = this._commands.converter.fromInternal(codeAction.command); - } - ret.isPreferred = codeAction.isPreferred; - return ret; - } - })); - } - - private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise { - const args = { resource, itemResolveCount }; - return this._commands.executeCommand('_executeCodeLensProvider', args) - .then(tryMapWith(item => { - return new types.CodeLens( - typeConverters.Range.to(item.range), - item.command ? this._commands.converter.fromInternal(item.command) : undefined); - })); - } - - private _resolveNotebookContentProviders(): Promise<{ - viewType: string; - displayName: string; - filenamePattern: vscode.NotebookFilenamePattern[]; - options: vscode.NotebookDocumentContentOptions; - }[] | undefined> { - return this._commands.executeCommand<{ - viewType: string; - displayName: string; - options: { transientOutputs: boolean; transientMetadata: TransientMetadata }; - filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[] - }[]>('_resolveNotebookContentProvider') - .then(tryMapWith(item => { - return { - viewType: item.viewType, - displayName: item.displayName, - options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata }, - filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern)) - }; - })); - } } function tryMapWith(f: (x: T) => R) { diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index c20a7a42b..649612e38 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -14,12 +14,13 @@ import * as modes from 'vs/editor/common/modes'; import type * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; import { revive } from 'vs/base/common/marshalling'; -import { Range } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { URI } from 'vs/base/common/uri'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { ISelection } from 'vs/editor/common/core/selection'; interface CommandHandler { callback: Function; @@ -36,18 +37,32 @@ export class ExtHostCommands implements ExtHostCommandsShape { readonly _serviceBrand: undefined; private readonly _commands = new Map(); + private readonly _apiCommands = new Map(); + private readonly _proxy: MainThreadCommandsShape; - private readonly _converter: CommandsConverter; private readonly _logService: ILogService; private readonly _argumentProcessors: ArgumentProcessor[]; + readonly converter: CommandsConverter; + constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @ILogService logService: ILogService ) { this._proxy = extHostRpc.getProxy(MainContext.MainThreadCommands); this._logService = logService; - this._converter = new CommandsConverter(this, logService); + this.converter = new CommandsConverter( + this, + id => { + // API commands that have no return type (void) can be + // converted to their internal command and don't need + // any indirection commands + const candidate = this._apiCommands.get(id); + return candidate?.result === ApiCommandResult.Void + ? candidate : undefined; + }, + logService + ); this._argumentProcessors = [ { processArgument(a) { @@ -77,14 +92,38 @@ export class ExtHostCommands implements ExtHostCommandsShape { ]; } - get converter(): CommandsConverter { - return this._converter; - } - registerArgumentProcessor(processor: ArgumentProcessor): void { this._argumentProcessors.push(processor); } + registerApiCommand(apiCommand: ApiCommand): extHostTypes.Disposable { + + + const registration = this.registerCommand(false, apiCommand.id, async (...apiArgs) => { + + const internalArgs = apiCommand.args.map((arg, i) => { + if (!arg.validate(apiArgs[i])) { + throw new Error(`Invalid argument '${arg.name}' when running '${apiCommand.id}', receieved: ${apiArgs[i]}`); + } + return arg.convert(apiArgs[i]); + }); + + const internalResult = await this.executeCommand(apiCommand.internalId, ...internalArgs); + return apiCommand.result.convert(internalResult, apiArgs, this.converter); + }, undefined, { + description: apiCommand.description, + args: apiCommand.args, + returns: apiCommand.result.description + }); + + this._apiCommands.set(apiCommand.id, apiCommand); + + return new extHostTypes.Disposable(() => { + registration.dispose(); + this._apiCommands.delete(apiCommand.id); + }); + } + registerCommand(global: boolean, id: string, callback: (...args: any[]) => T | Thenable, thisArg?: any, description?: ICommandHandlerDescription): extHostTypes.Disposable { this._logService.trace('ExtHostCommands#registerCommand', id); @@ -214,6 +253,8 @@ export class ExtHostCommands implements ExtHostCommandsShape { } } +export interface IExtHostCommands extends ExtHostCommands { } +export const IExtHostCommands = createDecorator('IExtHostCommands'); export class CommandsConverter { @@ -224,6 +265,7 @@ export class CommandsConverter { // --- conversion between internal and api commands constructor( private readonly _commands: ExtHostCommands, + private readonly _lookupApiCommand: (id: string) => ApiCommand | undefined, private readonly _logService: ILogService ) { this._delegatingCommandId = `_vscode_delegate_cmd_${Date.now().toString(36)}`; @@ -245,7 +287,20 @@ export class CommandsConverter { tooltip: command.tooltip }; - if (command.command && isNonEmptyArray(command.arguments)) { + if (!command.command) { + // falsy command id -> return converted command but don't attempt any + // argument or API-command dance since this command won't run anyways + return result; + } + + const apiCommand = this._lookupApiCommand(command.command); + if (apiCommand) { + // API command with return-value can be converted inplace + result.id = apiCommand.internalId; + result.arguments = apiCommand.args.map((arg, i) => arg.convert(command.arguments && command.arguments[i])); + + + } else if (isNonEmptyArray(command.arguments)) { // we have a contributed command with arguments. that // means we don't want to send the arguments around @@ -293,5 +348,55 @@ export class CommandsConverter { } -export interface IExtHostCommands extends ExtHostCommands { } -export const IExtHostCommands = createDecorator('IExtHostCommands'); + +export class ApiCommandArgument { + + static readonly Uri = new ApiCommandArgument('uri', 'Uri of a text document', v => URI.isUri(v), v => v); + static readonly Position = new ApiCommandArgument('position', 'A position in a text document', v => extHostTypes.Position.isPosition(v), extHostTypeConverter.Position.from); + static readonly Range = new ApiCommandArgument('range', 'A range in a text document', v => extHostTypes.Range.isRange(v), extHostTypeConverter.Range.from); + static readonly Selection = new ApiCommandArgument('selection', 'A selection in a text document', v => extHostTypes.Selection.isSelection(v), extHostTypeConverter.Selection.from); + static readonly Number = new ApiCommandArgument('number', '', v => typeof v === 'number', v => v); + static readonly String = new ApiCommandArgument('string', '', v => typeof v === 'string', v => v); + + static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof extHostTypes.CallHierarchyItem, extHostTypeConverter.CallHierarchyItem.to); + + constructor( + readonly name: string, + readonly description: string, + readonly validate: (v: V) => boolean, + readonly convert: (v: V) => O + ) { } + + optional(): ApiCommandArgument { + return new ApiCommandArgument( + this.name, `(optional) ${this.description}`, + value => value === undefined || value === null || this.validate(value), + value => value === undefined ? undefined : value === null ? null : this.convert(value) + ); + } + + with(name: string | undefined, description: string | undefined): ApiCommandArgument { + return new ApiCommandArgument(name ?? this.name, description ?? this.description, this.validate, this.convert); + } +} + +export class ApiCommandResult { + + static readonly Void = new ApiCommandResult('no result', v => v); + + constructor( + readonly description: string, + readonly convert: (v: V, apiArgs: any[], cmdConverter: CommandsConverter) => O + ) { } +} + +export class ApiCommand { + + constructor( + readonly id: string, + readonly internalId: string, + readonly description: string, + readonly args: ApiCommandArgument[], + readonly result: ApiCommandResult + ) { } +} diff --git a/src/vs/workbench/api/common/extHostCustomEditors.ts b/src/vs/workbench/api/common/extHostCustomEditors.ts index f3b3cbd84..becd0887e 100644 --- a/src/vs/workbench/api/common/extHostCustomEditors.ts +++ b/src/vs/workbench/api/common/extHostCustomEditors.ts @@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview'; import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import { EditorGroupColumn } from 'vs/workbench/common/editor'; import type * as vscode from 'vscode'; import { Cache } from './cache'; import * as extHostProtocol from './extHost.protocol'; @@ -252,7 +252,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor handle: extHostProtocol.WebviewHandle, viewType: string, title: string, - position: EditorViewColumn, + position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken, ): Promise { diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index b724e831c..4026a1d2b 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -23,7 +23,6 @@ import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostC import { convertToVSCPaths, convertToDAPaths, isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ISignService } from 'vs/platform/sign/common/sign'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; @@ -97,7 +96,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E private readonly _onDidChangeBreakpoints: Emitter; - private _aexCommands: Map; private _debugAdapters: Map; private _debugAdaptersTrackers: Map; @@ -105,14 +103,12 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E private _signService: ISignService | undefined; - constructor( @IExtHostRpcService extHostRpcService: IExtHostRpcService, - @IExtHostWorkspace private _workspaceService: IExtHostWorkspace, + @IExtHostWorkspace protected _workspaceService: IExtHostWorkspace, @IExtHostExtensionService private _extensionService: IExtHostExtensionService, @IExtHostDocumentsAndEditors private _editorsService: IExtHostDocumentsAndEditors, @IExtHostConfiguration protected _configurationService: IExtHostConfiguration, - @IExtHostCommands private _commandService: IExtHostCommands ) { this._configProviderHandleCounter = 0; this._configProviders = []; @@ -123,7 +119,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E this._trackerFactoryHandleCounter = 0; this._trackerFactories = []; - this._aexCommands = new Map(); this._debugAdapters = new Map(); this._debugAdaptersTrackers = new Map(); @@ -182,7 +177,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E private registerAllDebugTypes(extensionRegistry: ExtensionDescriptionRegistry) { const debugTypes: string[] = []; - this._aexCommands.clear(); for (const ed of extensionRegistry.getAllExtensionDescriptions()) { if (ed.contributes) { @@ -191,9 +185,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E for (const dbg of debuggers) { if (isDebuggerMainContribution(dbg)) { debugTypes.push(dbg.type); - if (dbg.adapterExecutableCommand) { - this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand); - } } } } @@ -312,10 +303,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E return new Disposable(() => { }); } - if (provider.debugAdapterExecutable) { - console.error('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.'); - } - const handle = this._configProviderHandleCounter++; this._configProviders.push({ type, handle, provider }); @@ -323,7 +310,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E !!provider.provideDebugConfigurations, !!provider.resolveDebugConfiguration, !!provider.resolveDebugConfigurationWithSubstitutedVariables, - !!provider.debugAdapterExecutable, // TODO@AW: deprecated handle); return new Disposable(() => { @@ -651,26 +637,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E }); } - // TODO@AW deprecated and legacy - public $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { - return asPromise(async () => { - const provider = this.getConfigProviderByHandle(configProviderHandle); - if (!provider) { - throw new Error('no DebugConfigurationProvider found'); - } - if (!provider.debugAdapterExecutable) { - throw new Error('DebugConfigurationProvider has no method debugAdapterExecutable'); - } - const folder = await this.getFolder(folderUri); - return provider.debugAdapterExecutable(folder, CancellationToken.None); - }).then(executable => { - if (!executable) { - throw new Error('nothing returned from DebugConfigurationProvider.debugAdapterExecutable'); - } - return this.convertToDto(executable); - }); - } - public async $provideDebugAdapter(adapterFactoryHandle: number, sessionDto: IDebugSessionDto): Promise { const adapterDescriptorFactory = this.getAdapterDescriptorFactoryByHandle(adapterFactoryHandle); if (!adapterDescriptorFactory) { @@ -830,18 +796,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E return Promise.resolve(new DebugAdapterServer(serverPort)); } - // TODO@AW legacy - const pair = this._configProviders.filter(p => p.type === session.type).pop(); - if (pair && pair.provider.debugAdapterExecutable) { - const func = pair.provider.debugAdapterExecutable; - return asPromise(() => func(session.workspaceFolder, CancellationToken.None)).then(executable => { - if (executable) { - return executable; - } - return undefined; - }); - } - if (adapterDescriptorFactory) { const extensionRegistry = await this._extensionService.getExtensionRegistry(); return asPromise(() => adapterDescriptorFactory.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry))).then(daDescriptor => { @@ -852,17 +806,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E }); } - // try deprecated command based extension API "adapterExecutableCommand" to determine the executable - // TODO@AW legacy - const aex = this._aexCommands.get(session.type); - if (aex) { - const folder = session.workspaceFolder; - const rootFolder = folder ? folder.uri.toString() : undefined; - return this._commandService.executeCommand(aex, rootFolder).then((ae: any) => { - return new DebugAdapterExecutable(ae.command, ae.args || []); - }); - } - // fallback: use executable information from package.json const extensionRegistry = await this._extensionService.getExtensionRegistry(); return Promise.resolve(this.daExecutableFromPackage(session, extensionRegistry)); @@ -987,7 +930,7 @@ export class ExtHostDebugConsole implements vscode.DebugConsole { export class ExtHostVariableResolverService extends AbstractVariableResolverService { - constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment) { + constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment, workspaceService?: IExtHostWorkspace) { super({ getFolderUri: (folderName: string): URI | undefined => { const found = folders.filter(f => f.name === folderName); @@ -999,7 +942,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ getWorkspaceFolderCount: (): number => { return folders.length; }, - getConfigurationValue: (folderUri: URI, section: string): string | undefined => { + getConfigurationValue: (folderUri: URI | undefined, section: string): string | undefined => { return configurationService.getConfiguration(undefined, folderUri).get(section); }, getExecPath: (): string | undefined => { @@ -1014,6 +957,18 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ } return undefined; }, + getWorkspaceFolderPathForFile: (): string | undefined => { + if (editorService && workspaceService) { + const activeEditor = editorService.activeEditor(); + if (activeEditor) { + const ws = workspaceService.getWorkspaceFolder(activeEditor.document.uri); + if (ws) { + return path.normalize(ws.uri.fsPath); + } + } + } + return undefined; + }, getSelectedText: (): string | undefined => { if (editorService) { const activeEditor = editorService.activeEditor(); @@ -1032,7 +987,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ } return undefined; } - }, undefined, env, !editorService); + }, undefined, env); } } @@ -1118,10 +1073,9 @@ export class WorkerExtHostDebugService extends ExtHostDebugServiceBase { @IExtHostWorkspace workspaceService: IExtHostWorkspace, @IExtHostExtensionService extensionService: IExtHostExtensionService, @IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors, - @IExtHostConfiguration configurationService: IExtHostConfiguration, - @IExtHostCommands commandService: IExtHostCommands + @IExtHostConfiguration configurationService: IExtHostConfiguration ) { - super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, commandService); + super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService); } protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { diff --git a/src/vs/workbench/api/common/extHostDecorations.ts b/src/vs/workbench/api/common/extHostDecorations.ts index 39ba91473..ea4858374 100644 --- a/src/vs/workbench/api/common/extHostDecorations.ts +++ b/src/vs/workbench/api/common/extHostDecorations.ts @@ -37,12 +37,12 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { this._proxy = extHostRpc.getProxy(MainContext.MainThreadDecorations); } - registerDecorationProvider(provider: vscode.FileDecorationProvider, extensionId: ExtensionIdentifier): vscode.Disposable { + registerFileDecorationProvider(provider: vscode.FileDecorationProvider, extensionId: ExtensionIdentifier): vscode.Disposable { const handle = ExtHostDecorations._handlePool++; this._provider.set(handle, { provider, extensionId }); this._proxy.$registerDecorationProvider(handle, extensionId.value); - const listener = provider.onDidChange(e => { + const listener = provider.onDidChangeFileDecorations && provider.onDidChangeFileDecorations(e => { if (!e) { this._proxy.$onDidChange(handle, null); return; @@ -75,7 +75,7 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { }); return new Disposable(() => { - listener.dispose(); + listener?.dispose(); this._proxy.$unregisterDecorationProvider(handle); this._provider.delete(handle); }); diff --git a/src/vs/workbench/api/common/extHostDocumentContentProviders.ts b/src/vs/workbench/api/common/extHostDocumentContentProviders.ts index eba5e7845..e4d2b49a4 100644 --- a/src/vs/workbench/api/common/extHostDocumentContentProviders.ts +++ b/src/vs/workbench/api/common/extHostDocumentContentProviders.ts @@ -13,6 +13,7 @@ import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors'; import { Schemas } from 'vs/base/common/network'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { splitLines } from 'vs/base/common/strings'; export class ExtHostDocumentContentProvider implements ExtHostDocumentContentProvidersShape { @@ -61,7 +62,7 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro } // create lines and compare - const lines = value.split(/\r\n|\r|\n/); + const lines = splitLines(value); // broadcast event when content changed if (!document.equalLines(lines)) { diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index c576f8f5e..0503a131b 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -178,23 +178,23 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ }; } - async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise { + async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise { switch (operation) { case FileOperation.MOVE: - await this._fireWillEvent(this._onWillRenameFile, { files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) }, timeout, token); + await this._fireWillEvent(this._onWillRenameFile, { files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) }, undoRedoGroupId, timeout, token); break; case FileOperation.DELETE: - await this._fireWillEvent(this._onWillDeleteFile, { files: files.map(f => URI.revive(f.target)) }, timeout, token); + await this._fireWillEvent(this._onWillDeleteFile, { files: files.map(f => URI.revive(f.target)) }, undoRedoGroupId, timeout, token); break; case FileOperation.CREATE: - await this._fireWillEvent(this._onWillCreateFile, { files: files.map(f => URI.revive(f.target)) }, timeout, token); + await this._fireWillEvent(this._onWillCreateFile, { files: files.map(f => URI.revive(f.target)) }, undoRedoGroupId, timeout, token); break; default: //ignore, dont send } } - private async _fireWillEvent(emitter: AsyncEmitter, data: Omit, timeout: number, token: CancellationToken): Promise { + private async _fireWillEvent(emitter: AsyncEmitter, data: Omit, undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise { const edits: WorkspaceEdit[] = []; @@ -222,7 +222,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors); dto.edits = dto.edits.concat(edits); } - return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit(dto); + return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit(dto, undoRedoGroupId); } } } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index b26a4ef04..49cde88e1 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -32,7 +32,6 @@ import { IdGenerator } from 'vs/base/common/idGenerator'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { Cache } from './cache'; import { StopWatch } from 'vs/base/common/stopwatch'; -import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; // --- adapter @@ -117,65 +116,57 @@ class CodeLensAdapter { private readonly _provider: vscode.CodeLensProvider ) { } - provideCodeLenses(resource: URI, token: CancellationToken): Promise { + async provideCodeLenses(resource: URI, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - return asPromise(() => this._provider.provideCodeLenses(doc, token)).then(lenses => { - - if (!lenses || token.isCancellationRequested) { - return undefined; - } - - const cacheId = this._cache.add(lenses); - const disposables = new DisposableStore(); - this._disposables.set(cacheId, disposables); - - const result: extHostProtocol.ICodeLensListDto = { - cacheId, - lenses: [], - }; - - for (let i = 0; i < lenses.length; i++) { - result.lenses.push({ - cacheId: [cacheId, i], - range: typeConvert.Range.from(lenses[i].range), - command: this._commands.toInternal(lenses[i].command, disposables) - }); - } - - return result; - }); + const lenses = await this._provider.provideCodeLenses(doc, token); + if (!lenses || token.isCancellationRequested) { + return undefined; + } + const cacheId = this._cache.add(lenses); + const disposables = new DisposableStore(); + this._disposables.set(cacheId, disposables); + const result: extHostProtocol.ICodeLensListDto = { + cacheId, + lenses: [], + }; + for (let i = 0; i < lenses.length; i++) { + result.lenses.push({ + cacheId: [cacheId, i], + range: typeConvert.Range.from(lenses[i].range), + command: this._commands.toInternal(lenses[i].command, disposables) + }); + } + return result; } - resolveCodeLens(symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise { + async resolveCodeLens(symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise { const lens = symbol.cacheId && this._cache.get(...symbol.cacheId); if (!lens) { - return Promise.resolve(undefined); + return undefined; } - let resolve: Promise; + let resolvedLens: vscode.CodeLens | undefined | null; if (typeof this._provider.resolveCodeLens !== 'function' || lens.isResolved) { - resolve = Promise.resolve(lens); + resolvedLens = lens; } else { - resolve = asPromise(() => this._provider.resolveCodeLens!(lens, token)); + resolvedLens = await this._provider.resolveCodeLens(lens, token); + } + if (!resolvedLens) { + resolvedLens = lens; } - return resolve.then(newLens => { - if (token.isCancellationRequested) { - return undefined; - } - - const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]); - if (!disposables) { - // We've already been disposed of - return undefined; - } - - newLens = newLens || lens; - symbol.command = this._commands.toInternal(newLens.command || CodeLensAdapter._badCmd, disposables); - return symbol; - }); + if (token.isCancellationRequested) { + return undefined; + } + const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]); + if (!disposables) { + // disposed in the meantime + return undefined; + } + symbol.command = this._commands.toInternal(resolvedLens.command ?? CodeLensAdapter._badCmd, disposables); + return symbol; } releaseCodeLenses(cachedId: number): void { @@ -320,18 +311,18 @@ class DocumentHighlightAdapter { } } -class OnTypeRenameAdapter { +class LinkedEditingRangeAdapter { constructor( private readonly _documents: ExtHostDocuments, - private readonly _provider: vscode.OnTypeRenameProvider + private readonly _provider: vscode.LinkedEditingRangeProvider ) { } - provideOnTypeRenameRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: RegExp; } | undefined> { + provideLinkedEditingRanges(resource: URI, position: IPosition, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); const pos = typeConvert.Position.to(position); - return asPromise(() => this._provider.provideOnTypeRenameRanges(doc, pos, token)).then(value => { + return asPromise(() => this._provider.provideLinkedEditingRanges(doc, pos, token)).then(value => { if (value && Array.isArray(value.ranges)) { return { ranges: coalesce(value.ranges.map(typeConvert.Range.from)), @@ -1329,7 +1320,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter - | OnTypeRenameAdapter; + | LinkedEditingRangeAdapter; class AdapterData { constructor( @@ -1571,18 +1562,17 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token), undefined); } - // --- on type rename + // --- linked editing - registerOnTypeRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider, wordPattern?: RegExp): vscode.Disposable { - const handle = this._addNewAdapter(new OnTypeRenameAdapter(this._documents, provider), extension); - const serializedWordPattern = wordPattern ? ExtHostLanguageFeatures._serializeRegExp(wordPattern) : undefined; - this._proxy.$registerOnTypeRenameProvider(handle, this._transformDocumentSelector(selector), serializedWordPattern); + registerLinkedEditingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.LinkedEditingRangeProvider): vscode.Disposable { + const handle = this._addNewAdapter(new LinkedEditingRangeAdapter(this._documents, provider), extension); + this._proxy.$registerLinkedEditingRangeProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } - $provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: extHostProtocol.IRegExpDto; } | undefined> { - return this._withAdapter(handle, OnTypeRenameAdapter, async adapter => { - const res = await adapter.provideOnTypeRenameRanges(URI.revive(resource), position, token); + $provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { + return this._withAdapter(handle, LinkedEditingRangeAdapter, async adapter => { + const res = await adapter.provideLinkedEditingRanges(URI.revive(resource), position, token); if (res) { return { ranges: res.ranges, @@ -1814,7 +1804,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token), undefined); } - registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider2): vscode.Disposable { + registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { const handle = this._nextHandle(); const eventHandle = typeof provider.onDidChangeFoldingRanges === 'function' ? this._nextHandle() : undefined; @@ -1823,8 +1813,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF let result = this._createDisposable(handle); if (eventHandle !== undefined) { - checkProposedApiEnabled(extension); - const subscription = provider.onDidChangeFoldingRanges!(_ => this._proxy.$emitFoldingRangeEvent(eventHandle)); + const subscription = provider.onDidChangeFoldingRanges!(() => this._proxy.$emitFoldingRangeEvent(eventHandle)); result = Disposable.from(result, subscription); } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 0846a2d55..bb136d1c0 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -9,7 +9,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadBulkEditsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, MainContext, MainThreadBulkEditsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; import { ILogService } from 'vs/platform/log/common/log'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; @@ -214,7 +214,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private readonly _proxy: MainThreadNotebookShape; private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape; private readonly _notebookContentProviders = new Map(); - private readonly _notebookKernels = new Map(); private readonly _notebookKernelProviders = new Map(); private readonly _documents = new ResourceMap(); private readonly _editors = new Map(); @@ -414,6 +413,35 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN return callback(provider, document); } + async showNotebookDocument(notebookDocument: vscode.NotebookDocument, options?: vscode.NotebookDocumentShowOptions): Promise { + let resolvedOptions: INotebookDocumentShowOptions; + if (typeof options === 'object') { + resolvedOptions = { + position: typeConverters.ViewColumn.from(options.viewColumn), + preserveFocus: options.preserveFocus, + selection: options.selection, + pinned: typeof options.preview === 'boolean' ? !options.preview : undefined + }; + } else { + resolvedOptions = { + preserveFocus: false + }; + } + + const editorId = await this._proxy.$tryShowNotebookDocument(notebookDocument.uri, notebookDocument.viewType, resolvedOptions); + const editor = editorId && this._editors.get(editorId)?.editor; + + if (editor) { + return editor; + } + + if (editorId) { + throw new Error(`Could NOT open editor for "${notebookDocument.toString()}" because another editor opened in the meantime.`); + } else { + throw new Error(`Could NOT open editor for "${notebookDocument.toString()}".`); + } + } + async $provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise { return this._withAdapter(handle, uri, (adapter, document) => { return adapter.provideKernels(document, token); @@ -487,28 +515,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); } - async $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { - const document = this._documents.get(URI.revive(uri)); - - if (!document || document.notebookDocument.viewType !== viewType) { - return; - } - - const kernelInfo = this._notebookKernels.get(kernelId); - - if (!kernelInfo) { - return; - } - - const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; - - if (cell) { - return withToken(token => (kernelInfo!.kernel.executeCell as any)(document.notebookDocument, cell.cell, token)); - } else { - return withToken(token => (kernelInfo!.kernel.executeAllCells as any)(document.notebookDocument, token)); - } - } - async $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise { const document = this._documents.get(URI.revive(uri)); if (!document) { diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 4adf04afd..2f2fd4cdb 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -448,7 +448,11 @@ function getIconUris(iconPath: QuickInputButton['iconPath']): { dark: URI, light } const dark = getDarkIconUri(iconPath as URI | { light: URI; dark: URI; }); const light = getLightIconUri(iconPath as URI | { light: URI; dark: URI; }); - return { dark, light }; + // Tolerate strings: https://github.com/microsoft/vscode/issues/110432#issuecomment-726144556 + return { + dark: typeof dark === 'string' ? URI.file(dark) : dark, + light: typeof light === 'string' ? URI.file(light) : light + }; } function getLightIconUri(iconPath: URI | { light: URI; dark: URI; }) { diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 83739e744..058061633 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -17,6 +17,7 @@ import { ISplice } from 'vs/base/common/sequence'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; type ProviderHandle = number; type GroupHandle = number; @@ -90,6 +91,49 @@ function compareResourceStatesDecorations(a: vscode.SourceControlResourceDecorat return result; } +function compareCommands(a: vscode.Command, b: vscode.Command): number { + if (a.command !== b.command) { + return a.command < b.command ? -1 : 1; + } + + if (a.title !== b.title) { + return a.title < b.title ? -1 : 1; + } + + if (a.tooltip !== b.tooltip) { + if (a.tooltip !== undefined && b.tooltip !== undefined) { + return a.tooltip < b.tooltip ? -1 : 1; + } else if (a.tooltip !== undefined) { + return 1; + } else if (b.tooltip !== undefined) { + return -1; + } + } + + if (a.arguments === b.arguments) { + return 0; + } else if (!a.arguments) { + return -1; + } else if (!b.arguments) { + return 1; + } else if (a.arguments.length !== b.arguments.length) { + return a.arguments.length - b.arguments.length; + } + + for (let i = 0; i < a.arguments.length; i++) { + const aArg = a.arguments[i]; + const bArg = b.arguments[i]; + + if (aArg === bArg) { + continue; + } + + return aArg < bArg ? -1 : 1; + } + + return 0; +} + function compareResourceStates(a: vscode.SourceControlResourceState, b: vscode.SourceControlResourceState): number { let result = comparePaths(a.resourceUri.fsPath, b.resourceUri.fsPath, true); @@ -97,6 +141,18 @@ function compareResourceStates(a: vscode.SourceControlResourceState, b: vscode.S return result; } + if (a.command && b.command) { + result = compareCommands(a.command, b.command); + } else if (a.command) { + return 1; + } else if (b.command) { + return -1; + } + + if (result !== 0) { + return result; + } + if (a.decorations && b.decorations) { result = compareResourceStatesDecorations(a.decorations, b.decorations); } else if (a.decorations) { @@ -166,17 +222,13 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { private _validateInput: IValidateInput | undefined; get validateInput(): IValidateInput | undefined { - if (!this._extension.enableProposedApi) { - throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`); - } + checkProposedApiEnabled(this._extension); return this._validateInput; } set validateInput(fn: IValidateInput | undefined) { - if (!this._extension.enableProposedApi) { - throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`); - } + checkProposedApiEnabled(this._extension); if (fn && typeof fn !== 'function') { throw new Error(`[${this._extension.identifier.value}]: Invalid SCM input box validation function`); @@ -223,8 +275,9 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG private _resourceHandlePool: number = 0; private _resourceStates: vscode.SourceControlResourceState[] = []; - private _resourceStatesMap: Map = new Map(); - private _resourceStatesCommandsMap: Map = new Map(); + private _resourceStatesMap = new Map(); + private _resourceStatesCommandsMap = new Map(); + private _resourceStatesDisposablesMap = new Map(); private readonly _onDidUpdateResourceStates = new Emitter(); readonly onDidUpdateResourceStates = this._onDidUpdateResourceStates.event; @@ -302,9 +355,16 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG const lightIconUri = r.decorations && getIconResource(r.decorations.light) || iconUri; const darkIconUri = r.decorations && getIconResource(r.decorations.dark) || iconUri; const icons: UriComponents[] = []; + let command: ICommandDto | undefined; if (r.command) { - this._resourceStatesCommandsMap.set(handle, r.command); + if (r.command.command === 'vscode.open' || r.command.command === 'vscode.diff') { + const disposables = new DisposableStore(); + command = this._commands.converter.toInternal(r.command, disposables); + this._resourceStatesDisposablesMap.set(handle, disposables); + } else { + this._resourceStatesCommandsMap.set(handle, r.command); + } } if (lightIconUri) { @@ -320,7 +380,7 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG const faded = r.decorations && !!r.decorations.faded; const contextValue = r.contextValue || ''; - const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue] as SCMRawResource; + const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue, command] as SCMRawResource; return { rawResource, handle }; }); @@ -340,6 +400,8 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG for (const handle of handlesToDelete) { this._resourceStatesMap.delete(handle); this._resourceStatesCommandsMap.delete(handle); + this._resourceStatesDisposablesMap.get(handle)?.dispose(); + this._resourceStatesDisposablesMap.delete(handle); } } diff --git a/src/vs/workbench/api/common/extHostStatusBar.ts b/src/vs/workbench/api/common/extHostStatusBar.ts index edbaec0f6..a89e9fe30 100644 --- a/src/vs/workbench/api/common/extHostStatusBar.ts +++ b/src/vs/workbench/api/common/extHostStatusBar.ts @@ -10,10 +10,21 @@ import { MainContext, MainThreadStatusBarShape, IMainContext, ICommandDto } from import { localize } from 'vs/nls'; import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; export class ExtHostStatusBarEntry implements vscode.StatusBarItem { private static ID_GEN = 0; + private static ALLOWED_BACKGROUND_COLORS = (() => { + const map = new Map(); + + // https://github.com/microsoft/vscode/issues/110214 + map.set('statusBarItem.errorBackground', new ThemeColor('statusBarItem.errorForeground')); + + return map; + })(); + private _id: number; private _alignment: number; private _priority?: number; @@ -26,6 +37,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { private _text: string = ''; private _tooltip?: string; private _color?: string | ThemeColor; + private _backgroundColor?: ThemeColor; private readonly _internalCommandRegistration = new DisposableStore(); private _command?: { readonly fromApi: string | vscode.Command, @@ -36,8 +48,9 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { private _proxy: MainThreadStatusBarShape; private _commands: CommandsConverter; private _accessibilityInformation?: vscode.AccessibilityInformation; + private _extension?: IExtensionDescription; - constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation) { + constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation, extension?: IExtensionDescription) { this._id = ExtHostStatusBarEntry.ID_GEN++; this._proxy = proxy; this._commands = commands; @@ -46,6 +59,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { this._alignment = alignment; this._priority = priority; this._accessibilityInformation = accessibilityInformation; + this._extension = extension; } public get id(): number { @@ -72,6 +86,14 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { return this._color; } + public get backgroundColor(): ThemeColor | undefined { + if (this._extension) { + checkProposedApiEnabled(this._extension); + } + + return this._backgroundColor; + } + public get command(): string | vscode.Command | undefined { return this._command?.fromApi; } @@ -95,6 +117,19 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { this.update(); } + public set backgroundColor(color: ThemeColor | undefined) { + if (this._extension) { + checkProposedApiEnabled(this._extension); + } + + if (color && !ExtHostStatusBarEntry.ALLOWED_BACKGROUND_COLORS.has(color.id)) { + color = undefined; + } + + this._backgroundColor = color; + this.update(); + } + public set command(command: string | vscode.Command | undefined) { if (this._command?.fromApi === command) { return; @@ -144,9 +179,15 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { this._timeoutHandle = setTimeout(() => { this._timeoutHandle = undefined; + // If a background color is set, the foreground is determined + let color = this._color; + if (this._backgroundColor) { + color = ExtHostStatusBarEntry.ALLOWED_BACKGROUND_COLORS.get(this._backgroundColor.id); + } + // Set to status bar - this._proxy.$setEntry(this.id, this._statusId, this._statusName, this.text, this.tooltip, this._command?.internal, this.color, - this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT, + this._proxy.$setEntry(this.id, this._statusId, this._statusName, this._text, this._tooltip, this._command?.internal, color, + this._backgroundColor, this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT, this._priority, this._accessibilityInformation); }, 0); } @@ -207,8 +248,8 @@ export class ExtHostStatusBar { this._statusMessage = new StatusBarMessage(this); } - createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation): vscode.StatusBarItem { - return new ExtHostStatusBarEntry(this._proxy, this._commands, id, name, alignment, priority, accessibilityInformation); + createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation, extension?: IExtensionDescription): vscode.StatusBarItem { + return new ExtHostStatusBarEntry(this._proxy, this._commands, id, name, alignment, priority, accessibilityInformation, extension); } setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): Disposable { diff --git a/src/vs/workbench/api/common/extHostStorage.ts b/src/vs/workbench/api/common/extHostStorage.ts index 0c38bb510..01712256f 100644 --- a/src/vs/workbench/api/common/extHostStorage.ts +++ b/src/vs/workbench/api/common/extHostStorage.ts @@ -7,7 +7,7 @@ import { MainContext, MainThreadStorageShape, ExtHostStorageShape } from './extH import { Emitter } from 'vs/base/common/event'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/storageKeys'; +import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync'; export interface IStorageChangeEvent { shared: boolean; diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index f0bb1dfac..9b0753a57 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -188,6 +188,10 @@ export namespace CustomExecutionDTO { customExecution: 'customExecution' }; } + + export function to(taskId: string, providedCustomExeutions: Map): types.CustomExecution | undefined { + return providedCustomExeutions.get(taskId); + } } @@ -274,15 +278,17 @@ export namespace TaskDTO { }; return result; } - export async function to(value: tasks.TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise { + export async function to(value: tasks.TaskDTO | undefined, workspace: IExtHostWorkspaceProvider, providedCustomExeutions: Map): Promise { if (value === undefined || value === null) { return undefined; } - let execution: types.ShellExecution | types.ProcessExecution | undefined; + let execution: types.ShellExecution | types.ProcessExecution | types.CustomExecution | undefined; if (ProcessExecutionDTO.is(value.execution)) { execution = ProcessExecutionDTO.to(value.execution); } else if (ShellExecutionDTO.is(value.execution)) { execution = ShellExecutionDTO.to(value.execution); + } else if (CustomExecutionDTO.is(value.execution)) { + execution = CustomExecutionDTO.to(value._id, providedCustomExeutions); } const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition); let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined; @@ -354,13 +360,6 @@ class TaskExecutionImpl implements vscode.TaskExecution { } export namespace TaskExecutionDTO { - export async function to(value: tasks.TaskExecutionDTO, tasks: ExtHostTaskBase, workspaceProvider: IExtHostWorkspaceProvider): Promise { - const task = await TaskDTO.to(value.task, workspaceProvider); - if (!task) { - throw new Error('Unexpected: Task cannot be created.'); - } - return new TaskExecutionImpl(tasks, value.id, task); - } export function from(value: vscode.TaskExecution): tasks.TaskExecutionDTO { return { id: (value as TaskExecutionImpl)._id, @@ -447,7 +446,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => { const result: vscode.Task[] = []; for (let value of values) { - const task = await TaskDTO.to(value, this._workspaceProvider); + const task = await TaskDTO.to(value, this._workspaceProvider, this._providedCustomExecutions2); if (task) { result.push(task); } @@ -573,7 +572,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask throw new Error(`Unexpected: Task of type [${taskDTO.definition.type}] cannot be resolved by provider of type [${handler.type}].`); } - const task = await TaskDTO.to(taskDTO, this._workspaceProvider); + const task = await TaskDTO.to(taskDTO, this._workspaceProvider, this._providedCustomExecutions2); if (!task) { throw new Error('Unexpected: Task cannot be resolved.'); } @@ -631,7 +630,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask return result; } const createdResult: Promise = new Promise(async (resolve, reject) => { - const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider); + const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider, this._providedCustomExecutions2); if (!taskToCreate) { reject('Unexpected: Task does not exist.'); } else { @@ -705,6 +704,10 @@ export class WorkerExtHostTask extends ExtHostTaskBase { } public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise { + if (!task.execution) { + throw new Error('Tasks to execute must include an execution'); + } + const dto = TaskDTO.from(task, extension); if (dto === undefined) { throw new Error('Task is not valid'); diff --git a/src/vs/workbench/api/common/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts new file mode 100644 index 000000000..02fc32cbf --- /dev/null +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -0,0 +1,794 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { mapFind } from 'vs/base/common/arrays'; +import { disposableTimeout } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { throttle } from 'vs/base/common/decorators'; +import { Emitter } from 'vs/base/common/event'; +import { once } from 'vs/base/common/functional'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { isDefined } from 'vs/base/common/types'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import { ExtHostTestingResource, ExtHostTestingShape, MainContext, MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { TestItem } from 'vs/workbench/api/common/extHostTypeConverters'; +import { Disposable, RequiredTestItem } from 'vs/workbench/api/common/extHostTypes'; +import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { AbstractIncrementalTestCollection, EMPTY_TEST_RESULT, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, RunTestForProviderRequest, RunTestsResult, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import type * as vscode from 'vscode'; + +const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`; + +export class ExtHostTesting implements ExtHostTestingShape { + private readonly providers = new Map(); + private readonly proxy: MainThreadTestingShape; + private readonly ownedTests = new OwnedTestCollection(); + private readonly testSubscriptions = new Map(); + + private workspaceObservers: WorkspaceFolderTestObserverFactory; + private textDocumentObservers: TextDocumentTestObserverFactory; + + constructor(@IExtHostRpcService rpc: IExtHostRpcService, @IExtHostDocumentsAndEditors private readonly documents: IExtHostDocumentsAndEditors, @IExtHostWorkspace private readonly workspace: IExtHostWorkspace) { + this.proxy = rpc.getProxy(MainContext.MainThreadTesting); + this.workspaceObservers = new WorkspaceFolderTestObserverFactory(this.proxy); + this.textDocumentObservers = new TextDocumentTestObserverFactory(this.proxy, documents); + } + + /** + * Implements vscode.test.registerTestProvider + */ + public registerTestProvider(provider: vscode.TestProvider): vscode.Disposable { + const providerId = generateUuid(); + this.providers.set(providerId, provider); + this.proxy.$registerTestProvider(providerId); + + return new Disposable(() => { + this.providers.delete(providerId); + this.proxy.$unregisterTestProvider(providerId); + }); + } + + /** + * Implements vscode.test.createTextDocumentTestObserver + */ + public createTextDocumentTestObserver(document: vscode.TextDocument) { + return this.textDocumentObservers.checkout(document.uri); + } + + /** + * Implements vscode.test.createWorkspaceTestObserver + */ + public createWorkspaceTestObserver(workspaceFolder: vscode.WorkspaceFolder) { + return this.workspaceObservers.checkout(workspaceFolder.uri); + } + + /** + * Implements vscode.test.runTests + */ + public async runTests(req: vscode.TestRunOptions) { + await this.proxy.$runTests({ + tests: req.tests + // Find workspace items first, then owned tests, then document tests. + // If a test instance exists in both the workspace and document, prefer + // the workspace because it's less ephemeral. + .map(test => this.workspaceObservers.getMirroredTestDataByReference(test) + ?? mapFind(this.testSubscriptions.values(), c => c.collection.getTestByReference(test)) + ?? this.textDocumentObservers.getMirroredTestDataByReference(test)) + .filter(isDefined) + .map(item => ({ providerId: item.providerId, testId: item.id })), + debug: req.debug + }); + } + + /** + * Handles a request to read tests for a file, or workspace. + * @override + */ + public $subscribeToTests(resource: ExtHostTestingResource, uriComponents: UriComponents) { + const uri = URI.revive(uriComponents); + const subscriptionKey = getTestSubscriptionKey(resource, uri); + if (this.testSubscriptions.has(subscriptionKey)) { + return; + } + + let method: undefined | ((p: vscode.TestProvider) => vscode.TestHierarchy | undefined); + if (resource === ExtHostTestingResource.TextDocument) { + const document = this.documents.getDocument(uri); + if (document) { + method = p => p.createDocumentTestHierarchy?.(document.document); + } + } else { + const folder = this.workspace.getWorkspaceFolder(uri, false); + if (folder) { + method = p => p.createWorkspaceTestHierarchy?.(folder); + } + } + + if (!method) { + return; + } + + const disposable = new DisposableStore(); + const collection = disposable.add(this.ownedTests.createForHierarchy(diff => this.proxy.$publishDiff(resource, uriComponents, diff))); + for (const [id, provider] of this.providers) { + try { + const hierarchy = method(provider); + if (!hierarchy) { + continue; + } + + disposable.add(hierarchy); + collection.addRoot(hierarchy.root, id); + hierarchy.onDidChangeTest(e => collection.onItemChange(e, id)); + } catch (e) { + console.error(e); + } + } + + this.testSubscriptions.set(subscriptionKey, { store: disposable, collection }); + } + + /** + * Disposes of a previous subscription to tests. + * @override + */ + public $unsubscribeFromTests(resource: ExtHostTestingResource, uriComponents: UriComponents) { + const uri = URI.revive(uriComponents); + const subscriptionKey = getTestSubscriptionKey(resource, uri); + this.testSubscriptions.get(subscriptionKey)?.store.dispose(); + this.testSubscriptions.delete(subscriptionKey); + } + + /** + * Receives a test update from the main thread. Called (eventually) whenever + * tests change. + * @override + */ + public $acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void { + if (resource === ExtHostTestingResource.TextDocument) { + this.textDocumentObservers.acceptDiff(URI.revive(uri), diff); + } else { + this.workspaceObservers.acceptDiff(URI.revive(uri), diff); + } + } + + /** + * Runs tests with the given set of IDs. Allows for test from multiple + * providers to be run. + * @override + */ + public async $runTestsForProvider(req: RunTestForProviderRequest): Promise { + const provider = this.providers.get(req.providerId); + if (!provider || !provider.runTests) { + return EMPTY_TEST_RESULT; + } + + const tests = req.ids.map(id => this.ownedTests.getTestById(id)?.actual).filter(isDefined); + if (!tests.length) { + return EMPTY_TEST_RESULT; + } + + await provider.runTests({ tests, debug: req.debug }, CancellationToken.None); + return EMPTY_TEST_RESULT; + } +} + +const keyMap: { [K in keyof Omit]: null } = { + label: null, + location: null, + state: null, + debuggable: null, + description: null, + runnable: null +}; + +const simpleProps = Object.keys(keyMap) as ReadonlyArray; + +const itemEqualityComparator = (a: vscode.TestItem) => { + const values: unknown[] = []; + for (const prop of simpleProps) { + values.push(a[prop]); + } + + return (b: vscode.TestItem) => { + for (let i = 0; i < simpleProps.length; i++) { + if (values[i] !== b[simpleProps[i]]) { + return false; + } + } + + return true; + }; +}; + +/** + * @private + */ +export interface OwnedCollectionTestItem extends InternalTestItem { + actual: vscode.TestItem; + previousChildren: Set; + previousEquals: (v: vscode.TestItem) => boolean; +} + +/** + * @private + */ +export class OwnedTestCollection { + protected readonly testIdToInternal = new Map(); + + /** + * Gets test information by ID, if it was defined and still exists in this + * extension host. + */ + public getTestById(id: string) { + return this.testIdToInternal.get(id); + } + + /** + * Creates a new test collection for a specific hierarchy for a workspace + * or document observation. + */ + public createForHierarchy(publishDiff: (diff: TestsDiff) => void = () => undefined) { + return new SingleUseTestCollection(this.testIdToInternal, publishDiff); + } +} + +/** + * Maintains tests created and registered for a single set of hierarchies + * for a workspace or document. + * @private + */ +export class SingleUseTestCollection implements IDisposable { + protected readonly testItemToInternal = new Map(); + protected diff: TestsDiff = []; + private disposed = false; + + constructor(private readonly testIdToInternal: Map, private readonly publishDiff: (diff: TestsDiff) => void) { } + + /** + * Adds a new root node to the collection. + */ + public addRoot(item: vscode.TestItem, providerId: string) { + this.addItem(item, providerId, null); + this.throttleSendDiff(); + } + + /** + * Gets test information by its reference, if it was defined and still exists + * in this extension host. + */ + public getTestByReference(item: vscode.TestItem) { + return this.testItemToInternal.get(item); + } + + /** + * Should be called when an item change is fired on the test provider. + */ + public onItemChange(item: vscode.TestItem, providerId: string) { + const existing = this.testItemToInternal.get(item); + if (!existing) { + if (!this.disposed) { + console.warn(`Received a TestProvider.onDidChangeTest for a test that wasn't seen before as a child.`); + } + return; + } + + this.addItem(item, providerId, existing.parent); + this.throttleSendDiff(); + } + + /** + * Gets a diff of all changes that have been made, and clears the diff queue. + */ + public collectDiff() { + const diff = this.diff; + this.diff = []; + return diff; + } + + public dispose() { + for (const item of this.testItemToInternal.values()) { + this.testIdToInternal.delete(item.id); + } + + this.testIdToInternal.clear(); + this.diff = []; + this.disposed = true; + } + + protected getId(): string { + return generateUuid(); + } + + private addItem(actual: vscode.TestItem, providerId: string, parent: string | null) { + let internal = this.testItemToInternal.get(actual); + if (!internal) { + internal = { + actual, + id: this.getId(), + parent, + item: TestItem.from(actual), + providerId, + previousChildren: new Set(), + previousEquals: itemEqualityComparator(actual), + }; + + this.testItemToInternal.set(actual, internal); + this.testIdToInternal.set(internal.id, internal); + this.diff.push([TestDiffOpType.Add, { id: internal.id, parent, providerId, item: internal.item }]); + } else if (!internal.previousEquals(actual)) { + internal.item = TestItem.from(actual); + internal.previousEquals = itemEqualityComparator(actual); + this.diff.push([TestDiffOpType.Update, { id: internal.id, parent, providerId, item: internal.item }]); + } + + // If there are children, track which ones are deleted + // and recursively and/update them. + if (actual.children) { + const deletedChildren = internal.previousChildren; + const currentChildren = new Set(); + for (const child of actual.children) { + const c = this.addItem(child, providerId, internal.id); + deletedChildren.delete(c.id); + currentChildren.add(c.id); + } + + for (const child of deletedChildren) { + this.removeItembyId(child); + } + + internal.previousChildren = currentChildren; + } + + + return internal; + } + + private removeItembyId(id: string) { + this.diff.push([TestDiffOpType.Remove, id]); + + const queue = [this.testIdToInternal.get(id)]; + while (queue.length) { + const item = queue.pop(); + if (!item) { + continue; + } + + this.testIdToInternal.delete(item.id); + this.testItemToInternal.delete(item.actual); + for (const child of item.previousChildren) { + queue.push(this.testIdToInternal.get(child)); + } + } + } + + @throttle(200) + protected throttleSendDiff() { + const diff = this.collectDiff(); + if (diff.length) { + this.publishDiff(diff); + } + } +} + +/** + * @private + */ +interface MirroredCollectionTestItem extends IncrementalTestCollectionItem { + revived: vscode.TestItem; + depth: number; + wrapped?: vscode.TestItem; +} + +class MirroredChangeCollector extends IncrementalChangeCollector { + private readonly added = new Set(); + private readonly updated = new Set(); + private readonly removed = new Set(); + + private readonly alreadyRemoved = new Set(); + + public get isEmpty() { + return this.added.size === 0 && this.removed.size === 0 && this.updated.size === 0; + } + + constructor(private readonly collection: MirroredTestCollection, private readonly emitter: Emitter) { + super(); + } + + /** + * @override + */ + public add(node: MirroredCollectionTestItem): void { + this.added.add(node); + } + + /** + * @override + */ + public update(node: MirroredCollectionTestItem): void { + Object.assign(node.revived, TestItem.to(node.item)); + if (!this.added.has(node)) { + this.updated.add(node); + } + } + + /** + * @override + */ + public remove(node: MirroredCollectionTestItem): void { + if (this.added.has(node)) { + this.added.delete(node); + return; + } + + this.updated.delete(node); + + if (node.parent && this.alreadyRemoved.has(node.parent)) { + this.alreadyRemoved.add(node.id); + return; + } + + this.removed.add(node); + } + + /** + * @override + */ + public getChangeEvent(): vscode.TestChangeEvent { + const { collection, added, updated, removed } = this; + return { + get added() { return [...added].map(collection.getPublicTestItem, collection); }, + get updated() { return [...updated].map(collection.getPublicTestItem, collection); }, + get removed() { return [...removed].map(collection.getPublicTestItem, collection); }, + get commonChangeAncestor() { + let ancestorPath: MirroredCollectionTestItem[] | undefined; + const buildAncestorPath = (node: MirroredCollectionTestItem | undefined) => { + if (!node) { + return undefined; + } + + // add the node and all its parents to the list of ancestors. If + // the node is detached, do not return a path (its parent will + // also have been passed to remove() and be present) + const path: MirroredCollectionTestItem[] = new Array(node.depth + 1); + for (let i = node.depth; i >= 0; i--) { + if (!node) { + return undefined; // detached child + } + + path[node.depth] = node; + node = node.parent ? collection.getMirroredTestDataById(node.parent) : undefined; + } + + return path; + }; + + const addAncestorPath = (node: MirroredCollectionTestItem) => { + // fast path: if the common ancestor is already the root, no more work to do + if (ancestorPath && ancestorPath.length === 0) { + return; + } + + const thisPath = buildAncestorPath(node); + if (!thisPath) { + return; + } + + if (!ancestorPath) { + ancestorPath = thisPath; + return; + } + + // removes node from the path to the ancestor that don't match + // the corresponding node in *this* path. + for (let i = ancestorPath.length - 1; i >= 0; i--) { + if (ancestorPath[i] !== thisPath[i]) { + ancestorPath.pop(); + } + } + }; + + const addParentAncestor = (node: MirroredCollectionTestItem) => { + if (ancestorPath && ancestorPath.length === 0) { + // no-op + } else if (node.parent === null) { + ancestorPath = []; + } else { + const parent = collection.getMirroredTestDataById(node.parent); + if (parent) { + addAncestorPath(parent); + } + } + }; + + for (const node of added) { addParentAncestor(node); } + for (const node of updated) { addAncestorPath(node); } + for (const node of removed) { addParentAncestor(node); } + + const ancestor = ancestorPath && ancestorPath[ancestorPath.length - 1]; + return ancestor ? collection.getPublicTestItem(ancestor) : null; + }, + }; + } + + public complete() { + if (!this.isEmpty) { + this.emitter.fire(this.getChangeEvent()); + } + } +} + +/** + * Maintains tests in this extension host sent from the main thread. + * @private + */ +export class MirroredTestCollection extends AbstractIncrementalTestCollection { + private changeEmitter = new Emitter(); + + /** + * Change emitter that fires with the same sematics as `TestObserver.onDidChangeTests`. + */ + public readonly onDidChangeTests = this.changeEmitter.event; + + /** + * Gets a list of root test items. + */ + public get rootTestItems() { + return this.getAllAsTestItem([...this.roots]); + } + + /** + * Translates the item IDs to TestItems for exposure to extensions. + */ + public getAllAsTestItem(itemIds: Iterable): vscode.TestItem[] { + let output: vscode.TestItem[] = []; + for (const itemId of itemIds) { + const item = this.items.get(itemId); + if (item) { + output.push(this.getPublicTestItem(item)); + } + } + + return output; + } + + /** + * + * If the test ID exists, returns its underlying ID. + */ + public getMirroredTestDataById(itemId: string) { + return this.items.get(itemId); + } + + /** + * If the test item is a mirrored test item, returns its underlying ID. + */ + public getMirroredTestDataByReference(item: vscode.TestItem) { + const id = getMirroredItemId(item); + return id ? this.items.get(id) : undefined; + } + + /** + * @override + */ + protected createItem(item: InternalTestItem, parent?: MirroredCollectionTestItem): MirroredCollectionTestItem { + return { ...item, revived: TestItem.to(item.item), depth: parent ? parent.depth + 1 : 0, children: new Set() }; + } + + /** + * @override + */ + protected createChangeCollector() { + return new MirroredChangeCollector(this, this.changeEmitter); + } + + /** + * Gets the public test item instance for the given mirrored record. + */ + public getPublicTestItem(item: MirroredCollectionTestItem): vscode.TestItem { + if (!item.wrapped) { + item.wrapped = new ExtHostTestItem(item, this); + } + + return item.wrapped; + } +} + +const getMirroredItemId = (item: vscode.TestItem) => { + return (item as any)[MirroredItemId] as string | undefined; +}; + +const MirroredItemId = Symbol('MirroredItemId'); + +class ExtHostTestItem implements vscode.TestItem, RequiredTestItem { + readonly #internal: MirroredCollectionTestItem; + readonly #collection: MirroredTestCollection; + + public get label() { return this.#internal.revived.label; } + public get description() { return this.#internal.revived.description; } + public get state() { return this.#internal.revived.state; } + public get location() { return this.#internal.revived.location; } + public get runnable() { return this.#internal.revived.runnable ?? true; } + public get debuggable() { return this.#internal.revived.debuggable ?? false; } + public get children() { + return this.#collection.getAllAsTestItem(this.#internal.children); + } + + get [MirroredItemId]() { return this.#internal.id; } + + constructor(internal: MirroredCollectionTestItem, collection: MirroredTestCollection) { + this.#internal = internal; + this.#collection = collection; + } + + public toJSON() { + const serialized: RequiredTestItem = { + label: this.label, + description: this.description, + state: this.state, + location: this.location, + runnable: this.runnable, + debuggable: this.debuggable, + children: this.children.map(c => (c as ExtHostTestItem).toJSON()), + }; + + return serialized; + } +} + +interface IObserverData { + observers: number; + tests: MirroredTestCollection; + listener: IDisposable; + pendingDeletion?: IDisposable; +} + +abstract class AbstractTestObserverFactory { + private readonly resources = new Map(); + + public checkout(resourceUri: URI): vscode.TestObserver { + const resourceKey = resourceUri.toString(); + const resource = this.resources.get(resourceKey) ?? this.createObserverData(resourceUri); + + resource.observers++; + + return { + onDidChangeTest: resource.tests.onDidChangeTests, + onDidDiscoverInitialTests: new Emitter().event, // todo@connor4312 + get tests() { + return resource.tests.rootTestItems; + }, + dispose: once(() => { + if (!--resource.observers) { + resource.pendingDeletion = this.eventuallyDispose(resourceUri); + } + }), + }; + } + + /** + * Gets the internal test data by its reference, in any observer. + */ + public getMirroredTestDataByReference(ref: vscode.TestItem) { + for (const { tests } of this.resources.values()) { + const v = tests.getMirroredTestDataByReference(ref); + if (v) { + return v; + } + } + + return undefined; + } + + /** + * Called when no observers are listening for the resource any more. Should + * defer unlistening on the resource, and return a disposiable + * to halt the process in case new listeners come in. + */ + protected eventuallyDispose(resourceUri: URI) { + return disposableTimeout(() => this.unlisten(resourceUri), 10 * 1000); + } + + /** + * Starts listening to test information for the given resource. + */ + protected abstract listen(resourceUri: URI, onDiff: (diff: TestsDiff) => void): Disposable; + + private createObserverData(resourceUri: URI): IObserverData { + const tests = new MirroredTestCollection(); + const listener = this.listen(resourceUri, diff => tests.apply(diff)); + const data: IObserverData = { observers: 0, tests, listener }; + this.resources.set(resourceUri.toString(), data); + return data; + } + + /** + * Called when a resource is no longer in use. + */ + protected unlisten(resourceUri: URI) { + const key = resourceUri.toString(); + const resource = this.resources.get(key); + if (resource) { + resource.observers = -1; + resource.pendingDeletion?.dispose(); + resource.listener.dispose(); + this.resources.delete(key); + } + } +} + +class WorkspaceFolderTestObserverFactory extends AbstractTestObserverFactory { + private diffListeners = new Map void>(); + + constructor(private readonly proxy: MainThreadTestingShape) { + super(); + } + + /** + * Publishees the diff for the workspace folder with the given uri. + */ + public acceptDiff(resourceUri: URI, diff: TestsDiff) { + this.diffListeners.get(resourceUri.toString())?.(diff); + } + + /** + * @override + */ + public listen(resourceUri: URI, onDiff: (diff: TestsDiff) => void) { + this.proxy.$subscribeToDiffs(ExtHostTestingResource.Workspace, resourceUri); + + const uriString = resourceUri.toString(); + this.diffListeners.set(uriString, onDiff); + + return new Disposable(() => { + this.proxy.$unsubscribeFromDiffs(ExtHostTestingResource.Workspace, resourceUri); + this.diffListeners.delete(uriString); + }); + } +} + +class TextDocumentTestObserverFactory extends AbstractTestObserverFactory { + private diffListeners = new Map void>(); + + constructor(private readonly proxy: MainThreadTestingShape, private documents: IExtHostDocumentsAndEditors) { + super(); + } + + /** + * Publishees the diff for the document with the given uri. + */ + public acceptDiff(resourceUri: URI, diff: TestsDiff) { + this.diffListeners.get(resourceUri.toString())?.(diff); + } + + /** + * @override + */ + public listen(resourceUri: URI, onDiff: (diff: TestsDiff) => void) { + const document = this.documents.getDocument(resourceUri); + if (!document) { + return new Disposable(() => undefined); + } + + const uriString = resourceUri.toString(); + this.diffListeners.set(uriString, onDiff); + + const disposeListener = this.documents.onDidRemoveDocuments(evt => { + if (evt.some(delta => delta.document.uri.toString() === uriString)) { + this.unlisten(resourceUri); + } + }); + + this.proxy.$subscribeToDiffs(ExtHostTestingResource.TextDocument, resourceUri); + return new Disposable(() => { + this.proxy.$unsubscribeFromDiffs(ExtHostTestingResource.TextDocument, resourceUri); + disposeListener.dispose(); + this.diffListeners.delete(uriString); + }); + } +} diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index d7194fc23..63d307018 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -17,7 +17,6 @@ import { TreeItemCollapsibleState, ThemeIcon, MarkdownString as MarkdownStringTy import { isUndefinedOrNull, isString } from 'vs/base/common/types'; import { equals, coalesce } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; -import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters'; import { IMarkdownString } from 'vs/base/common/htmlContent'; @@ -33,7 +32,6 @@ function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeIte if (label && typeof label === 'object' && typeof label.label === 'string') { - checkProposedApiEnabled(extension); let highlights: [number, number][] | undefined = undefined; if (Array.isArray(label.highlights)) { highlights = (<[number, number][]>label.highlights).filter((highlight => highlight.length === 2 && typeof highlight[0] === 'number' && typeof highlight[1] === 'number')); @@ -183,7 +181,7 @@ type TreeData = { message: boolean, element: T | Root | false }; interface TreeNode extends IDisposable { item: ITreeItem; - extensionItem: vscode.TreeItem2; + extensionItem: vscode.TreeItem; parent: TreeNode | Root; children?: TreeNode[]; } @@ -293,7 +291,7 @@ class ExtHostTreeView extends Disposable { return this.elements.get(treeItemHandle); } - reveal(element: T, options?: IRevealOptions): Promise { + reveal(element: T | undefined, options?: IRevealOptions): Promise { options = options ? options : { select: true, focus: false }; const select = isUndefinedOrNull(options.select) ? true : options.select; const focus = isUndefinedOrNull(options.focus) ? false : options.focus; @@ -302,10 +300,15 @@ class ExtHostTreeView extends Disposable { if (typeof this.dataProvider.getParent !== 'function') { return Promise.reject(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`)); } - return this.refreshPromise - .then(() => this.resolveUnknownParentChain(element)) - .then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1]) - .then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus, expand })), error => this.logService.error(error)); + + if (element) { + return this.refreshPromise + .then(() => this.resolveUnknownParentChain(element)) + .then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1]) + .then(treeNode => this.proxy.$reveal(this.viewId, { item: treeNode.item, parentChain: parentChain.map(p => p.item) }, { select, focus, expand })), error => this.logService.error(error)); + } else { + return this.proxy.$reveal(this.viewId, undefined, { select, focus, expand }); + } } private _message: string = ''; @@ -375,7 +378,7 @@ class ExtHostTreeView extends Disposable { if (element) { const node = this.nodes.get(element); if (node) { - const resolve = await this.dataProvider.resolveTreeItem(element, node.extensionItem); + const resolve = await this.dataProvider.resolveTreeItem(node.extensionItem, element) ?? node.extensionItem; // Resolvable elements. Currently only tooltip. node.item.tooltip = this.getTooltip(resolve.tooltip); return node.item; @@ -565,13 +568,12 @@ class ExtHostTreeView extends Disposable { private getTooltip(tooltip?: string | vscode.MarkdownString): string | IMarkdownString | undefined { if (MarkdownStringType.isMarkdownString(tooltip)) { - checkProposedApiEnabled(this.extension); return MarkdownString.from(tooltip); } return tooltip; } - private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem2, parent: TreeNode | Root): TreeNode { + private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode | Root): TreeNode { const disposable = new DisposableStore(); const handle = this.createHandle(element, extensionTreeItem, parent); const icon = this.getLightIconPath(extensionTreeItem); @@ -600,7 +602,7 @@ class ExtHostTreeView extends Disposable { }; } - private getThemeIcon(extensionTreeItem: vscode.TreeItem2): ThemeIcon | undefined { + private getThemeIcon(extensionTreeItem: vscode.TreeItem): ThemeIcon | undefined { return extensionTreeItem.iconPath instanceof ThemeIcon ? extensionTreeItem.iconPath : undefined; } diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts index ca249267d..29a50ae27 100644 --- a/src/vs/workbench/api/common/extHostTunnelService.ts +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -6,10 +6,11 @@ import { ExtHostTunnelServiceShape, MainContext, MainThreadTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import * as vscode from 'vscode'; -import { RemoteTunnel, TunnelOptions } from 'vs/platform/remote/common/tunnel'; +import { RemoteTunnel, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export interface TunnelDto { remoteAddress: { port: number, host: string }; @@ -32,7 +33,7 @@ export interface Tunnel extends vscode.Disposable { export interface IExtHostTunnelService extends ExtHostTunnelServiceShape { readonly _serviceBrand: undefined; - openTunnel(forward: TunnelOptions): Promise; + openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise; getTunnels(): Promise; onDidChangeTunnels: vscode.Event; setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise; @@ -51,23 +52,17 @@ export class ExtHostTunnelService implements IExtHostTunnelService { this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService); } - async openTunnel(forward: TunnelOptions): Promise { + async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise { return undefined; } async getTunnels(): Promise { return []; } - async $findCandidatePorts(): Promise<{ host: string, port: number; detail: string; }[]> { - return []; - } - async $filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise { - return candidates.map(() => true); - } async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise { await this._proxy.$tunnelServiceReady(); return { dispose: () => { } }; } - $forwardPort(tunnelOptions: TunnelOptions): Promise | undefined { return undefined; } + $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise | undefined { return undefined; } async $closeTunnel(remote: { host: string, port: number }): Promise { } async $onDidTunnelsChange(): Promise { } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index c0eb2a21f..d80dfae4b 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -7,13 +7,12 @@ import * as modes from 'vs/editor/common/modes'; import * as types from './extHostTypes'; import * as search from 'vs/workbench/contrib/search/common/search'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model'; import type * as vscode from 'vscode'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress'; -import { SaveReason } from 'vs/workbench/common/editor'; +import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor'; import { IPosition } from 'vs/editor/common/core/position'; import * as editorRange from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; @@ -33,6 +32,7 @@ import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { CellOutputKind, IDisplayOutput, INotebookDecorationRenderOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ITestItem, ITestState } from 'vs/workbench/contrib/testing/common/testCollection'; export interface PositionLike { line: number; @@ -220,7 +220,7 @@ export namespace DiagnosticSeverity { } export namespace ViewColumn { - export function from(column?: vscode.ViewColumn): EditorViewColumn { + export function from(column?: vscode.ViewColumn): EditorGroupColumn { if (typeof column === 'number' && column >= types.ViewColumn.One) { return column - 1; // adjust zero index (ViewColumn.ONE => 0) } @@ -232,12 +232,12 @@ export namespace ViewColumn { return ACTIVE_GROUP; // default is always the active group } - export function to(position: EditorViewColumn): vscode.ViewColumn { + export function to(position: EditorGroupColumn): vscode.ViewColumn { if (typeof position === 'number' && position >= 0) { return position + 1; // adjust to index (ViewColumn.ONE => 1) } - throw new Error(`invalid 'EditorViewColumn'`); + throw new Error(`invalid 'EditorGroupColumn'`); } } @@ -274,8 +274,8 @@ export namespace MarkdownString { if (isCodeblock(markup)) { const { language, value } = markup; res = { value: '```' + language + '\n' + value + '\n```\n' }; - } else if (htmlContent.isMarkdownString(markup)) { - res = markup; + } else if (types.MarkdownString.isMarkdownString(markup)) { + res = { value: markup.value, isTrusted: markup.isTrusted, supportThemeIcons: markup.supportThemeIcons }; } else if (typeof markup === 'string') { res = { value: markup }; } else { @@ -343,7 +343,7 @@ export namespace MarkdownString { return result; } - export function fromStrict(value: string | types.MarkdownString): undefined | string | htmlContent.IMarkdownString { + export function fromStrict(value: string | vscode.MarkdownString): undefined | string | htmlContent.IMarkdownString { if (!value) { return undefined; } @@ -1183,6 +1183,7 @@ export namespace FoldingRangeKind { export interface TextEditorOpenOptions extends vscode.TextDocumentShowOptions { background?: boolean; + override?: boolean; } export namespace TextEditorOpenOptions { @@ -1194,6 +1195,7 @@ export namespace TextEditorOpenOptions { inactive: options.background, preserveFocus: options.preserveFocus, selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined, + override: typeof options.override === 'boolean' ? false : undefined }; } @@ -1253,50 +1255,6 @@ export namespace LanguageSelector { } } -export namespace LogLevel { - export function from(extLevel: types.LogLevel): _MainLogLevel { - switch (extLevel) { - case types.LogLevel.Trace: - return _MainLogLevel.Trace; - case types.LogLevel.Debug: - return _MainLogLevel.Debug; - case types.LogLevel.Info: - return _MainLogLevel.Info; - case types.LogLevel.Warning: - return _MainLogLevel.Warning; - case types.LogLevel.Error: - return _MainLogLevel.Error; - case types.LogLevel.Critical: - return _MainLogLevel.Critical; - case types.LogLevel.Off: - return _MainLogLevel.Off; - default: - return _MainLogLevel.Info; - } - } - - export function to(mainLevel: _MainLogLevel): types.LogLevel { - switch (mainLevel) { - case _MainLogLevel.Trace: - return types.LogLevel.Trace; - case _MainLogLevel.Debug: - return types.LogLevel.Debug; - case _MainLogLevel.Info: - return types.LogLevel.Info; - case _MainLogLevel.Warning: - return types.LogLevel.Warning; - case _MainLogLevel.Error: - return types.LogLevel.Error; - case _MainLogLevel.Critical: - return types.LogLevel.Critical; - case _MainLogLevel.Off: - return types.LogLevel.Off; - default: - return types.LogLevel.Info; - } - } -} - export namespace NotebookCellOutput { export function from(output: types.NotebookCellOutput): IDisplayOutput { return output.toJSON(); @@ -1395,3 +1353,64 @@ export namespace NotebookDecorationRenderOptions { }; } } + +export namespace TestState { + export function from(item: vscode.TestState): ITestState { + return { + runState: item.runState, + duration: item.duration, + messages: item.messages.map(message => ({ + message: MarkdownString.fromStrict(message.message) || '', + severity: message.severity, + expectedOutput: message.expectedOutput, + actualOutput: message.actualOutput, + location: message.location ? location.from(message.location) : undefined, + })), + }; + } + + export function to(item: ITestState): vscode.TestState { + return new types.TestState( + item.runState, + item.messages.map(message => ({ + message: typeof message.message === 'string' ? message.message : MarkdownString.to(message.message), + severity: message.severity, + expectedOutput: message.expectedOutput, + actualOutput: message.actualOutput, + location: message.location && location.to({ + range: message.location.range, + uri: URI.revive(message.location.uri) + }), + })), + item.duration, + ); + } +} + + +export namespace TestItem { + export function from(item: vscode.TestItem): ITestItem { + return { + label: item.label, + location: item.location ? location.from(item.location) : undefined, + debuggable: item.debuggable, + description: item.description, + runnable: item.runnable, + state: TestState.from(item.state), + }; + } + + export function to(item: ITestItem): vscode.TestItem { + return { + label: item.label, + location: item.location && location.to({ + range: item.location.range, + uri: URI.revive(item.location.uri) + }), + debuggable: item.debuggable, + description: item.description, + runnable: item.runnable, + state: TestState.to(item.state), + }; + } +} diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 6eb858eee..069c56583 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { coalesceInPlace, equals } from 'vs/base/common/arrays'; -import { escapeCodicons } from 'vs/base/common/codicons'; import { illegalArgument } from 'vs/base/common/errors'; import { IRelativePattern } from 'vs/base/common/glob'; -import { isMarkdownString } from 'vs/base/common/htmlContent'; +import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent'; import { ResourceMap } from 'vs/base/common/map'; import { isStringArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -1276,54 +1275,10 @@ export class CodeLens { } } - -export class CodeInset { - - range: Range; - height?: number; - - constructor(range: Range, height?: number) { - this.range = range; - this.height = height; - } -} - - @es5ClassCompat -export class MarkdownString { +export class MarkdownString implements vscode.MarkdownString { - value: string; - isTrusted?: boolean; - readonly supportThemeIcons?: boolean; - - constructor(value?: string, supportThemeIcons: boolean = false) { - this.value = value ?? ''; - this.supportThemeIcons = supportThemeIcons; - } - - appendText(value: string): MarkdownString { - // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this.value += (this.supportThemeIcons ? escapeCodicons(value) : value) - .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') - .replace(/\n/, '\n\n'); - - return this; - } - - appendMarkdown(value: string): MarkdownString { - this.value += value; - - return this; - } - - appendCodeblock(code: string, language: string = ''): MarkdownString { - this.value += '\n```'; - this.value += language; - this.value += '\n'; - this.value += code; - this.value += '\n```\n'; - return this; - } + readonly #delegate: BaseMarkdownString; static isMarkdownString(thing: any): thing is vscode.MarkdownString { if (thing instanceof MarkdownString) { @@ -1331,15 +1286,55 @@ export class MarkdownString { } return thing && thing.appendCodeblock && thing.appendMarkdown && thing.appendText && (thing.value !== undefined); } + + constructor(value?: string, supportThemeIcons: boolean = false) { + this.#delegate = new BaseMarkdownString(value, { supportThemeIcons }); + } + + get value(): string { + return this.#delegate.value; + } + set value(value: string) { + this.#delegate.value = value; + } + + get isTrusted(): boolean | undefined { + return this.#delegate.isTrusted; + } + + set isTrusted(value: boolean | undefined) { + this.#delegate.isTrusted = value; + } + + get supportThemeIcons(): boolean | undefined { + return this.#delegate.supportThemeIcons; + } + + appendText(value: string): vscode.MarkdownString { + this.#delegate.appendText(value); + return this; + } + + appendMarkdown(value: string): vscode.MarkdownString { + this.#delegate.appendMarkdown(value); + return this; + } + + appendCodeblock(value: string, language?: string): vscode.MarkdownString { + this.#delegate.appendCodeblock(language ?? '', value); + return this; + } + + } @es5ClassCompat export class ParameterInformation { label: string | [number, number]; - documentation?: string | MarkdownString; + documentation?: string | vscode.MarkdownString; - constructor(label: string | [number, number], documentation?: string | MarkdownString) { + constructor(label: string | [number, number], documentation?: string | vscode.MarkdownString) { this.label = label; this.documentation = documentation; } @@ -1349,11 +1344,11 @@ export class ParameterInformation { export class SignatureInformation { label: string; - documentation?: string | MarkdownString; + documentation?: string | vscode.MarkdownString; parameters: ParameterInformation[]; activeParameter?: number; - constructor(label: string, documentation?: string | MarkdownString) { + constructor(label: string, documentation?: string | vscode.MarkdownString) { this.label = label; this.documentation = documentation; this.parameters = []; @@ -1439,7 +1434,7 @@ export class CompletionItem implements vscode.CompletionItem { kind?: CompletionItemKind; tags?: CompletionItemTag[]; detail?: string; - documentation?: string | MarkdownString; + documentation?: string | vscode.MarkdownString; sortText?: string; filterText?: string; preselect?: boolean; @@ -2218,13 +2213,18 @@ export enum ConfigurationTarget { @es5ClassCompat export class RelativePattern implements IRelativePattern { base: string; - baseFolder?: URI; - pattern: string; - constructor(base: vscode.WorkspaceFolder | string, pattern: string) { + // expose a `baseFolder: URI` property as a workaround for the short-coming + // of `IRelativePattern` only supporting `base: string` which always translates + // to a `file://` URI. With `baseFolder` we can support non-file based folders + // in searches + // (https://github.com/microsoft/vscode/commit/6326543b11cf4998c8fd1564cab5c429a2416db3) + readonly baseFolder?: URI; + + constructor(base: vscode.WorkspaceFolder | URI | string, pattern: string) { if (typeof base !== 'string') { - if (!base || !URI.isUri(base.uri)) { + if (!base || !URI.isUri(base) && !URI.isUri(base.uri)) { throw illegalArgument('base'); } } @@ -2234,7 +2234,11 @@ export class RelativePattern implements IRelativePattern { } if (typeof base === 'string') { + this.baseFolder = URI.file(base); this.base = base; + } else if (URI.isUri(base)) { + this.baseFolder = base; + this.base = base.fsPath; } else { this.baseFolder = base.uri; this.base = base.uri.fsPath; @@ -2369,16 +2373,6 @@ export class EvaluatableExpression implements vscode.EvaluatableExpression { } } -export enum LogLevel { - Trace = 1, - Debug = 2, - Info = 3, - Warning = 4, - Error = 5, - Critical = 6, - Off = 7 -} - //#region file api export enum FileChangeType { @@ -2749,8 +2743,8 @@ export enum ExtensionKind { export class FileDecoration { static validate(d: FileDecoration): void { - if (d.badge && d.badge.length !== 1) { - throw new Error(`The 'badge'-property must be undefined or a single character`); + if (d.badge && d.badge.length !== 1 && d.badge.length !== 2) { + throw new Error(`The 'badge'-property must be undefined or a short character`); } if (!d.color && !d.badge && !d.tooltip) { throw new Error(`The decoration is empty`); @@ -2920,3 +2914,64 @@ export enum StandardTokenType { String = 2, RegEx = 4 } + + +export class LinkedEditingRanges { + constructor(public readonly ranges: Range[], public readonly wordPattern?: RegExp) { + } +} + +//#region Testing +export enum TestRunState { + Unset = 0, + Running = 1, + Passed = 2, + Failed = 3, + Skipped = 4, + Errored = 5 +} + +export enum TestMessageSeverity { + Error = 0, + Warning = 1, + Information = 2, + Hint = 3 +} + +@es5ClassCompat +export class TestState { + #runState: TestRunState; + #duration?: number; + #messages: ReadonlyArray>; + + public get runState() { + return this.#runState; + } + + public get duration() { + return this.#duration; + } + + public get messages() { + return this.#messages; + } + + constructor(runState: TestRunState, messages: vscode.TestMessage[] = [], duration?: number) { + this.#runState = runState; + this.#messages = Object.freeze(messages.map(m => Object.freeze(m))); + this.#duration = duration; + } +} + +type AllowedUndefined = 'description' | 'location'; + +/** + * Test item without any optional properties. Only some properties are + * permitted to be undefined, but they must still exist. + */ +export type RequiredTestItem = { + [K in keyof Required]: K extends AllowedUndefined ? vscode.TestItem[K] : Required[K] +}; + + +//#endregion diff --git a/src/vs/workbench/api/common/extHostWebviewPanels.ts b/src/vs/workbench/api/common/extHostWebviewPanels.ts index d29fdc631..797702b88 100644 --- a/src/vs/workbench/api/common/extHostWebviewPanels.ts +++ b/src/vs/workbench/api/common/extHostWebviewPanels.ts @@ -12,7 +12,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { convertWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import { EditorGroupColumn } from 'vs/workbench/common/editor'; import type * as vscode from 'vscode'; import * as extHostProtocol from './extHost.protocol'; import * as extHostTypes from './extHostTypes'; @@ -273,7 +273,7 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel viewType: string, title: string, state: any, - position: EditorViewColumn, + position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions ): Promise { const entry = this._serializers.get(viewType); diff --git a/src/vs/workbench/api/common/extHostWorkspace.ts b/src/vs/workbench/api/common/extHostWorkspace.ts index 7ba5fbca0..3e0de07e3 100644 --- a/src/vs/workbench/api/common/extHostWorkspace.ts +++ b/src/vs/workbench/api/common/extHostWorkspace.ts @@ -10,17 +10,18 @@ import { Emitter, Event } from 'vs/base/common/event'; import { TernarySearchTree } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { Counter } from 'vs/base/common/numbers'; -import { isLinux } from 'vs/base/common/platform'; import { basename, basenameOrAuthority, dirname, isEqual, relativePath } from 'vs/base/common/resources'; import { compare } from 'vs/base/common/strings'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { Severity } from 'vs/platform/notification/common/notification'; import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes'; @@ -59,6 +60,11 @@ function delta(oldFolders: vscode.WorkspaceFolder[], newFolders: vscode.Workspac return arrayDelta(oldSortedFolders, newSortedFolders, compare); } +function ignorePathCasing(uri: URI, extHostFileSystemInfo: IExtHostFileSystemInfo): boolean { + const capabilities = extHostFileSystemInfo.getCapabilities(uri.scheme); + return !(capabilities && (capabilities & FileSystemProviderCapabilities.PathCaseSensitive)); +} + interface MutableWorkspaceFolder extends vscode.WorkspaceFolder { name: string; index: number; @@ -66,7 +72,7 @@ interface MutableWorkspaceFolder extends vscode.WorkspaceFolder { class ExtHostWorkspaceImpl extends Workspace { - static toExtHostWorkspace(data: IWorkspaceData | null, previousConfirmedWorkspace?: ExtHostWorkspaceImpl, previousUnconfirmedWorkspace?: ExtHostWorkspaceImpl): { workspace: ExtHostWorkspaceImpl | null, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } { + static toExtHostWorkspace(data: IWorkspaceData | null, previousConfirmedWorkspace: ExtHostWorkspaceImpl | undefined, previousUnconfirmedWorkspace: ExtHostWorkspaceImpl | undefined, extHostFileSystemInfo: IExtHostFileSystemInfo): { workspace: ExtHostWorkspaceImpl | null, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } { if (!data) { return { workspace: null, added: [], removed: [] }; } @@ -99,7 +105,7 @@ class ExtHostWorkspaceImpl extends Workspace { // make sure to restore sort order based on index newWorkspaceFolders.sort((f1, f2) => f1.index < f2.index ? -1 : 1); - const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders, configuration ? URI.revive(configuration) : null, !!isUntitled); + const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders, configuration ? URI.revive(configuration) : null, !!isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo)); const { added, removed } = delta(oldWorkspace ? oldWorkspace.workspaceFolders : [], workspace.workspaceFolders, compareWorkspaceFolderByUri); return { workspace, added, removed }; @@ -117,10 +123,11 @@ class ExtHostWorkspaceImpl extends Workspace { } private readonly _workspaceFolders: vscode.WorkspaceFolder[] = []; - private readonly _structure = TernarySearchTree.forUris(!isLinux); + private readonly _structure: TernarySearchTree; - constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], configuration: URI | null, private _isUntitled: boolean) { - super(id, folders.map(f => new WorkspaceFolder(f)), configuration); + constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], configuration: URI | null, private _isUntitled: boolean, ignorePathCasing: (key: URI) => boolean) { + super(id, folders.map(f => new WorkspaceFolder(f)), configuration, ignorePathCasing); + this._structure = TernarySearchTree.forUris(ignorePathCasing); // setup the workspace folder data structure folders.forEach(folder => { @@ -170,22 +177,25 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac private readonly _proxy: MainThreadWorkspaceShape; private readonly _messageService: MainThreadMessageServiceShape; + private readonly _extHostFileSystemInfo: IExtHostFileSystemInfo; private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = []; constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @IExtHostInitDataService initData: IExtHostInitDataService, + @IExtHostFileSystemInfo extHostFileSystemInfo: IExtHostFileSystemInfo, @ILogService logService: ILogService, ) { this._logService = logService; + this._extHostFileSystemInfo = extHostFileSystemInfo; this._requestIdProvider = new Counter(); this._barrier = new Barrier(); this._proxy = extHostRpc.getProxy(MainContext.MainThreadWorkspace); this._messageService = extHostRpc.getProxy(MainContext.MainThreadMessageService); const data = initData.workspace; - this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled) : undefined; + this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo)) : undefined; } $initializeWorkspace(data: IWorkspaceData | null): void { @@ -391,13 +401,13 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac configuration: this._actualWorkspace.configuration, folders, isUntitled: this._actualWorkspace.isUntitled - } as IWorkspaceData, this._actualWorkspace).workspace || undefined; + } as IWorkspaceData, this._actualWorkspace, undefined, this._extHostFileSystemInfo).workspace || undefined; } } $acceptWorkspaceData(data: IWorkspaceData | null): void { - const { workspace, added, removed } = ExtHostWorkspaceImpl.toExtHostWorkspace(data, this._confirmedWorkspace, this._unconfirmedWorkspace); + const { workspace, added, removed } = ExtHostWorkspaceImpl.toExtHostWorkspace(data, this._confirmedWorkspace, this._unconfirmedWorkspace, this._extHostFileSystemInfo); // Update our workspace object. We have a confirmed workspace, so we drop our // unconfirmed workspace. @@ -457,17 +467,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac } : options.previewOptions; - let includePattern: string | undefined; - let folder: URI | undefined; - if (options.include) { - if (typeof options.include === 'string') { - includePattern = options.include; - } else { - includePattern = options.include.pattern; - folder = (options.include as RelativePattern).baseFolder || URI.file(options.include.base); - } - } - + const { includePattern, folder } = parseSearchInclude(options.include); const excludePattern = (typeof options.exclude === 'string') ? options.exclude : options.exclude ? options.exclude.pattern : undefined; const queryOptions: ITextQueryBuilderOptions = { @@ -562,14 +562,12 @@ function parseSearchInclude(include: RelativePattern | string | undefined): { in includePattern = include; } else { includePattern = include.pattern; - - // include.base must be an absolute path includeFolder = include.baseFolder || URI.file(include.base); } } return { - includePattern: includePattern, + includePattern, folder: includeFolder }; } diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 2a393e99c..32027cbe2 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -73,19 +73,19 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.DebugToolBar, description: localize('menus.debugToolBar', "The debug toolbar menu") }, - { - key: 'menuBar/webNavigation', - id: MenuId.MenubarWebNavigationMenu, - description: localize('menus.webNavigation', "The top level navigational menu (web only)"), - proposed: true, - supportsSubmenus: false - }, { key: 'menuBar/file', id: MenuId.MenubarFileMenu, description: localize('menus.file', "The top level file menu"), proposed: true }, + { + key: 'menuBar/home', + id: MenuId.MenubarHomeMenu, + description: localize('menus.home', "The home indicator context menu (web only)"), + proposed: true, + supportsSubmenus: false + }, { key: 'scm/title', id: MenuId.SCMTitle, @@ -461,7 +461,7 @@ namespace schema { type: 'string' }, enablement: { - description: localize('vscode.extension.contributes.commandType.precondition', '(Optional) Condition which must be true to enable the command'), + description: localize('vscode.extension.contributes.commandType.precondition', '(Optional) Condition which must be true to enable the command in the UI (menu and keybindings). Does not prevent executing the command by other means, like the `executeCommand`-api.'), type: 'string' }, icon: { @@ -586,6 +586,10 @@ submenusExtensionPoint.setHandler(extensions => { collector.warn(localize('submenuId.invalid.id', "`{0}` is not a valid submenu identifier", entry.value.id)); return; } + if (_submenus.has(entry.value.id)) { + collector.warn(localize('submenuId.duplicate.id', "The `{0}` submenu was already previously registered.", entry.value.id)); + return; + } if (!entry.value.label) { collector.warn(localize('submenuId.invalid.label', "`{0}` is not a valid submenu label", entry.value.label)); return; @@ -616,6 +620,7 @@ submenusExtensionPoint.setHandler(extensions => { const _apiMenusByKey = new Map(Iterable.map(Iterable.from(apiMenus), menu => ([menu.key, menu]))); const _menuRegistrations = new DisposableStore(); +const _submenuMenuItems = new Map>(); const menusExtensionPoint = ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: (schema.IUserFriendlyMenuItem | schema.IUserFriendlySubmenuItem)[] }>({ extensionPoint: 'menus', @@ -627,6 +632,7 @@ menusExtensionPoint.setHandler(extensions => { // remove all previous menu registrations _menuRegistrations.clear(); + _submenuMenuItems.clear(); const items: { id: MenuId, item: IMenuItem | ISubmenuItem }[] = []; @@ -694,6 +700,20 @@ menusExtensionPoint.setHandler(extensions => { continue; } + let submenuRegistrations = _submenuMenuItems.get(menu.id.id); + + if (!submenuRegistrations) { + submenuRegistrations = new Set(); + _submenuMenuItems.set(menu.id.id, submenuRegistrations); + } + + if (submenuRegistrations.has(submenu.id.id)) { + collector.warn(localize('submenuItem.duplicate', "The `{0}` submenu was already contributed to the `{1}` menu.", menuItem.submenu, entry.key)); + continue; + } + + submenuRegistrations.add(submenu.id.id); + item = { submenu: submenu.id, icon: submenu.icon, title: submenu.label, group: undefined, order: undefined, when: undefined }; } diff --git a/src/vs/workbench/api/common/shared/editor.ts b/src/vs/workbench/api/common/shared/editor.ts deleted file mode 100644 index 247c9b178..000000000 --- a/src/vs/workbench/api/common/shared/editor.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IEditorGroupsService, IEditorGroup, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { GroupIdentifier } from 'vs/workbench/common/editor'; -import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; - -export type EditorViewColumn = number; - -export function viewColumnToEditorGroup(editorGroupService: IEditorGroupsService, position?: EditorViewColumn): GroupIdentifier { - if (typeof position !== 'number' || position === ACTIVE_GROUP) { - return ACTIVE_GROUP; // prefer active group when position is undefined or passed in as such - } - - const groups = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE); - - let candidate = groups[position]; - if (candidate) { - return candidate.id; // found direct match - } - - let firstGroup = groups[0]; - if (groups.length === 1 && firstGroup.count === 0) { - return firstGroup.id; // first editor should always open in first group independent from position provided - } - - return SIDE_GROUP; // open to the side if group not found or we are instructed to -} - -export function editorGroupToViewColumn(editorGroupService: IEditorGroupsService, editorGroup: IEditorGroup | GroupIdentifier): EditorViewColumn { - const group = (typeof editorGroup === 'number') ? editorGroupService.getGroup(editorGroup) : editorGroup; - if (!group) { - throw new Error('Invalid group provided'); - } - - return editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).indexOf(group); -} \ No newline at end of file diff --git a/src/vs/workbench/api/common/shared/tasks.ts b/src/vs/workbench/api/common/shared/tasks.ts index 17396177e..4de859f67 100644 --- a/src/vs/workbench/api/common/shared/tasks.ts +++ b/src/vs/workbench/api/common/shared/tasks.ts @@ -113,7 +113,7 @@ export interface TaskProcessStartedDTO { export interface TaskProcessEndedDTO { id: string; - exitCode: number; + exitCode: number | undefined; } diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts index b3857616f..bb5ec1cd7 100644 --- a/src/vs/workbench/api/node/extHostCLIServer.ts +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -15,7 +15,7 @@ import { ILogService } from 'vs/platform/log/common/log'; export interface OpenCommandPipeArgs { type: 'open'; fileURIs?: string[]; - folderURIs: string[]; + folderURIs?: string[]; forceNewWindow?: boolean; diffMode?: boolean; addMode?: boolean; @@ -24,6 +24,11 @@ export interface OpenCommandPipeArgs { waitMarkerFilePath?: string; } +export interface OpenExternalCommandPipeArgs { + type: 'openExternal'; + uris: string[]; +} + export interface StatusPipeArgs { type: 'status'; } @@ -34,6 +39,8 @@ export interface RunCommandPipeArgs { args: any[]; } +export type PipeCommand = OpenCommandPipeArgs | StatusPipeArgs | RunCommandPipeArgs | OpenExternalCommandPipeArgs; + export interface ICommandsExecuter { executeCommand(id: string, ...args: any[]): Promise; } @@ -73,11 +80,14 @@ export class CLIServerBase { req.setEncoding('utf8'); req.on('data', (d: string) => chunks.push(d)); req.on('end', () => { - const data: OpenCommandPipeArgs | StatusPipeArgs | RunCommandPipeArgs | any = JSON.parse(chunks.join('')); + const data: PipeCommand | any = JSON.parse(chunks.join('')); switch (data.type) { case 'open': this.open(data, res); break; + case 'openExternal': + this.openExternal(data, res); + break; case 'status': this.getStatus(data, res); break; @@ -133,6 +143,14 @@ export class CLIServerBase { res.end(); } + private openExternal(data: OpenExternalCommandPipeArgs, res: http.ServerResponse) { + for (const uri of data.uris) { + this._commands.executeCommand('_workbench.openExternal', URI.parse(uri), { allowTunneling: true }); + } + res.writeHead(200); + res.end(); + } + private async getStatus(data: StatusPipeArgs, res: http.ServerResponse) { try { const status = await this._commands.executeCommand('_issues.getSystemStatus'); diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index c68c242a4..ff76f109b 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -14,7 +14,6 @@ import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensi import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug'; import { IExtHostConfiguration, ExtHostConfigProvider } from '../common/extHostConfiguration'; -import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; @@ -24,13 +23,14 @@ import { SignService } from 'vs/platform/sign/node/signService'; import { hasChildProcesses, prepareCommand, runInExternalTerminal } from 'vs/workbench/contrib/debug/node/terminals'; import { IDisposable } from 'vs/base/common/lifecycle'; import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; +import { createCancelablePromise, firstParallel } from 'vs/base/common/async'; export class ExtHostDebugService extends ExtHostDebugServiceBase { readonly _serviceBrand: undefined; - private _integratedTerminalInstance?: vscode.Terminal; + private _integratedTerminalInstances = new DebugTerminalCollection(); private _terminalDisposedListener: IDisposable | undefined; constructor( @@ -39,10 +39,9 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { @IExtHostExtensionService extensionService: IExtHostExtensionService, @IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors, @IExtHostConfiguration configurationService: IExtHostConfiguration, - @IExtHostTerminalService private _terminalService: IExtHostTerminalService, - @IExtHostCommands commandService: IExtHostCommands + @IExtHostTerminalService private _terminalService: IExtHostTerminalService ) { - super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, commandService); + super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService); } protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined { @@ -76,40 +75,44 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { if (!this._terminalDisposedListener) { // React on terminal disposed and check if that is the debug terminal #12956 this._terminalDisposedListener = this._terminalService.onDidCloseTerminal(terminal => { - if (this._integratedTerminalInstance && this._integratedTerminalInstance === terminal) { - this._integratedTerminalInstance = undefined; - } + this._integratedTerminalInstances.onTerminalClosed(terminal); }); } - let needNewTerminal = true; // be pessimistic - if (this._integratedTerminalInstance) { - const pid = await this._integratedTerminalInstance.processId; - needNewTerminal = await hasChildProcesses(pid); // if no processes running in terminal reuse terminal - } - const configProvider = await this._configurationService.getConfigProvider(); const shell = this._terminalService.getDefaultShell(true, configProvider); + const shellArgs = this._terminalService.getDefaultShellArgs(true, configProvider); + + const shellConfig = JSON.stringify({ shell, shellArgs }); + let terminal = await this._integratedTerminalInstances.checkout(shellConfig); + let cwdForPrepareCommand: string | undefined; + let giveShellTimeToInitialize = false; - if (needNewTerminal || !this._integratedTerminalInstance) { - + if (!terminal) { const options: vscode.TerminalOptions = { shellPath: shell, - // shellArgs: this._terminalService._getDefaultShellArgs(configProvider), + shellArgs: shellArgs, cwd: args.cwd, name: args.title || nls.localize('debug.terminal.title', "debuggee"), }; - this._integratedTerminalInstance = this._terminalService.createTerminalFromOptions(options, true); + giveShellTimeToInitialize = true; + terminal = this._terminalService.createTerminalFromOptions(options, true); + this._integratedTerminalInstances.insert(terminal, shellConfig); + } else { cwdForPrepareCommand = args.cwd; } - const terminal = this._integratedTerminalInstance; - terminal.show(); - const shellProcessId = await this._integratedTerminalInstance.processId; + const shellProcessId = await terminal.processId; + + if (giveShellTimeToInitialize) { + // give a new terminal some time to initialize the shell + await new Promise(resolve => setTimeout(resolve, 1000)); + } + const command = prepareCommand(shell, args.args, cwdForPrepareCommand, args.env); terminal.sendText(command, true); @@ -123,6 +126,49 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { } protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { - return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment); + return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment, this._workspaceService); + } +} + +class DebugTerminalCollection { + /** + * Delay before a new terminal is a candidate for reuse. See #71850 + */ + private static minUseDelay = 1000; + + private _terminalInstances = new Map(); + + public async checkout(config: string) { + const entries = [...this._terminalInstances.keys()]; + const promises = entries.map((terminal) => createCancelablePromise(async ct => { + const pid = await terminal.processId; + if (await hasChildProcesses(pid)) { + return null; + } + + // important: date check and map operations must be synchronous + const now = Date.now(); + const termInfo = this._terminalInstances.get(terminal); + if (!termInfo || termInfo.lastUsedAt + DebugTerminalCollection.minUseDelay > now || ct.isCancellationRequested) { + return null; + } + + if (termInfo.config !== config) { + return null; + } + + termInfo.lastUsedAt = now; + return terminal; + })); + + return await firstParallel(promises, (t): t is vscode.Terminal => !!t); + } + + public insert(terminal: vscode.Terminal, termConfig: string) { + this._terminalInstances.set(terminal, { lastUsedAt: Date.now(), config: termConfig }); + } + + public onTerminalClosed(terminal: vscode.Terminal) { + this._terminalInstances.delete(terminal); } } diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 01fc86aec..9d5951b84 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -50,6 +50,10 @@ export class ExtHostTask extends ExtHostTaskBase { } public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise { + if (!task.execution) { + throw new Error('Tasks to execute must include an execution'); + } + const tTask = (task as types.Task); // We have a preserved ID. So the task didn't change. if (tTask._id !== undefined) { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index cdc252bc1..01a34ff3f 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -23,6 +23,7 @@ import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/co import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { withNullAsUndefined } from 'vs/base/common/types'; export class ExtHostTerminalService extends BaseExtHostTerminalService { @@ -56,7 +57,15 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { public createTerminalFromOptions(options: vscode.TerminalOptions, isFeatureTerminal?: boolean): vscode.Terminal { const terminal = new ExtHostTerminal(this._proxy, options, options.name); this._terminals.push(terminal); - terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv, options.hideFromUser, isFeatureTerminal); + terminal.create( + withNullAsUndefined(options.shellPath), + withNullAsUndefined(options.shellArgs), + withNullAsUndefined(options.cwd), + withNullAsUndefined(options.env), + /*options.waitOnExit*/ undefined, + withNullAsUndefined(options.strictEnv), + withNullAsUndefined(options.hideFromUser), + withNullAsUndefined(isFeatureTerminal)); return terminal; } diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index ffd3bd982..922451d10 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -16,7 +16,8 @@ import { isLinux } from 'vs/base/common/platform'; import { IExtHostTunnelService, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { asPromise } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; -import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; +import { TunnelOptions, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; class ExtensionTunnel implements vscode.Tunnel { private _onDispose: Emitter = new Emitter(); @@ -36,9 +37,9 @@ class ExtensionTunnel implements vscode.Tunnel { export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService { readonly _serviceBrand: undefined; private readonly _proxy: MainThreadTunnelServiceShape; - private _forwardPortProvider: ((tunnelOptions: TunnelOptions) => Thenable | undefined) | undefined; + private _forwardPortProvider: ((tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable | undefined) | undefined; private _showCandidatePort: (host: string, port: number, detail: string) => Thenable = () => { return Promise.resolve(true); }; - private _extensionTunnels: Map> = new Map(); + private _extensionTunnels: Map> = new Map(); private _onDidChangeTunnels: Emitter = new Emitter(); onDidChangeTunnels: vscode.Event = this._onDidChangeTunnels.event; @@ -53,8 +54,8 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe } } - async openTunnel(forward: TunnelOptions): Promise { - const tunnel = await this._proxy.$openTunnel(forward); + async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise { + const tunnel = await this._proxy.$openTunnel(forward, extension.displayName); if (tunnel) { const disposableTunnel: vscode.Tunnel = new ExtensionTunnel(tunnel.remoteAddress, tunnel.localAddress, () => { return this._proxy.$closeTunnel(tunnel.remoteAddress); @@ -69,21 +70,25 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe return this._proxy.$getTunnels(); } - registerCandidateFinder(): Promise { - return this._proxy.$registerCandidateFinder(); - } - - $filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise { - return Promise.all(candidates.map(candidate => { - return this._showCandidatePort(candidate.host, candidate.port, candidate.detail); - })); + registerCandidateFinder(): void { + // Every two seconds, scan to see if the candidate ports have changed; + if (isLinux) { + let oldPorts: { host: string, port: number, detail: string }[] | undefined = undefined; + setInterval(async () => { + const newPorts = await this.findCandidatePorts(); + if (!oldPorts || (JSON.stringify(oldPorts) !== JSON.stringify(newPorts))) { + oldPorts = newPorts; + this._proxy.$onFoundNewCandidates(oldPorts.filter(async (candidate) => await this._showCandidatePort(candidate.host, candidate.port, candidate.detail))); + return; + } + }, 2000); + } } async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise { if (provider) { if (provider.showCandidatePort) { this._showCandidatePort = provider.showCandidatePort; - await this._proxy.$setCandidateFilter(); } if (provider.tunnelFactory) { this._forwardPortProvider = provider.tunnelFactory; @@ -98,11 +103,14 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe }); } - async $closeTunnel(remote: { host: string, port: number }): Promise { + async $closeTunnel(remote: { host: string, port: number }, silent?: boolean): Promise { if (this._extensionTunnels.has(remote.host)) { const hostMap = this._extensionTunnels.get(remote.host)!; if (hostMap.has(remote.port)) { - hostMap.get(remote.port)!.dispose(); + if (silent) { + hostMap.get(remote.port)!.disposeListener.dispose(); + } + hostMap.get(remote.port)!.tunnel.dispose(); hostMap.delete(remote.port); } } @@ -112,16 +120,16 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe this._onDidChangeTunnels.fire(); } - $forwardPort(tunnelOptions: TunnelOptions): Promise | undefined { + $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise | undefined { if (this._forwardPortProvider) { - const providedPort = this._forwardPortProvider!(tunnelOptions); + const providedPort = this._forwardPortProvider(tunnelOptions, tunnelCreationOptions); if (providedPort !== undefined) { return asPromise(() => providedPort).then(tunnel => { if (!this._extensionTunnels.has(tunnelOptions.remoteAddress.host)) { this._extensionTunnels.set(tunnelOptions.remoteAddress.host, new Map()); } - this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, tunnel); - this._register(tunnel.onDidDispose(() => this._proxy.$closeTunnel(tunnel.remoteAddress))); + const disposeListener = this._register(tunnel.onDidDispose(() => this._proxy.$closeTunnel(tunnel.remoteAddress))); + this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, { tunnel, disposeListener }); return Promise.resolve(TunnelDto.fromApiTunnel(tunnel)); }); } @@ -130,11 +138,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe } - async $findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]> { - if (!isLinux) { - return []; - } - + async findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]> { const ports: { host: string, port: number, detail: string }[] = []; let tcp: string = ''; let tcp6: string = ''; @@ -189,15 +193,19 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe return ports; } - private getSockets(stdout: string) { + private getSockets(stdout: string): { pid: number, socket: number }[] { const lines = stdout.trim().split('\n'); - return lines.map(line => { + const mapped: { pid: number, socket: number }[] = []; + lines.forEach(line => { const match = /\/proc\/(\d+)\/fd\/\d+ -> socket:\[(\d+)\]/.exec(line)!; - return { - pid: parseInt(match[1], 10), - socket: parseInt(match[2], 10) - }; + if (match && match.length >= 3) { + mapped.push({ + pid: parseInt(match[1], 10), + socket: parseInt(match[2], 10) + }); + } }); + return mapped; } private loadListeningPorts(...stdouts: string[]): { socket: number, ip: string, port: number }[] { diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 35f9395f7..cdd2cdfc3 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -8,7 +8,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { SyncActionDescriptor, MenuId, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions, CATEGORIES } from 'vs/workbench/common/actions'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -21,7 +21,6 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { InEditorZenModeContext, IsCenteredLayoutContext, EditorAreaVisibleContext } from 'vs/workbench/common/editor'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SideBarVisibleContext } from 'vs/workbench/common/viewlet'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IViewDescriptorService, IViewsService, FocusedViewContext, ViewContainerLocation, IViewDescriptor } from 'vs/workbench/common/views'; import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -76,7 +75,7 @@ export class ToggleActivityBarVisibilityAction extends Action2 { const visibility = layoutService.isVisible(Parts.ACTIVITYBAR_PART); const newVisibilityValue = !visibility; - configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); + configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue); } } @@ -196,7 +195,7 @@ export class ToggleSidebarPositionAction extends Action { const position = this.layoutService.getSideBarPosition(); const newPositionValue = (position === Position.LEFT) ? 'right' : 'left'; - return this.configurationService.updateValue(ToggleSidebarPositionAction.sidebarPositionConfigurationKey, newPositionValue, ConfigurationTarget.USER); + return this.configurationService.updateValue(ToggleSidebarPositionAction.sidebarPositionConfigurationKey, newPositionValue); } static getLabel(layoutService: IWorkbenchLayoutService): string { @@ -317,7 +316,7 @@ export class ToggleStatusbarVisibilityAction extends Action { const visibility = this.layoutService.isVisible(Parts.STATUSBAR_PART); const newVisibilityValue = !visibility; - return this.configurationService.updateValue(ToggleStatusbarVisibilityAction.statusbarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); + return this.configurationService.updateValue(ToggleStatusbarVisibilityAction.statusbarVisibleKey, newVisibilityValue); } } @@ -419,14 +418,13 @@ export class ToggleMenuBarAction extends Action { constructor( id: string, label: string, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(id, label); } run(): Promise { - let currentVisibilityValue = getMenuBarVisibility(this.configurationService, this.environmentService); + let currentVisibilityValue = getMenuBarVisibility(this.configurationService); if (typeof currentVisibilityValue !== 'string') { currentVisibilityValue = 'default'; } diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 5c8d8c70d..b71489ac3 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -356,19 +356,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // List if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - list.focusPreviousPage(); - list.reveal(list.getFocus()[0]); + focused.focusPreviousPage(); } // Tree else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.focusPreviousPage(fakeKeyboardEvent); - list.reveal(list.getFocus()[0]); + focused.focusPreviousPage(fakeKeyboardEvent); } // Ensure DOM Focus @@ -386,19 +380,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // List if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - list.focusNextPage(); - list.reveal(list.getFocus()[0]); + focused.focusNextPage(); } // Tree else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.focusNextPage(fakeKeyboardEvent); - list.reveal(list.getFocus()[0]); + focused.focusNextPage(fakeKeyboardEvent); } // Ensure DOM Focus diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index 7344a3a29..5833c3291 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -12,13 +12,10 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IPanel } from 'vs/workbench/common/panel'; -import { Action2, MenuId, registerAction2, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions, CATEGORIES } from 'vs/workbench/common/actions'; import { Direction } from 'vs/base/browser/ui/grid/grid'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { isAncestor } from 'vs/base/browser/dom'; @@ -275,29 +272,6 @@ export class FocusPreviousPart extends Action { } } -class GoHomeContributor implements IWorkbenchContribution { - - constructor( - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService - ) { - const homeIndicator = environmentService.options?.homeIndicator; - if (homeIndicator) { - registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.goHome`, - title: nls.localize('goHome', "Go Home"), - menu: { id: MenuId.MenubarWebNavigationMenu } - }); - } - async run(): Promise { - window.location.href = homeIndicator.href; - } - }); - } - } -} - // --- Actions Registration const actionsRegistry = Registry.as(Extensions.WorkbenchActions); @@ -308,6 +282,3 @@ actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateLeftAc actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateRightAction, undefined), 'View: Navigate to the View on the Right', CATEGORIES.View.value); actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextPart, { primary: KeyCode.F6 }), 'View: Focus Next Part', CATEGORIES.View.value); actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousPart, { primary: KeyMod.Shift | KeyCode.F6 }), 'View: Focus Previous Part', CATEGORIES.View.value); - -const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(GoHomeContributor, LifecyclePhase.Ready); diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 700fb6ce0..b73573760 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -141,7 +141,7 @@ export class OpenWorkspaceConfigFileAction extends Action { async run(): Promise { const configuration = this.workspaceContextService.getWorkspace().configuration; if (configuration) { - await this.editorService.openEditor({ resource: configuration }); + await this.editorService.openEditor({ resource: configuration, options: { pinned: true } }); } } } diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index 24dc2f9d6..7f43e0fb0 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -9,7 +9,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/commo import * as resources from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { FileKind } from 'vs/platform/files/common/files'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -18,6 +18,10 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import { IOpenWindowOptions, IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder'; export const ADD_ROOT_FOLDER_LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace..."); @@ -61,7 +65,7 @@ CommandsRegistry.registerCommand({ title: nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"), canSelectFolders: true, canSelectMany: true, - defaultUri: dialogsService.defaultFolderPath() + defaultUri: await dialogsService.defaultFolderPath() }); if (!folders || !folders.length) { @@ -116,3 +120,46 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, async functio return; }); + +// API Command registration + +interface IOpenFolderAPICommandOptions { + forceNewWindow?: boolean; + forceReuseWindow?: boolean; + noRecentEntry?: boolean; +} + +CommandsRegistry.registerCommand({ + id: 'vscode.openFolder', + handler: (accessor: ServicesAccessor, uri?: URI, arg?: boolean | IOpenFolderAPICommandOptions) => { + const commandService = accessor.get(ICommandService); + + // Be compatible to previous args by converting to options + if (typeof arg === 'boolean') { + arg = { forceNewWindow: arg }; + } + + // Without URI, ask to pick a folder or workpsace to open + if (!uri) { + return commandService.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg?.forceNewWindow }); + } + + uri = URI.revive(uri); + + const options: IOpenWindowOptions = { + forceNewWindow: arg?.forceNewWindow, + forceReuseWindow: arg?.forceReuseWindow, + noRecentEntry: arg?.noRecentEntry + }; + + const uriToOpen: IWindowOpenable = (hasWorkspaceFileExtension(uri) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri }; + return commandService.executeCommand('_files.windowOpen', [uriToOpen], options); + }, + description: { + description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.', + args: [ + { name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || value instanceof URI }, + { name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Wheter the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' } + ] + } +}); diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 53f39bc84..baab13434 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -16,7 +16,7 @@ import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd'; import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; import { normalizeDriveLetter } from 'vs/base/common/labels'; import { MIME_BINARY } from 'vs/base/common/mime'; -import { isWindows, isWeb } from 'vs/base/common/platform'; +import { isWindows } from 'vs/base/common/platform'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorIdentifier, GroupIdentifier } from 'vs/workbench/common/editor'; @@ -27,7 +27,6 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { isStandalone } from 'vs/base/browser/browser'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Emitter } from 'vs/base/common/event'; @@ -320,8 +319,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: event.dataTransfer.setData(DataTransfers.TEXT, sources.map(source => source.resource.scheme === Schemas.file ? normalize(normalizeDriveLetter(source.resource.fsPath)) : source.resource.toString()).join(lineDelimiter)); // Download URL: enables support to drag a tab as file to desktop (only single file supported) - // Disabled for PWA web due to: https://github.com/microsoft/vscode/issues/83441 - if (!sources[0].isDirectory && (!isWeb || !isStandalone)) { + if (!sources[0].isDirectory) { event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, basename(sources[0].resource), FileAccess.asBrowserUri(sources[0].resource).toString()].join(':')); } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 13f62c492..460594ee0 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { EventType, addDisposableListener, isAncestor, getClientArea, Dimension, position, size, IDimension } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo } from 'vs/base/browser/dom'; import { onDidChangeFullscreen, isFullscreen } from 'vs/base/browser/browser'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -16,7 +16,7 @@ import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -41,7 +41,6 @@ import { INotificationService, NotificationsFilter } from 'vs/platform/notificat import { IThemeService } from 'vs/platform/theme/common/themeService'; import { WINDOW_ACTIVE_BORDER, WINDOW_INACTIVE_BORDER } from 'vs/workbench/common/theme'; import { LineNumbersType } from 'vs/editor/common/config/editorOptions'; -import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; import { URI } from 'vs/base/common/uri'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; @@ -91,21 +90,6 @@ enum Classes { WINDOW_BORDER = 'border' } -interface PanelActivityState { - id: string; - name?: string; - pinned: boolean; - order: number; - visible: boolean; -} - -interface SideBarActivityState { - id: string; - pinned: boolean; - order: number; - visible: boolean; -} - export abstract class Layout extends Disposable implements IWorkbenchLayoutService { declare readonly _serviceBrand: undefined; @@ -319,7 +303,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this._register(addDisposableListener(this.container, EventType.SCROLL, () => this.container.scrollTop = 0)); // Menubar visibility changes - if ((isWindows || isLinux || isWeb) && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if ((isWindows || isLinux || isWeb) && getTitleBarStyle(this.configurationService) === 'custom') { this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); } @@ -357,8 +341,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } + // Change edge snapping accordingly + this.workbenchGrid.edgeSnapping = this.state.fullscreen; + // Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if (getTitleBarStyle(this.configurationService) === 'custom') { // Propagate to grid this.workbenchGrid.setViewVisible(this.titleBarPartView, this.isVisible(Parts.TITLEBAR_PART)); @@ -407,7 +394,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Menubar visibility - const newMenubarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService); + const newMenubarVisibility = getMenuBarVisibility(this.configurationService); this.setMenubarVisibility(newMenubarVisibility, !!skipLayout); // Centered Layout @@ -451,7 +438,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private updateWindowBorder(skipLayout: boolean = false) { - if (isWeb || getTitleBarStyle(this.configurationService, this.environmentService) !== 'custom') { + if (isWeb || getTitleBarStyle(this.configurationService) !== 'custom') { return; } @@ -495,7 +482,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.state.fullscreen = isFullscreen(); // Menubar visibility - this.state.menuBar.visibility = getMenuBarVisibility(this.configurationService, this.environmentService); + this.state.menuBar.visibility = getMenuBarVisibility(this.configurationService); // Activity bar visibility this.state.activityBar.hidden = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); @@ -582,167 +569,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const { views } = defaultLayout; if (views?.length) { this.state.views.defaults = views.map(v => v.id); - - return; - } - - // TODO@eamodio Everything below here is deprecated and will be removed once Codespaces migrates - - const { sidebar } = defaultLayout; - if (sidebar) { - if (sidebar.visible !== undefined) { - if (sidebar.visible) { - storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE); - } else { - storageService.store(Storage.SIDEBAR_HIDDEN, true, StorageScope.WORKSPACE); - } - } - - if (sidebar.containers?.length) { - const sidebarState: SideBarActivityState[] = []; - - let order = -1; - for (const container of sidebar.containers.sort((a, b) => (a.order ?? 1) - (b.order ?? 1))) { - let viewletId; - switch (container.id) { - case 'explorer': - viewletId = 'workbench.view.explorer'; - break; - case 'run': - viewletId = 'workbench.view.debug'; - break; - case 'scm': - viewletId = 'workbench.view.scm'; - break; - case 'search': - viewletId = 'workbench.view.search'; - break; - case 'extensions': - viewletId = 'workbench.view.extensions'; - break; - case 'remote': - viewletId = 'workbench.view.remote'; - break; - default: - viewletId = `workbench.view.extension.${container.id}`; - } - - if (container.active) { - storageService.store(SidebarPart.activeViewletSettingsKey, viewletId, StorageScope.WORKSPACE); - } - - if (container.order !== undefined || (container.active === undefined && container.visible !== undefined)) { - order = container.order ?? (order + 1); - const state: SideBarActivityState = { - id: viewletId, - order: order, - pinned: (container.active || container.visible) ?? true, - visible: (container.active || container.visible) ?? true - }; - - sidebarState.push(state); - } - - if (container.views !== undefined) { - const viewsState: { id: string, isHidden?: boolean, order?: number }[] = []; - const viewsWorkspaceState: { [id: string]: { collapsed: boolean, isHidden?: boolean, size?: number } } = {}; - - for (const view of container.views) { - if (view.order !== undefined || view.visible !== undefined) { - viewsState.push({ - id: view.id, - isHidden: view.visible === undefined ? undefined : !view.visible, - order: view.order === undefined ? undefined : view.order - }); - } - - if (view.collapsed !== undefined) { - viewsWorkspaceState[view.id] = { - collapsed: view.collapsed, - isHidden: view.visible === undefined ? undefined : !view.visible, - }; - } - } - - storageService.store(`${viewletId}.state.hidden`, JSON.stringify(viewsState), StorageScope.GLOBAL); - storageService.store(`${viewletId}.state`, JSON.stringify(viewsWorkspaceState), StorageScope.WORKSPACE); - } - } - - if (sidebarState.length) { - storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, JSON.stringify(sidebarState), StorageScope.GLOBAL); - } - } - } - - const { panel } = defaultLayout; - if (panel) { - if (panel.visible !== undefined) { - if (panel.visible) { - storageService.store(Storage.PANEL_HIDDEN, false, StorageScope.WORKSPACE); - } else { - storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE); - } - } - - if (panel.containers?.length) { - const panelState: PanelActivityState[] = []; - - let order = -1; - for (const container of panel.containers.sort((a, b) => (a.order ?? 1) - (b.order ?? 1))) { - let name; - let panelId = container.id; - switch (panelId) { - case 'terminal': - name = 'Terminal'; - panelId = 'workbench.panel.terminal'; - break; - case 'debug': - name = 'Debug Console'; - panelId = 'workbench.panel.repl'; - break; - case 'problems': - name = 'Problems'; - panelId = 'workbench.panel.markers'; - break; - case 'output': - name = 'Output'; - panelId = 'workbench.panel.output'; - break; - case 'comments': - name = 'Comments'; - panelId = 'workbench.panel.comments'; - break; - case 'refactor': - name = 'Refactor Preview'; - panelId = 'refactorPreview'; - break; - default: - continue; - } - - if (container.active) { - storageService.store(PanelPart.activePanelSettingsKey, panelId, StorageScope.WORKSPACE); - } - - if (container.order !== undefined || (container.active === undefined && container.visible !== undefined)) { - order = container.order ?? (order + 1); - const state: PanelActivityState = { - id: panelId, - name: name, - order: order, - pinned: (container.active || container.visible) ?? true, - visible: (container.active || container.visible) ?? true - }; - - panelState.push(state); - } - } - - if (panelState.length) { - storageService.store(PanelPart.PINNED_PANELS, JSON.stringify(panelState), StorageScope.GLOBAL); - } - } } } @@ -750,7 +576,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const initialFilesToOpen = this.getInitialFilesToOpen(); // Only restore editors if we are not instructed to open files initially - this.state.editor.restoreEditors = initialFilesToOpen === undefined; + // or when `window.restoreWindows` setting is explicitly set to `preserve` + const forceRestoreEditors = this.configurationService.getValue('window.restoreWindows') === 'preserve'; + this.state.editor.restoreEditors = !!forceRestoreEditors || initialFilesToOpen === undefined; // Files to open, diff or create if (initialFilesToOpen !== undefined) { @@ -996,7 +824,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const container = this.getContainer(part); - return !!container && isAncestor(activeElement, container); + return !!container && isAncestorUsingFlowTo(activeElement, container); } focusPart(part: Parts): void { @@ -1050,7 +878,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi isVisible(part: Parts): boolean { switch (part) { case Parts.TITLEBAR_PART: - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + if (getTitleBarStyle(this.configurationService) === 'native') { return false; } else if (!this.state.fullscreen && !isWeb) { return true; @@ -1246,7 +1074,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // State if (this.state.zenMode.active) { - this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE); + this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE, StorageTarget.USER); // Exit zen mode on shutdown unless configured to keep this.state.zenMode.transitionDisposables.add(this.storageService.onWillSaveState(e => { @@ -1310,6 +1138,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.container.prepend(workbenchGrid.element); this.container.setAttribute('role', 'application'); this.workbenchGrid = workbenchGrid; + this.workbenchGrid.edgeSnapping = this.state.fullscreen; [titleBar, editorPart, activityBar, panelPart, sideBar, statusBar].forEach((part: Part) => { this._register(part.onDidVisibilityChange((visible) => { @@ -1331,18 +1160,18 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi ? grid.getViewCachedVisibleSize(this.sideBarPartView) : grid.getViewSize(this.sideBarPartView).width; - this.storageService.store(Storage.SIDEBAR_SIZE, sideBarSize, StorageScope.GLOBAL); + this.storageService.store(Storage.SIDEBAR_SIZE, sideBarSize, StorageScope.GLOBAL, StorageTarget.MACHINE); const panelSize = this.state.panel.hidden ? grid.getViewCachedVisibleSize(this.panelPartView) : (this.state.panel.position === Position.BOTTOM ? grid.getViewSize(this.panelPartView).height : grid.getViewSize(this.panelPartView).width); - this.storageService.store(Storage.PANEL_SIZE, panelSize, StorageScope.GLOBAL); - this.storageService.store(Storage.PANEL_DIMENSION, positionToString(this.state.panel.position), StorageScope.GLOBAL); + this.storageService.store(Storage.PANEL_SIZE, panelSize, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(Storage.PANEL_DIMENSION, positionToString(this.state.panel.position), StorageScope.GLOBAL, StorageTarget.MACHINE); const gridSize = grid.getViewSize(); - this.storageService.store(Storage.GRID_WIDTH, gridSize.width, StorageScope.GLOBAL); - this.storageService.store(Storage.GRID_HEIGHT, gridSize.height, StorageScope.GLOBAL); + this.storageService.store(Storage.GRID_WIDTH, gridSize.width, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(Storage.GRID_HEIGHT, gridSize.height, StorageScope.GLOBAL, StorageTarget.MACHINE); })); } @@ -1373,7 +1202,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi centerEditorLayout(active: boolean, skipLayout?: boolean): void { this.state.editor.centered = active; - this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE); + this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE, StorageTarget.USER); let smartActive = active; const activeEditor = this.editorService.activeEditor; @@ -1486,7 +1315,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Remember in settings if (hidden) { - this.storageService.store(Storage.EDITOR_HIDDEN, true, StorageScope.WORKSPACE); + this.storageService.store(Storage.EDITOR_HIDDEN, true, StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(Storage.EDITOR_HIDDEN, StorageScope.WORKSPACE); } @@ -1547,7 +1376,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Remember in settings const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; if (hidden !== defaultHidden) { - this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE); + this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE); } @@ -1612,14 +1441,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Remember in settings if (!hidden) { - this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE); + this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE); // Remember this setting only when panel is hiding if (this.state.panel.wasLastMaximized) { - this.storageService.store(Storage.PANEL_LAST_IS_MAXIMIZED, true, StorageScope.WORKSPACE); + this.storageService.store(Storage.PANEL_LAST_IS_MAXIMIZED, true, StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(Storage.PANEL_LAST_IS_MAXIMIZED, StorageScope.WORKSPACE); @@ -1637,10 +1466,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if (!this.state.panel.hidden) { if (this.state.panel.position === Position.BOTTOM) { this.state.panel.lastNonMaximizedHeight = size.height; - this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_HEIGHT, this.state.panel.lastNonMaximizedHeight, StorageScope.GLOBAL); + this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_HEIGHT, this.state.panel.lastNonMaximizedHeight, StorageScope.GLOBAL, StorageTarget.MACHINE); } else { this.state.panel.lastNonMaximizedWidth = size.width; - this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_WIDTH, this.state.panel.lastNonMaximizedWidth, StorageScope.GLOBAL); + this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_WIDTH, this.state.panel.lastNonMaximizedWidth, StorageScope.GLOBAL, StorageTarget.MACHINE); } } @@ -1715,7 +1544,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.state.panel.position = position; // Save panel position - this.storageService.store(Storage.PANEL_POSITION, newPositionValue, StorageScope.WORKSPACE); + this.storageService.store(Storage.PANEL_POSITION, newPositionValue, StorageScope.WORKSPACE, StorageTarget.USER); // Adjust CSS const panelContainer = assertIsDefined(panelPart.getContainer()); diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index b32718632..cf008e43f 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -220,6 +220,10 @@ body.web { opacity: 1 !important; } +.monaco-workbench input[type="checkbox"]:focus { + outline-offset: 2px; +} + .monaco-workbench [tabindex="0"]:active, .monaco-workbench [tabindex="-1"]:active, .monaco-workbench select:active, diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index fca512810..49bbae451 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -10,15 +10,14 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { dispose } from 'vs/base/common/lifecycle'; -import { SyncActionDescriptor, IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IMenuService, MenuId, IMenu, registerAction2, Action2, IAction2Options } from 'vs/platform/actions/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ActivityAction, ActivityActionViewItem, ICompositeBar, ICompositeBarColors, ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions'; -import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { CATEGORIES } from 'vs/workbench/common/actions'; import { IActivity } from 'vs/workbench/common/activity'; import { ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_ACTIVE_FOCUS_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; @@ -26,44 +25,31 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { Codicon } from 'vs/base/common/codicons'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { getCurrentAuthenticationSessionInfo, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { AuthenticationSession } from 'vs/editor/common/modes'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProductService } from 'vs/platform/product/common/productService'; -import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export class ViewContainerActivityAction extends ActivityAction { private static readonly preventDoubleClickDelay = 300; - private readonly viewletService: IViewletService; - private readonly layoutService: IWorkbenchLayoutService; - private readonly telemetryService: ITelemetryService; - private readonly configurationService: IConfigurationService; - - private lastRun: number; + private lastRun = 0; constructor( activity: IActivity, - @IViewletService viewletService: IViewletService, - @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @ITelemetryService telemetryService: ITelemetryService, - @IConfigurationService configurationService: IConfigurationService + @IViewletService private readonly viewletService: IViewletService, + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(activity); - - this.lastRun = 0; - this.viewletService = viewletService; - this.layoutService = layoutService; - this.telemetryService = telemetryService; - this.configurationService = configurationService; } updateActivity(activity: IActivity): void { @@ -105,6 +91,7 @@ export class ViewContainerActivityAction extends ActivityAction { this.logAction('show'); await this.viewletService.openViewlet(this.activity.id, true); + return this.activate(); } @@ -117,21 +104,18 @@ export class ViewContainerActivityAction extends ActivityAction { } } -export const ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts'; +class MenuActivityActionViewItem extends ActivityActionViewItem { -export class AccountsActionViewItem extends ActivityActionViewItem { constructor( + private readonly menuId: MenuId, action: ActivityAction, colors: (theme: IColorTheme) => ICompositeBarColors, @IThemeService themeService: IThemeService, - @IContextMenuService protected contextMenuService: IContextMenuService, - @IMenuService protected menuService: IMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IAuthenticationService private readonly authenticationService: IAuthenticationService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IStorageService private readonly storageService: IStorageService, - @IProductService private readonly productService: IProductService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IMenuService protected readonly menuService: IMenuService, + @IContextMenuService protected readonly contextMenuService: IContextMenuService, + @IContextKeyService protected readonly contextKeyService: IContextKeyService, + @IConfigurationService protected readonly configurationService: IConfigurationService, + @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService ) { super(action, { draggable: false, colors, icon: true }, themeService); } @@ -161,12 +145,103 @@ export class AccountsActionViewItem extends ActivityActionViewItem { })); } - private async getActions(accountsMenu: IMenu) { + protected async showContextMenu(e?: MouseEvent): Promise { + const disposables = new DisposableStore(); + + const menu = disposables.add(this.menuService.createMenu(this.menuId, this.contextKeyService)); + const actions = await this.resolveActions(menu, disposables); + + const isUsingCustomMenu = isWeb || (getTitleBarStyle(this.configurationService) !== 'native' && !isMacintosh); // see #40262 + const position = this.configurationService.getValue('workbench.sideBar.location'); + + this.contextMenuService.showContextMenu({ + getAnchor: () => isUsingCustomMenu ? this.container : e || this.container, + anchorAlignment: isUsingCustomMenu ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined, + anchorAxisAlignment: isUsingCustomMenu ? AnchorAxisAlignment.HORIZONTAL : AnchorAxisAlignment.VERTICAL, + getActions: () => actions, + onHide: () => disposables.dispose() + }); + } + + protected async resolveActions(menu: IMenu, disposables: DisposableStore): Promise { + const actions: IAction[] = []; + + disposables.add(createAndFillInActionBarActions(menu, undefined, { primary: [], secondary: actions })); + + return actions; + } +} + +export class HomeActivityActionViewItem extends MenuActivityActionViewItem { + + static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator'; + + constructor( + private readonly goHomeHref: string, + action: ActivityAction, + colors: (theme: IColorTheme) => ICompositeBarColors, + @IThemeService themeService: IThemeService, + @IMenuService menuService: IMenuService, + @IContextMenuService contextMenuService: IContextMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IStorageService private readonly storageService: IStorageService + ) { + super(MenuId.MenubarHomeMenu, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService); + } + + protected async resolveActions(homeMenu: IMenu, disposables: DisposableStore): Promise { + const actions = []; + + // Go Home + actions.push(disposables.add(new Action('goHome', nls.localize('goHome', "Go Home"), undefined, true, async () => window.location.href = this.goHomeHref))); + actions.push(disposables.add(new Separator())); + + // Contributed + const contributedActions = await super.resolveActions(homeMenu, disposables); + actions.push(...contributedActions); + + // Hide + if (contributedActions.length > 0) { + actions.push(disposables.add(new Separator())); + } + actions.push(disposables.add(new Action('hide', nls.localize('hide', "Hide"), undefined, true, async () => { + this.storageService.store(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, false, StorageScope.GLOBAL, StorageTarget.USER); + }))); + + return actions; + } +} + +export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { + + static readonly ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts'; + + constructor( + action: ActivityAction, + colors: (theme: IColorTheme) => ICompositeBarColors, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IMenuService menuService: IMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IAuthenticationService private readonly authenticationService: IAuthenticationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IStorageService private readonly storageService: IStorageService, + @IProductService private readonly productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(MenuId.AccountsContext, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService); + } + + protected async resolveActions(accountsMenu: IMenu, disposables: DisposableStore): Promise { + await super.resolveActions(accountsMenu, disposables); + const otherCommands = accountsMenu.getActions(); const providers = this.authenticationService.getProviderIds(); - const allSessions = providers.map(async id => { + const allSessions = providers.map(async providerId => { try { - const sessions = await this.authenticationService.getSessions(id); + const sessions = await this.authenticationService.getSessions(providerId); const groupedSessions: { [label: string]: AuthenticationSession[] } = {}; sessions.forEach(session => { @@ -177,14 +252,9 @@ export class AccountsActionViewItem extends ActivityActionViewItem { } }); - return { - providerId: id, - sessions: groupedSessions - }; + return { providerId, sessions: groupedSessions }; } catch { - return { - providerId: id - }; + return { providerId }; } }); @@ -196,132 +266,67 @@ export class AccountsActionViewItem extends ActivityActionViewItem { if (sessionInfo.sessions) { Object.keys(sessionInfo.sessions).forEach(accountName => { - const hasEmbedderAccountSession = sessionInfo.sessions[accountName].some(session => session.id === (authenticationSession?.id)); - const manageExtensionsAction = new Action(`configureSessions${accountName}`, nls.localize('manageTrustedExtensions', "Manage Trusted Extensions"), '', true, _ => { + const manageExtensionsAction = disposables.add(new Action(`configureSessions${accountName}`, nls.localize('manageTrustedExtensions', "Manage Trusted Extensions"), '', true, () => { return this.authenticationService.manageTrustedExtensionsForAccount(sessionInfo.providerId, accountName); - }); - const signOutAction = new Action('signOut', nls.localize('signOut', "Sign Out"), '', true, _ => { - return this.authenticationService.signOutOfAccount(sessionInfo.providerId, accountName); - }); + })); - const actions = [manageExtensionsAction]; + const signOutAction = disposables.add(new Action('signOut', nls.localize('signOut', "Sign Out"), '', true, () => { + return this.authenticationService.signOutOfAccount(sessionInfo.providerId, accountName); + })); + + const providerSubMenuActions = [manageExtensionsAction]; + + const hasEmbedderAccountSession = sessionInfo.sessions[accountName].some(session => session.id === (authenticationSession?.id)); if (!hasEmbedderAccountSession || authenticationSession?.canSignOut) { - actions.push(signOutAction); + providerSubMenuActions.push(signOutAction); } - const menu = new SubmenuAction('activitybar.submenu', `${accountName} (${providerDisplayName})`, actions); - menus.push(menu); + const providerSubMenu = disposables.add(new SubmenuAction('activitybar.submenu', `${accountName} (${providerDisplayName})`, providerSubMenuActions)); + menus.push(providerSubMenu); }); } else { - const menu = new Action('providerUnavailable', nls.localize('authProviderUnavailable', '{0} is currently unavailable', providerDisplayName)); - menus.push(menu); + const providerUnavailableAction = disposables.add(new Action('providerUnavailable', nls.localize('authProviderUnavailable', '{0} is currently unavailable', providerDisplayName))); + menus.push(providerUnavailableAction); } }); if (menus.length && otherCommands.length) { - menus.push(new Separator()); + menus.push(disposables.add(new Separator())); } otherCommands.forEach((group, i) => { const actions = group[1]; menus = menus.concat(actions); if (i !== otherCommands.length - 1) { - menus.push(new Separator()); + menus.push(disposables.add(new Separator())); } }); if (menus.length) { - menus.push(new Separator()); + menus.push(disposables.add(new Separator())); } - menus.push(new Action('hide', nls.localize('hide', "Hide"), undefined, true, _ => { - this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL); - return Promise.resolve(); - })); + menus.push(disposables.add(new Action('hide', nls.localize('hide', "Hide"), undefined, true, async () => { + this.storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + }))); return menus; } - - private async showContextMenu(e?: MouseEvent): Promise { - const accountsActions: IAction[] = []; - const accountsMenu = this.menuService.createMenu(MenuId.AccountsContext, this.contextKeyService); - const actionsDisposable = createAndFillInActionBarActions(accountsMenu, undefined, { primary: [], secondary: accountsActions }); - const native = getTitleBarStyle(this.configurationService, this.environmentService) === 'native'; - const position = this.configurationService.getValue('workbench.sideBar.location'); - - const containerPosition = DOM.getDomNodePagePosition(this.container); - const location = { x: containerPosition.left + (position === 'left' ? containerPosition.width : 0), y: containerPosition.top }; - const actions = await this.getActions(accountsMenu); - this.contextMenuService.showContextMenu({ - getAnchor: () => !native ? location : e || this.container, - anchorAlignment: !native ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined, - getActions: () => actions, - onHide: () => { - accountsMenu.dispose(); - dispose(actionsDisposable); - } - }); - } } -export class GlobalActivityActionViewItem extends ActivityActionViewItem { +export class GlobalActivityActionViewItem extends MenuActivityActionViewItem { constructor( action: ActivityAction, colors: (theme: IColorTheme) => ICompositeBarColors, @IThemeService themeService: IThemeService, - @IMenuService private readonly menuService: IMenuService, - @IContextMenuService protected readonly contextMenuService: IContextMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IMenuService menuService: IMenuService, + @IContextMenuService contextMenuService: IContextMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService ) { - super(action, { draggable: false, colors, icon: true }, themeService); - } - - render(container: HTMLElement): void { - super.render(container); - - // Context menus are triggered on mouse down so that an item can be picked - // and executed with releasing the mouse over it - - this._register(DOM.addDisposableListener(this.container, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { - DOM.EventHelper.stop(e, true); - this.showContextMenu(e); - })); - - this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { - let event = new StandardKeyboardEvent(e); - if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { - DOM.EventHelper.stop(e, true); - this.showContextMenu(); - } - })); - - this._register(DOM.addDisposableListener(this.container, TouchEventType.Tap, (e: GestureEvent) => { - DOM.EventHelper.stop(e, true); - this.showContextMenu(); - })); - } - - private showContextMenu(e?: MouseEvent): void { - const globalActivityActions: IAction[] = []; - const globalActivityMenu = this.menuService.createMenu(MenuId.GlobalActivity, this.contextKeyService); - const actionsDisposable = createAndFillInActionBarActions(globalActivityMenu, undefined, { primary: [], secondary: globalActivityActions }); - const native = getTitleBarStyle(this.configurationService, this.environmentService) === 'native'; - const position = this.configurationService.getValue('workbench.sideBar.location'); - - const containerPosition = DOM.getDomNodePagePosition(this.container); - const location = { x: containerPosition.left + (position === 'left' ? containerPosition.width : 0), y: containerPosition.top + containerPosition.height }; - this.contextMenuService.showContextMenu({ - getAnchor: () => !native ? location : e || this.container, - anchorAlignment: !native ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined, - getActions: () => globalActivityActions, - onHide: () => { - globalActivityMenu.dispose(); - dispose(actionsDisposable); - } - }); + super(MenuId.GlobalActivity, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService); } } @@ -338,106 +343,62 @@ export class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinne } } -class SwitchSideBarViewAction extends Action { +class SwitchSideBarViewAction extends Action2 { constructor( - id: string, - name: string, - @IViewletService private readonly viewletService: IViewletService, - @IActivityBarService private readonly activityBarService: IActivityBarService + desc: Readonly, + private readonly offset: number ) { - super(id, name); + super(desc); } - async run(offset: number): Promise { - const visibleViewletIds = this.activityBarService.getVisibleViewContainerIds(); + async run(accessor: ServicesAccessor): Promise { + const activityBarService = accessor.get(IActivityBarService); + const viewletService = accessor.get(IViewletService); - const activeViewlet = this.viewletService.getActiveViewlet(); + const visibleViewletIds = activityBarService.getVisibleViewContainerIds(); + + const activeViewlet = viewletService.getActiveViewlet(); if (!activeViewlet) { return; } let targetViewletId: string | undefined; for (let i = 0; i < visibleViewletIds.length; i++) { if (visibleViewletIds[i] === activeViewlet.getId()) { - targetViewletId = visibleViewletIds[(i + visibleViewletIds.length + offset) % visibleViewletIds.length]; + targetViewletId = visibleViewletIds[(i + visibleViewletIds.length + this.offset) % visibleViewletIds.length]; break; } } - await this.viewletService.openViewlet(targetViewletId, true); + await viewletService.openViewlet(targetViewletId, true); } } -export class PreviousSideBarViewAction extends SwitchSideBarViewAction { - - static readonly ID = 'workbench.action.previousSideBarView'; - static readonly LABEL = nls.localize('previousSideBarView', 'Previous Side Bar View'); - - constructor( - id: string, - name: string, - @IViewletService viewletService: IViewletService, - @IActivityBarService activityBarService: IActivityBarService - ) { - super(id, name, viewletService, activityBarService); - } - - run(): Promise { - return super.run(-1); - } -} - -export class NextSideBarViewAction extends SwitchSideBarViewAction { - - static readonly ID = 'workbench.action.nextSideBarView'; - static readonly LABEL = nls.localize('nextSideBarView', 'Next Side Bar View'); - - constructor( - id: string, - name: string, - @IViewletService viewletService: IViewletService, - @IActivityBarService activityBarService: IActivityBarService - ) { - super(id, name, viewletService, activityBarService); - } - - run(): Promise { - return super.run(1); - } -} - -export class HomeAction extends Action { - - constructor( - private readonly href: string, - name: string, - icon: Codicon - ) { - super('workbench.action.home', name, icon.classNames); - } - - async run(event: MouseEvent): Promise { - let openInNewWindow = false; - if (isMacintosh) { - openInNewWindow = event.metaKey; - } else { - openInNewWindow = event.ctrlKey; - } - - if (openInNewWindow) { - DOM.windowOpenNoOpener(this.href); - } else { - window.location.href = this.href; +registerAction2( + class PreviousSideBarViewAction extends SwitchSideBarViewAction { + constructor() { + super({ + id: 'workbench.action.previousSideBarView', + title: { value: nls.localize('previousSideBarView', "Previous Side Bar View"), original: 'Previous Side Bar View' }, + category: CATEGORIES.View, + f1: true + }, -1); } } -} +); -export class HomeActionViewItem extends ActionViewItem { - - constructor(action: IAction) { - super(undefined, action, { icon: true, label: false, useEventAsContext: true }); +registerAction2( + class NextSideBarViewAction extends SwitchSideBarViewAction { + constructor() { + super({ + id: 'workbench.action.nextSideBarView', + title: { value: nls.localize('nextSideBarView', "Next Side Bar View"), original: 'Next Side Bar View' }, + category: CATEGORIES.View, + f1: true + }, 1); + } } -} +); registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { const activityBarBackgroundColor = theme.getColor(ACTIVITY_BAR_BACKGROUND); @@ -510,6 +471,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = left: 9px; height: 32px; width: 32px; + z-index: 1; } .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:before, @@ -549,7 +511,3 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = } } }); - -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(PreviousSideBarViewAction), 'View: Previous Side Bar View', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NextSideBarViewAction), 'View: Next Side Bar View', CATEGORIES.View.value); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 2a3a93d01..39a3ca76e 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -8,24 +8,24 @@ import * as nls from 'vs/nls'; import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { GLOBAL_ACTIVITY_ID, IActivity, ACCOUNTS_ACTIVITY_ID } from 'vs/workbench/common/activity'; import { Part } from 'vs/workbench/browser/part'; -import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActionViewItem, HomeAction, HomeActionViewItem, ACCOUNTS_VISIBILITY_PREFERENCE_KEY } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; +import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActivityActionViewItem, HomeActivityActionViewItem } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; import { IBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { ToggleActivityBarVisibilityAction, ToggleMenuBarAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions'; -import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BORDER } from 'vs/workbench/common/theme'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar'; import { Dimension, createCSSRule, asCSSUrl, addDisposableListener, EventType } from 'vs/base/browser/dom'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions'; import { IViewDescriptorService, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewContainerModel, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { isUndefinedOrNull, assertIsDefined, isString } from 'vs/base/common/types'; +import { assertIsDefined } from 'vs/base/common/types'; import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Schemas } from 'vs/base/common/network'; @@ -34,7 +34,6 @@ import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menuba import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getMenuBarVisibility } from 'vs/platform/windows/common/windows'; import { isWeb } from 'vs/base/common/platform'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { Before2D } from 'vs/workbench/browser/dnd'; import { Codicon, iconRegistry } from 'vs/base/common/codicons'; import { Action, Separator } from 'vs/base/common/actions'; @@ -43,41 +42,45 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { CATEGORIES } from 'vs/workbench/common/actions'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; interface IPlaceholderViewContainer { - id: string; - name?: string; - iconUrl?: UriComponents; - iconCSS?: string; - views?: { when?: string }[]; + readonly id: string; + readonly name?: string; + readonly iconUrl?: UriComponents; + readonly themeIcon?: ThemeIcon; + readonly views?: { when?: string }[]; } interface IPinnedViewContainer { - id: string; - pinned: boolean; - order?: number; - visible: boolean; + readonly id: string; + readonly pinned: boolean; + readonly order?: number; + readonly visible: boolean; } interface ICachedViewContainer { - id: string; + readonly id: string; name?: string; - icon?: URI | string; - pinned: boolean; - order?: number; + icon?: URI | ThemeIcon; + readonly pinned: boolean; + readonly order?: number; visible: boolean; views?: { when?: string }[]; } +const settingsViewBarIcon = registerIcon('settings-view-bar-icon', Codicon.settingsGear, nls.localize('settingsViewBarIcon', 'Settings icon in the view bar.')); +const accountsViewBarIcon = registerIcon('accounts-view-bar-icon', Codicon.account, nls.localize('accountsViewBarIcon', 'Accounts icon in the view bar.')); + export class ActivitybarPart extends Part implements IActivityBarService { declare readonly _serviceBrand: undefined; - private static readonly ACTION_HEIGHT = 48; - static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2'; + private static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2'; private static readonly PLACEHOLDER_VIEW_CONTAINERS = 'workbench.activity.placeholderViewlets'; - private static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator'; + private static readonly ACTION_HEIGHT = 48; private static readonly ACCOUNTS_ACTION_INDEX = 0; + //#region IView readonly minimumWidth: number = 48; @@ -100,17 +103,17 @@ export class ActivitybarPart extends Part implements IActivityBarService { private globalActivityAction: ActivityAction | undefined; private globalActivityActionBar: ActionBar | undefined; - private readonly globalActivity: ICompositeActivity[] = []; private globalActivitiesContainer: HTMLElement | undefined; + private readonly globalActivity: ICompositeActivity[] = []; private accountsActivityAction: ActivityAction | undefined; - private accountsActivity: ICompositeActivity[] = []; + private readonly accountsActivity: ICompositeActivity[] = []; private readonly compositeActions = new Map(); private readonly viewContainerDisposables = new Map(); - private readonly keyboardNavigationDisposables = new DisposableStore(); + private readonly keyboardNavigationDisposables = this._register(new DisposableStore()); private readonly location = ViewContainerLocation.Sidebar; @@ -125,27 +128,36 @@ export class ActivitybarPart extends Part implements IActivityBarService { @IContextKeyService private readonly contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService); - storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.PINNED_VIEW_CONTAINERS, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: ACCOUNTS_VISIBILITY_PREFERENCE_KEY, version: 1 }); - - this.migrateFromOldCachedViewContainersValue(); - for (const cachedViewContainer of this.cachedViewContainers) { - if (environmentService.remoteAuthority // In remote window, hide activity bar entries until registered. - || this.shouldBeHidden(cachedViewContainer.id, cachedViewContainer) + if ( + environmentService.remoteAuthority || // In remote window, hide activity bar entries until registered + this.shouldBeHidden(cachedViewContainer.id, cachedViewContainer) ) { cachedViewContainer.visible = false; } } + this.compositeBar = this.createCompositeBar(); + + this.onDidRegisterViewContainers(this.getViewContainers()); + + this.registerListeners(); + } + + private createCompositeBar() { const cachedItems = this.cachedViewContainers - .map(v => ({ id: v.id, name: v.name, visible: v.visible, order: v.order, pinned: v.pinned })); - this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, cachedItems, { + .map(container => ({ + id: container.id, + name: container.name, + visible: container.visible, + order: container.order, + pinned: container.pinned + })); + + return this._register(this.instantiationService.createInstance(CompositeBar, cachedItems, { icon: true, orientation: ActionsOrientation.VERTICAL, preventLoopNavigation: true, @@ -154,8 +166,9 @@ export class ActivitybarPart extends Part implements IActivityBarService { getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction, getOnCompositeClickAction: (compositeId: string) => new Action(compositeId, '', '', true, () => this.viewsService.isViewContainerVisible(compositeId) ? Promise.resolve(this.viewsService.closeViewContainer(compositeId)) : this.viewsService.openViewContainer(compositeId)), getContextMenuActions: () => { - const menuBarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService); const actions = []; + + // Home if (this.homeBarContainer) { actions.push(new Action( 'toggleHomeBarAction', @@ -166,22 +179,26 @@ export class ActivitybarPart extends Part implements IActivityBarService { )); } + // Menu + const menuBarVisibility = getMenuBarVisibility(this.configurationService); if (menuBarVisibility === 'compact' || (menuBarVisibility === 'hidden' && isWeb)) { actions.push(this.instantiationService.createInstance(ToggleMenuBarAction, ToggleMenuBarAction.ID, menuBarVisibility === 'compact' ? nls.localize('hideMenu', "Hide Menu") : nls.localize('showMenu', "Show Menu"))); } - const toggleAccountsVisibilityAction = new Action( + // Accounts + actions.push(new Action( 'toggleAccountsVisibility', this.accountsVisibilityPreference ? nls.localize('hideAccounts', "Hide Accounts") : nls.localize('showAccounts', "Show Accounts"), undefined, true, async () => { this.accountsVisibilityPreference = !this.accountsVisibilityPreference; } - ); - - actions.push(toggleAccountsVisibilityAction); + )); actions.push(new Separator()); + // Toggle Sidebar actions.push(this.instantiationService.createInstance(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.getLabel(this.layoutService))); + + // Toggle Activity Bar actions.push(new Action( ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar"), @@ -204,19 +221,12 @@ export class ActivitybarPart extends Part implements IActivityBarService { colors: (theme: IColorTheme) => this.getActivitybarItemColors(theme), overflowActionSize: ActivitybarPart.ACTION_HEIGHT })); - - this.onDidRegisterViewContainers(this.getViewContainers()); - this.registerListeners(); - } - - focusActivityBar(): void { - this.compositeBar.focus(); } private getContextMenuActionsForComposite(compositeId: string): Action[] { - const viewContainer = this.viewDescriptorService.getViewContainerById(compositeId)!; - const actions = []; + + const viewContainer = this.viewDescriptorService.getViewContainerById(compositeId)!; const defaultLocation = this.viewDescriptorService.getDefaultViewContainerLocation(viewContainer)!; if (defaultLocation !== this.viewDescriptorService.getViewContainerLocation(viewContainer)) { actions.push(new Action('resetLocationAction', nls.localize('resetLocation', "Reset Location"), undefined, true, async () => { @@ -253,13 +263,13 @@ export class ActivitybarPart extends Part implements IActivityBarService { disposables.clear(); this.onDidRegisterExtensions(); this.compositeBar.onDidChange(() => this.saveCachedViewContainers(), this, disposables); - this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables); + this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables); })); // Register for configuration changes this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('window.menuBarVisibility')) { - if (getMenuBarVisibility(this.configurationService, this.environmentService) === 'compact') { + if (getMenuBarVisibility(this.configurationService) === 'compact') { this.installMenubar(); } else { this.uninstallMenubar(); @@ -277,6 +287,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (from === this.location) { this.onDidDeregisterViewContainer(container); } + if (to === this.location) { this.onDidRegisterViewContainers([container]); } @@ -306,6 +317,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private onDidViewContainerVisible(id: string): void { const viewContainer = this.getViewContainer(id); if (viewContainer) { + // Update the composite bar by adding this.compositeBar.addComposite(viewContainer); this.compositeBar.activateComposite(viewContainer.id); @@ -313,7 +325,8 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (viewContainer.hideIfEmpty) { const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer); if (viewContainerModel.activeViewDescriptors.length === 0) { - this.hideComposite(viewContainer.id); // Update the composite bar by hiding + // Update the composite bar by hiding + this.hideComposite(viewContainer.id); } } } @@ -339,6 +352,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (typeof priority !== 'number') { priority = 0; } + const activity: ICompositeActivity = { badge, clazz, priority }; const activityCache = activityId === GLOBAL_ACTIVITY_ID ? this.globalActivity : this.accountsActivity; @@ -387,16 +401,18 @@ export class ActivitybarPart extends Part implements IActivityBarService { private getCumulativeNumberBadge(activityCache: ICompositeActivity[], priority: number): NumberBadge { const numberActivities = activityCache.filter(activity => activity.badge instanceof NumberBadge && activity.priority === priority); - let number = numberActivities.reduce((result, activity) => { return result + (activity.badge).number; }, 0); - let descriptorFn = (): string => { + const number = numberActivities.reduce((result, activity) => { return result + (activity.badge).number; }, 0); + const descriptorFn = (): string => { return numberActivities.reduce((result, activity, index) => { result = result + (activity.badge).getDescription(); if (index < numberActivities.length - 1) { - result = result + '\n'; + result = `${result}\n`; } + return result; }, ''); }; + return new NumberBadge(number, descriptorFn); } @@ -414,11 +430,19 @@ export class ActivitybarPart extends Part implements IActivityBarService { } private installMenubar() { + if (this.menuBar) { + return; // prevent menu bar from installing twice #110720 + } + this.menuBarContainer = document.createElement('div'); this.menuBarContainer.classList.add('menubar'); const content = assertIsDefined(this.content); - content.prepend(this.menuBarContainer); + if (this.homeBarContainer) { + content.insertBefore(this.menuBarContainer, this.homeBarContainer.nextSibling); + } else { + content.prepend(this.menuBarContainer); + } // Menubar: install a custom menu bar depending on configuration this.menuBar = this._register(this.instantiationService.createInstance(CustomMenubarControl)); @@ -442,12 +466,12 @@ export class ActivitybarPart extends Part implements IActivityBarService { codicon = Codicon.code; } - this.createHomeBar(homeIndicator.href, homeIndicator.title, codicon); + this.createHomeBar(homeIndicator.href, codicon); this.onDidChangeHomeBarVisibility(); } // Install menubar if compact - if (getMenuBarVisibility(this.configurationService, this.environmentService) === 'compact') { + if (getMenuBarVisibility(this.configurationService) === 'compact') { this.installMenubar(); } @@ -456,11 +480,11 @@ export class ActivitybarPart extends Part implements IActivityBarService { // Global action bar this.globalActivitiesContainer = document.createElement('div'); - this.globalActivitiesContainer.classList.add('global-activity'); this.content.appendChild(this.globalActivitiesContainer); this.createGlobalActivityActionBar(this.globalActivitiesContainer); + // Keyboard Navigation this.registerKeyboardNavigationListeners(); return this.content; @@ -528,23 +552,19 @@ export class ActivitybarPart extends Part implements IActivityBarService { } })); } - - - } - private createHomeBar(href: string, title: string, icon: Codicon): void { + private createHomeBar(href: string, icon: Codicon): void { this.homeBarContainer = document.createElement('div'); this.homeBarContainer.setAttribute('aria-label', nls.localize('homeIndicator', "Home")); this.homeBarContainer.setAttribute('role', 'toolbar'); this.homeBarContainer.classList.add('home-bar'); this.homeBar = this._register(new ActionBar(this.homeBarContainer, { + actionViewItemProvider: action => this.instantiationService.createInstance(HomeActivityActionViewItem, href, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme)), orientation: ActionsOrientation.VERTICAL, - animated: false, ariaLabel: nls.localize('home', "Home"), - actionViewItemProvider: action => new HomeActionViewItem(action), - allowContextMenu: true, + animated: false, preventLoopNavigation: true, ignoreOrientationForPreviousAndNextKey: true })); @@ -552,35 +572,15 @@ export class ActivitybarPart extends Part implements IActivityBarService { const homeBarIconBadge = document.createElement('div'); homeBarIconBadge.classList.add('home-bar-icon-badge'); this.homeBarContainer.appendChild(homeBarIconBadge); - this.homeBar.push(this._register(this.instantiationService.createInstance(HomeAction, href, title, icon))); + + this.homeBar.push(this._register(new ActivityAction({ + id: 'workbench.actions.home', + name: nls.localize('home', "Home"), + cssClass: icon.classNames + }))); const content = assertIsDefined(this.content); - content.prepend(this.homeBarContainer); - } - - updateStyles(): void { - super.updateStyles(); - - const container = assertIsDefined(this.getContainer()); - const background = this.getColor(ACTIVITY_BAR_BACKGROUND) || ''; - container.style.backgroundColor = background; - - const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || ''; - container.classList.toggle('bordered', !!borderColor); - container.style.borderColor = borderColor ? borderColor : ''; - } - - private getActivitybarItemColors(theme: IColorTheme): ICompositeBarColors { - return { - activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND), - inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND), - activeBorderColor: theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER), - activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND), - badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND), - badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND), - dragAndDropBorder: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BORDER), - activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined, - }; + content.appendChild(this.homeBarContainer); } private createGlobalActivityActionBar(container: HTMLElement): void { @@ -591,7 +591,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { } if (action.id === 'workbench.actions.accounts') { - return this.instantiationService.createInstance(AccountsActionViewItem, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme)); + return this.instantiationService.createInstance(AccountsActivityActionViewItem, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme)); } throw new Error(`No view item for action '${action.id}'`); @@ -603,18 +603,18 @@ export class ActivitybarPart extends Part implements IActivityBarService { ignoreOrientationForPreviousAndNextKey: true })); - this.globalActivityAction = new ActivityAction({ + this.globalActivityAction = this._register(new ActivityAction({ id: 'workbench.actions.manage', name: nls.localize('manage', "Manage"), - cssClass: Codicon.settingsGear.classNames - }); + cssClass: ThemeIcon.asClassName(settingsViewBarIcon) + })); if (this.accountsVisibilityPreference) { - this.accountsActivityAction = new ActivityAction({ + this.accountsActivityAction = this._register(new ActivityAction({ id: 'workbench.actions.accounts', name: nls.localize('accounts', "Accounts"), - cssClass: Codicon.account.classNames - }); + cssClass: ThemeIcon.asClassName(accountsViewBarIcon) + })); this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX }); } @@ -628,11 +628,11 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.globalActivityActionBar.pull(ActivitybarPart.ACCOUNTS_ACTION_INDEX); this.accountsActivityAction = undefined; } else { - this.accountsActivityAction = new ActivityAction({ + this.accountsActivityAction = this._register(new ActivityAction({ id: 'workbench.actions.accounts', name: nls.localize('accounts', "Accounts"), cssClass: Codicon.account.classNames - }); + })); this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX }); } } @@ -723,7 +723,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { return ActivitybarPart.toActivity(id, name, icon, focusCommand?.id || id); } - private static toActivity(id: string, name: string, icon: URI | string | undefined, keybindingId: string | undefined): IActivity { + private static toActivity(id: string, name: string, icon: URI | ThemeIcon | undefined, keybindingId: string | undefined): IActivity { let cssClass: string | undefined = undefined; let iconUrl: URI | undefined = undefined; if (URI.isUri(icon)) { @@ -736,9 +736,10 @@ export class ActivitybarPart extends Part implements IActivityBarService { -webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%; -webkit-mask-size: 24px; `); - } else if (isString(icon)) { - cssClass = icon; + } else if (ThemeIcon.isThemeIcon(icon)) { + cssClass = ThemeIcon.asClassName(icon); } + return { id, name, cssClass, iconUrl, keybindingId }; } @@ -810,6 +811,35 @@ export class ActivitybarPart extends Part implements IActivityBarService { .map(v => v.id); } + focusActivityBar(): void { + this.compositeBar.focus(); + } + + updateStyles(): void { + super.updateStyles(); + + const container = assertIsDefined(this.getContainer()); + const background = this.getColor(ACTIVITY_BAR_BACKGROUND) || ''; + container.style.backgroundColor = background; + + const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || ''; + container.classList.toggle('bordered', !!borderColor); + container.style.borderColor = borderColor ? borderColor : ''; + } + + private getActivitybarItemColors(theme: IColorTheme): ICompositeBarColors { + return { + activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND), + inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND), + activeBorderColor: theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER), + activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND), + badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND), + badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND), + dragAndDropBorder: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BORDER), + activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined, + }; + } + layout(width: number, height: number): void { if (!this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) { return; @@ -834,6 +864,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private getViewContainer(id: string): ViewContainer | undefined { const viewContainer = this.viewDescriptorService.getViewContainerById(id); + return viewContainer && this.viewDescriptorService.getViewContainerLocation(viewContainer) === this.location ? viewContainer : undefined; } @@ -841,7 +872,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { return this.viewDescriptorService.getViewContainersByLocation(this.location); } - private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { + private onDidStorageValueChange(e: IStorageValueChangeEvent): void { if (e.key === ActivitybarPart.PINNED_VIEW_CONTAINERS && e.scope === StorageScope.GLOBAL && this.pinnedViewContainersValue !== this.getStoredPinnedViewContainersValue() /* This checks if current window changed the value or not */) { this._pinnedViewContainersValue = undefined; @@ -870,11 +901,11 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.compositeBar.setCompositeBarItems(newCompositeItems); } - if (e.key === ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) { + if (e.key === HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) { this.onDidChangeHomeBarVisibility(); } - if (e.key === ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) { + if (e.key === AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) { this.toggleAccountsActivity(); } } @@ -917,12 +948,13 @@ export class ActivitybarPart extends Part implements IActivityBarService { const cachedViewContainer = this._cachedViewContainers.filter(cached => cached.id === placeholderViewContainer.id)[0]; if (cachedViewContainer) { cachedViewContainer.name = placeholderViewContainer.name; - cachedViewContainer.icon = placeholderViewContainer.iconCSS ? placeholderViewContainer.iconCSS : + cachedViewContainer.icon = placeholderViewContainer.themeIcon ? ThemeIcon.revive(placeholderViewContainer.themeIcon) : placeholderViewContainer.iconUrl ? URI.revive(placeholderViewContainer.iconUrl) : undefined; cachedViewContainer.views = placeholderViewContainer.views; } } } + return this._cachedViewContainers; } @@ -933,10 +965,11 @@ export class ActivitybarPart extends Part implements IActivityBarService { visible, order }))); + this.setPlaceholderViewContainers(cachedViewContainers.map(({ id, icon, name, views }) => ({ id, iconUrl: URI.isUri(icon) ? icon : undefined, - iconCSS: isString(icon) ? icon : undefined, + themeIcon: ThemeIcon.isThemeIcon(icon) ? icon : undefined, name, views }))); @@ -971,7 +1004,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { } private setStoredPinnedViewContainersValue(value: string): void { - this.storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, value, StorageScope.GLOBAL); + this.storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, value, StorageScope.GLOBAL, StorageTarget.USER); } private getPlaceholderViewContainers(): IPlaceholderViewContainer[] { @@ -1003,37 +1036,23 @@ export class ActivitybarPart extends Part implements IActivityBarService { } private setStoredPlaceholderViewContainersValue(value: string): void { - this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.GLOBAL); + this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.GLOBAL, StorageTarget.MACHINE); } private get homeBarVisibilityPreference(): boolean { - return this.storageService.getBoolean(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, StorageScope.GLOBAL, true); + return this.storageService.getBoolean(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, StorageScope.GLOBAL, true); } private set homeBarVisibilityPreference(value: boolean) { - this.storageService.store(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL); + this.storageService.store(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL, StorageTarget.USER); } private get accountsVisibilityPreference(): boolean { - return this.storageService.getBoolean(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true); + return this.storageService.getBoolean(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true); } private set accountsVisibilityPreference(value: boolean) { - this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL); - } - - private migrateFromOldCachedViewContainersValue(): void { - const value = this.storageService.get('workbench.activity.pinnedViewlets', StorageScope.GLOBAL); - if (value !== undefined) { - const storedStates: Array = JSON.parse(value); - const cachedViewContainers = storedStates.map(c => { - const serialized: ICachedViewContainer = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: undefined, visible: true, name: undefined, icon: undefined, views: undefined } : c; - serialized.visible = isUndefinedOrNull(serialized.visible) ? true : serialized.visible; - return serialized; - }); - this.storeCachedViewContainersState(cachedViewContainers); - this.storageService.remove('workbench.activity.pinnedViewlets', StorageScope.GLOBAL); - } + this.storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL, StorageTarget.USER); } toJSON(): object { diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index 39fa9ebde..57d6f2a85 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -9,10 +9,6 @@ margin-bottom: 4px; } -.monaco-workbench .activitybar > .content > .home-bar > .monaco-action-bar .action-item { - margin-bottom: 0; -} - .monaco-workbench .activitybar > .content .composite-bar > .monaco-action-bar .action-item::before, .monaco-workbench .activitybar > .content .composite-bar > .monaco-action-bar .action-item::after { position: absolute; diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css index ced2d8158..4f2d86b17 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css @@ -51,10 +51,6 @@ position: relative; width: 100%; height: 48px; - display: flex; - align-items: center; - justify-content: center; - order: -1; } .monaco-workbench .activitybar > .content > .home-bar > .home-bar-icon-badge { @@ -88,10 +84,6 @@ margin-bottom: auto; } -.monaco-workbench .activitybar > .content > .composite-bar-excess { - height: 100%; -} - /** Menu Bar */ .monaco-workbench .activitybar .menubar { diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 5c481fafa..1350ddfe5 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -18,7 +18,7 @@ import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite'; import { IComposite } from 'vs/workbench/common/composite'; import { CompositeProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -199,7 +199,7 @@ export abstract class CompositePart extends Part { // Store in preferences const id = this.activeComposite.getId(); if (id !== this.defaultCompositeId) { - this.storageService.store(this.activeCompositeSettingsKey, id, StorageScope.WORKSPACE); + this.storageService.store(this.activeCompositeSettingsKey, id, StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(this.activeCompositeSettingsKey, StorageScope.WORKSPACE); } diff --git a/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts b/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts new file mode 100644 index 000000000..b5fc1d1fa --- /dev/null +++ b/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IDialogHandler, IDialogResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IDialogsModel, IDialogViewItem } from 'vs/workbench/common/dialogs'; +import { BrowserDialogHandler } from 'vs/workbench/browser/parts/dialogs/dialogHandler'; +import { DialogService } from 'vs/workbench/services/dialogs/common/dialogService'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; + +export class DialogHandlerContribution extends Disposable implements IWorkbenchContribution { + private impl: IDialogHandler; + + private model: IDialogsModel; + private currentDialog: IDialogViewItem | undefined; + + constructor( + @IDialogService private dialogService: IDialogService, + @ILogService logService: ILogService, + @ILayoutService layoutService: ILayoutService, + @IThemeService themeService: IThemeService, + @IKeybindingService keybindingService: IKeybindingService, + @IProductService productService: IProductService, + @IClipboardService clipboardService: IClipboardService + ) { + super(); + + this.impl = new BrowserDialogHandler(logService, layoutService, themeService, keybindingService, productService, clipboardService); + + this.model = (this.dialogService as DialogService).model; + + this._register(this.model.onDidShowDialog(() => { + if (!this.currentDialog) { + this.processDialogs(); + } + })); + + this.processDialogs(); + } + + private async processDialogs(): Promise { + while (this.model.dialogs.length) { + this.currentDialog = this.model.dialogs[0]; + + let result: IDialogResult | undefined = undefined; + if (this.currentDialog.args.confirmArgs) { + const args = this.currentDialog.args.confirmArgs; + result = await this.impl.confirm(args.confirmation); + } else if (this.currentDialog.args.inputArgs) { + const args = this.currentDialog.args.inputArgs; + result = await this.impl.input(args.severity, args.message, args.buttons, args.inputs, args.options); + } else if (this.currentDialog.args.showArgs) { + const args = this.currentDialog.args.showArgs; + result = await this.impl.show(args.severity, args.message, args.buttons, args.options); + } else { + await this.impl.about(); + } + + this.currentDialog.close(result); + this.currentDialog = undefined; + } + } +} + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, LifecyclePhase.Starting); diff --git a/src/vs/workbench/services/dialogs/browser/dialogService.ts b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts similarity index 91% rename from src/vs/workbench/services/dialogs/browser/dialogService.ts rename to src/vs/workbench/browser/parts/dialogs/dialogHandler.ts index 85d83f37d..fe65e6dad 100644 --- a/src/vs/workbench/services/dialogs/browser/dialogService.ts +++ b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IDialogService, IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult, IInputResult, ICheckbox, IInput } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult, IInputResult, ICheckbox, IInput, IDialogHandler } from 'vs/platform/dialogs/common/dialogs'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { ILogService } from 'vs/platform/log/common/log'; import Severity from 'vs/base/common/severity'; @@ -17,12 +17,9 @@ import { EventHelper } from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IProductService } from 'vs/platform/product/common/productService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { fromNow } from 'vs/base/common/date'; -export class DialogService implements IDialogService { - - declare readonly _serviceBrand: undefined; +export class BrowserDialogHandler implements IDialogHandler { private static readonly ALLOWABLE_COMMANDS = [ 'copy', @@ -91,7 +88,7 @@ export class DialogService implements IDialogService { keyEventProcessor: (event: StandardKeyboardEvent) => { const resolved = this.keybindingService.softDispatch(event, this.layoutService.container); if (resolved && resolved.commandId) { - if (DialogService.ALLOWABLE_COMMANDS.indexOf(resolved.commandId) === -1) { + if (BrowserDialogHandler.ALLOWABLE_COMMANDS.indexOf(resolved.commandId) === -1) { EventHelper.stop(event, true); } } @@ -144,5 +141,3 @@ export class DialogService implements IDialogService { } } } - -registerSingleton(IDialogService, DialogService, true); diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index e9bbf8c7c..2de9f55a9 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -20,7 +20,7 @@ import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/commo import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; -import { BinarySize } from 'vs/platform/files/common/files'; +import { ByteSize } from 'vs/platform/files/common/files'; export interface IOpenCallbacks { openInternal: (input: EditorInput, options: EditorOptions | undefined) => Promise; @@ -182,7 +182,7 @@ interface ResourceViewerDelegate { class ResourceViewer { - private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally + private static readonly MAX_OPEN_INTERNAL_SIZE = ByteSize.MB * 200; // max size until we offer an action to open internally static show( descriptor: IResourceDescriptor, @@ -211,7 +211,7 @@ class FileTooLargeFileView { scrollbar: DomScrollableElement, delegate: ResourceViewerDelegate ) { - const size = BinarySize.formatSize(descriptorSize); + const size = ByteSize.formatSize(descriptorSize); delegate.metadataClb(size); clearNode(container); @@ -233,7 +233,7 @@ class FileSeemsBinaryFileView { scrollbar: DomScrollableElement, delegate: ResourceViewerDelegate ) { - delegate.metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : ''); + delegate.metadataClb(typeof descriptor.size === 'number' ? ByteSize.formatSize(descriptor.size) : ''); clearNode(container); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index b743f504b..541ea9b2c 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -491,7 +491,7 @@ export class BreadcrumbsControl { if (element instanceof FileElement) { if (element.kind === FileKind.FILE) { // open file in any editor - this._editorService.openEditor({ resource: element.uri, options: { pinned: pinned } }, group); + this._editorService.openEditor({ resource: element.uri, options: { pinned } }, group); } else { // show next picker let items = this._widget.getItems(); @@ -508,7 +508,8 @@ export class BreadcrumbsControl { resource: model.uri, options: { selection: Range.collapseToStart(element.symbol.selectionRange), - selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport + selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport, + pinned } }, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP); } @@ -753,13 +754,14 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // open symbol in editor return editors.openEditor({ resource: outlineElement.uri, - options: { selection: Range.collapseToStart(element.symbol.selectionRange) } + options: { selection: Range.collapseToStart(element.symbol.selectionRange), pinned: true } }, SIDE_GROUP); } else if (element && URI.isUri(element.resource)) { // open file in editor return editors.openEditor({ resource: element.resource, + options: { pinned: true } }, SIDE_GROUP); } else { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index d1585c4da..001398ec0 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -99,7 +99,7 @@ export abstract class BreadcrumbsPicker { this._treeContainer = document.createElement('div'); this._treeContainer.style.background = color ? color.toString() : ''; this._treeContainer.style.paddingTop = '2px'; - this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getColorTheme().getColor(widgetShadow)}`; + this._treeContainer.style.boxShadow = `0 0 8px 2px ${this._themeService.getColorTheme().getColor(widgetShadow)}`; this._domNode.appendChild(this._treeContainer); this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 }; @@ -436,7 +436,6 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { } protected _getTargetFromEvent(element: any): any | undefined { - // todo@joh if (element && !IWorkspaceFolder.isIWorkspaceFolder(element) && !(element as IFileStat).isDirectory) { return new FileElement((element as IFileStat).resource, FileKind.FILE); } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 05f02021e..fdb0a57ac 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -55,6 +55,8 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess, AllEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { FileAccess } from 'vs/base/common/network'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; // Register String Editor Registry.as(EditorExtensions.Editors).registerEditor( @@ -238,27 +240,27 @@ export abstract class AbstractSideBySideEditorInputFactory implements IEditorInp const secondaryInput = secondaryInputFactory.deserialize(instantiationService, deserialized.secondarySerialized); if (primaryInput && secondaryInput) { - return this.createEditorInput(deserialized.name, deserialized.description, secondaryInput, primaryInput); + return this.createEditorInput(instantiationService, deserialized.name, deserialized.description, secondaryInput, primaryInput); } } return undefined; } - protected abstract createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput; + protected abstract createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput; } class SideBySideEditorInputFactory extends AbstractSideBySideEditorInputFactory { - protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { return new SideBySideEditorInput(name, description, secondaryInput, primaryInput); } } class DiffEditorInputFactory extends AbstractSideBySideEditorInputFactory { - protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { - return new DiffEditorInput(name, description, secondaryInput, primaryInput); + protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + return instantiationService.createInstance(DiffEditorInput, name, description, secondaryInput, primaryInput, undefined); } } @@ -462,6 +464,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands. MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_KEEP_EDITORS_COMMAND_ID, title: nls.localize('toggleKeepEditors', "Keep Editors Open"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI; } | ThemeIcon; } @@ -494,14 +497,14 @@ appendEditorToolItem( { id: SplitEditorAction.ID, title: nls.localize('splitEditorRight', "Split Editor Right"), - icon: { id: 'codicon/split-horizontal' } + icon: Codicon.splitHorizontal }, ContextKeyExpr.not('splitEditorsVertically'), 100000, // towards the end { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitEditorDown', "Split Editor Down"), - icon: { id: 'codicon/split-vertical' } + icon: Codicon.splitVertical } ); @@ -509,14 +512,14 @@ appendEditorToolItem( { id: SplitEditorAction.ID, title: nls.localize('splitEditorDown', "Split Editor Down"), - icon: { id: 'codicon/split-vertical' } + icon: Codicon.splitVertical }, ContextKeyExpr.has('splitEditorsVertically'), 100000, // towards the end { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitEditorRight', "Split Editor Right"), - icon: { id: 'codicon/split-horizontal' } + icon: Codicon.splitHorizontal } ); @@ -525,14 +528,14 @@ appendEditorToolItem( { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close"), - icon: { id: 'codicon/close' } + icon: Codicon.close }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext.toNegated(), ActiveEditorStickyContext.toNegated()), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All"), - icon: { id: 'codicon/close-all' } + icon: Codicon.closeAll } ); @@ -541,14 +544,14 @@ appendEditorToolItem( { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close"), - icon: { id: 'codicon/close-dirty' } + icon: Codicon.closeDirty }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext, ActiveEditorStickyContext.toNegated()), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All"), - icon: { id: 'codicon/close-all' } + icon: Codicon.closeAll } ); @@ -557,14 +560,14 @@ appendEditorToolItem( { id: editorCommands.UNPIN_EDITOR_COMMAND_ID, title: nls.localize('unpin', "Unpin"), - icon: { id: 'codicon/pinned' } + icon: Codicon.pinned }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext.toNegated(), ActiveEditorStickyContext), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close"), - icon: { id: 'codicon/close' } + icon: Codicon.close } ); @@ -573,23 +576,28 @@ appendEditorToolItem( { id: editorCommands.UNPIN_EDITOR_COMMAND_ID, title: nls.localize('unpin', "Unpin"), - icon: { id: 'codicon/pinned-dirty' } + icon: Codicon.pinnedDirty }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext, ActiveEditorStickyContext), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close"), - icon: { id: 'codicon/close' } + icon: Codicon.close } ); +const previousChangeIcon = registerIcon('diff-editor-previous-change', Codicon.arrowUp, nls.localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.')); +const nextChangeIcon = registerIcon('diff-editor-next-change', Codicon.arrowDown, nls.localize('nextChangeIcon', 'Icon for the next change action in the diff editor.')); +const toggleWhitespace = registerIcon('diff-editor-toggle-whitespace', Codicon.whitespace, nls.localize('toggleWhitespace', 'Icon for the toggle whitespace action in the diff editor.')); + + // Diff Editor Title Menu: Previous Change appendEditorToolItem( { id: editorCommands.GOTO_PREVIOUS_CHANGE, title: nls.localize('navigate.prev.label', "Previous Change"), - icon: { id: 'codicon/arrow-up' } + icon: previousChangeIcon }, TextCompareEditorActiveContext, 10 @@ -600,7 +608,7 @@ appendEditorToolItem( { id: editorCommands.GOTO_NEXT_CHANGE, title: nls.localize('navigate.next.label', "Next Change"), - icon: { id: 'codicon/arrow-down' } + icon: nextChangeIcon }, TextCompareEditorActiveContext, 11 @@ -611,7 +619,7 @@ appendEditorToolItem( { id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, title: nls.localize('ignoreTrimWhitespace.label', "Ignore Leading/Trailing Whitespace Differences"), - icon: { id: 'codicon/whitespace' } + icon: toggleWhitespace }, ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', true)), 20 @@ -622,7 +630,7 @@ appendEditorToolItem( { id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, title: nls.localize('showTrimWhitespace.label', "Show Leading/Trailing Whitespace Differences"), - icon: { id: 'codicon/whitespace~disabled' } + icon: ThemeIcon.modify(toggleWhitespace, 'disabled') }, ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', false)), 20 diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index ef35a87e7..c05ce6d79 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -38,7 +38,8 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { openSideBySideDirection: 'right', closeEmptyGroups: true, labelFormat: 'default', - splitSizing: 'distribute' + splitSizing: 'distribute', + splitOnDragAndDrop: true }; export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean { @@ -64,12 +65,12 @@ export interface IEditorOpeningEvent extends IEditorIdentifier { /** * The options used when opening the editor. */ - options?: IEditorOptions; + readonly options?: IEditorOptions; /** * Context indicates how the editor open event is initialized. */ - context?: OpenEditorContext; + readonly context?: OpenEditorContext; /** * Allows to prevent the opening of an editor by providing a callback diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 4b62e9478..dcece5343 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -7,22 +7,26 @@ import * as nls from 'vs/nls'; import { isObject, isString, isUndefined, isNumber, withNullAsUndefined } from 'vs/base/common/types'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditorInput, IVisibleEditorPane, ActiveEditorStickyContext, EditorsOrder } from 'vs/workbench/common/editor'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditorInput, IVisibleEditorPane, ActiveEditorStickyContext, EditorsOrder, viewColumnToEditorGroup, EditorGroupColumn } from 'vs/workbench/common/editor'; +import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IListService } from 'vs/platform/list/browser/listService'; +import { IListService, IOpenEvent } from 'vs/platform/list/browser/listService'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { distinct, coalesce } from 'vs/base/common/arrays'; import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors'; export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup'; @@ -36,6 +40,7 @@ export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOt export const MOVE_ACTIVE_EDITOR_COMMAND_ID = 'moveActiveEditor'; export const LAYOUT_EDITOR_GROUPS_COMMAND_ID = 'layoutEditorGroups'; export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor'; +export const TOGGLE_KEEP_EDITORS_COMMAND_ID = 'workbench.action.toggleKeepEditors'; export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup'; export const PIN_EDITOR_COMMAND_ID = 'workbench.action.pinEditor'; @@ -44,6 +49,9 @@ export const UNPIN_EDITOR_COMMAND_ID = 'workbench.action.unpinEditor'; export const TOGGLE_DIFF_SIDE_BY_SIDE = 'toggle.diff.renderSideBySide'; export const GOTO_NEXT_CHANGE = 'workbench.action.compareEditor.nextChange'; export const GOTO_PREVIOUS_CHANGE = 'workbench.action.compareEditor.previousChange'; +export const DIFF_FOCUS_PRIMARY_SIDE = 'workbench.action.compareEditor.focusPrimarySide'; +export const DIFF_FOCUS_SECONDARY_SIDE = 'workbench.action.compareEditor.focusSecondarySide'; +export const DIFF_FOCUS_OTHER_SIDE = 'workbench.action.compareEditor.focusOtherSide'; export const TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE = 'toggle.diff.ignoreTrimWhitespace'; export const SPLIT_EDITOR_UP = 'workbench.action.splitEditorUp'; @@ -58,6 +66,10 @@ export const FOCUS_BELOW_GROUP_WITHOUT_WRAP_COMMAND_ID = 'workbench.action.focus export const OPEN_EDITOR_AT_INDEX_COMMAND_ID = 'workbench.action.openEditorAtIndex'; +export const API_OPEN_EDITOR_COMMAND_ID = '_workbench.open'; +export const API_OPEN_DIFF_EDITOR_COMMAND_ID = '_workbench.diff'; +export const API_OPEN_WITH_EDITOR_COMMAND_ID = '_workbench.openWith'; + export interface ActiveEditorMoveArguments { to: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next'; by: 'tab' | 'group'; @@ -227,13 +239,45 @@ function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IVisi } function registerEditorGroupsLayoutCommand(): void { - CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => { - if (!args || typeof args !== 'object') { + + function applyEditorLayout(accessor: ServicesAccessor, layout: EditorGroupLayout): void { + if (!layout || typeof layout !== 'object') { return; } const editorGroupService = accessor.get(IEditorGroupsService); - editorGroupService.applyLayout(args); + editorGroupService.applyLayout(layout); + } + + CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => { + applyEditorLayout(accessor, args); + }); + + // API Command + CommandsRegistry.registerCommand({ + id: 'vscode.setEditorLayout', + handler: (accessor: ServicesAccessor, args: EditorGroupLayout) => applyEditorLayout(accessor, args), + description: { + description: 'Set Editor Layout', + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'required': ['groups'], + 'properties': { + 'orientation': { + 'type': 'number', + 'default': 0, + 'enum': [0, 1] + }, + 'groups': { + '$ref': '#/definitions/editorGroupsSchema', + 'default': [{}, {}] + } + } + } + }] + } }); } @@ -265,30 +309,68 @@ function registerDiffEditorCommands(): void { handler: accessor => navigateInDiffEditor(accessor, false) }); - function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void { + function getActiveTextDiffEditor(accessor: ServicesAccessor): TextDiffEditor | undefined { const editorService = accessor.get(IEditorService); - const candidates = [editorService.activeEditorPane, ...editorService.visibleEditorPanes].filter(editor => editor instanceof TextDiffEditor); - if (candidates.length > 0) { - const navigator = (candidates[0]).getDiffNavigator(); + for (const editor of [editorService.activeEditorPane, ...editorService.visibleEditorPanes]) { + if (editor instanceof TextDiffEditor) { + return editor; + } + } + + return undefined; + } + + function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void { + const activeTextDiffEditor = getActiveTextDiffEditor(accessor); + + if (activeTextDiffEditor) { + const navigator = activeTextDiffEditor.getDiffNavigator(); if (navigator) { next ? navigator.next() : navigator.previous(); } } } + enum FocusTextDiffEditorMode { + Original, + Modified, + Toggle + } + + function focusInDiffEditor(accessor: ServicesAccessor, mode: FocusTextDiffEditorMode): void { + const activeTextDiffEditor = getActiveTextDiffEditor(accessor); + + if (activeTextDiffEditor) { + switch (mode) { + case FocusTextDiffEditorMode.Original: + activeTextDiffEditor.getControl()?.getOriginalEditor().focus(); + break; + case FocusTextDiffEditorMode.Modified: + activeTextDiffEditor.getControl()?.getModifiedEditor().focus(); + break; + case FocusTextDiffEditorMode.Toggle: + if (activeTextDiffEditor.getControl()?.getModifiedEditor().hasWidgetFocus()) { + return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original); + } else { + return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified); + } + } + } + } + function toggleDiffSideBySide(accessor: ServicesAccessor): void { const configurationService = accessor.get(IConfigurationService); const newValue = !configurationService.getValue('diffEditor.renderSideBySide'); - configurationService.updateValue('diffEditor.renderSideBySide', newValue, ConfigurationTarget.USER); + configurationService.updateValue('diffEditor.renderSideBySide', newValue); } function toggleDiffIgnoreTrimWhitespace(accessor: ServicesAccessor): void { const configurationService = accessor.get(IConfigurationService); const newValue = !configurationService.getValue('diffEditor.ignoreTrimWhitespace'); - configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue, ConfigurationTarget.USER); + configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue); } KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -299,6 +381,30 @@ function registerDiffEditorCommands(): void { handler: accessor => toggleDiffSideBySide(accessor) }); + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_PRIMARY_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_SECONDARY_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_OTHER_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Toggle) + }); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: TOGGLE_DIFF_SIDE_BY_SIDE, @@ -320,6 +426,88 @@ function registerDiffEditorCommands(): void { }); } +function registerOpenEditorAPICommands(): void { + + function mixinContext(context: IOpenEvent | undefined, options: ITextEditorOptions | undefined, column: EditorGroupColumn | undefined): [ITextEditorOptions | undefined, EditorGroupColumn | undefined] { + if (!context) { + return [options, column]; + } + + return [ + { ...context.editorOptions, ...(options ?? Object.create(null)) }, + context.sideBySide ? SIDE_GROUP : column + ]; + } + + CommandsRegistry.registerCommand(API_OPEN_EDITOR_COMMAND_ID, async function (accessor: ServicesAccessor, resourceArg: UriComponents, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?], label?: string, context?: IOpenEvent) { + const editorService = accessor.get(IEditorService); + const editorGroupService = accessor.get(IEditorGroupsService); + const openerService = accessor.get(IOpenerService); + + const resource = URI.revive(resourceArg); + const [columnArg, optionsArg] = columnAndOptions ?? []; + + // use editor options or editor view column as a hint to use the editor service for opening + if (optionsArg || typeof columnArg === 'number') { + const [options, column] = mixinContext(context, optionsArg, columnArg); + + await editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, column)); + } + + // do not allow to execute commands from here + else if (resource.scheme === 'command') { + return; + } + + // finally, delegate to opener service + else { + await openerService.open(resource, { openToSide: context?.sideBySide, editorOptions: context?.editorOptions }); + } + }); + + CommandsRegistry.registerCommand(API_OPEN_DIFF_EDITOR_COMMAND_ID, async function (accessor: ServicesAccessor, leftResource: UriComponents, rightResource: UriComponents, label?: string, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?], context?: IOpenEvent) { + const editorService = accessor.get(IEditorService); + const editorGroupService = accessor.get(IEditorGroupsService); + + const [columnArg, optionsArg] = columnAndOptions ?? []; + const [options, column] = mixinContext(context, optionsArg, columnArg); + + await editorService.openEditor({ + leftResource: URI.revive(leftResource), + rightResource: URI.revive(rightResource), + label, + options + }, viewColumnToEditorGroup(editorGroupService, column)); + }); + + CommandsRegistry.registerCommand(API_OPEN_WITH_EDITOR_COMMAND_ID, (accessor: ServicesAccessor, resource: UriComponents, id: string, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?]) => { + const editorService = accessor.get(IEditorService); + const editorGroupsService = accessor.get(IEditorGroupsService); + const configurationService = accessor.get(IConfigurationService); + const quickInputService = accessor.get(IQuickInputService); + + const [columnArg, optionsArg] = columnAndOptions ?? []; + let group: IEditorGroup | undefined = undefined; + + if (columnArg === SIDE_GROUP) { + const direction = preferredSideBySideGroupDirection(configurationService); + + let neighbourGroup = editorGroupsService.findGroup({ direction }); + if (!neighbourGroup) { + neighbourGroup = editorGroupsService.addGroup(editorGroupsService.activeGroup, direction); + } + group = neighbourGroup; + } else { + group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, columnArg)) ?? editorGroupsService.activeGroup; + } + + const textOptions: ITextEditorOptions = optionsArg ? { ...optionsArg, override: false } : { override: false }; + + const input = editorService.createEditorInput({ resource: URI.revive(resource) }); + return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService); + }); +} + function registerOpenEditorAtIndexCommands(): void { const openEditorAtIndex: ICommandHandler = (accessor: ServicesAccessor, editorIndex: number): void => { const editorService = accessor.get(IEditorService); @@ -710,6 +898,31 @@ function registerOtherEditorCommands(): void { } }); + CommandsRegistry.registerCommand({ + id: TOGGLE_KEEP_EDITORS_COMMAND_ID, + handler: accessor => { + const configurationService = accessor.get(IConfigurationService); + const notificationService = accessor.get(INotificationService); + const openerService = accessor.get(IOpenerService); + + // Update setting + const currentSetting = configurationService.getValue('workbench.editor.enablePreview'); + const newSetting = currentSetting === true ? false : true; + configurationService.updateValue('workbench.editor.enablePreview', newSetting); + + // Inform user + notificationService.prompt( + Severity.Info, + newSetting ? + nls.localize('enablePreview', "Preview editors have been enabled in settings.") : + nls.localize('disablePreview', "Preview editors have been disabled in settings."), + [{ + label: nls.localize('learnMode', "Learn More"), run: () => openerService.open('https://go.microsoft.com/fwlink/?linkid=2147473') + }] + ); + } + }); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: PIN_EDITOR_COMMAND_ID, weight: KeybindingWeight.WorkbenchContrib, @@ -877,6 +1090,7 @@ export function setup(): void { registerActiveEditorMoveCommand(); registerEditorGroupsLayoutCommand(); registerDiffEditorCommands(); + registerOpenEditorAPICommands(); registerOpenEditorAtIndexCommands(); registerCloseEditorCommands(); registerOtherEditorCommands(); diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 9c48c54fe..50f571563 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -12,7 +12,7 @@ import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; -import { GroupDirection, MergeGroupMode, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { GroupDirection, IEditorGroupsService, MergeGroupMode, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -25,6 +25,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; +import { ByteSize } from 'vs/platform/files/common/files'; interface IDropOperation { splitDirection?: GroupDirection; @@ -34,7 +35,7 @@ class DropOverlay extends Themable { private static readonly OVERLAY_ID = 'monaco-workbench-editor-drop-overlay'; - private static readonly MAX_FILE_UPLOAD_SIZE = 100 * 1024 * 1024; // 100mb + private static readonly MAX_FILE_UPLOAD_SIZE = 100 * ByteSize.MB; private container: HTMLElement | undefined; private overlay: HTMLElement | undefined; @@ -54,7 +55,8 @@ class DropOverlay extends Themable { @IInstantiationService private instantiationService: IInstantiationService, @IFileDialogService private readonly fileDialogService: IFileDialogService, @IEditorService private readonly editorService: IEditorService, - @INotificationService private readonly notificationService: INotificationService + @INotificationService private readonly notificationService: INotificationService, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService ) { super(themeService); @@ -143,8 +145,14 @@ class DropOverlay extends Themable { } } - // Position overlay - this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup); + // Position overlay and conditionally enable or disable + // editor group splitting support based on setting and + // keymodifiers used. + let splitOnDragAndDrop = !!this.editorGroupService.partOptions.splitOnDragAndDrop; + if (this.isToggleSplitOperation(e)) { + splitOnDragAndDrop = !splitOnDragAndDrop; + } + this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup, splitOnDragAndDrop); // Make sure to stop any running cleanup scheduler to remove the overlay if (this.cleanupOverlayScheduler.isScheduled()) { @@ -319,7 +327,7 @@ class DropOverlay extends Themable { // Try to come up with a good file path for the untitled // editor by asking the file dialog service for the default let proposedFilePath: URI | undefined = undefined; - const defaultFilePath = this.fileDialogService.defaultFilePath(); + const defaultFilePath = await this.fileDialogService.defaultFilePath(); if (defaultFilePath) { proposedFilePath = joinPath(defaultFilePath, name); } @@ -362,24 +370,33 @@ class DropOverlay extends Themable { return (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh); } - private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean): void { + private isToggleSplitOperation(e: DragEvent): boolean { + return (e.altKey && !isMacintosh) || (e.shiftKey && isMacintosh); + } + + private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean, enableSplitting: boolean): void { const preferSplitVertically = this.accessor.partOptions.openSideBySideDirection === 'right'; const editorControlWidth = this.groupView.element.clientWidth; const editorControlHeight = this.groupView.element.clientHeight - this.getOverlayOffsetHeight(); let edgeWidthThresholdFactor: number; - if (isDraggingGroup) { - edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction - } else { - edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors - } - let edgeHeightThresholdFactor: number; - if (isDraggingGroup) { - edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction + if (enableSplitting) { + if (isDraggingGroup) { + edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction + } else { + edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors + } + + if (isDraggingGroup) { + edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction + } else { + edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors + } } else { - edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors + edgeWidthThresholdFactor = 0; + edgeHeightThresholdFactor = 0; } const edgeWidthThreshold = editorControlWidth * edgeWidthThresholdFactor; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index d01c17c4a..a212456dd 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1742,28 +1742,17 @@ class EditorOpeningEvent implements IEditorOpeningEvent { private override: (() => Promise) | undefined = undefined; constructor( - private _group: GroupIdentifier, - private _editor: EditorInput, + public readonly groupId: GroupIdentifier, + public readonly editor: EditorInput, private _options: EditorOptions | undefined, - private _context: OpenEditorContext | undefined + public readonly context: OpenEditorContext | undefined ) { } - get groupId(): GroupIdentifier { - return this._group; - } - - get editor(): EditorInput { - return this._editor; - } - get options(): EditorOptions | undefined { return this._options; } - get context(): OpenEditorContext | undefined { - return this._context; - } prevent(callback: () => Promise): void { this.override = callback; @@ -1775,9 +1764,9 @@ class EditorOpeningEvent implements IEditorOpeningEvent { } export interface EditorReplacement { - editor: EditorInput; - replacement: EditorInput; - options?: EditorOptions; + readonly editor: EditorInput; + readonly replacement: EditorInput; + readonly options?: EditorOptions; } registerThemingParticipant((theme, collector, environment) => { diff --git a/src/vs/workbench/browser/parts/editor/editorPane.ts b/src/vs/workbench/browser/parts/editor/editorPane.ts index 07a33a40c..92dc1975b 100644 --- a/src/vs/workbench/browser/parts/editor/editorPane.ts +++ b/src/vs/workbench/browser/parts/editor/editorPane.ts @@ -9,7 +9,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorGroup, IEditorGroupsService, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { LRUCache, Touch } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; @@ -156,7 +156,7 @@ export abstract class EditorPane extends Composite implements IEditorPane { let editorMemento = EditorPane.EDITOR_MEMENTOS.get(mementoKey); if (!editorMemento) { - editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE), limit, editorGroupService); + editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE), limit, editorGroupService); EditorPane.EDITOR_MEMENTOS.set(mementoKey, editorMemento); } @@ -220,18 +220,7 @@ export class EditorMemento implements IEditorMemento { // Automatically clear when editor input gets disposed if any if (resourceOrEditor instanceof EditorInput) { - const editor = resourceOrEditor; - - if (!this.editorDisposables) { - this.editorDisposables = new Map(); - } - - if (!this.editorDisposables.has(editor)) { - this.editorDisposables.set(editor, Event.once(resourceOrEditor.onDispose)(() => { - this.clearEditorState(resource); - this.editorDisposables?.delete(editor); - })); - } + this.clearEditorStateOnDispose(resource, resourceOrEditor); } } @@ -240,7 +229,7 @@ export class EditorMemento implements IEditorMemento { loadEditorState(group: IEditorGroup, resourceOrEditor: URI | EditorInput, fallbackToOtherGroupState?: boolean): T | undefined { const resource = this.doGetResource(resourceOrEditor); if (!resource || !group) { - return undefined; // we are not in a good state to load any state for a resource + return; // we are not in a good state to load any state for a resource } const cache = this.doLoad(); @@ -261,12 +250,16 @@ export class EditorMemento implements IEditorMemento { } } - return undefined; + return; } clearEditorState(resource: URI, group?: IEditorGroup): void; clearEditorState(editor: EditorInput, group?: IEditorGroup): void; clearEditorState(resourceOrEditor: URI | EditorInput, group?: IEditorGroup): void { + if (resourceOrEditor instanceof EditorInput) { + this.editorDisposables?.delete(resourceOrEditor); + } + const resource = this.doGetResource(resourceOrEditor); if (resource) { const cache = this.doLoad(); @@ -286,6 +279,19 @@ export class EditorMemento implements IEditorMemento { } } + clearEditorStateOnDispose(resource: URI, editor: EditorInput): void { + if (!this.editorDisposables) { + this.editorDisposables = new Map(); + } + + if (!this.editorDisposables.has(editor)) { + this.editorDisposables.set(editor, Event.once(editor.onDispose)(() => { + this.clearEditorState(resource); + this.editorDisposables?.delete(editor); + })); + } + } + moveEditorState(source: URI, target: URI, comparer: IExtUri): void { const cache = this.doLoad(); @@ -342,7 +348,7 @@ export class EditorMemento implements IEditorMemento { saveState(): void { const cache = this.doLoad(); - // Cleanup once during shutdown + // Cleanup once during session if (!this.cleanedUp) { this.cleanUp(); this.cleanedUp = true; diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index e6e216a5e..828156890 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -19,7 +19,7 @@ import { IEditorGroupsAccessor, IEditorGroupView, getEditorPartOptions, impactsE import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; import { EditorDropTarget, IEditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget'; import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; @@ -148,8 +148,8 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.gridWidgetView = new GridWidgetView(); - this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE); - this.globalMemento = this.getMemento(StorageScope.GLOBAL); + this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + this.globalMemento = this.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); this._whenRestored = new Promise(resolve => (this.whenRestoredResolve = resolve)); diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index bfa6f87dc..1d85f0ad5 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/editorstatus'; import * as nls from 'vs/nls'; import { runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; -import { format, compare } from 'vs/base/common/strings'; +import { format, compare, splitLines } from 'vs/base/common/strings'; import { extname, basename, isEqual } from 'vs/base/common/resources'; import { areFunctions, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -346,12 +346,12 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { [{ label: nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"), run: () => { - this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER); + this.configurationService.updateValue('editor.accessibilitySupport', 'on'); } }, { label: nls.localize('screenReaderDetectedExplanation.answerNo', "No"), run: () => { - this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER); + this.configurationService.updateValue('editor.accessibilitySupport', 'off'); } }], { sticky: true } @@ -918,7 +918,7 @@ class ShowCurrentMarkerInStatusbarContribution extends Disposable { this.currentMarker = this.getMarker(); if (this.hasToUpdateStatus(previousMarker, this.currentMarker)) { if (this.currentMarker) { - const line = this.currentMarker.message.split(/\r\n|\r|\n/g)[0]; + const line = splitLines(this.currentMarker.message)[0]; const text = `${this.getType(this.currentMarker)} ${line}`; if (!this.statusBarEntryAccessor.value) { this.statusBarEntryAccessor.value = this.statusbarService.addEntry({ text: '', ariaLabel: '' }, 'statusbar.currentProblem', nls.localize('currentProblem', "Current Problem"), StatusbarAlignment.LEFT); @@ -1046,12 +1046,19 @@ export class ChangeModeAction extends Action { @IQuickInputService private readonly quickInputService: IQuickInputService, @IPreferencesService private readonly preferencesService: IPreferencesService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextFileService private readonly textFileService: ITextFileService + @ITextFileService private readonly textFileService: ITextFileService, + @ICommandService private readonly commandService: ICommandService ) { super(actionId, actionLabel); } async run(): Promise { + const activeEditorPane = this.editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined; + if (activeEditorPane?.isNotebookEditor) { + // it's inside notebook editor + return this.commandService.executeCommand('notebook.cell.changeLanguage'); + } + const activeTextEditorControl = getCodeEditor(this.editorService.activeTextEditorControl); if (!activeTextEditorControl) { await this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); @@ -1143,7 +1150,7 @@ export class ChangeModeAction extends Action { // User decided to configure settings for current language if (pick === configureModeSettings) { - this.preferencesService.openGlobalSettings(true, { editSetting: `[${withUndefinedAsNull(currentModeId)}]` }); + this.preferencesService.openGlobalSettings(true, { revealSetting: { key: `[${withUndefinedAsNull(currentModeId)}]`, edit: true } }); return; } diff --git a/src/vs/workbench/browser/parts/editor/editorWidgets.ts b/src/vs/workbench/browser/parts/editor/editorWidgets.ts index 5d7217725..02601bc97 100644 --- a/src/vs/workbench/browser/parts/editor/editorWidgets.ts +++ b/src/vs/workbench/browser/parts/editor/editorWidgets.ts @@ -38,6 +38,8 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget { super(); this._domNode = $('.floating-click-widget'); + this._domNode.style.padding = '10px'; + this._domNode.style.cursor = 'pointer'; if (keyBindingAction) { const keybinding = keybindingService.lookupKeybinding(keyBindingAction); diff --git a/src/vs/workbench/browser/parts/editor/editorsObserver.ts b/src/vs/workbench/browser/parts/editor/editorsObserver.ts index 2733dfb40..285653b99 100644 --- a/src/vs/workbench/browser/parts/editor/editorsObserver.ts +++ b/src/vs/workbench/browser/parts/editor/editorsObserver.ts @@ -5,7 +5,7 @@ import { IEditorInput, IEditorInputFactoryRegistry, IEditorIdentifier, GroupIdentifier, Extensions, IEditorPartOptionsChangeEvent, EditorsOrder, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { Event, Emitter } from 'vs/base/common/event'; import { IEditorGroupsService, IEditorGroup, GroupChangeKind, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -339,7 +339,7 @@ export class EditorsObserver extends Disposable { if (this.mostRecentEditorsMap.isEmpty()) { this.storageService.remove(EditorsObserver.STORAGE_KEY, StorageScope.WORKSPACE); } else { - this.storageService.store(EditorsObserver.STORAGE_KEY, JSON.stringify(this.serialize()), StorageScope.WORKSPACE); + this.storageService.store(EditorsObserver.STORAGE_KEY, JSON.stringify(this.serialize()), StorageScope.WORKSPACE, StorageTarget.MACHINE); } } diff --git a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css index 98ddd9e96..3b8bac456 100644 --- a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css @@ -21,7 +21,7 @@ padding-right: 6px; } -/* todo@joh move somewhere else */ +/* breadcrumbs-picker-style */ .monaco-workbench .monaco-breadcrumbs-picker .arrow { position: absolute; diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index bed3f2094..bcd835f4c 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -589,8 +589,8 @@ export class TabsTitleControl extends TitleControl { const tabActionRunner = new EditorCommandsContextActionRunner({ groupId: this.group.id, editorIndex: index }); - const tabActionBar = new ActionBar(tabActionsContainer, { ariaLabel: localize('ariaLabelTabActions', "Tab actions"), actionRunner: tabActionRunner, }); - tabActionBar.onDidBeforeRun(e => { + const tabActionBar = new ActionBar(tabActionsContainer, { ariaLabel: localize('ariaLabelTabActions', "Tab actions"), actionRunner: tabActionRunner }); + tabActionBar.onBeforeRun(e => { if (e.action.id === this.closeEditorAction.id) { this.blockRevealActiveTabOnce(); } diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 115d6ee98..4557a2c48 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -24,7 +24,6 @@ import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/co import { DisposableStore } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { URI } from 'vs/base/common/uri'; -import { Event } from 'vs/base/common/event'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -32,6 +31,7 @@ import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/edit import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { isEqual } from 'vs/base/common/resources'; import { multibyteAwareBtoa } from 'vs/base/browser/dom'; +import { IFileService } from 'vs/platform/files/common/files'; /** * The text editor that leverages the diff text editor for the editing experience. @@ -62,9 +62,28 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, @IEditorService editorService: IEditorService, @IThemeService themeService: IThemeService, - @IEditorGroupsService editorGroupService: IEditorGroupsService + @IEditorGroupsService editorGroupService: IEditorGroupsService, + @IFileService private readonly fileService: IFileService ) { super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService); + + // Listen to file system provider changes + this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onDidFileSystemProviderChange(e.scheme))); + this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onDidFileSystemProviderChange(e.scheme))); + } + + private onDidFileSystemProviderChange(scheme: string): void { + const control = this.getControl(); + const input = this.input; + + if (control && input instanceof DiffEditorInput) { + if (input.originalInput.resource?.scheme === scheme || input.modifiedInput.resource?.scheme === scheme) { + control.updateOptions({ + readOnly: input.modifiedInput.isReadonly(), + originalEditable: !input.originalInput.isReadonly() + }); + } + } } protected onWillCloseEditorInGroup(editor: IEditorInput): void { @@ -175,7 +194,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan const originalInput = input.originalInput; const modifiedInput = input.modifiedInput; - const binaryDiffInput = new DiffEditorInput(input.getName(), input.getDescription(), originalInput, modifiedInput, true); + const binaryDiffInput = this.instantiationService.createInstance(DiffEditorInput, input.getName(), input.getDescription(), originalInput, modifiedInput, true); // Forward binary flag to input if supported const fileEditorInputFactory = Registry.as(EditorInputExtensions.EditorInputFactories).getFileEditorInputFactory(); @@ -217,16 +236,17 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan // Handle diff editor specially by merging in diffEditor configuration if (isObject(configuration.diffEditor)) { - // User settings defines `diffEditor.codeLens`, but there is also `editor.codeLens`. - // Due to the mixin, the two settings cannot be distinguished anymore. - // - // So we map `diffEditor.codeLens` to `diffEditor.originalCodeLens` and `diffEditor.modifiedCodeLens`. const diffEditorConfiguration = objects.deepClone(configuration.diffEditor); - diffEditorConfiguration.originalCodeLens = diffEditorConfiguration.codeLens; - diffEditorConfiguration.modifiedCodeLens = diffEditorConfiguration.codeLens; + + // User settings defines `diffEditor.codeLens`, but here we rename that to `diffEditor.diffCodeLens` to avoid collisions with `editor.codeLens`. + diffEditorConfiguration.diffCodeLens = diffEditorConfiguration.codeLens; delete diffEditorConfiguration.codeLens; - objects.mixin(editorConfiguration, diffEditorConfiguration); + // User settings defines `diffEditor.wordWrap`, but here we rename that to `diffEditor.diffWordWrap` to avoid collisions with `editor.wordWrap`. + diffEditorConfiguration.diffWordWrap = <'off' | 'on' | 'inherit' | undefined>diffEditorConfiguration.wordWrap; + delete diffEditorConfiguration.wordWrap; + + Object.assign(editorConfiguration, diffEditorConfiguration); } return editorConfiguration; @@ -309,12 +329,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan // Otherwise save it else { - super.saveTextEditorViewState(resource); - - // Make sure to clean up when the input gets disposed - Event.once(input.onDispose)(() => { - super.clearTextEditorViewState([resource], this.group); - }); + super.saveTextEditorViewState(resource, input); } } diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index f2a77542e..9cae4d214 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -67,6 +67,7 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa @IEditorGroupsService protected editorGroupService: IEditorGroupsService ) { super(id, telemetryService, themeService, storageService); + this._instantiationService = instantiationService; this.editorMemento = this.getEditorMemento(editorGroupService, BaseTextEditor.TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100); @@ -223,13 +224,27 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa return this.editorControl; } - protected saveTextEditorViewState(resource: URI): void { + getViewState(): IEditorViewState | undefined { + const resource = this.input?.resource; + if (resource) { + return withNullAsUndefined(this.retrieveTextEditorViewState(resource)); + } + + return undefined; + } + + + protected saveTextEditorViewState(resource: URI, cleanUpOnDispose?: IEditorInput): void { const editorViewState = this.retrieveTextEditorViewState(resource); if (!editorViewState || !this.group) { return; } this.editorMemento.saveEditorState(this.group, resource, editorViewState); + + if (cleanUpOnDispose) { + this.editorMemento.clearEditorStateOnDispose(resource, cleanUpOnDispose); + } } protected shouldRestoreTextEditorViewState(editor: IEditorInput, context?: IEditorOpenContext): boolean { @@ -243,15 +258,6 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa return true; } - getViewState(): IEditorViewState | undefined { - const resource = this.input?.resource; - if (resource) { - return withNullAsUndefined(this.retrieveTextEditorViewState(resource)); - } - - return undefined; - } - protected retrieveTextEditorViewState(resource: URI): IEditorViewState | null { const control = this.getControl(); if (!isCodeEditor(control)) { @@ -284,9 +290,9 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa } protected clearTextEditorViewState(resources: URI[], group?: IEditorGroup): void { - resources.forEach(resource => { + for (const resource of resources) { this.editorMemento.clearEditorState(resource, group); - }); + } } private updateEditorConfiguration(configuration?: IEditorConfiguration): void { diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index a99323c2e..2bb2640dc 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -16,7 +16,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { Event } from 'vs/base/common/event'; import { ScrollType, IEditor } from 'vs/editor/common/editorCommon'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -158,12 +157,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor { // Otherwise save it else { - super.saveTextEditorViewState(resource); - - // Make sure to clean up when the input gets disposed - Event.once(input.onDispose)(() => { - super.clearTextEditorViewState([resource]); - }); + super.saveTextEditorViewState(resource, input); } } } diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index c6be6928d..8bd0a4022 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -105,18 +105,27 @@ overflow: hidden; } +.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container > .monaco-button-dropdown, +.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container > .monaco-button { + margin: 4px 5px; /* allows button focus outline to be visible */ +} + .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-button { + outline-offset: 2px !important; +} + +.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-text-button { width: fit-content; width: -moz-fit-content; padding: 5px 10px; - margin: 4px 5px; /* allows button focus outline to be visible */ + display: inline-block; /* to enable ellipsis in text overflow */ font-size: 12px; overflow: hidden; text-overflow: ellipsis; } -.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-text-button { - display: inline-block; /* to enable ellipsis in text overflow */ +.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-dropdown-button { + padding: 5px } /** Notification: Progress */ diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css b/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css index f1ab3c2cf..987f9a368 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css @@ -26,7 +26,7 @@ } .monaco-workbench > .notifications-toasts .notification-toast-container > .notification-toast { - margin: 5px; /* enables separation and drop shadows around toasts */ + margin: 8px; /* enables separation and drop shadows around toasts */ transform: translate3d(0px, 100%, 0px); /* move the notification 50px to the bottom (to prevent bleed through) */ opacity: 0; /* fade the toast in */ transition: transform 300ms ease-out, opacity 300ms ease-out; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts index 477b90cfc..7aaa25068 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts @@ -12,14 +12,16 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { CLEAR_NOTIFICATION, EXPAND_NOTIFICATION, COLLAPSE_NOTIFICATION, CLEAR_ALL_NOTIFICATIONS, HIDE_NOTIFICATIONS_CENTER } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -const clearIcon = registerIcon('notifications-clear', Codicon.close); -const clearAllIcon = registerIcon('notifications-clear-all', Codicon.clearAll); -const hideIcon = registerIcon('notifications-hide', Codicon.chevronDown); -const expandIcon = registerIcon('notifications-expand', Codicon.chevronUp); -const collapseIcon = registerIcon('notifications-collapse', Codicon.chevronDown); -const configureIcon = registerIcon('notifications-configure', Codicon.gear); +const clearIcon = registerIcon('notifications-clear', Codicon.close, localize('clearIcon', 'Icon for the clear action in notifications.')); +const clearAllIcon = registerIcon('notifications-clear-all', Codicon.clearAll, localize('clearAllIcon', 'Icon for the clear all action in notifications.')); +const hideIcon = registerIcon('notifications-hide', Codicon.chevronDown, localize('hideIcon', 'Icon for the hide action in notifications.')); +const expandIcon = registerIcon('notifications-expand', Codicon.chevronUp, localize('expandIcon', 'Icon for the expand action in notifications.')); +const collapseIcon = registerIcon('notifications-collapse', Codicon.chevronDown, localize('collapseIcon', 'Icon for the collapse action in notifications.')); +const configureIcon = registerIcon('notifications-configure', Codicon.gear, localize('configureIcon', 'Icon for the configure action in notifications.')); export class ClearNotificationAction extends Action { @@ -31,7 +33,7 @@ export class ClearNotificationAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, clearIcon.classNames); + super(id, label, ThemeIcon.asClassName(clearIcon)); } async run(notification: INotificationViewItem): Promise { @@ -49,7 +51,7 @@ export class ClearAllNotificationsAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, clearAllIcon.classNames); + super(id, label, ThemeIcon.asClassName(clearAllIcon)); } async run(): Promise { @@ -67,7 +69,7 @@ export class HideNotificationsCenterAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, hideIcon.classNames); + super(id, label, ThemeIcon.asClassName(hideIcon)); } async run(): Promise { @@ -85,7 +87,7 @@ export class ExpandNotificationAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, expandIcon.classNames); + super(id, label, ThemeIcon.asClassName(expandIcon)); } async run(notification: INotificationViewItem): Promise { @@ -103,7 +105,7 @@ export class CollapseNotificationAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, collapseIcon.classNames); + super(id, label, ThemeIcon.asClassName(collapseIcon)); } async run(notification: INotificationViewItem): Promise { @@ -121,7 +123,7 @@ export class ConfigureNotificationAction extends Action { label: string, public readonly configurationActions: ReadonlyArray ) { - super(id, label, configureIcon.classNames); + super(id, label, ThemeIcon.asClassName(configureIcon)); } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 6e8b4e940..d4074bbeb 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -251,7 +251,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente protected updateStyles(): void { if (this.notificationsCenterContainer && this.notificationsCenterHeader) { const widgetShadowColor = this.getColor(widgetShadow); - this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : ''; + this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : ''; const borderColor = this.getColor(NOTIFICATIONS_CENTER_BORDER); this.notificationsCenterContainer.style.border = borderColor ? `1px solid ${borderColor}` : ''; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index a935e757a..20b125013 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -15,20 +15,20 @@ import { IListService, WorkbenchList } from 'vs/platform/list/browser/listServic // Center export const SHOW_NOTIFICATIONS_CENTER = 'notifications.showList'; export const HIDE_NOTIFICATIONS_CENTER = 'notifications.hideList'; -export const TOGGLE_NOTIFICATIONS_CENTER = 'notifications.toggleList'; +const TOGGLE_NOTIFICATIONS_CENTER = 'notifications.toggleList'; // Toasts -export const HIDE_NOTIFICATION_TOAST = 'notifications.hideToasts'; -export const FOCUS_NOTIFICATION_TOAST = 'notifications.focusToasts'; -export const FOCUS_NEXT_NOTIFICATION_TOAST = 'notifications.focusNextToast'; -export const FOCUS_PREVIOUS_NOTIFICATION_TOAST = 'notifications.focusPreviousToast'; -export const FOCUS_FIRST_NOTIFICATION_TOAST = 'notifications.focusFirstToast'; -export const FOCUS_LAST_NOTIFICATION_TOAST = 'notifications.focusLastToast'; +const HIDE_NOTIFICATION_TOAST = 'notifications.hideToasts'; +const FOCUS_NOTIFICATION_TOAST = 'notifications.focusToasts'; +const FOCUS_NEXT_NOTIFICATION_TOAST = 'notifications.focusNextToast'; +const FOCUS_PREVIOUS_NOTIFICATION_TOAST = 'notifications.focusPreviousToast'; +const FOCUS_FIRST_NOTIFICATION_TOAST = 'notifications.focusFirstToast'; +const FOCUS_LAST_NOTIFICATION_TOAST = 'notifications.focusLastToast'; // Notification export const COLLAPSE_NOTIFICATION = 'notification.collapse'; export const EXPAND_NOTIFICATION = 'notification.expand'; -export const TOGGLE_NOTIFICATION = 'notification.toggle'; +const TOGGLE_NOTIFICATION = 'notification.toggle'; export const CLEAR_NOTIFICATION = 'notification.clear'; export const CLEAR_ALL_NOTIFICATIONS = 'notifications.clearAll'; @@ -159,12 +159,20 @@ export function registerNotificationCommands(center: INotificationsCenterControl }); // Hide Toasts - KeybindingsRegistry.registerCommandAndKeybindingRule({ + CommandsRegistry.registerCommand(HIDE_NOTIFICATION_TOAST, accessor => toasts.hide()); + + KeybindingsRegistry.registerKeybindingRule({ id: HIDE_NOTIFICATION_TOAST, - weight: KeybindingWeight.WorkbenchContrib + 50, + weight: KeybindingWeight.WorkbenchContrib - 50, // lower when not focused (e.g. let editor suggest win over this command) when: NotificationsToastsVisibleContext, - primary: KeyCode.Escape, - handler: accessor => toasts.hide() + primary: KeyCode.Escape + }); + + KeybindingsRegistry.registerKeybindingRule({ + id: HIDE_NOTIFICATION_TOAST, + weight: KeybindingWeight.WorkbenchContrib + 100, // higher when focused + when: ContextKeyExpr.and(NotificationsToastsVisibleContext, NotificationFocusedContext), + primary: KeyCode.Escape }); // Focus Toasts diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 22f915231..6eb5f2cee 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -482,7 +482,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast toast.style.background = backgroundColor ? backgroundColor : ''; const widgetShadowColor = this.getColor(widgetShadow); - toast.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : ''; + toast.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : ''; const borderColor = this.getColor(NOTIFICATIONS_TOAST_BORDER); toast.style.border = borderColor ? `1px solid ${borderColor}` : ''; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index a78fd3199..51272ab8a 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -8,11 +8,11 @@ import { clearNode, addDisposableListener, EventType, EventHelper, $ } from 'vs/ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { ButtonGroup } from 'vs/base/browser/ui/button/button'; +import { ButtonBar } from 'vs/base/browser/ui/button/button'; import { attachButtonStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { ActionRunner, ActionWithMenuAction, IAction, IActionRunner } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { dispose, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -285,7 +285,8 @@ export class NotificationTemplateRenderer extends Disposable { @IOpenerService private readonly openerService: IOpenerService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IThemeService private readonly themeService: IThemeService, - @IKeybindingService private readonly keybindingService: IKeybindingService + @IKeybindingService private readonly keybindingService: IKeybindingService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, ) { super(); @@ -441,27 +442,40 @@ export class NotificationTemplateRenderer extends Disposable { const primaryActions = notification.actions ? notification.actions.primary : undefined; if (notification.expanded && isNonEmptyArray(primaryActions)) { - const buttonGroup = new ButtonGroup(this.template.buttonsContainer, primaryActions.length, { title: true /* assign titles to buttons in case they overflow */ }); - buttonGroup.buttons.forEach((button, index) => { - const action = primaryActions[index]; - button.label = action.label; - - this.inputDisposables.add(button.onDidClick(e => { - EventHelper.stop(e, true); - + const that = this; + const actionRunner: IActionRunner = new class extends ActionRunner { + protected async runAction(action: IAction): Promise { // Run action - this.actionRunner.run(action, notification); + that.actionRunner.run(action, notification); // Hide notification (unless explicitly prevented) if (!(action instanceof ChoiceAction) || !action.keepOpen) { notification.close(); } + } + }(); + const buttonToolbar = this.inputDisposables.add(new ButtonBar(this.template.buttonsContainer)); + for (const action of primaryActions) { + const buttonOptions = { title: true, /* assign titles to buttons in case they overflow */ }; + const dropdownActions = action instanceof ChoiceAction ? action.menu + : action instanceof ActionWithMenuAction ? action.actions : undefined; + const button = this.inputDisposables.add( + dropdownActions + ? buttonToolbar.addButtonWithDropdown({ + ...buttonOptions, + contextMenuProvider: this.contextMenuService, + actions: dropdownActions, + actionRunner + }) + : buttonToolbar.addButton(buttonOptions)); + button.label = action.label; + this.inputDisposables.add(button.onDidClick(e => { + EventHelper.stop(e, true); + actionRunner.run(action); })); this.inputDisposables.add(attachButtonStyler(button, this.themeService)); - }); - - this.inputDisposables.add(buttonGroup); + } } } diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 96d407d05..27fc613ff 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -84,10 +84,6 @@ justify-content: center; } -.monaco-workbench .part.panel > .composite.title > .composite-bar-excess { - width: 100px; -} - .monaco-workbench .part.panel > .composite.title> .panel-switcher-container > .monaco-action-bar { line-height: 27px; /* matches panel titles in settings */ height: 35px; diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 41674f53f..74619ecec 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -18,11 +18,13 @@ import { IActivity } from 'vs/workbench/common/activity'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ActivePanelContext, PanelPositionContext } from 'vs/workbench/common/panel'; import { ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp); -const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown); -const closeIcon = registerIcon('panel-close', Codicon.close); +const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp, nls.localize('maximizeIcon', 'Icon to maximize a panel.')); +const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown, nls.localize('restoreIcon', 'Icon to restore a panel.')); +const closeIcon = registerIcon('panel-close', Codicon.close, nls.localize('closeIcon', 'Icon to close a panel.')); export class ClosePanelAction extends Action { @@ -34,7 +36,7 @@ export class ClosePanelAction extends Action { name: string, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { - super(id, name, closeIcon.classNames); + super(id, name, ThemeIcon.asClassName(closeIcon)); } async run(): Promise { @@ -106,11 +108,11 @@ export class ToggleMaximizedPanelAction extends Action { @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IEditorGroupsService editorGroupsService: IEditorGroupsService ) { - super(id, label, layoutService.isPanelMaximized() ? restoreIcon.classNames : maximizeIcon.classNames); + super(id, label, layoutService.isPanelMaximized() ? ThemeIcon.asClassName(restoreIcon) : ThemeIcon.asClassName(maximizeIcon)); this.toDispose.add(editorGroupsService.onDidLayout(() => { const maximized = this.layoutService.isPanelMaximized(); - this.class = maximized ? restoreIcon.classNames : maximizeIcon.classNames; + this.class = maximized ? ThemeIcon.asClassName(restoreIcon) : ThemeIcon.asClassName(maximizeIcon); this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL; })); } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index e7480b64c..332a9ab4c 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -13,7 +13,7 @@ import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/ import { Panel, PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel'; import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService'; import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -37,7 +37,6 @@ import { ViewContainer, IViewDescriptorService, IViewContainerModel, ViewContain import { MenuId } from 'vs/platform/actions/common/actions'; import { ViewMenuActions, ViewContainerMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { Before2D, CompositeDragAndDropObserver, ICompositeDragAndDrop, toggleDropEffect } from 'vs/workbench/browser/dnd'; import { IActivity } from 'vs/workbench/common/activity'; @@ -118,7 +117,6 @@ export class PanelPart extends CompositePart implements IPanelService { @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IExtensionService private readonly extensionService: IExtensionService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super( notificationService, @@ -140,7 +138,6 @@ export class PanelPart extends CompositePart implements IPanelService { ); this.panelRegistry = Registry.as(PanelExtensions.Panels); - storageKeysSyncRegistryService.registerStorageKey({ key: PanelPart.PINNED_PANELS, version: 1 }); this.dndHandler = new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Panel, (id: string, focus?: boolean) => (this.openPanel(id, focus) as Promise).then(panel => panel || null), @@ -338,7 +335,7 @@ export class PanelPart extends CompositePart implements IPanelService { disposables.clear(); this.onDidRegisterExtensions(); this.compositeBar.onDidChange(() => this.saveCachedPanels(), this, disposables); - this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables); + this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables); })); } @@ -670,7 +667,7 @@ export class PanelPart extends CompositePart implements IPanelService { return this.toolBar.getItemsWidth(); } - private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { + private onDidStorageValueChange(e: IStorageValueChangeEvent): void { if (e.key === PanelPart.PINNED_PANELS && e.scope === StorageScope.GLOBAL && this.cachedPanelsValue !== this.getStoredCachedPanelsValue() /* This checks if current window changed the value or not */) { this._cachedPanelsValue = undefined; @@ -760,7 +757,7 @@ export class PanelPart extends CompositePart implements IPanelService { } private setStoredCachedViewletsValue(value: string): void { - this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL); + this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL, StorageTarget.USER); } private getPlaceholderViewContainers(): IPlaceholderViewContainer[] { @@ -792,7 +789,7 @@ export class PanelPart extends CompositePart implements IPanelService { } private setStoredPlaceholderViewContainersValue(value: string): void { - this.storageService.store(PanelPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.WORKSPACE); + this.storageService.store(PanelPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.WORKSPACE, StorageTarget.MACHINE); } private getViewContainer(panelId: string): ViewContainer | undefined { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index ced7b6480..759039699 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -23,7 +23,7 @@ import { isThemeColor } from 'vs/editor/common/editorCommon'; import { Color } from 'vs/base/common/color'; import { EventHelper, createStyleSheet, addDisposableListener, EventType, hide, show, isAncestor, appendChildren } from 'vs/base/browser/dom'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { coalesce } from 'vs/base/common/arrays'; @@ -32,7 +32,6 @@ import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/la import { assertIsDefined } from 'vs/base/common/types'; import { Emitter } from 'vs/base/common/event'; import { Command } from 'vs/editor/common/modes'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -102,10 +101,10 @@ class StatusbarViewModel extends Disposable { } private registerListeners(): void { - this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + this._register(this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e))); } - private onDidStorageChange(event: IWorkspaceStorageChangeEvent): void { + private onDidStorageValueChange(event: IStorageValueChangeEvent): void { if (event.key === StatusbarViewModel.HIDDEN_ENTRIES_KEY && event.scope === StorageScope.GLOBAL) { // Keep current hidden entries @@ -275,7 +274,7 @@ class StatusbarViewModel extends Disposable { private saveState(): void { if (this.hidden.size > 0) { - this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(Array.from(this.hidden.values())), StorageScope.GLOBAL); + this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(Array.from(this.hidden.values())), StorageScope.GLOBAL, StorageTarget.USER); } else { this.storageService.remove(StatusbarViewModel.HIDDEN_ENTRIES_KEY, StorageScope.GLOBAL); } @@ -405,12 +404,9 @@ export class StatusbarPart extends Part implements IStatusbarService { @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @IContextMenuService private contextMenuService: IContextMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { super(Parts.STATUSBAR_PART, { hasTitle: false }, themeService, storageService, layoutService); - storageKeysSyncRegistryService.registerStorageKey({ key: StatusbarViewModel.HIDDEN_ENTRIES_KEY, version: 1 }); - this.registerListeners(); } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 1f3b424ac..99fcf2b6e 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -11,7 +11,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { isMacintosh, isWeb, isIOS } from 'vs/base/common/platform'; +import { isMacintosh, isWeb, isIOS, isNative } from 'vs/base/common/platform'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -21,7 +21,7 @@ import { MENUBAR_SELECTION_FOREGROUND, MENUBAR_SELECTION_BACKGROUND, MENUBAR_SEL import { URI } from 'vs/base/common/uri'; import { ILabelService } from 'vs/platform/label/common/label'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -122,7 +122,7 @@ export abstract class MenubarControl extends Disposable { this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); // Listen to update service - this.updateService.onStateChange(() => this.updateMenubar()); + this.updateService.onStateChange(() => this.onUpdateStateChange()); // Listen for changes in recently opened menu this._register(this.workspacesService.onRecentlyOpenedChange(() => { this.onRecentlyOpenedChange(); })); @@ -148,6 +148,14 @@ export abstract class MenubarControl extends Disposable { return label; } + protected onUpdateStateChange(): void { + this.updateMenubar(); + } + + protected onUpdateKeybindings(): void { + this.updateMenubar(); + } + protected getOpenRecentActions(): (Separator | IAction & { uri: URI })[] { if (!this.recentlyOpened) { return []; @@ -191,13 +199,27 @@ export abstract class MenubarControl extends Disposable { if (event.affectsConfiguration('editor.accessibilitySupport')) { this.notifyUserOfCustomMenubarAccessibility(); } + + // Since we try not update when hidden, we should + // try to update the recently opened list on visibility changes + if (event.affectsConfiguration('window.menuBarVisibility')) { + this.onRecentlyOpenedChange(); + } } - private onRecentlyOpenedChange(): void { - this.workspacesService.getRecentlyOpened().then(recentlyOpened => { - this.recentlyOpened = recentlyOpened; - this.updateMenubar(); - }); + private get menubarHidden(): boolean { + return isMacintosh && isNative ? false : getMenuBarVisibility(this.configurationService) === 'hidden'; + } + + protected onRecentlyOpenedChange(): void { + + // Do not update recently opened when the menubar is hidden #108712 + if (!this.menubarHidden) { + this.workspacesService.getRecentlyOpened().then(recentlyOpened => { + this.recentlyOpened = recentlyOpened; + this.updateMenubar(); + }); + } } private createOpenRecentMenuAction(recent: IRecent): IAction & { uri: URI } { @@ -241,7 +263,7 @@ export abstract class MenubarControl extends Disposable { } const hasBeenNotified = this.storageService.getBoolean('menubar/accessibleMenubarNotified', StorageScope.GLOBAL, false); - const usingCustomMenubar = getTitleBarStyle(this.configurationService, this.environmentService) === 'custom'; + const usingCustomMenubar = getTitleBarStyle(this.configurationService) === 'custom'; if (hasBeenNotified || usingCustomMenubar || !this.accessibilityService.isScreenReaderOptimized()) { return; @@ -257,7 +279,7 @@ export abstract class MenubarControl extends Disposable { } ]); - this.storageService.store('menubar/accessibleMenubarNotified', true, StorageScope.GLOBAL); + this.storageService.store('menubar/accessibleMenubarNotified', true, StorageScope.GLOBAL, StorageTarget.USER); } } @@ -266,6 +288,7 @@ export class CustomMenubarControl extends MenubarControl { private container: HTMLElement | undefined; private alwaysOnMnemonics: boolean = false; private focusInsideMenubar: boolean = false; + private visible: boolean = true; private readonly _onVisibilityChange: Emitter; private readonly _onFocusStateChange: Emitter; @@ -475,7 +498,7 @@ export class CustomMenubarControl extends MenubarControl { } private get currentMenubarVisibility(): MenuBarVisibility { - return getMenuBarVisibility(this.configurationService, this.environmentService); + return getMenuBarVisibility(this.configurationService); } private get currentDisableMenuBarAltFocus(): boolean { @@ -530,6 +553,12 @@ export class CustomMenubarControl extends MenubarControl { return currentSidebarLocation === 'right' ? Direction.Left : Direction.Right; } + private onDidVisibilityChange(visible: boolean): void { + this.visible = visible; + this.onRecentlyOpenedChange(); + this._onVisibilityChange.fire(visible); + } + private setupCustomMenubar(firstTime: boolean): void { // If there is no container, we cannot setup the menubar if (!this.container) { @@ -554,7 +583,7 @@ export class CustomMenubarControl extends MenubarControl { } })); - this._register(this.menubar.onVisibilityChange(e => this._onVisibilityChange.fire(e))); + this._register(this.menubar.onVisibilityChange(e => this.onDidVisibilityChange(e))); // Before we focus the menubar, stop updates to it so that focus-related context keys will work this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_IN, () => { @@ -645,29 +674,15 @@ export class CustomMenubarControl extends MenubarControl { visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, - compactMode: this.currentCompactMenuMode, - getCompactMenuActions: () => { - if (!isWeb) { - return []; // only for web - } - - const webNavigationActions: IAction[] = []; - const webNavigationMenu = this.menuService.createMenu(MenuId.MenubarWebNavigationMenu, this.contextKeyService); - for (const groups of webNavigationMenu.getActions()) { - const [, actions] = groups; - for (const action of actions) { - action.label = mnemonicMenuLabel(this.calculateActionLabel(action)); - webNavigationActions.push(action); - } - } - webNavigationMenu.dispose(); - - return webNavigationActions; - } + compactMode: this.currentCompactMenuMode }; } protected onDidChangeWindowFocus(hasFocus: boolean): void { + if (!this.visible) { + return; + } + super.onDidChangeWindowFocus(hasFocus); if (this.container) { @@ -682,6 +697,30 @@ export class CustomMenubarControl extends MenubarControl { } } + protected onUpdateStateChange(): void { + if (!this.visible) { + return; + } + + super.onUpdateStateChange(); + } + + protected onRecentlyOpenedChange(): void { + if (!this.visible) { + return; + } + + super.onRecentlyOpenedChange(); + } + + protected onUpdateKeybindings(): void { + if (!this.visible) { + return; + } + + super.onUpdateKeybindings(); + } + protected registerListeners(): void { super.registerListeners(); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 1dc512aa7..4783cb522 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -53,8 +53,8 @@ export class TitlebarPart extends Part implements ITitleService { readonly minimumWidth: number = 0; readonly maximumWidth: number = Number.POSITIVE_INFINITY; - get minimumHeight(): number { return isMacintosh && !isWeb ? 22 / getZoomFactor() : (30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1)); } - get maximumHeight(): number { return isMacintosh && !isWeb ? 22 / getZoomFactor() : (30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1)); } + get minimumHeight(): number { return 30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1); } + get maximumHeight(): number { return this.minimumHeight; } //#endregion @@ -100,7 +100,7 @@ export class TitlebarPart extends Part implements ITitleService { this.contextMenu = this._register(menuService.createMenu(MenuId.TitleBarContext, contextKeyService)); - this.titleBarStyle = getTitleBarStyle(this.configurationService, this.environmentService); + this.titleBarStyle = getTitleBarStyle(this.configurationService); this.registerListeners(); } @@ -461,13 +461,13 @@ export class TitlebarPart extends Part implements ITitleService { } protected get currentMenubarVisibility(): MenuBarVisibility { - return getMenuBarVisibility(this.configurationService, this.environmentService); + return getMenuBarVisibility(this.configurationService); } updateLayout(dimension: Dimension): void { this.lastLayoutDimensions = dimension; - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if (getTitleBarStyle(this.configurationService) === 'custom') { // Only prevent zooming behavior on macOS or when the menubar is not visible if ((!isWeb && isMacintosh) || this.currentMenubarVisibility === 'hidden') { this.title.style.zoom = `${1 / getZoomFactor()}`; diff --git a/src/vs/workbench/contrib/views/browser/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css similarity index 98% rename from src/vs/workbench/contrib/views/browser/media/views.css rename to src/vs/workbench/browser/parts/views/media/views.css index 8b30fccc5..8f2df0476 100644 --- a/src/vs/workbench/contrib/views/browser/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -64,11 +64,15 @@ } .monaco-workbench .pane > .pane-body > .welcome-view .monaco-button { - max-width: 260px; margin-left: auto; margin-right: auto; } +.monaco-workbench .pane > .pane-body.wide > .welcome-view .monaco-button { + margin-left: inherit; + max-width: 260px; +} + .monaco-workbench .pane > .pane-body .welcome-view-content { padding: 0 20px 0 20px; box-sizing: border-box; diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 3669114bf..dbd8ffce1 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -3,20 +3,55 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { toDisposable } from 'vs/base/common/lifecycle'; +import 'vs/css!./media/views'; +import { toDisposable, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { MenuId } from 'vs/platform/actions/common/actions'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ITreeView, ITreeViewDescriptor, IViewsRegistry, Extensions, IViewDescriptorService } from 'vs/workbench/common/views'; +import { MenuId, IMenuService, MenuItemAction, registerAction2, Action2, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { IContextKeyService, ContextKeyExpr, ContextKeyEqualsExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ITreeView, ITreeViewDescriptor, IViewsRegistry, Extensions, IViewDescriptorService, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, ViewContainer, ViewContainerLocation, ResolvableTreeItem } from 'vs/workbench/common/views'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { Registry } from 'vs/platform/registry/common/platform'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IAction, ActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; +import { MenuEntryActionViewItem, createAndFillInContextMenuActions, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import * as DOM from 'vs/base/browser/dom'; +import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { URI } from 'vs/base/common/uri'; +import { dirname, basename } from 'vs/base/common/resources'; +import { FileKind } from 'vs/platform/files/common/files'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; +import { localize } from 'vs/nls'; +import { timeout } from 'vs/base/common/async'; +import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; +import { isString } from 'vs/base/common/types'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; +import { FuzzyScore, createMatches } from 'vs/base/common/filters'; +import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; +import { isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; +import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { IIconLabelMarkdownString } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { renderMarkdownAsPlaintext } from 'vs/base/browser/markdownRenderer'; +import { API_OPEN_DIFF_EDITOR_COMMAND_ID, API_OPEN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; +import { Codicon } from 'vs/base/common/codicons'; export class TreeViewPane extends ViewPane { @@ -87,3 +122,1050 @@ export class TreeViewPane extends ViewPane { this.treeView.setVisibility(this.isBodyVisible()); } } + +class Root implements ITreeItem { + label = { label: 'root' }; + handle = '0'; + parentHandle: string | undefined = undefined; + collapsibleState = TreeItemCollapsibleState.Expanded; + children: ITreeItem[] | undefined = undefined; +} + +const noDataProviderMessage = localize('no-dataprovider', "There is no data provider registered that can provide view data."); + +class Tree extends WorkbenchAsyncDataTree { } + +export class TreeView extends Disposable implements ITreeView { + + private isVisible: boolean = false; + private _hasIconForParentNode = false; + private _hasIconForLeafNode = false; + + private readonly collapseAllContextKey: RawContextKey; + private readonly collapseAllContext: IContextKey; + private readonly collapseAllToggleContextKey: RawContextKey; + private readonly collapseAllToggleContext: IContextKey; + private readonly refreshContextKey: RawContextKey; + private readonly refreshContext: IContextKey; + + private focused: boolean = false; + private domNode!: HTMLElement; + private treeContainer!: HTMLElement; + private _messageValue: string | undefined; + private _canSelectMany: boolean = false; + private messageElement!: HTMLDivElement; + private tree: Tree | undefined; + private treeLabels: ResourceLabels | undefined; + + private root: ITreeItem; + private elementsToRefresh: ITreeItem[] = []; + + private readonly _onDidExpandItem: Emitter = this._register(new Emitter()); + readonly onDidExpandItem: Event = this._onDidExpandItem.event; + + private readonly _onDidCollapseItem: Emitter = this._register(new Emitter()); + readonly onDidCollapseItem: Event = this._onDidCollapseItem.event; + + private _onDidChangeSelection: Emitter = this._register(new Emitter()); + readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; + + private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); + readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; + + private readonly _onDidChangeActions: Emitter = this._register(new Emitter()); + readonly onDidChangeActions: Event = this._onDidChangeActions.event; + + private readonly _onDidChangeWelcomeState: Emitter = this._register(new Emitter()); + readonly onDidChangeWelcomeState: Event = this._onDidChangeWelcomeState.event; + + private readonly _onDidChangeTitle: Emitter = this._register(new Emitter()); + readonly onDidChangeTitle: Event = this._onDidChangeTitle.event; + + private readonly _onDidChangeDescription: Emitter = this._register(new Emitter()); + readonly onDidChangeDescription: Event = this._onDidChangeDescription.event; + + private readonly _onDidCompleteRefresh: Emitter = this._register(new Emitter()); + + constructor( + readonly id: string, + private _title: string, + @IThemeService private readonly themeService: IThemeService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ICommandService private readonly commandService: ICommandService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IProgressService protected readonly progressService: IProgressService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @IKeybindingService private readonly keybindingService: IKeybindingService, + @INotificationService private readonly notificationService: INotificationService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IHoverService private readonly hoverService: IHoverService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(); + this.root = new Root(); + this.collapseAllContextKey = new RawContextKey(`treeView.${this.id}.enableCollapseAll`, false); + this.collapseAllContext = this.collapseAllContextKey.bindTo(contextKeyService); + this.collapseAllToggleContextKey = new RawContextKey(`treeView.${this.id}.toggleCollapseAll`, false); + this.collapseAllToggleContext = this.collapseAllToggleContextKey.bindTo(contextKeyService); + this.refreshContextKey = new RawContextKey(`treeView.${this.id}.enableRefresh`, false); + this.refreshContext = this.refreshContextKey.bindTo(contextKeyService); + + this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); + this._register(this.themeService.onDidColorThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('explorer.decorations')) { + this.doRefresh([this.root]); /** soft refresh **/ + } + })); + this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { + if (views.some(v => v.id === this.id)) { + this.tree?.updateOptions({ overrideStyles: { listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND } }); + } + })); + this.registerActions(); + + this.create(); + } + + get viewContainer(): ViewContainer { + return this.viewDescriptorService.getViewContainerByViewId(this.id)!; + } + + get viewLocation(): ViewContainerLocation { + return this.viewDescriptorService.getViewLocationById(this.id)!; + } + + private _dataProvider: ITreeViewDataProvider | undefined; + get dataProvider(): ITreeViewDataProvider | undefined { + return this._dataProvider; + } + + set dataProvider(dataProvider: ITreeViewDataProvider | undefined) { + if (this.tree === undefined) { + this.createTree(); + } + + if (dataProvider) { + const self = this; + this._dataProvider = new class implements ITreeViewDataProvider { + private _isEmpty: boolean = true; + private _onDidChangeEmpty: Emitter = new Emitter(); + public onDidChangeEmpty: Event = this._onDidChangeEmpty.event; + + get isTreeEmpty(): boolean { + return this._isEmpty; + } + + async getChildren(node?: ITreeItem): Promise { + let children: ITreeItem[]; + if (node && node.children) { + children = node.children; + } else { + node = node ?? self.root; + children = await (node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node)); + node.children = children; + } + if (node instanceof Root) { + const oldEmpty = this._isEmpty; + this._isEmpty = children.length === 0; + if (oldEmpty !== this._isEmpty) { + this._onDidChangeEmpty.fire(); + } + } + return children; + } + }; + if (this._dataProvider.onDidChangeEmpty) { + this._register(this._dataProvider.onDidChangeEmpty(() => this._onDidChangeWelcomeState.fire())); + } + this.updateMessage(); + this.refresh(); + } else { + this._dataProvider = undefined; + this.updateMessage(); + } + + this._onDidChangeWelcomeState.fire(); + } + + private _message: string | undefined; + get message(): string | undefined { + return this._message; + } + + set message(message: string | undefined) { + this._message = message; + this.updateMessage(); + this._onDidChangeWelcomeState.fire(); + } + + get title(): string { + return this._title; + } + + set title(name: string) { + this._title = name; + this._onDidChangeTitle.fire(this._title); + } + + private _description: string | undefined; + get description(): string | undefined { + return this._description; + } + + set description(description: string | undefined) { + this._description = description; + this._onDidChangeDescription.fire(this._description); + } + + get canSelectMany(): boolean { + return this._canSelectMany; + } + + set canSelectMany(canSelectMany: boolean) { + this._canSelectMany = canSelectMany; + } + + get hasIconForParentNode(): boolean { + return this._hasIconForParentNode; + } + + get hasIconForLeafNode(): boolean { + return this._hasIconForLeafNode; + } + + get visible(): boolean { + return this.isVisible; + } + + get showCollapseAllAction(): boolean { + return !!this.collapseAllContext.get(); + } + + set showCollapseAllAction(showCollapseAllAction: boolean) { + this.collapseAllContext.set(showCollapseAllAction); + } + + get showRefreshAction(): boolean { + return !!this.refreshContext.get(); + } + + set showRefreshAction(showRefreshAction: boolean) { + this.refreshContext.set(showRefreshAction); + } + + private registerActions() { + const that = this; + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.treeView.${that.id}.refresh`, + title: localize('refresh', "Refresh"), + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.refreshContextKey), + group: 'navigation', + order: Number.MAX_SAFE_INTEGER - 1, + }, + icon: Codicon.refresh + }); + } + async run(): Promise { + return that.refresh(); + } + })); + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.treeView.${that.id}.collapseAll`, + title: localize('collapseAll', "Collapse All"), + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.collapseAllContextKey), + group: 'navigation', + order: Number.MAX_SAFE_INTEGER, + }, + precondition: that.collapseAllToggleContextKey, + icon: Codicon.collapseAll + }); + } + async run(): Promise { + if (that.tree) { + return new CollapseAllAction(that.tree, true).run(); + } + } + })); + } + + setVisibility(isVisible: boolean): void { + isVisible = !!isVisible; + if (this.isVisible === isVisible) { + return; + } + + this.isVisible = isVisible; + + if (this.tree) { + if (this.isVisible) { + DOM.show(this.tree.getHTMLElement()); + } else { + DOM.hide(this.tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it + } + + if (this.isVisible && this.elementsToRefresh.length) { + this.doRefresh(this.elementsToRefresh); + this.elementsToRefresh = []; + } + } + + this._onDidChangeVisibility.fire(this.isVisible); + } + + focus(reveal: boolean = true): void { + if (this.tree && this.root.children && this.root.children.length > 0) { + // Make sure the current selected element is revealed + const selectedElement = this.tree.getSelection()[0]; + if (selectedElement && reveal) { + this.tree.reveal(selectedElement, 0.5); + } + + // Pass Focus to Viewer + this.tree.domFocus(); + } else if (this.tree) { + this.tree.domFocus(); + } else { + this.domNode.focus(); + } + } + + show(container: HTMLElement): void { + DOM.append(container, this.domNode); + } + + private create() { + this.domNode = DOM.$('.tree-explorer-viewlet-tree-view'); + this.messageElement = DOM.append(this.domNode, DOM.$('.message')); + this.treeContainer = DOM.append(this.domNode, DOM.$('.customview-tree')); + this.treeContainer.classList.add('file-icon-themable-tree', 'show-file-icons'); + const focusTracker = this._register(DOM.trackFocus(this.domNode)); + this._register(focusTracker.onDidFocus(() => this.focused = true)); + this._register(focusTracker.onDidBlur(() => this.focused = false)); + } + + private createTree() { + const actionViewItemProvider = (action: IAction) => { + if (action instanceof MenuItemAction) { + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); + } + + return undefined; + }; + const treeMenus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); + const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.id }, () => task)); + const aligner = new Aligner(this.themeService); + const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner); + const widgetAriaLabel = this._title; + + this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer, new TreeViewDelegate(), [renderer], + dataSource, { + identityProvider: new TreeViewIdentityProvider(), + accessibilityProvider: { + getAriaLabel(element: ITreeItem): string { + if (element.accessibilityInformation) { + return element.accessibilityInformation.label; + } + + if (isString(element.tooltip)) { + return element.tooltip; + } else { + let buildAriaLabel: string = ''; + if (element.label) { + buildAriaLabel += element.label.label + ' '; + } + if (element.description) { + buildAriaLabel += element.description; + } + return buildAriaLabel; + } + }, + getRole(element: ITreeItem): string | undefined { + return element.accessibilityInformation?.role ?? 'treeitem'; + }, + getWidgetAriaLabel(): string { + return widgetAriaLabel; + } + }, + keyboardNavigationLabelProvider: { + getKeyboardNavigationLabel: (item: ITreeItem) => { + return item.label ? item.label.label : (item.resourceUri ? basename(URI.revive(item.resourceUri)) : undefined); + } + }, + expandOnlyOnTwistieClick: (e: ITreeItem) => !!e.command, + collapseByDefault: (e: ITreeItem): boolean => { + return e.collapsibleState !== TreeItemCollapsibleState.Expanded; + }, + multipleSelectionSupport: this.canSelectMany, + overrideStyles: { + listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND + } + }) as WorkbenchAsyncDataTree); + treeMenus.setContextKeyService(this.tree.contextKeyService); + aligner.tree = this.tree; + const actionRunner = new MultipleSelectionActionRunner(this.notificationService, () => this.tree!.getSelection()); + renderer.actionRunner = actionRunner; + + this.tree.contextKeyService.createKey(this.id, true); + this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner))); + this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements))); + this._register(this.tree.onDidChangeCollapseState(e => { + if (!e.node.element) { + return; + } + + const element: ITreeItem = Array.isArray(e.node.element.element) ? e.node.element.element[0] : e.node.element.element; + if (e.node.collapsed) { + this._onDidCollapseItem.fire(element); + } else { + this._onDidExpandItem.fire(element); + } + })); + this.tree.setInput(this.root).then(() => this.updateContentAreas()); + + this._register(this.tree.onDidOpen(e => { + if (!e.browserEvent) { + return; + } + const selection = this.tree!.getSelection(); + const command = selection.length === 1 ? selection[0].command : undefined; + if (command) { + let args = command.arguments || []; + if (command.id === API_OPEN_EDITOR_COMMAND_ID || command.id === API_OPEN_DIFF_EDITOR_COMMAND_ID) { + // Some commands owned by us should receive the + // `IOpenEvent` as context to open properly + args = [...args, e]; + } + + this.commandService.executeCommand(command.id, ...args); + } + })); + + } + + private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent, actionRunner: MultipleSelectionActionRunner): void { + this.hoverService.hideHover(); + const node: ITreeItem | null = treeEvent.element; + if (node === null) { + return; + } + const event: UIEvent = treeEvent.browserEvent; + + event.preventDefault(); + event.stopPropagation(); + + this.tree!.setFocus([node]); + const actions = treeMenus.getResourceContextActions(node); + if (!actions.length) { + return; + } + this.contextMenuService.showContextMenu({ + getAnchor: () => treeEvent.anchor, + + getActions: () => actions, + + getActionViewItem: (action) => { + const keybinding = this.keybindingService.lookupKeybinding(action.id); + if (keybinding) { + return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + } + return undefined; + }, + + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + this.tree!.domFocus(); + } + }, + + getActionsContext: () => ({ $treeViewId: this.id, $treeItemHandle: node.handle }), + + actionRunner + }); + } + + protected updateMessage(): void { + if (this._message) { + this.showMessage(this._message); + } else if (!this.dataProvider) { + this.showMessage(noDataProviderMessage); + } else { + this.hideMessage(); + } + this.updateContentAreas(); + } + + private showMessage(message: string): void { + this.messageElement.classList.remove('hide'); + this.resetMessageElement(); + this._messageValue = message; + if (!isFalsyOrWhitespace(this._message)) { + this.messageElement.textContent = this._messageValue; + } + this.layout(this._height, this._width); + } + + private hideMessage(): void { + this.resetMessageElement(); + this.messageElement.classList.add('hide'); + this.layout(this._height, this._width); + } + + private resetMessageElement(): void { + DOM.clearNode(this.messageElement); + } + + private _height: number = 0; + private _width: number = 0; + layout(height: number, width: number) { + if (height && width) { + this._height = height; + this._width = width; + const treeHeight = height - DOM.getTotalHeight(this.messageElement); + this.treeContainer.style.height = treeHeight + 'px'; + if (this.tree) { + this.tree.layout(treeHeight, width); + } + } + } + + getOptimalWidth(): number { + if (this.tree) { + const parentNode = this.tree.getHTMLElement(); + const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.outline-item-label > a')); + return DOM.getLargestChildWidth(parentNode, childNodes); + } + return 0; + } + + async refresh(elements?: ITreeItem[]): Promise { + if (this.dataProvider && this.tree) { + if (this.refreshing) { + await Event.toPromise(this._onDidCompleteRefresh.event); + } + if (!elements) { + elements = [this.root]; + // remove all waiting elements to refresh if root is asked to refresh + this.elementsToRefresh = []; + } + for (const element of elements) { + element.children = undefined; // reset children + } + if (this.isVisible) { + return this.doRefresh(elements); + } else { + if (this.elementsToRefresh.length) { + const seen: Set = new Set(); + this.elementsToRefresh.forEach(element => seen.add(element.handle)); + for (const element of elements) { + if (!seen.has(element.handle)) { + this.elementsToRefresh.push(element); + } + } + } else { + this.elementsToRefresh.push(...elements); + } + } + } + return undefined; + } + + async expand(itemOrItems: ITreeItem | ITreeItem[]): Promise { + const tree = this.tree; + if (tree) { + itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems]; + await Promise.all(itemOrItems.map(element => { + return tree.expand(element, false); + })); + } + } + + setSelection(items: ITreeItem[]): void { + if (this.tree) { + this.tree.setSelection(items); + } + } + + setFocus(item: ITreeItem): void { + if (this.tree) { + this.focus(); + this.tree.setFocus([item]); + } + } + + async reveal(item: ITreeItem): Promise { + if (this.tree) { + return this.tree.reveal(item); + } + } + + private refreshing: boolean = false; + private async doRefresh(elements: ITreeItem[]): Promise { + const tree = this.tree; + if (tree && this.visible) { + this.refreshing = true; + await Promise.all(elements.map(element => tree.updateChildren(element, true, true))); + this.refreshing = false; + this._onDidCompleteRefresh.fire(); + this.updateContentAreas(); + if (this.focused) { + this.focus(false); + } + this.updateCollapseAllToggle(); + } + } + + private updateCollapseAllToggle() { + if (this.showCollapseAllAction) { + this.collapseAllToggleContext.set(!!this.root.children && (this.root.children.length > 0) && + this.root.children.some(value => value.collapsibleState !== TreeItemCollapsibleState.None)); + } + } + + private updateContentAreas(): void { + const isTreeEmpty = !this.root.children || this.root.children.length === 0; + // Hide tree container only when there is a message and tree is empty and not refreshing + if (this._messageValue && isTreeEmpty && !this.refreshing) { + this.treeContainer.classList.add('hide'); + this.domNode.setAttribute('tabindex', '0'); + } else { + this.treeContainer.classList.remove('hide'); + this.domNode.removeAttribute('tabindex'); + } + } +} + +class TreeViewIdentityProvider implements IIdentityProvider { + getId(element: ITreeItem): { toString(): string; } { + return element.handle; + } +} + +class TreeViewDelegate implements IListVirtualDelegate { + + getHeight(element: ITreeItem): number { + return TreeRenderer.ITEM_HEIGHT; + } + + getTemplateId(element: ITreeItem): string { + return TreeRenderer.TREE_TEMPLATE_ID; + } +} + +class TreeDataSource implements IAsyncDataSource { + + constructor( + private treeView: ITreeView, + private withProgress: (task: Promise) => Promise + ) { + } + + hasChildren(element: ITreeItem): boolean { + return !!this.treeView.dataProvider && (element.collapsibleState !== TreeItemCollapsibleState.None); + } + + async getChildren(element: ITreeItem): Promise { + if (this.treeView.dataProvider) { + return this.withProgress(this.treeView.dataProvider.getChildren(element)); + } + return []; + } +} + +// todo@jrieken,sandy make this proper and contributable from extensions +registerThemingParticipant((theme, collector) => { + + const matchBackgroundColor = theme.getColor(listFilterMatchHighlight); + if (matchBackgroundColor) { + collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); + collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); + } + const matchBorderColor = theme.getColor(listFilterMatchHighlightBorder); + if (matchBorderColor) { + collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); + collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); + } + const link = theme.getColor(textLinkForeground); + if (link) { + collector.addRule(`.tree-explorer-viewlet-tree-view > .message a { color: ${link}; }`); + } + const focusBorderColor = theme.getColor(focusBorder); + if (focusBorderColor) { + collector.addRule(`.tree-explorer-viewlet-tree-view > .message a:focus { outline: 1px solid ${focusBorderColor}; outline-offset: -1px; }`); + } + const codeBackground = theme.getColor(textCodeBlockBackground); + if (codeBackground) { + collector.addRule(`.tree-explorer-viewlet-tree-view > .message code { background-color: ${codeBackground}; }`); + } +}); + +interface ITreeExplorerTemplateData { + elementDisposable: IDisposable; + container: HTMLElement; + resourceLabel: IResourceLabel; + icon: HTMLElement; + actionBar: ActionBar; +} + +class TreeRenderer extends Disposable implements ITreeRenderer { + static readonly ITEM_HEIGHT = 22; + static readonly TREE_TEMPLATE_ID = 'treeExplorer'; + + private _actionRunner: MultipleSelectionActionRunner | undefined; + private _hoverDelegate: IHoverDelegate; + + constructor( + private treeViewId: string, + private menus: TreeMenus, + private labels: ResourceLabels, + private actionViewItemProvider: IActionViewItemProvider, + private aligner: Aligner, + @IThemeService private readonly themeService: IThemeService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @ILabelService private readonly labelService: ILabelService, + @IHoverService private readonly hoverService: IHoverService + ) { + super(); + this._hoverDelegate = { + showHover: (options: IHoverDelegateOptions): IDisposable | undefined => { + return this.hoverService.showHover(options); + } + }; + } + + get templateId(): string { + return TreeRenderer.TREE_TEMPLATE_ID; + } + + set actionRunner(actionRunner: MultipleSelectionActionRunner) { + this._actionRunner = actionRunner; + } + + renderTemplate(container: HTMLElement): ITreeExplorerTemplateData { + container.classList.add('custom-view-tree-node-item'); + + const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); + + const resourceLabel = this.labels.create(container, { supportHighlights: true, hoverDelegate: this._hoverDelegate }); + const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); + const actionBar = new ActionBar(actionsContainer, { + actionViewItemProvider: this.actionViewItemProvider + }); + + return { resourceLabel, icon, actionBar, container, elementDisposable: Disposable.None }; + } + + private getHover(label: string | undefined, resource: URI | null, node: ITreeItem): string | IIconLabelMarkdownString | undefined { + if (!(node instanceof ResolvableTreeItem) || !node.hasResolve) { + if (resource) { + return undefined; + } else if (!node.tooltip) { + return label; + } else if (!isString(node.tooltip)) { + return { markdown: node.tooltip, markdownNotSupportedFallback: resource ? undefined : renderMarkdownAsPlaintext(node.tooltip) }; // Passing undefined as the fallback for a resource falls back to the old native hover + } else { + return node.tooltip; + } + } + + return { + markdown: (): Promise => { + return new Promise(async (resolve) => { + await node.resolve(); + resolve(node.tooltip); + }); + }, + markdownNotSupportedFallback: resource ? undefined : '' // Passing undefined as the fallback for a resource falls back to the old native hover + }; + } + + renderElement(element: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { + templateData.elementDisposable.dispose(); + const node = element.element; + const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; + const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : (resource ? { label: basename(resource) } : undefined); + const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : undefined; + const label = treeItemLabel ? treeItemLabel.label : undefined; + const matches = (treeItemLabel && treeItemLabel.highlights && label) ? treeItemLabel.highlights.map(([start, end]) => { + if (start < 0) { + start = label.length + start; + } + if (end < 0) { + end = label.length + end; + } + if ((start >= label.length) || (end > label.length)) { + return ({ start: 0, end: 0 }); + } + if (start > end) { + const swap = start; + start = end; + end = swap; + } + return ({ start, end }); + }) : undefined; + const icon = this.themeService.getColorTheme().type === ColorScheme.LIGHT ? node.icon : node.iconDark; + const iconUrl = icon ? URI.revive(icon) : null; + const title = this.getHover(label, resource, node); + + // reset + templateData.actionBar.clear(); + templateData.icon.style.color = ''; + + if (resource || this.isFileKindThemeIcon(node.themeIcon)) { + const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); + const labelResource = resource ? resource : URI.parse('missing:_icon_resource'); + templateData.resourceLabel.setResource({ name: label, description, resource: labelResource }, { + fileKind: this.getFileKind(node), + title, + hideIcon: !!iconUrl, + fileDecorations, + extraClasses: ['custom-view-tree-node-item-resourceLabel'], + matches: matches ? matches : createMatches(element.filterData), + strikethrough: treeItemLabel?.strikethrough + }); + } else { + templateData.resourceLabel.setResource({ name: label, description }, { + title, + hideIcon: true, + extraClasses: ['custom-view-tree-node-item-resourceLabel'], + matches: matches ? matches : createMatches(element.filterData), + strikethrough: treeItemLabel?.strikethrough + }); + } + + if (iconUrl) { + templateData.icon.className = 'custom-view-tree-node-item-icon'; + templateData.icon.style.backgroundImage = DOM.asCSSUrl(iconUrl); + } else { + let iconClass: string | undefined; + if (node.themeIcon && !this.isFileKindThemeIcon(node.themeIcon)) { + iconClass = ThemeIcon.asClassName(node.themeIcon); + if (node.themeIcon.color) { + templateData.icon.style.color = this.themeService.getColorTheme().getColor(node.themeIcon.color.id)?.toString() ?? ''; + } + } + templateData.icon.className = iconClass ? `custom-view-tree-node-item-icon ${iconClass}` : ''; + templateData.icon.style.backgroundImage = ''; + } + + templateData.actionBar.context = { $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; + templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); + if (this._actionRunner) { + templateData.actionBar.actionRunner = this._actionRunner; + } + this.setAlignment(templateData.container, node); + const disposableStore = new DisposableStore(); + templateData.elementDisposable = disposableStore; + disposableStore.add(this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node))); + } + + private setAlignment(container: HTMLElement, treeItem: ITreeItem) { + container.parentElement!.classList.toggle('align-icon-with-twisty', this.aligner.alignIconWithTwisty(treeItem)); + } + + private isFileKindThemeIcon(icon: ThemeIcon | undefined): boolean { + if (icon) { + return icon.id === FileThemeIcon.id || icon.id === FolderThemeIcon.id; + } else { + return false; + } + } + + private getFileKind(node: ITreeItem): FileKind { + if (node.themeIcon) { + switch (node.themeIcon.id) { + case FileThemeIcon.id: + return FileKind.FILE; + case FolderThemeIcon.id: + return FileKind.FOLDER; + } + } + return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded ? FileKind.FOLDER : FileKind.FILE; + } + + disposeElement(resource: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { + templateData.elementDisposable.dispose(); + } + + disposeTemplate(templateData: ITreeExplorerTemplateData): void { + templateData.resourceLabel.dispose(); + templateData.actionBar.dispose(); + templateData.elementDisposable.dispose(); + } +} + +class Aligner extends Disposable { + private _tree: WorkbenchAsyncDataTree | undefined; + + constructor(private themeService: IThemeService) { + super(); + } + + set tree(tree: WorkbenchAsyncDataTree) { + this._tree = tree; + } + + public alignIconWithTwisty(treeItem: ITreeItem): boolean { + if (treeItem.collapsibleState !== TreeItemCollapsibleState.None) { + return false; + } + if (!this.hasIcon(treeItem)) { + return false; + } + + if (this._tree) { + const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); + if (this.hasIcon(parent)) { + return !!parent.children && parent.children.some(c => c.collapsibleState !== TreeItemCollapsibleState.None && !this.hasIcon(c)); + } + return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c)); + } else { + return false; + } + } + + private hasIcon(node: ITreeItem): boolean { + const icon = this.themeService.getColorTheme().type === ColorScheme.LIGHT ? node.icon : node.iconDark; + if (icon) { + return true; + } + if (node.resourceUri || node.themeIcon) { + const fileIconTheme = this.themeService.getFileIconTheme(); + const isFolder = node.themeIcon ? node.themeIcon.id === FolderThemeIcon.id : node.collapsibleState !== TreeItemCollapsibleState.None; + if (isFolder) { + return fileIconTheme.hasFileIcons && fileIconTheme.hasFolderIcons; + } + return fileIconTheme.hasFileIcons; + } + return false; + } +} + +class MultipleSelectionActionRunner extends ActionRunner { + + constructor(notificationService: INotificationService, private getSelectedResources: (() => ITreeItem[])) { + super(); + this._register(this.onDidRun(e => { + if (e.error) { + notificationService.error(localize('command-error', 'Error running command {1}: {0}. This is likely caused by the extension that contributes {1}.', e.error.message, e.action.id)); + } + })); + } + + runAction(action: IAction, context: TreeViewItemHandleArg): Promise { + const selection = this.getSelectedResources(); + let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined; + let actionInSelected: boolean = false; + if (selection.length > 1) { + selectionHandleArgs = selection.map(selected => { + if (selected.handle === context.$treeItemHandle) { + actionInSelected = true; + } + return { $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle }; + }); + } + + if (!actionInSelected) { + selectionHandleArgs = undefined; + } + + return action.run(...[context, selectionHandleArgs]); + } +} + +class TreeMenus extends Disposable implements IDisposable { + private contextKeyService: IContextKeyService | undefined; + + constructor( + private id: string, + @IMenuService private readonly menuService: IMenuService + ) { + super(); + } + + getResourceActions(element: ITreeItem): IAction[] { + return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).primary; + } + + getResourceContextActions(element: ITreeItem): IAction[] { + return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary; + } + + public setContextKeyService(service: IContextKeyService) { + this.contextKeyService = service; + } + + private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } { + if (!this.contextKeyService) { + return { primary: [], secondary: [] }; + } + const contextKeyService = this.contextKeyService.createScoped(); + contextKeyService.createKey('view', this.id); + contextKeyService.createKey(context.key, context.value); + + const menu = this.menuService.createMenu(menuId, contextKeyService); + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); + + menu.dispose(); + contextKeyService.dispose(); + + return result; + } +} + +export class CustomTreeView extends TreeView { + + private activated: boolean = false; + + constructor( + id: string, + title: string, + @IThemeService themeService: IThemeService, + @IInstantiationService instantiationService: IInstantiationService, + @ICommandService commandService: ICommandService, + @IConfigurationService configurationService: IConfigurationService, + @IProgressService progressService: IProgressService, + @IContextMenuService contextMenuService: IContextMenuService, + @IKeybindingService keybindingService: IKeybindingService, + @INotificationService notificationService: INotificationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IContextKeyService contextKeyService: IContextKeyService, + @IHoverService hoverService: IHoverService, + @IExtensionService private readonly extensionService: IExtensionService, + ) { + super(id, title, themeService, instantiationService, commandService, configurationService, progressService, contextMenuService, keybindingService, notificationService, viewDescriptorService, hoverService, contextKeyService); + } + + setVisibility(isVisible: boolean): void { + super.setVisibility(isVisible); + if (this.visible) { + this.activate(); + } + } + + private activate() { + if (!this.activated) { + this.progressService.withProgress({ location: this.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) + .then(() => timeout(2000)) + .then(() => { + this.updateMessage(); + }); + this.activated = true; + } + } +} + diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 4fa93931e..410c67244 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -18,15 +18,15 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; +import { IThemeService, Themable, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { PaneView, IPaneViewOptions, IPaneOptions, Pane, IPaneStyles } from 'vs/base/browser/ui/splitview/paneview'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel } from 'vs/workbench/common/views'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel, defaultViewIcon } from 'vs/workbench/common/views'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { assertIsDefined, isString } from 'vs/base/common/types'; +import { assertIsDefined } from 'vs/base/common/types'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -50,6 +50,8 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { URI } from 'vs/base/common/uri'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { Codicon } from 'vs/base/common/codicons'; export interface IPaneColors extends IColorMapping { dropBackground?: ColorIdentifier; @@ -70,6 +72,9 @@ type WelcomeActionClassification = { uri: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; }; +const viewPaneContainerExpandedIcon = registerIcon('view-pane-container-expanded', Codicon.chevronDown, nls.localize('viewPaneContainerExpandedIcon', 'Icon for an expanded view pane container.')); +const viewPaneContainerCollapsedIcon = registerIcon('view-pane-container-collapsed', Codicon.chevronRight, nls.localize('viewPaneContainerCollapsedIcon', 'Icon for a collapsed view pane container.')); + const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); interface IItem { @@ -263,7 +268,10 @@ export abstract class ViewPane extends Pane implements IView { if (changed) { this._onDidChangeBodyVisibility.fire(expanded); } - + if (this.twistiesContainer) { + this.twistiesContainer.classList.remove(...ThemeIcon.asClassNameArray(this.getTwistyIcon(!expanded))); + this.twistiesContainer.classList.add(...ThemeIcon.asClassNameArray(this.getTwistyIcon(expanded))); + } return changed; } @@ -288,7 +296,7 @@ export abstract class ViewPane extends Pane implements IView { protected renderHeader(container: HTMLElement): void { this.headerContainer = container; - this.renderTwisties(container); + this.twistiesContainer = append(container, $(ThemeIcon.asCSSSelector(this.getTwistyIcon(this.isExpanded())))); this.renderHeaderTitle(container, this.title); @@ -316,8 +324,8 @@ export abstract class ViewPane extends Pane implements IView { this.updateActionsVisibility(); } - protected renderTwisties(container: HTMLElement): void { - this.twistiesContainer = append(container, $('.twisties.codicon.codicon-chevron-right')); + protected getTwistyIcon(expanded: boolean): ThemeIcon { + return expanded ? viewPaneContainerExpandedIcon : viewPaneContainerCollapsedIcon; } style(styles: IPaneStyles): void { @@ -338,8 +346,8 @@ export abstract class ViewPane extends Pane implements IView { } } - private getIcon(): string | URI { - return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || 'codicon-window'; + private getIcon(): ThemeIcon | URI { + return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || defaultViewIcon; } protected renderHeaderTitle(container: HTMLElement, title: string): void { @@ -357,9 +365,8 @@ export abstract class ViewPane extends Pane implements IView { -webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%; -webkit-mask-size: 16px; `); - } else if (isString(icon)) { - this.iconContainer.classList.add('codicon'); - cssClass = icon; + } else if (ThemeIcon.isThemeIcon(icon)) { + cssClass = ThemeIcon.asClassName(icon); } if (cssClass) { @@ -556,9 +563,7 @@ export abstract class ViewPane extends Pane implements IView { this.bodyContainer.classList.add('welcome'); this.viewWelcomeContainer.innerText = ''; - let buttonIndex = 0; - - for (const { content, preconditions } of contents) { + for (const { content, precondition } of contents) { const lines = content.split('\n'); for (let line of lines) { @@ -581,21 +586,15 @@ export abstract class ViewPane extends Pane implements IView { disposables.add(button); disposables.add(attachButtonStyler(button, this.themeService)); - if (preconditions) { - const precondition = preconditions[buttonIndex]; + if (precondition) { + const updateEnablement = () => button.enabled = this.contextKeyService.contextMatchesRules(precondition); + updateEnablement(); - if (precondition) { - const updateEnablement = () => button.enabled = this.contextKeyService.contextMatchesRules(precondition); - updateEnablement(); - - const keys = new Set(); - precondition.keys().forEach(key => keys.add(key)); - const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys)); - onDidChangeContext(updateEnablement, null, disposables); - } + const keys = new Set(); + precondition.keys().forEach(key => keys.add(key)); + const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys)); + onDidChangeContext(updateEnablement, null, disposables); } - - buttonIndex++; } else { const p = append(this.viewWelcomeContainer, $('p')); @@ -1319,7 +1318,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { saveState(): void { this.panes.forEach((view) => view.saveState()); - this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE); + this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE, StorageTarget.USER); } private onContextMenu(event: StandardMouseEvent, viewDescriptor: IViewDescriptor): void { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 80544aab3..e7d260b00 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -59,6 +59,8 @@ import { localize } from 'vs/nls'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; class BrowserMain extends Disposable { @@ -198,9 +200,13 @@ class BrowserMain extends Disposable { serviceCollection.set(IFileService, fileService); await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath); + // IURIIdentityService + const uriIdentityService = new UriIdentityService(fileService); + serviceCollection.set(IUriIdentityService, uriIdentityService); + // Long running services (workspace, config, storage) const [configurationService, storageService] = await Promise.all([ - this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => { + this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -330,8 +336,8 @@ class BrowserMain extends Disposable { } } - private async createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { - const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, logService); + private async createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { + const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, uriIdentityService, logService); try { await workspaceService.initialize(payload); diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index c04818dc5..fdc99470b 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -108,6 +108,11 @@ import { isStandalone } from 'vs/base/browser/browser'; ], 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'splitSizing' }, "Controls the sizing of editor groups when splitting them.") }, + 'workbench.editor.splitOnDragAndDrop': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('splitOnDragAndDrop', "Controls if editor groups can be split from drag and drop operations by dropping an editor or file on the edges of the editor area.") + }, 'workbench.editor.focusRecentEditorAfterClose': { 'type': 'boolean', 'description': nls.localize('focusRecentEditorAfterClose', "Controls whether tabs are closed in most recently used order or from left to right."), @@ -120,13 +125,13 @@ import { isStandalone } from 'vs/base/browser/browser'; }, 'workbench.editor.enablePreview': { 'type': 'boolean', - 'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are explicitly set to be kept open (e.g. via double click or editing) and show up with an italic font style."), + 'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors do not keep open and are reused until explicitly set to be kept open (e.g. via double click or editing) and show up with an italic font style."), 'default': true }, 'workbench.editor.enablePreviewFromQuickOpen': { 'type': 'boolean', - 'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether editors opened from Quick Open show as preview. Preview editors are reused until they are explicitly set to be kept open (e.g. via double click or editing)."), - 'default': true + 'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether editors opened from Quick Open show as preview. Preview editors do not keep open and are reused until explicitly set to be kept open (e.g. via double click or editing)."), + 'default': false }, 'workbench.editor.closeOnFileDelete': { 'type': 'boolean', diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index dce86a911..2172c0944 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -17,7 +17,7 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions'; import { Position, Parts, IWorkbenchLayoutService, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; -import { IStorageService, WillSaveStateReason, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, WillSaveStateReason, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -297,7 +297,7 @@ export class Workbench extends Layout { // local storage and not global storage because it would not make // much sense to synchronize to other machines. if (isNative) { - storageService.store('editorFontInfo', serializedFontInfoRaw, StorageScope.GLOBAL); + storageService.store('editorFontInfo', serializedFontInfoRaw, StorageScope.GLOBAL, StorageTarget.MACHINE); } else { window.localStorage.setItem('vscode.editorFontInfo', serializedFontInfoRaw); } @@ -415,8 +415,7 @@ export class Workbench extends Layout { mark('didStartWorkbench'); // Perf reporting (devtools) - performance.mark('workbench-end'); - performance.measure('perf: workbench create & restore', 'workbench-start', 'workbench-end'); + performance.measure('perf: workbench create & restore', 'didLoadWorkbenchMain', 'didStartWorkbench'); } } } diff --git a/src/vs/workbench/common/component.ts b/src/vs/workbench/common/component.ts index cbe3c7d61..f4454acc5 100644 --- a/src/vs/workbench/common/component.ts +++ b/src/vs/workbench/common/component.ts @@ -5,7 +5,7 @@ import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; export class Component extends Themable { @@ -35,8 +35,8 @@ export class Component extends Themable { return this.id; } - protected getMemento(scope: StorageScope): MementoObject { - return this.memento.getMemento(scope); + protected getMemento(scope: StorageScope, target: StorageTarget): MementoObject { + return this.memento.getMemento(scope, target); } protected saveState(): void { diff --git a/src/vs/workbench/common/dialogs.ts b/src/vs/workbench/common/dialogs.ts new file mode 100644 index 000000000..5de8eac68 --- /dev/null +++ b/src/vs/workbench/common/dialogs.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IDialog, IDialogResult } from 'vs/platform/dialogs/common/dialogs'; + +export interface IDialogViewItem { + args: IDialog; + close(result?: IDialogResult): void; +} + +export interface IDialogHandle { + item: IDialogViewItem; + result: Promise; +} + +export interface IDialogsModel { + readonly onDidShowDialog: Event; + + readonly dialogs: IDialogViewItem[]; + + show(dialog: IDialog): IDialogHandle; +} + +export class DialogsModel extends Disposable implements IDialogsModel { + readonly dialogs: IDialogViewItem[] = []; + + private readonly _onDidShowDialog = this._register(new Emitter()); + readonly onDidShowDialog = this._onDidShowDialog.event; + + show(dialog: IDialog): IDialogHandle { + let resolver: (value?: IDialogResult) => void; + + const item: IDialogViewItem = { + args: dialog, + close: (result) => { this.dialogs.splice(0, 1); resolver(result); } + }; + + this.dialogs.push(item); + this._onDidShowDialog.fire(); + + return { + item, + result: new Promise(resolve => { resolver = resolve; }) + }; + } +} diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 30a375204..f104149d8 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -14,13 +14,13 @@ import { IInstantiationService, IConstructorSignature0, ServicesAccessor, Brande import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITextModel } from 'vs/editor/common/model'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { GroupsOrder, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ICompositeControl, IComposite } from 'vs/workbench/common/composite'; import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { IPathData } from 'vs/platform/windows/common/windows'; import { coalesce, firstOrDefault } from 'vs/base/common/arrays'; -import { IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; +import { ACTIVE_GROUP, IResourceEditorInputType, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IRange } from 'vs/editor/common/core/range'; import { IExtUri } from 'vs/base/common/resources'; @@ -178,7 +178,7 @@ export interface IFileEditorInputFactory { /** * Creates new new editor input capable of showing files. */ - createFileEditorInput(resource: URI, preferredResource: URI | undefined, encoding: string | undefined, mode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput; + createFileEditorInput(resource: URI, preferredResource: URI | undefined, preferredName: string | undefined, preferredDescription: string | undefined, preferredEncoding: string | undefined, preferredMode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput; /** * Check if the provided object is a file editor input. @@ -704,6 +704,24 @@ export interface IFileEditorInput extends IEditorInput, IEncodingSupport, IModeS */ setPreferredResource(preferredResource: URI): void; + /** + * Sets the preferred name to use for this file input. + * + * Note: for certain file schemes the input may decide to ignore this + * name and use our standard naming. Specifically for schemes we own, + * we do not let others override the name. + */ + setPreferredName(name: string): void; + + /** + * Sets the preferred description to use for this file input. + * + * Note: for certain file schemes the input may decide to ignore this + * description and use our standard naming. Specifically for schemes we own, + * we do not let others override the description. + */ + setPreferredDescription(description: string): void; + /** * Sets the preferred encoding to use for this file input. */ @@ -734,7 +752,7 @@ export class SideBySideEditorInput extends EditorInput { constructor( protected readonly name: string | undefined, - private readonly description: string | undefined, + protected readonly description: string | undefined, private readonly _secondary: EditorInput, private readonly _primary: EditorInput ) { @@ -1266,6 +1284,7 @@ interface IEditorPartConfiguration { labelFormat?: 'default' | 'short' | 'medium' | 'long'; restoreViewState?: boolean; splitSizing?: 'split' | 'distribute'; + splitOnDragAndDrop?: boolean; limit?: { enabled?: boolean; value?: number; @@ -1421,13 +1440,15 @@ export const enum CloseDirection { export interface IEditorMemento { saveEditorState(group: IEditorGroup, resource: URI, state: T): void; - saveEditorState(group: IEditorGroup, editor: EditorInput, state: T): void; + saveEditorState(group: IEditorGroup, editor: IEditorInput, state: T): void; loadEditorState(group: IEditorGroup, resource: URI): T | undefined; - loadEditorState(group: IEditorGroup, editor: EditorInput): T | undefined; + loadEditorState(group: IEditorGroup, editor: IEditorInput): T | undefined; clearEditorState(resource: URI, group?: IEditorGroup): void; - clearEditorState(editor: EditorInput, group?: IEditorGroup): void; + clearEditorState(editor: IEditorInput, group?: IEditorGroup): void; + + clearEditorStateOnDispose(resource: URI, editor: IEditorInput): void; moveEditorState(source: URI, target: URI, comparer: IExtUri): void; } @@ -1569,3 +1590,42 @@ export function computeEditorAriaLabel(input: IEditorInput, index: number | unde return ariaLabel; } + + +//#region Editor Group Column + +/** + * A way to address editor groups through a column based system + * where `0` is the first column. Will fallback to `SIDE_GROUP` + * in case the column does not exist yet. + */ +export type EditorGroupColumn = number; + +export function viewColumnToEditorGroup(editorGroupService: IEditorGroupsService, viewColumn?: EditorGroupColumn): GroupIdentifier { + if (typeof viewColumn !== 'number' || viewColumn === ACTIVE_GROUP) { + return ACTIVE_GROUP; // prefer active group when position is undefined or passed in as such + } + + const groups = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE); + + let candidateGroup = groups[viewColumn]; + if (candidateGroup) { + return candidateGroup.id; // found direct match + } + + let firstGroup = groups[0]; + if (groups.length === 1 && firstGroup.count === 0) { + return firstGroup.id; // first editor should always open in first group independent from position provided + } + + return SIDE_GROUP; // open to the side if group not found or we are instructed to +} + +export function editorGroupToViewColumn(editorGroupService: IEditorGroupsService, editorGroup: IEditorGroup | GroupIdentifier): EditorGroupColumn { + let group = (typeof editorGroup === 'number') ? editorGroupService.getGroup(editorGroup) : editorGroup; + group = group ?? editorGroupService.activeGroup; + + return editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).indexOf(group); +} + +//#endregion diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index 4274f2eb8..38051db4d 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -3,11 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorModel, EditorInput, SideBySideEditorInput, TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor'; +import { EditorModel, EditorInput, SideBySideEditorInput, TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID, Verbosity } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; import { localize } from 'vs/nls'; +import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; +import { dirname } from 'vs/base/common/resources'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IFileService } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; /** * The base editor input for the diff editor. It is made up of two editor inputs, the original version @@ -21,10 +26,12 @@ export class DiffEditorInput extends SideBySideEditorInput { constructor( protected name: string | undefined, - description: string | undefined, + protected description: string | undefined, public readonly originalInput: EditorInput, public readonly modifiedInput: EditorInput, - private readonly forceOpenAsBinary?: boolean + private readonly forceOpenAsBinary: boolean | undefined, + @ILabelService private readonly labelService: ILabelService, + @IFileService private readonly fileService: IFileService ) { super(name, description, originalInput, modifiedInput); } @@ -35,12 +42,51 @@ export class DiffEditorInput extends SideBySideEditorInput { getName(): string { if (!this.name) { + + // Craft a name from original and modified input that includes the + // relative path in case both sides have different parents and we + // compare file resources. + const fileResources = this.asFileResources(); + if (fileResources && dirname(fileResources.original).path !== dirname(fileResources.modified).path) { + return `${this.labelService.getUriLabel(fileResources.original, { relative: true })} ↔ ${this.labelService.getUriLabel(fileResources.modified, { relative: true })}`; + } + return localize('sideBySideLabels', "{0} ↔ {1}", this.originalInput.getName(), this.modifiedInput.getName()); } return this.name; } + getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string | undefined { + if (typeof this.description !== 'string') { + + // Pass the description of the modified side in case both original + // and modified input have the same parent and we compare file resources. + const fileResources = this.asFileResources(); + if (fileResources && dirname(fileResources.original).path === dirname(fileResources.modified).path) { + return this.modifiedInput.getDescription(verbosity); + } + } + + return this.description; + } + + private asFileResources(): { original: URI, modified: URI } | undefined { + if ( + this.originalInput instanceof AbstractTextResourceEditorInput && + this.modifiedInput instanceof AbstractTextResourceEditorInput && + this.fileService.canHandleResource(this.originalInput.preferredResource) && + this.fileService.canHandleResource(this.modifiedInput.preferredResource) + ) { + return { + original: this.originalInput.preferredResource, + modified: this.modifiedInput.preferredResource + }; + } + + return undefined; + } + async resolve(): Promise { // Create Model - we never reuse our cached model if refresh is true because we cannot diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index ee4f35bae..a72d26526 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -55,6 +55,7 @@ export class ResourceEditorInput extends AbstractTextResourceEditorInput impleme setName(name: string): void { if (this.name !== name) { this.name = name; + this._onDidChangeLabel.fire(); } } diff --git a/src/vs/workbench/common/editor/textResourceEditorInput.ts b/src/vs/workbench/common/editor/textResourceEditorInput.ts index e6d7c4d4a..e55b55679 100644 --- a/src/vs/workbench/common/editor/textResourceEditorInput.ts +++ b/src/vs/workbench/common/editor/textResourceEditorInput.ts @@ -56,7 +56,7 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput implem private updateLabel(): void { // Clear any cached labels from before - this._basename = undefined; + this._name = undefined; this._shortDescription = undefined; this._mediumDescription = undefined; this._longDescription = undefined; @@ -76,17 +76,13 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput implem } } + private _name: string | undefined = undefined; getName(): string { - return this.basename; - } - - private _basename: string | undefined; - private get basename(): string { - if (!this._basename) { - this._basename = this.labelService.getUriBasenameLabel(this._preferredResource); + if (typeof this._name !== 'string') { + this._name = this.labelService.getUriBasenameLabel(this._preferredResource); } - return this._basename; + return this._name; } getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string | undefined { @@ -103,49 +99,55 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput implem private _shortDescription: string | undefined = undefined; private get shortDescription(): string { - if (!this._shortDescription) { + if (typeof this._shortDescription !== 'string') { this._shortDescription = this.labelService.getUriBasenameLabel(dirname(this._preferredResource)); } + return this._shortDescription; } private _mediumDescription: string | undefined = undefined; private get mediumDescription(): string { - if (!this._mediumDescription) { + if (typeof this._mediumDescription !== 'string') { this._mediumDescription = this.labelService.getUriLabel(dirname(this._preferredResource), { relative: true }); } + return this._mediumDescription; } private _longDescription: string | undefined = undefined; private get longDescription(): string { - if (!this._longDescription) { + if (typeof this._longDescription !== 'string') { this._longDescription = this.labelService.getUriLabel(dirname(this._preferredResource)); } + return this._longDescription; } private _shortTitle: string | undefined = undefined; private get shortTitle(): string { - if (!this._shortTitle) { + if (typeof this._shortTitle !== 'string') { this._shortTitle = this.getName(); } + return this._shortTitle; } private _mediumTitle: string | undefined = undefined; private get mediumTitle(): string { - if (!this._mediumTitle) { + if (typeof this._mediumTitle !== 'string') { this._mediumTitle = this.labelService.getUriLabel(this._preferredResource, { relative: true }); } + return this._mediumTitle; } private _longTitle: string | undefined = undefined; private get longTitle(): string { - if (!this._longTitle) { + if (typeof this._longTitle !== 'string') { this._longTitle = this.labelService.getUriLabel(this._preferredResource); } + return this._longTitle; } diff --git a/src/vs/workbench/common/memento.ts b/src/vs/workbench/common/memento.ts index 309b0517b..e9854ff82 100644 --- a/src/vs/workbench/common/memento.ts +++ b/src/vs/workbench/common/memento.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { isEmptyObject } from 'vs/base/common/types'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -22,13 +22,13 @@ export class Memento { this.id = Memento.COMMON_PREFIX + id; } - getMemento(scope: StorageScope): MementoObject { + getMemento(scope: StorageScope, target: StorageTarget): MementoObject { // Scope by Workspace if (scope === StorageScope.WORKSPACE) { let workspaceMemento = Memento.workspaceMementos.get(this.id); if (!workspaceMemento) { - workspaceMemento = new ScopedMemento(this.id, scope, this.storageService); + workspaceMemento = new ScopedMemento(this.id, scope, target, this.storageService); Memento.workspaceMementos.set(this.id, workspaceMemento); } @@ -38,7 +38,7 @@ export class Memento { // Scope Global let globalMemento = Memento.globalMementos.get(this.id); if (!globalMemento) { - globalMemento = new ScopedMemento(this.id, scope, this.storageService); + globalMemento = new ScopedMemento(this.id, scope, target, this.storageService); Memento.globalMementos.set(this.id, globalMemento); } @@ -65,7 +65,7 @@ class ScopedMemento { private readonly mementoObj: MementoObject; - constructor(private id: string, private scope: StorageScope, private storageService: IStorageService) { + constructor(private id: string, private scope: StorageScope, private target: StorageTarget, private storageService: IStorageService) { this.mementoObj = this.load(); } @@ -92,7 +92,7 @@ class ScopedMemento { save(): void { if (!isEmptyObject(this.mementoObj)) { - this.storageService.store(this.id, JSON.stringify(this.mementoObj), this.scope); + this.storageService.store(this.id, JSON.stringify(this.mementoObj), this.scope, this.target); } else { this.storageService.remove(this.id, this.scope); } diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index b9fb58ac4..e7df71486 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter, INotificationProgressProperties } from 'vs/platform/notification/common/notification'; +import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter, INotificationProgressProperties, IPromptChoiceWithMenu } from 'vs/platform/notification/common/notification'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -695,6 +695,7 @@ export class ChoiceAction extends Action { readonly onDidRun = this._onDidRun.event; private readonly _keepOpen: boolean; + private readonly _menu: ChoiceAction[] | undefined; constructor(id: string, choice: IPromptChoice) { super(id, choice.label, undefined, true, async () => { @@ -707,6 +708,11 @@ export class ChoiceAction extends Action { }); this._keepOpen = !!choice.keepOpen; + this._menu = !choice.isSecondary && (choice).menu ? (choice).menu.map((c, index) => new ChoiceAction(`${id}.${index}`, c)) : undefined; + } + + get menu(): ChoiceAction[] | undefined { + return this._menu; } get keepOpen(): boolean { diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index f7451eda5..53f29efbd 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, editorWidgetForeground, editorErrorForeground, editorWarningForeground, editorInfoForeground, treeIndentGuidesStroke } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, editorWidgetForeground, editorErrorForeground, editorWarningForeground, editorInfoForeground, treeIndentGuidesStroke, errorForeground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; @@ -401,6 +401,18 @@ export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusB hc: Color.black.transparent(0.3), }, nls.localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_ERROR_ITEM_BACKGROUND = registerColor('statusBarItem.errorBackground', { + dark: darken(errorForeground, .4), + light: darken(errorForeground, .4), + hc: null, +}, nls.localize('statusBarErrorItemBackground', "Status bar error items background color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); + +export const STATUS_BAR_ERROR_ITEM_FOREGROUND = registerColor('statusBarItem.errorForeground', { + dark: Color.white, + light: Color.white, + hc: Color.white, +}, nls.localize('statusBarErrorItemForeground', "Status bar error items foreground color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); + // < --- Activity Bar --- > export const ACTIVITY_BAR_BACKGROUND = registerColor('activityBar.background', { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 24f43b1d7..0744cecf6 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -25,8 +25,13 @@ import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { mixin } from 'vs/base/common/objects'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test'; +export const testViewIcon = registerIcon('test-view-icon', Codicon.beaker, localize('testViewIcon', 'View icon of the test view.')); + +export const defaultViewIcon = registerIcon('default-view-icon', Codicon.window, localize('defaultViewIcon', 'Default view icon.')); export namespace Extensions { export const ViewContainersRegistry = 'workbench.registry.view.containers'; @@ -48,7 +53,7 @@ export interface IViewContainerDescriptor { readonly storageId?: string; - readonly icon?: string | URI; + readonly icon?: ThemeIcon | URI; readonly alwaysUseContainerInfo?: boolean; @@ -216,7 +221,7 @@ export interface IViewDescriptor { readonly canMoveView?: boolean; - readonly containerIcon?: string | URI; + readonly containerIcon?: ThemeIcon | URI; readonly containerTitle?: string; @@ -252,7 +257,7 @@ export interface IAddedViewDescriptorState { export interface IViewContainerModel { readonly title: string; - readonly icon: string | URI | undefined; + readonly icon: ThemeIcon | URI | undefined; readonly onDidChangeContainerInfo: Event<{ title?: boolean, icon?: boolean }>; readonly allViewDescriptors: ReadonlyArray; @@ -290,11 +295,7 @@ export interface IViewContentDescriptor { readonly when?: ContextKeyExpression | 'default'; readonly group?: string; readonly order?: number; - - /** - * ordered preconditions for each button in the content - */ - readonly preconditions?: (ContextKeyExpression | undefined)[]; + readonly precondition?: ContextKeyExpression | undefined; } export interface IViewsRegistry { @@ -703,6 +704,27 @@ export class ResolvableTreeItem implements ITreeItem { get hasResolve(): boolean { return this._hasResolve; } + public resetResolve() { + this.resolved = false; + } + public asTreeItem(): ITreeItem { + return { + handle: this.handle, + parentHandle: this.parentHandle, + collapsibleState: this.collapsibleState, + label: this.label, + description: this.description, + icon: this.icon, + iconDark: this.iconDark, + themeIcon: this.themeIcon, + resourceUri: this.resourceUri, + tooltip: this.tooltip, + contextValue: this.contextValue, + command: this.command, + children: this.children, + accessibilityInformation: this.accessibilityInformation + }; + } } export interface ITreeViewDataProvider { diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts index 68c8751e0..dfe8325aa 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts @@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { DefaultEndOfLine } from 'vs/editor/common/model'; -import { hashPath } from 'vs/workbench/services/backup/node/backupFileService'; +import { hashPath } from 'vs/workbench/services/backup/electron-browser/backupFileService'; import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker'; import { workbenchInstantiationService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts index f2e134c7c..efb89fafb 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -10,7 +10,7 @@ import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; -import { hashPath } from 'vs/workbench/services/backup/node/backupFileService'; +import { hashPath } from 'vs/workbench/services/backup/electron-browser/backupFileService'; import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -33,7 +33,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { HotExitConfiguration } from 'vs/platform/files/common/files'; import { ShutdownReason, ILifecycleService, BeforeShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFileDialogService, ConfirmResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker'; import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/electron-browser/workbenchTestServices'; @@ -48,6 +48,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; import { CancellationToken } from 'vs/base/common/cancellation'; import { timeout } from 'vs/base/common/async'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backuprestorer'); const backupHome = path.join(userdataDir, 'Backups'); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts index 96b5e0e7e..714e45927 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { groupBy } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { compare } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { WorkspaceEditMetadata } from 'vs/editor/common/modes'; import { IProgress } from 'vs/platform/progress/common/progress'; -import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; +import { UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; import { ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; -import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; export class ResourceNotebookCellEdit extends ResourceEdit { @@ -29,10 +29,11 @@ export class ResourceNotebookCellEdit extends ResourceEdit { export class BulkCellEdits { constructor( - private _undoRedoGroup: UndoRedoGroup, + private readonly _undoRedoGroup: UndoRedoGroup, + undoRedoSource: UndoRedoSource | undefined, private readonly _progress: IProgress, + private readonly _token: CancellationToken, private readonly _edits: ResourceNotebookCellEdit[], - @INotebookService private readonly _notebookService: INotebookService, @INotebookEditorModelResolverService private readonly _notebookModelService: INotebookEditorModelResolverService, ) { } @@ -41,6 +42,9 @@ export class BulkCellEdits { const editsByNotebook = groupBy(this._edits, (a, b) => compare(a.resource.toString(), b.resource.toString())); for (let group of editsByNotebook) { + if (this._token.isCancellationRequested) { + break; + } const [first] = group; const ref = await this._notebookModelService.resolve(first.resource); @@ -52,7 +56,6 @@ export class BulkCellEdits { // apply edits const edits = group.map(entry => entry.cellEdit); - this._notebookService.transformEditsOutputs(ref.object.notebook, edits); ref.object.notebook.applyEdits(ref.object.notebook.versionId, edits, true, undefined, () => undefined, this._undoRedoGroup); ref.dispose(); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts index defc9283a..ffc54667e 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts @@ -16,7 +16,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { BulkTextEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkTextEdits'; import { BulkFileEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkFileEdits'; import { BulkCellEdits, ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits'; -import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; +import { UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; +import { LinkedList } from 'vs/base/common/linkedList'; +import { CancellationToken } from 'vs/base/common/cancellation'; class BulkEdit { @@ -24,7 +26,10 @@ class BulkEdit { private readonly _label: string | undefined, private readonly _editor: ICodeEditor | undefined, private readonly _progress: IProgress, + private readonly _token: CancellationToken, private readonly _edits: ResourceEdit[], + private readonly _undoRedoGroup: UndoRedoGroup, + private readonly _undoRedoSource: UndoRedoSource | undefined, @IInstantiationService private readonly _instaService: IInstantiationService, @ILogService private readonly _logService: ILogService, ) { @@ -58,20 +63,24 @@ class BulkEdit { } } - this._progress.report({ total: this._edits.length }); - const progress: IProgress = { report: _ => this._progress.report({ increment: 1 }) }; - - const undoRedoGroup = new UndoRedoGroup(); + // Show infinte progress when there is only 1 item since we do not know how long it takes + const increment = this._edits.length > 1 ? 0 : undefined; + this._progress.report({ increment, total: 100 }); + // Increment by percentage points since progress API expects that + const progress: IProgress = { report: _ => this._progress.report({ increment: 100 / this._edits.length }) }; let index = 0; for (let range of ranges) { + if (this._token.isCancellationRequested) { + break; + } const group = this._edits.slice(index, index + range); if (group[0] instanceof ResourceFileEdit) { - await this._performFileEdits(group, undoRedoGroup, progress); + await this._performFileEdits(group, this._undoRedoGroup, this._undoRedoSource, progress); } else if (group[0] instanceof ResourceTextEdit) { - await this._performTextEdits(group, undoRedoGroup, progress); + await this._performTextEdits(group, this._undoRedoGroup, this._undoRedoSource, progress); } else if (group[0] instanceof ResourceNotebookCellEdit) { - await this._performCellEdits(group, undoRedoGroup, progress); + await this._performCellEdits(group, this._undoRedoGroup, this._undoRedoSource, progress); } else { console.log('UNKNOWN EDIT'); } @@ -79,21 +88,21 @@ class BulkEdit { } } - private async _performFileEdits(edits: ResourceFileEdit[], undoRedoGroup: UndoRedoGroup, progress: IProgress) { + private async _performFileEdits(edits: ResourceFileEdit[], undoRedoGroup: UndoRedoGroup, undoRedoSource: UndoRedoSource | undefined, progress: IProgress) { this._logService.debug('_performFileEdits', JSON.stringify(edits)); - const model = this._instaService.createInstance(BulkFileEdits, this._label || localize('workspaceEdit', "Workspace Edit"), undoRedoGroup, progress, edits); + const model = this._instaService.createInstance(BulkFileEdits, this._label || localize('workspaceEdit', "Workspace Edit"), undoRedoGroup, undoRedoSource, progress, this._token, edits); await model.apply(); } - private async _performTextEdits(edits: ResourceTextEdit[], undoRedoGroup: UndoRedoGroup, progress: IProgress): Promise { + private async _performTextEdits(edits: ResourceTextEdit[], undoRedoGroup: UndoRedoGroup, undoRedoSource: UndoRedoSource | undefined, progress: IProgress): Promise { this._logService.debug('_performTextEdits', JSON.stringify(edits)); - const model = this._instaService.createInstance(BulkTextEdits, this._label || localize('workspaceEdit', "Workspace Edit"), this._editor, undoRedoGroup, progress, edits); + const model = this._instaService.createInstance(BulkTextEdits, this._label || localize('workspaceEdit', "Workspace Edit"), this._editor, undoRedoGroup, undoRedoSource, progress, this._token, edits); await model.apply(); } - private async _performCellEdits(edits: ResourceNotebookCellEdit[], undoRedoGroup: UndoRedoGroup, progress: IProgress): Promise { + private async _performCellEdits(edits: ResourceNotebookCellEdit[], undoRedoGroup: UndoRedoGroup, undoRedoSource: UndoRedoSource | undefined, progress: IProgress): Promise { this._logService.debug('_performCellEdits', JSON.stringify(edits)); - const model = this._instaService.createInstance(BulkCellEdits, undoRedoGroup, progress, edits); + const model = this._instaService.createInstance(BulkCellEdits, undoRedoGroup, undoRedoSource, progress, this._token, edits); await model.apply(); } } @@ -102,6 +111,7 @@ export class BulkEditService implements IBulkEditService { declare readonly _serviceBrand: undefined; + private readonly _activeUndoRedoGroups = new LinkedList(); private _previewHandler?: IBulkEditPreviewHandler; constructor( @@ -129,7 +139,7 @@ export class BulkEditService implements IBulkEditService { return { ariaSummary: localize('nothing', "Made no edits") }; } - if (this._previewHandler && (options?.showPreview || edits.some(value => value.metadata?.needsConfirmation))) { + if (this._previewHandler && !options?.suppressPreview && (options?.showPreview || edits.some(value => value.metadata?.needsConfirmation))) { edits = await this._previewHandler(edits, options); } @@ -147,11 +157,33 @@ export class BulkEditService implements IBulkEditService { codeEditor = undefined; } + // undo-redo-group: if a group id is passed then try to find it + // in the list of active edits. otherwise (or when not found) + // create a separate undo-redo-group + let undoRedoGroup: UndoRedoGroup | undefined; + let undoRedoGroupRemove = () => { }; + if (typeof options?.undoRedoGroupId === 'number') { + for (let candidate of this._activeUndoRedoGroups) { + if (candidate.id === options.undoRedoGroupId) { + undoRedoGroup = candidate; + break; + } + } + } + if (!undoRedoGroup) { + undoRedoGroup = new UndoRedoGroup(); + undoRedoGroupRemove = this._activeUndoRedoGroups.push(undoRedoGroup); + } + const bulkEdit = this._instaService.createInstance( BulkEdit, options?.quotableLabel || options?.label, - codeEditor, options?.progress ?? Progress.None, - edits + codeEditor, + options?.progress ?? Progress.None, + options?.token ?? CancellationToken.None, + edits, + undoRedoGroup, + options?.undoRedoSource ); try { @@ -162,6 +194,8 @@ export class BulkEditService implements IBulkEditService { // console.log(err); this._logService.error(err); throw err; + } finally { + undoRedoGroupRemove(); } } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts index b58430484..f4ac30ca0 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts @@ -5,21 +5,27 @@ import { WorkspaceFileEditOptions } from 'vs/editor/common/modes'; -import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; +import { IFileService, FileSystemProviderCapabilities, IFileContent } from 'vs/platform/files/common/files'; import { IProgress } from 'vs/platform/progress/common/progress'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; -import { IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoService, UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; +import { IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoService, UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { VSBuffer } from 'vs/base/common/buffer'; import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; import * as resources from 'vs/base/common/resources'; +import { CancellationToken } from 'vs/base/common/cancellation'; + +interface IFileOperationUndoRedoInfo { + undoRedoGroupId?: number; + isUndoing?: boolean; +} interface IFileOperation { uris: URI[]; - perform(): Promise; + perform(token: CancellationToken): Promise; } class Noop implements IFileOperation { @@ -36,6 +42,7 @@ class RenameOperation implements IFileOperation { readonly newUri: URI, readonly oldUri: URI, readonly options: WorkspaceFileEditOptions, + readonly undoRedoInfo: IFileOperationUndoRedoInfo, @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, @IFileService private readonly _fileService: IFileService, ) { } @@ -44,14 +51,14 @@ class RenameOperation implements IFileOperation { return [this.newUri, this.oldUri]; } - async perform(): Promise { + async perform(token: CancellationToken): Promise { // rename if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) { return new Noop(); // not overwriting, but ignoring, and the target file exists } - await this._workingCopyFileService.move([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite }); - return new RenameOperation(this.oldUri, this.newUri, this.options, this._workingCopyFileService, this._fileService); + await this._workingCopyFileService.move([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite, ...this.undoRedoInfo }, token); + return new RenameOperation(this.oldUri, this.newUri, this.options, { isUndoing: true }, this._workingCopyFileService, this._fileService); } toString(): string { @@ -64,11 +71,44 @@ class RenameOperation implements IFileOperation { } } +class CopyOperation implements IFileOperation { + + constructor( + readonly newUri: URI, + readonly oldUri: URI, + readonly options: WorkspaceFileEditOptions, + readonly undoRedoInfo: IFileOperationUndoRedoInfo, + @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, + @IFileService private readonly _fileService: IFileService, + @IInstantiationService private readonly _instaService: IInstantiationService + ) { } + + get uris() { + return [this.newUri, this.oldUri]; + } + + async perform(token: CancellationToken): Promise { + // copy + if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) { + return new Noop(); // not overwriting, but ignoring, and the target file exists + } + + const stat = await this._workingCopyFileService.copy([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite, ...this.undoRedoInfo }, token); + const folder = this.options.folder || (stat.length === 1 && stat[0].isDirectory); + return this._instaService.createInstance(DeleteOperation, this.newUri, { recursive: true, folder, ...this.options }, { isUndoing: true }, false); + } + + toString(): string { + return `(copy ${this.oldUri} to ${this.newUri})`; + } +} + class CreateOperation implements IFileOperation { constructor( readonly newUri: URI, readonly options: WorkspaceFileEditOptions, + readonly undoRedoInfo: IFileOperationUndoRedoInfo, readonly contents: VSBuffer | undefined, @IFileService private readonly _fileService: IFileService, @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, @@ -79,17 +119,22 @@ class CreateOperation implements IFileOperation { return [this.newUri]; } - async perform(): Promise { + async perform(token: CancellationToken): Promise { // create file if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) { return new Noop(); // not overwriting, but ignoring, and the target file exists } - await this._workingCopyFileService.create(this.newUri, this.contents, { overwrite: this.options.overwrite }); - return this._instaService.createInstance(DeleteOperation, this.newUri, this.options, true); + if (this.options.folder) { + await this._workingCopyFileService.createFolder(this.newUri, { ...this.undoRedoInfo }, token); + } else { + await this._workingCopyFileService.create(this.newUri, this.contents, { overwrite: this.options.overwrite, ...this.undoRedoInfo }, token); + } + return this._instaService.createInstance(DeleteOperation, this.newUri, this.options, { isUndoing: true }, !this.options.folder && !this.contents); } toString(): string { - return `(create ${resources.basename(this.newUri)} with ${this.contents?.byteLength || 0} bytes)`; + return this.options.folder ? `create ${resources.basename(this.newUri)} folder` + : `(create ${resources.basename(this.newUri)} with ${this.contents?.byteLength || 0} bytes)`; } } @@ -98,6 +143,7 @@ class DeleteOperation implements IFileOperation { constructor( readonly oldUri: URI, readonly options: WorkspaceFileEditOptions, + readonly undoRedoInfo: IFileOperationUndoRedoInfo, private readonly _undoesCreateOperation: boolean, @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, @IFileService private readonly _fileService: IFileService, @@ -110,7 +156,7 @@ class DeleteOperation implements IFileOperation { return [this.oldUri]; } - async perform(): Promise { + async perform(token: CancellationToken): Promise { // delete file if (!await this._fileService.exists(this.oldUri)) { if (!this.options.ignoreIfNotExists) { @@ -119,18 +165,22 @@ class DeleteOperation implements IFileOperation { return new Noop(); } - let contents: VSBuffer | undefined; - if (!this._undoesCreateOperation) { + let fileContent: IFileContent | undefined; + if (!this._undoesCreateOperation && !this.options.folder) { try { - contents = (await this._fileService.readFile(this.oldUri)).value; + fileContent = await this._fileService.readFile(this.oldUri); } catch (err) { this._logService.critical(err); } } - const useTrash = this._fileService.hasCapability(this.oldUri, FileSystemProviderCapabilities.Trash) && this._configurationService.getValue('files.enableTrash'); - await this._workingCopyFileService.delete([this.oldUri], { useTrash, recursive: this.options.recursive }); - return this._instaService.createInstance(CreateOperation, this.oldUri, this.options, contents); + const useTrash = !this.options.skipTrashBin && this._fileService.hasCapability(this.oldUri, FileSystemProviderCapabilities.Trash) && this._configurationService.getValue('files.enableTrash'); + await this._workingCopyFileService.delete([this.oldUri], { useTrash, recursive: this.options.recursive, ...this.undoRedoInfo }, token); + + if (typeof this.options.maxSize === 'number' && fileContent && (fileContent?.size > this.options.maxSize)) { + return new Noop(); + } + return this._instaService.createInstance(CreateOperation, this.oldUri, this.options, { isUndoing: true }, fileContent?.value); } toString(): string { @@ -162,7 +212,7 @@ class FileUndoRedoElement implements IWorkspaceUndoRedoElement { private async _reverse() { for (let i = 0; i < this.operations.length; i++) { const op = this.operations[i]; - const undo = await op.perform(); + const undo = await op.perform(CancellationToken.None); this.operations[i] = undo; } } @@ -177,7 +227,9 @@ export class BulkFileEdits { constructor( private readonly _label: string, private readonly _undoRedoGroup: UndoRedoGroup, + private readonly _undoRedoSource: UndoRedoSource | undefined, private readonly _progress: IProgress, + private readonly _token: CancellationToken, private readonly _edits: ResourceFileEdit[], @IInstantiationService private readonly _instaService: IInstantiationService, @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, @@ -185,27 +237,35 @@ export class BulkFileEdits { async apply(): Promise { const undoOperations: IFileOperation[] = []; + const undoRedoInfo = { undoRedoGroupId: this._undoRedoGroup.id }; for (const edit of this._edits) { - this._progress.report(undefined); + + if (this._token.isCancellationRequested) { + break; + } const options = edit.options || {}; let op: IFileOperation | undefined; - if (edit.newResource && edit.oldResource) { + if (edit.newResource && edit.oldResource && !options.copy) { // rename - op = this._instaService.createInstance(RenameOperation, edit.newResource, edit.oldResource, options); + op = this._instaService.createInstance(RenameOperation, edit.newResource, edit.oldResource, options, undoRedoInfo); + } else if (edit.newResource && edit.oldResource && options.copy) { + op = this._instaService.createInstance(CopyOperation, edit.newResource, edit.oldResource, options, undoRedoInfo); } else if (!edit.newResource && edit.oldResource) { // delete file - op = this._instaService.createInstance(DeleteOperation, edit.oldResource, options, false); + op = this._instaService.createInstance(DeleteOperation, edit.oldResource, options, undoRedoInfo, false); } else if (edit.newResource && !edit.oldResource) { // create file - op = this._instaService.createInstance(CreateOperation, edit.newResource, options, undefined); + op = this._instaService.createInstance(CreateOperation, edit.newResource, options, undoRedoInfo, undefined); } if (op) { - const undoOp = await op.perform(); + const undoOp = await op.perform(this._token); undoOperations.push(undoOp); } + + this._progress.report(undefined); } - this._undoRedoService.pushElement(new FileUndoRedoElement(this._label, undoOperations), this._undoRedoGroup); + this._undoRedoService.pushElement(new FileUndoRedoElement(this._label, undoOperations), this._undoRedoGroup, this._undoRedoSource); } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index 533654d98..852da6043 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -14,11 +14,12 @@ import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'v import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IProgress } from 'vs/platform/progress/common/progress'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { IUndoRedoService, UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoService, UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; import { SingleModelEditStackElement, MultiModelEditStackElement } from 'vs/editor/common/model/editStack'; import { ResourceMap } from 'vs/base/common/map'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; +import { CancellationToken } from 'vs/base/common/cancellation'; type ValidationResult = { canApply: true } | { canApply: false, reason: URI }; @@ -39,6 +40,18 @@ class ModelEditTask implements IDisposable { this._modelReference.dispose(); } + isNoOp() { + if (this._edits.length > 0) { + // contains textual edits + return false; + } + if (this._newEol !== undefined && this._newEol !== this.model.getEndOfLineSequence()) { + // contains an eol change that is a real change + return false; + } + return true; + } + addEdit(resourceEdit: ResourceTextEdit): void { this._expectedModelVersionId = resourceEdit.versionId; const { textEdit } = resourceEdit; @@ -122,7 +135,9 @@ export class BulkTextEdits { private readonly _label: string, private readonly _editor: ICodeEditor | undefined, private readonly _undoRedoGroup: UndoRedoGroup, + private readonly _undoRedoSource: UndoRedoSource | undefined, private readonly _progress: IProgress, + private readonly _token: CancellationToken, edits: ResourceTextEdit[], @IEditorWorkerService private readonly _editorWorker: IEditorWorkerService, @IModelService private readonly _modelService: IModelService, @@ -210,6 +225,9 @@ export class BulkTextEdits { this._validateBeforePrepare(); const tasks = await this._createEditsTasks(); + if (this._token.isCancellationRequested) { + return; + } try { const validation = this._validateTasks(tasks); @@ -219,10 +237,12 @@ export class BulkTextEdits { if (tasks.length === 1) { // This edit touches a single model => keep things simple const task = tasks[0]; - const singleModelEditStackElement = new SingleModelEditStackElement(task.model, task.getBeforeCursorState()); - this._undoRedoService.pushElement(singleModelEditStackElement, this._undoRedoGroup); - task.apply(); - singleModelEditStackElement.close(); + if (!task.isNoOp()) { + const singleModelEditStackElement = new SingleModelEditStackElement(task.model, task.getBeforeCursorState()); + this._undoRedoService.pushElement(singleModelEditStackElement, this._undoRedoGroup, this._undoRedoSource); + task.apply(); + singleModelEditStackElement.close(); + } this._progress.report(undefined); } else { // prepare multi model undo element @@ -230,7 +250,7 @@ export class BulkTextEdits { this._label, tasks.map(t => new SingleModelEditStackElement(t.model, t.getBeforeCursorState())) ); - this._undoRedoService.pushElement(multiModelEditStackElement, this._undoRedoGroup); + this._undoRedoService.pushElement(multiModelEditStackElement, this._undoRedoGroup, this._undoRedoSource); for (const task of tasks) { task.apply(); this._progress.report(undefined); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts index 139ee6ddb..7a757cf09 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts @@ -28,6 +28,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; async function getBulkEditPane(viewsService: IViewsService): Promise { const view = await viewsService.openView(BulkEditPane.ID, true); @@ -169,7 +170,7 @@ registerAction2(class ApplyAction extends Action2 { id: 'refactorPreview.apply', title: { value: localize('apply', "Apply Refactoring"), original: 'Apply Refactoring' }, category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, - icon: { id: 'codicon/check' }, + icon: Codicon.check, precondition: ContextKeyExpr.and(BulkEditPreviewContribution.ctxEnabled, BulkEditPane.ctxHasCheckedChanges), menu: [{ id: MenuId.BulkEditTitle, @@ -203,7 +204,7 @@ registerAction2(class DiscardAction extends Action2 { id: 'refactorPreview.discard', title: { value: localize('Discard', "Discard Refactoring"), original: 'Discard Refactoring' }, category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, - icon: { id: 'codicon/clear-all' }, + icon: Codicon.clearAll, precondition: BulkEditPreviewContribution.ctxEnabled, menu: [{ id: MenuId.BulkEditTitle, @@ -264,7 +265,7 @@ registerAction2(class GroupByFile extends Action2 { id: 'refactorPreview.groupByFile', title: { value: localize('groupByFile', "Group Changes By File"), original: 'Group Changes By File' }, category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, - icon: { id: 'codicon/ungroup-by-ref-type' }, + icon: Codicon.ungroupByRefType, precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPane.ctxGroupByFile.negate(), BulkEditPreviewContribution.ctxEnabled), menu: [{ id: MenuId.BulkEditTitle, @@ -291,7 +292,7 @@ registerAction2(class GroupByType extends Action2 { id: 'refactorPreview.groupByType', title: { value: localize('groupByType', "Group Changes By Type"), original: 'Group Changes By Type' }, category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, - icon: { id: 'codicon/group-by-ref-type' }, + icon: Codicon.groupByRefType, precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPane.ctxGroupByFile, BulkEditPreviewContribution.ctxEnabled), menu: [{ id: MenuId.BulkEditTitle, @@ -318,7 +319,7 @@ registerAction2(class ToggleGrouping extends Action2 { id: 'refactorPreview.toggleGrouping', title: { value: localize('groupByType', "Group Changes By Type"), original: 'Group Changes By Type' }, category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, - icon: { id: 'codicon/list-tree' }, + icon: Codicon.listTree, toggled: BulkEditPane.ctxGroupByFile.negate(), precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPreviewContribution.ctxEnabled), menu: [{ @@ -341,6 +342,8 @@ Registry.as(WorkbenchExtensions.Workbench).regi BulkEditPreviewContribution, LifecyclePhase.Ready ); +const refactorPreviewViewIcon = registerIcon('refactor-preview-view-icon', Codicon.lightbulb, localize('refactorPreviewViewIcon', 'View icon of the refactor preview view.')); + const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: BulkEditPane.ID, name: localize('panel', "Refactor Preview"), @@ -349,7 +352,7 @@ const container = Registry.as(ViewContainerExtensions.V ViewPaneContainer, [BulkEditPane.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }] ), - icon: Codicon.lightbulb.classNames, + icon: refactorPreviewViewIcon, storageId: BulkEditPane.ID }, ViewContainerLocation.Panel); @@ -358,5 +361,5 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews name: localize('panel', "Refactor Preview"), when: BulkEditPreviewContribution.ctxEnabled, ctorDescriptor: new SyncDescriptor(BulkEditPane), - containerIcon: Codicon.lightbulb.classNames, + containerIcon: refactorPreviewViewIcon, }], container); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index f36f5f0e9..ab0172eec 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -12,7 +12,7 @@ import { registerThemingParticipant, IColorTheme, ICssStyleCollector, IThemeServ import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { BulkEditPreviewProvider, BulkFileOperations, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -26,7 +26,7 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; -import { basename } from 'vs/base/common/resources'; +import { basename, dirname } from 'vs/base/common/resources'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -34,7 +34,7 @@ import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import type { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -128,7 +128,7 @@ export class BulkEditPane extends ViewPane { this._tree = >this._instaService.createInstance( WorkbenchAsyncDataTree, this.id, treeContainer, new BulkEditDelegate(), - [new TextEditElementRenderer(), this._instaService.createInstance(FileElementRenderer, resourceLabels), new CategoryElementRenderer()], + [this._instaService.createInstance(TextEditElementRenderer), this._instaService.createInstance(FileElementRenderer, resourceLabels), this._instaService.createInstance(CategoryElementRenderer)], this._treeDataSource, { accessibilityProvider: this._instaService.createInstance(BulkEditAccessibilityProvider), @@ -293,12 +293,12 @@ export class BulkEditPane extends ViewPane { this._setTreeInput(input); // (3) remember preference - this._storageService.store(BulkEditPane._memGroupByFile, this._treeDataSource.groupByFile, StorageScope.GLOBAL); + this._storageService.store(BulkEditPane._memGroupByFile, this._treeDataSource.groupByFile, StorageScope.GLOBAL, StorageTarget.USER); this._ctxGroupByFile.set(this._treeDataSource.groupByFile); } } - private async _openElementAsEditor(e: IOpenEvent): Promise { + private async _openElementAsEditor(e: IOpenEvent): Promise { type Mutable = { -readonly [P in keyof T]: T[P] }; @@ -356,8 +356,9 @@ export class BulkEditPane extends ViewPane { leftResource, rightResource: previewUri, label, + description: this._labelService.getUriLabel(dirname(leftResource), { relative: true }), options - }); + }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts index 4aecbf746..a1b366c48 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts @@ -22,6 +22,7 @@ import { ResourceMap } from 'vs/base/common/map'; import { localize } from 'vs/nls'; import { extUri } from 'vs/base/common/resources'; import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; +import { Codicon } from 'vs/base/common/codicons'; export class CheckedStates { @@ -116,7 +117,7 @@ export class BulkCategory { private static readonly _defaultMetadata = Object.freeze({ label: localize('default', "Other"), - icon: { id: 'codicon/symbol-file' }, + icon: Codicon.symbolFile, needsConfirmation: false }); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts index 4db2acf28..e9dddb2a2 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts @@ -21,7 +21,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import type { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { basename } from 'vs/base/common/resources'; -import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { compare } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; @@ -386,6 +386,8 @@ export class CategoryElementRenderer implements ITreeRenderer, _index: number, template: TextEditElementTemplate): void { diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index 32c4c2f8b..85c2711cb 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -18,12 +18,13 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { PeekContext } from 'vs/editor/contrib/peekView/peekView'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Range } from 'vs/editor/common/core/range'; import { IPosition } from 'vs/editor/common/core/position'; import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; -import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; const _ctxHasCallHierarchyProvider = new RawContextKey('editorHasCallHierarchyProvider', false); const _ctxCallHierarchyVisible = new RawContextKey('callHierarchyVisible', false); @@ -128,7 +129,7 @@ class CallHierarchyController implements IEditorContribution { this._widget.showLoading(); this._sessionDisposables.add(this._widget.onDidClose(() => { this.endCallHierarchy(); - this._storageService.store(CallHierarchyController._StorageDirection, this._widget!.direction, StorageScope.GLOBAL); + this._storageService.store(CallHierarchyController._StorageDirection, this._widget!.direction, StorageScope.GLOBAL, StorageTarget.USER); })); this._sessionDisposables.add({ dispose() { cts.dispose(true); } }); this._sessionDisposables.add(this._widget); @@ -207,7 +208,7 @@ registerAction2(class extends EditorAction2 { super({ id: 'editor.showIncomingCalls', title: { value: localize('title.incoming', "Show Incoming Calls"), original: 'Show Incoming Calls' }, - icon: registerIcon('callhierarchy-incoming', Codicon.callIncoming), + icon: registerIcon('callhierarchy-incoming', Codicon.callIncoming, localize('showIncomingCallsIcons', 'Icon for incoming calls in the call hierarchy view.')), precondition: ContextKeyExpr.and(_ctxCallHierarchyVisible, _ctxCallHierarchyDirection.isEqualTo(CallHierarchyDirection.CallsFrom)), keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -232,7 +233,7 @@ registerAction2(class extends EditorAction2 { super({ id: 'editor.showOutgoingCalls', title: { value: localize('title.outgoing', "Show Outgoing Calls"), original: 'Show Outgoing Calls' }, - icon: registerIcon('callhierarchy-outgoing', Codicon.callOutgoing), + icon: registerIcon('callhierarchy-outgoing', Codicon.callOutgoing, localize('showOutgoingCallsIcon', 'Icon for outgoing calls in the call hierarchy view.')), precondition: ContextKeyExpr.and(_ctxCallHierarchyVisible, _ctxCallHierarchyDirection.isEqualTo(CallHierarchyDirection.CallsTo)), keybinding: { weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index b6866acaa..6fcd9f8ab 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -28,7 +28,7 @@ import { registerThemingParticipant, themeColorFromId, IThemeService, IColorThem import { IPosition } from 'vs/editor/common/core/position'; import { IAction } from 'vs/base/common/actions'; import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Color } from 'vs/base/common/color'; import { TreeMouseEventTarget, ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { URI } from 'vs/base/common/uri'; @@ -45,7 +45,7 @@ const enum State { class LayoutInfo { static store(info: LayoutInfo, storageService: IStorageService): void { - storageService.store('callHierarchyPeekLayout', JSON.stringify(info), StorageScope.GLOBAL); + storageService.store('callHierarchyPeekLayout', JSON.stringify(info), StorageScope.GLOBAL, StorageTarget.MACHINE); } static retrieve(storageService: IStorageService): LayoutInfo { @@ -277,7 +277,7 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { this.dispose(); this._editorService.openEditor({ resource: e.element.item.uri, - options: { selection: e.element.item.selectionRange } + options: { selection: e.element.item.selectionRange, pinned: true } }); } })); @@ -289,7 +289,7 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { this.dispose(); this._editorService.openEditor({ resource: element.item.uri, - options: { selection: element.item.selectionRange } + options: { selection: element.item.selectionRange, pinned: true } }); } })); diff --git a/src/vs/workbench/contrib/cli/node/cli.contribution.ts b/src/vs/workbench/contrib/cli/node/cli.contribution.ts index 2000693fd..30972a411 100644 --- a/src/vs/workbench/contrib/cli/node/cli.contribution.ts +++ b/src/vs/workbench/contrib/cli/node/cli.contribution.ts @@ -8,12 +8,8 @@ import * as path from 'vs/base/common/path'; import * as cp from 'child_process'; import * as pfs from 'vs/base/node/pfs'; import * as extpath from 'vs/base/node/extpath'; -import * as platform from 'vs/base/common/platform'; import { promisify } from 'util'; -import { Action } from 'vs/base/common/actions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import product from 'vs/platform/product/common/product'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -21,6 +17,9 @@ import Severity from 'vs/base/common/severity'; import { ILogService } from 'vs/platform/log/common/log'; import { FileAccess } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; function ignore(code: string, value: T): (err: any) => Promise { return err => err.code === code ? Promise.resolve(value) : Promise.reject(err); @@ -39,45 +38,65 @@ function isAvailable(): Promise { return Promise.resolve(pfs.exists(getSource())); } -class InstallAction extends Action { +const category = nls.localize('shellCommand', "Shell Command"); - static readonly ID = 'workbench.action.installCommandLine'; - static readonly LABEL = nls.localize('install', "Install '{0}' command in PATH", product.applicationName); +class InstallAction extends Action2 { - constructor( - id: string, - label: string, - @INotificationService private readonly notificationService: INotificationService, - @IDialogService private readonly dialogService: IDialogService, - @ILogService private readonly logService: ILogService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.installCommandLine', + title: { + value: nls.localize('install', "Install '{0}' command in PATH", product.applicationName), + original: `Shell Command: Install \'${product.applicationName}\' command in PATH` + }, + category, + f1: true, + precondition: ContextKeyExpr.and(IsMacNativeContext, ContextKeyExpr.equals('remoteName', '')) + }); } - private get target(): string { - return `/usr/local/bin/${this.productService.applicationName}`; - } + run(accessor: ServicesAccessor): Promise { + const productService = accessor.get(IProductService); + const notificationService = accessor.get(INotificationService); + const logService = accessor.get(ILogService); + const dialogService = accessor.get(IDialogService); + const target = `/usr/local/bin/${productService.applicationName}`; - run(): Promise { return isAvailable().then(isAvailable => { if (!isAvailable) { const message = nls.localize('not available', "This command is not available"); - this.notificationService.info(message); + notificationService.info(message); return undefined; } - return this.isInstalled() + return this.isInstalled(target) .then(isInstalled => { if (!isAvailable || isInstalled) { return Promise.resolve(null); } else { - return pfs.unlink(this.target) + return pfs.unlink(target) .then(undefined, ignore('ENOENT', null)) - .then(() => pfs.symlink(getSource(), this.target)) + .then(() => pfs.symlink(getSource(), target)) .then(undefined, err => { if (err.code === 'EACCES' || err.code === 'ENOENT') { - return this.createBinFolderAndSymlinkAsAdmin(); + return new Promise((resolve, reject) => { + const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; + + dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => { + switch (result.choice) { + case 0 /* OK */: + const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + target + '\'\\" with administrator privileges"'; + + promisify(cp.exec)(command, {}) + .then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'.")))) + .then(() => resolve(), reject); + break; + case 1 /* Cancel */: + reject(new Error(nls.localize('aborted', "Aborted"))); + break; + } + }); + }); } return Promise.reject(err); @@ -85,113 +104,84 @@ class InstallAction extends Action { } }) .then(() => { - this.logService.trace('cli#install', this.target); - this.notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", this.productService.applicationName)); + logService.trace('cli#install', target); + notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", productService.applicationName)); }); }); } - private isInstalled(): Promise { - return pfs.lstat(this.target) + private isInstalled(target: string): Promise { + return pfs.lstat(target) .then(stat => stat.isSymbolicLink()) - .then(() => extpath.realpath(this.target)) + .then(() => extpath.realpath(target)) .then(link => link === getSource()) .then(undefined, ignore('ENOENT', false)); } - - private createBinFolderAndSymlinkAsAdmin(): Promise { - return new Promise((resolve, reject) => { - const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; - - this.dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => { - switch (result.choice) { - case 0 /* OK */: - const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + this.target + '\'\\" with administrator privileges"'; - - promisify(cp.exec)(command, {}) - .then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'.")))) - .then(() => resolve(), reject); - break; - case 1 /* Cancel */: - reject(new Error(nls.localize('aborted', "Aborted"))); - break; - } - }); - }); - } } -class UninstallAction extends Action { +class UninstallAction extends Action2 { - static readonly ID = 'workbench.action.uninstallCommandLine'; - static readonly LABEL = nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName); - - constructor( - id: string, - label: string, - @INotificationService private readonly notificationService: INotificationService, - @ILogService private readonly logService: ILogService, - @IDialogService private readonly dialogService: IDialogService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.uninstallCommandLine', + title: { + value: nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName), + original: `Shell Command: Uninstall \'${product.applicationName}\' command from PATH` + }, + category, + f1: true, + precondition: ContextKeyExpr.and(IsMacNativeContext, ContextKeyExpr.equals('remoteName', '')) + }); } - private get target(): string { - return `/usr/local/bin/${this.productService.applicationName}`; - } + run(accessor: ServicesAccessor): Promise { + const productService = accessor.get(IProductService); + const notificationService = accessor.get(INotificationService); + const logService = accessor.get(ILogService); + const dialogService = accessor.get(IDialogService); + const target = `/usr/local/bin/${productService.applicationName}`; - run(): Promise { return isAvailable().then(isAvailable => { if (!isAvailable) { const message = nls.localize('not available', "This command is not available"); - this.notificationService.info(message); + notificationService.info(message); return undefined; } const uninstall = () => { - return pfs.unlink(this.target) + return pfs.unlink(target) .then(undefined, ignore('ENOENT', null)); }; return uninstall().then(undefined, err => { if (err.code === 'EACCES') { - return this.deleteSymlinkAsAdmin(); + return new Promise(async (resolve, reject) => { + const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; + + const { choice } = await dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 }); + switch (choice) { + case 0 /* OK */: + const command = 'osascript -e "do shell script \\"rm \'' + target + '\'\\" with administrator privileges"'; + + promisify(cp.exec)(command, {}) + .then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", target)))) + .then(() => resolve(), reject); + break; + case 1 /* Cancel */: + reject(new Error(nls.localize('aborted', "Aborted"))); + break; + } + }); } return Promise.reject(err); }).then(() => { - this.logService.trace('cli#uninstall', this.target); - this.notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", this.productService.applicationName)); + logService.trace('cli#uninstall', target); + notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", productService.applicationName)); }); }); } - - private deleteSymlinkAsAdmin(): Promise { - return new Promise(async (resolve, reject) => { - const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; - - const { choice } = await this.dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 }); - switch (choice) { - case 0 /* OK */: - const command = 'osascript -e "do shell script \\"rm \'' + this.target + '\'\\" with administrator privileges"'; - - promisify(cp.exec)(command, {}) - .then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", this.target)))) - .then(() => resolve(), reject); - break; - case 1 /* Cancel */: - reject(new Error(nls.localize('aborted', "Aborted"))); - break; - } - }); - } } -if (platform.isMacintosh) { - const category = nls.localize('shellCommand', "Shell Command"); - - const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); - workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(InstallAction), `Shell Command: Install \'${product.applicationName}\' command in PATH`, category); - workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(UninstallAction), `Shell Command: Uninstall \'${product.applicationName}\' command from PATH`, category); -} +registerAction2(InstallAction); +registerAction2(UninstallAction); diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts index 1779568eb..87cbcc7d5 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts @@ -21,7 +21,7 @@ import { IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOpti import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -120,7 +120,7 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget { if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_E)) { alert(nls.localize('emergencyConfOn', "Now changing the setting `editor.accessibilitySupport` to 'on'.")); - this._configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER); + this._configurationService.updateValue('editor.accessibilitySupport', 'on'); e.preventDefault(); e.stopPropagation(); diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index d01c06d5e..c3ede411a 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -50,7 +50,7 @@ class DiffEditorHelperContribution extends Disposable implements IDiffEditorCont [{ label: nls.localize('removeTimeout', "Remove limit"), run: () => { - this._configurationService.updateValue('diffEditor.maxComputationTime', 0, ConfigurationTarget.USER); + this._configurationService.updateValue('diffEditor.maxComputationTime', 0); } }], {} diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts index 9b8cf7a37..cb7b1e97b 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts @@ -12,7 +12,7 @@ import { Delayer } from 'vs/base/common/async'; import { KeyCode } from 'vs/base/common/keyCodes'; import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; -import { SimpleButton, findCloseIcon, findNextMatchIcon, findPreviousMatchIcon, findReplaceIcon, findReplaceAllIcon } from 'vs/editor/contrib/find/findWidget'; +import { SimpleButton, findNextMatchIcon, findPreviousMatchIcon, findReplaceIcon, findReplaceAllIcon } from 'vs/editor/contrib/find/findWidget'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -21,6 +21,7 @@ import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/b import { ReplaceInput, IReplaceInputStyles } from 'vs/base/browser/ui/findinput/replaceInput'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; +import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); @@ -146,7 +147,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this.prevBtn = this._register(new SimpleButton({ label: NLS_PREVIOUS_MATCH_BTN_LABEL, - className: findPreviousMatchIcon.classNames, + icon: findPreviousMatchIcon, onTrigger: () => { this.find(true); } @@ -154,7 +155,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this.nextBtn = this._register(new SimpleButton({ label: NLS_NEXT_MATCH_BTN_LABEL, - className: findNextMatchIcon.classNames, + icon: findNextMatchIcon, onTrigger: () => { this.find(false); } @@ -162,7 +163,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { const closeBtn = this._register(new SimpleButton({ label: NLS_CLOSE_BTN_LABEL, - className: findCloseIcon.classNames, + icon: widgetClose, onTrigger: () => { this.hide(); } @@ -220,7 +221,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._replaceBtn = this._register(new SimpleButton({ label: NLS_REPLACE_BTN_LABEL, - className: findReplaceIcon.classNames, + icon: findReplaceIcon, onTrigger: () => { this.replaceOne(); } @@ -229,7 +230,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { // Replace all button this._replaceAllBtn = this._register(new SimpleButton({ label: NLS_REPLACE_ALL_BTN_LABEL, - className: findReplaceAllIcon.classNames, + icon: findReplaceAllIcon, onTrigger: () => { this.replaceAll(); } @@ -452,6 +453,6 @@ registerThemingParticipant((theme, collector) => { const widgetShadowColor = theme.getColor(widgetShadow); if (widgetShadowColor) { - collector.addRule(`.monaco-workbench .simple-fr-find-part-wrapper { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + collector.addRule(`.monaco-workbench .simple-fr-find-part-wrapper { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`); } }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index e15e3ee78..6226e8fb8 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -12,12 +12,13 @@ import { Delayer } from 'vs/base/common/async'; import { KeyCode } from 'vs/base/common/keyCodes'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; -import { SimpleButton, findPreviousMatchIcon, findNextMatchIcon, findCloseIcon } from 'vs/editor/contrib/find/findWidget'; +import { SimpleButton, findPreviousMatchIcon, findNextMatchIcon } from 'vs/editor/contrib/find/findWidget'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput } from 'vs/platform/browser/contextScopedHistoryWidget'; +import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); @@ -94,7 +95,7 @@ export abstract class SimpleFindWidget extends Widget { this.prevBtn = this._register(new SimpleButton({ label: NLS_PREVIOUS_MATCH_BTN_LABEL, - className: findPreviousMatchIcon.classNames, + icon: findPreviousMatchIcon, onTrigger: () => { this.find(true); } @@ -102,7 +103,7 @@ export abstract class SimpleFindWidget extends Widget { this.nextBtn = this._register(new SimpleButton({ label: NLS_NEXT_MATCH_BTN_LABEL, - className: findNextMatchIcon.classNames, + icon: findNextMatchIcon, onTrigger: () => { this.find(false); } @@ -110,7 +111,7 @@ export abstract class SimpleFindWidget extends Widget { const closeBtn = this._register(new SimpleButton({ label: NLS_CLOSE_BTN_LABEL, - className: findCloseIcon.classNames, + icon: widgetClose, onTrigger: () => { this.hide(); } @@ -285,6 +286,6 @@ registerThemingParticipant((theme, collector) => { const widgetShadowColor = theme.getColor(widgetShadow); if (widgetShadowColor) { - collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`); } }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts b/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts index 8268057af..f7a7ea98d 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts @@ -11,7 +11,6 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; /** * Shows a message when opening a large file which has been memory optimized (and features disabled). @@ -24,14 +23,9 @@ export class LargeFileOptimizationsWarner extends Disposable implements IEditorC private readonly _editor: ICodeEditor, @INotificationService private readonly _notificationService: INotificationService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); - // opt-in to syncing - const neverShowAgainId = 'editor.contrib.largeFileOptimizationsWarner'; - storageKeysSyncRegistryService.registerStorageKey({ key: neverShowAgainId, version: 1 }); - this._register(this._editor.onDidChangeModel((e) => { const model = this._editor.getModel(); if (!model) { @@ -61,7 +55,7 @@ export class LargeFileOptimizationsWarner extends Disposable implements IEditorC }); } } - ], { neverShowAgain: { id: neverShowAgainId } }); + ], { neverShowAgain: { id: 'editor.contrib.largeFileOptimizationsWarner' } }); } })); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts index c05ec1cfa..eb4566740 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import { IKeyMods, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IEditor } from 'vs/editor/common/editorCommon'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IRange } from 'vs/editor/common/core/range'; import { AbstractGotoLineQuickAccessProvider } from 'vs/editor/contrib/quickAccess/gotoLineQuickAccess'; @@ -17,6 +16,7 @@ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; export class GotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProvider { @@ -41,10 +41,12 @@ export class GotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProv return this.editorService.activeTextEditorControl; } - protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { + protected gotoLocation(context: IQuickAccessTextEditorContext, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { // Check for sideBySide use if ((options.keyMods.ctrlCmd || options.forceSideBySide) && this.editorService.activeEditor) { + context.restoreViewState?.(); // since we open to the side, restore view state in this editor + this.editorService.openEditor(this.editorService.activeEditor, { selection: options.range, pinned: options.keyMods.alt || this.configuration.openEditorPinned, @@ -54,7 +56,7 @@ export class GotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProv // Otherwise let parent handle it else { - super.gotoLocation(editor, options); + super.gotoLocation(context, options); } } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts index 5f73a423d..bb7642fe7 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts @@ -26,6 +26,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccessProvider { @@ -42,11 +43,12 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess //#region DocumentSymbols (text editor required) - protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable { + protected provideWithTextEditor(context: IQuickAccessTextEditorContext, picker: IQuickPick, token: CancellationToken): IDisposable { if (this.canPickFromTableOfContents()) { return this.doGetTableOfContentsPicks(picker); } - return super.provideWithTextEditor(editor, picker, token); + + return super.provideWithTextEditor(context, picker, token); } private get configuration() { @@ -62,10 +64,12 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess return this.editorService.activeTextEditorControl; } - protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { + protected gotoLocation(context: IQuickAccessTextEditorContext, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { // Check for sideBySide use if ((options.keyMods.ctrlCmd || options.forceSideBySide) && this.editorService.activeEditor) { + context.restoreViewState?.(); // since we open to the side, restore view state in this editor + this.editorService.openEditor(this.editorService.activeEditor, { selection: options.range, pinned: options.keyMods.alt || this.configuration.openEditorPinned, @@ -75,7 +79,7 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess // Otherwise let parent handle it else { - super.gotoLocation(editor, options); + super.gotoLocation(context, options); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index f27703545..76ffc0b60 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -287,8 +287,7 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { ? setting : Object.keys(setting).filter(x => setting[x]); - const codeActionsOnSave = settingItems - .map(x => new CodeActionKind(x)); + const codeActionsOnSave = this.createCodeActionsOnSave(settingItems); if (!Array.isArray(setting)) { codeActionsOnSave.sort((a, b) => { @@ -319,6 +318,15 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { await this.applyOnSaveActions(textEditorModel, codeActionsOnSave, excludedActions, progress, token); } + private createCodeActionsOnSave(settingItems: readonly string[]): CodeActionKind[] { + const kinds = settingItems.map(x => new CodeActionKind(x)); + + // Remove subsets + return kinds.filter(kind => { + return kinds.every(otherKind => otherKind.equals(kind) || !otherKind.contains(kind)); + }); + } + private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], progress: IProgress, token: CancellationToken): Promise { const getActionProgress = new class implements IProgress { diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts index ef2018bcd..858bf7be6 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; @@ -42,7 +42,7 @@ export class ToggleColumnSelectionAction extends Action { public async run(): Promise { const oldValue = this._configurationService.getValue('editor.columnSelection'); const codeEditor = this._getCodeEditor(); - await this._configurationService.updateValue('editor.columnSelection', !oldValue, ConfigurationTarget.USER); + await this._configurationService.updateValue('editor.columnSelection', !oldValue); const newValue = this._configurationService.getValue('editor.columnSelection'); if (!codeEditor || codeEditor !== this._getCodeEditor() || oldValue === newValue || !codeEditor.hasModel()) { return; diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts index d40ee948f..fbd79e000 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; @@ -25,7 +25,7 @@ export class ToggleMinimapAction extends Action { public run(): Promise { const newValue = !this._configurationService.getValue('editor.minimap.enabled'); - return this._configurationService.updateValue('editor.minimap.enabled', newValue, ConfigurationTarget.USER); + return this._configurationService.updateValue('editor.minimap.enabled', newValue); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts index 2c340ac02..36a63179b 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import * as platform from 'vs/base/common/platform'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -33,7 +33,7 @@ export class ToggleMultiCursorModifierAction extends Action { const editorConf = this.configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor'); const newValue: 'ctrlCmd' | 'alt' = (editorConf.multiCursorModifier === 'ctrlCmd' ? 'alt' : 'ctrlCmd'); - return this.configurationService.updateValue(ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, newValue, ConfigurationTarget.USER); + return this.configurationService.updateValue(ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, newValue); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts index cc439ce45..d530f1a87 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; @@ -26,7 +26,7 @@ export class ToggleRenderControlCharacterAction extends Action { public run(): Promise { let newRenderControlCharacters = !this._configurationService.getValue('editor.renderControlCharacters'); - return this._configurationService.updateValue('editor.renderControlCharacters', newRenderControlCharacters, ConfigurationTarget.USER); + return this._configurationService.updateValue('editor.renderControlCharacters', newRenderControlCharacters); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts index 80a26aee4..5a5174b89 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; @@ -34,7 +34,7 @@ export class ToggleRenderWhitespaceAction extends Action { newRenderWhitespace = 'none'; } - return this._configurationService.updateValue('editor.renderWhitespace', newRenderWhitespace, ConfigurationTarget.USER); + return this._configurationService.updateValue('editor.renderWhitespace', newRenderWhitespace); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index 17acd2da4..a0df024be 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -10,33 +10,25 @@ import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { EditorOption, EditorOptions } from 'vs/editor/common/config/editorOptions'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { DefaultSettingsEditorContribution } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Codicon } from 'vs/base/common/codicons'; const transientWordWrapState = 'transientWordWrapState'; const isWordWrapMinifiedKey = 'isWordWrapMinified'; const isDominatedByLongLinesKey = 'isDominatedByLongLines'; -const inDiffEditorKey = 'inDiffEditor'; /** * State written/read by the toggle word wrap action and associated with a particular model. */ interface IWordWrapTransientState { - readonly forceWordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; - readonly forceWordWrapMinified: boolean; -} - -interface IWordWrapState { - readonly configuredWordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded' | undefined; - readonly configuredWordWrapMinified: boolean; - readonly transientState: IWordWrapTransientState | null; + readonly wordWrapOverride: 'on' | 'off'; } /** @@ -49,70 +41,10 @@ export function writeTransientState(model: ITextModel, state: IWordWrapTransient /** * Read (in memory) the word wrap state for a particular model. */ -function readTransientState(model: ITextModel, codeEditorService: ICodeEditorService): IWordWrapTransientState { +function readTransientState(model: ITextModel, codeEditorService: ICodeEditorService): IWordWrapTransientState | null { return codeEditorService.getTransientModelProperty(model, transientWordWrapState); } -function readWordWrapState(model: ITextModel, configurationService: ITextResourceConfigurationService, codeEditorService: ICodeEditorService): IWordWrapState { - const editorConfig = configurationService.getValue(model.uri, 'editor') as { wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; wordWrapMinified: boolean }; - let _configuredWordWrap = editorConfig && (typeof editorConfig.wordWrap === 'string' || typeof editorConfig.wordWrap === 'boolean') ? editorConfig.wordWrap : undefined; - - // Compatibility with old true or false values - if (_configuredWordWrap === true) { - _configuredWordWrap = 'on'; - } else if (_configuredWordWrap === false) { - _configuredWordWrap = 'off'; - } - - const _configuredWordWrapMinified = editorConfig && typeof editorConfig.wordWrapMinified === 'boolean' ? editorConfig.wordWrapMinified : undefined; - const _transientState = readTransientState(model, codeEditorService); - return { - configuredWordWrap: _configuredWordWrap, - configuredWordWrapMinified: (typeof _configuredWordWrapMinified === 'boolean' ? _configuredWordWrapMinified : EditorOptions.wordWrapMinified.defaultValue), - transientState: _transientState - }; -} - -function toggleWordWrap(editor: ICodeEditor, state: IWordWrapState): IWordWrapState { - if (state.transientState) { - // toggle off => go to null - return { - configuredWordWrap: state.configuredWordWrap, - configuredWordWrapMinified: state.configuredWordWrapMinified, - transientState: null - }; - } - - let transientState: IWordWrapTransientState; - - const actualWrappingInfo = editor.getOption(EditorOption.wrappingInfo); - if (actualWrappingInfo.isWordWrapMinified) { - // => wrapping due to minified file - transientState = { - forceWordWrap: 'off', - forceWordWrapMinified: false - }; - } else if (state.configuredWordWrap !== 'off') { - // => wrapping is configured to be on (or some variant) - transientState = { - forceWordWrap: 'off', - forceWordWrapMinified: false - }; - } else { - // => wrapping is configured to be off - transientState = { - forceWordWrap: 'on', - forceWordWrapMinified: state.configuredWordWrapMinified - }; - } - - return { - configuredWordWrap: state.configuredWordWrap, - configuredWordWrapMinified: state.configuredWordWrapMinified, - transientState: transientState - }; -} - const TOGGLE_WORD_WRAP_ID = 'editor.action.toggleWordWrap'; class ToggleWordWrapAction extends EditorAction { @@ -138,14 +70,7 @@ class ToggleWordWrapAction extends EditorAction { if (!editor.hasModel()) { return; } - if (editor.getOption(EditorOption.inDiffEditor)) { - // Cannot change wrapping settings inside the diff editor - const notificationService = accessor.get(INotificationService); - notificationService.info(nls.localize('wordWrap.notInDiffEditor', "Cannot toggle word wrap in a diff editor.")); - return; - } - const textResourceConfigurationService = accessor.get(ITextResourceConfigurationService); const codeEditorService = accessor.get(ICodeEditorService); const model = editor.getModel(); @@ -154,12 +79,21 @@ class ToggleWordWrapAction extends EditorAction { } // Read the current state - const currentState = readWordWrapState(model, textResourceConfigurationService, codeEditorService); + const transientState = readTransientState(model, codeEditorService); + // Compute the new state - const newState = toggleWordWrap(editor, currentState); + let newState: IWordWrapTransientState | null; + if (transientState) { + newState = null; + } else { + const actualWrappingInfo = editor.getOption(EditorOption.wrappingInfo); + const wordWrapOverride = (actualWrappingInfo.wrappingColumn === -1 ? 'on' : 'off'); + newState = { wordWrapOverride }; + } + // Write the new state // (this will cause an event and the controller will apply the state) - writeTransientState(model, newState.transientState, codeEditorService); + writeTransientState(model, newState, codeEditorService); } } @@ -168,93 +102,75 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution public static readonly ID = 'editor.contrib.toggleWordWrapController'; constructor( - private readonly editor: ICodeEditor, - @IContextKeyService readonly contextKeyService: IContextKeyService, - @ITextResourceConfigurationService readonly configurationService: ITextResourceConfigurationService, - @ICodeEditorService readonly codeEditorService: ICodeEditorService + private readonly _editor: ICodeEditor, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService ) { super(); - const options = this.editor.getOptions(); + const options = this._editor.getOptions(); const wrappingInfo = options.get(EditorOption.wrappingInfo); - const isWordWrapMinified = this.contextKeyService.createKey(isWordWrapMinifiedKey, wrappingInfo.isWordWrapMinified); - const isDominatedByLongLines = this.contextKeyService.createKey(isDominatedByLongLinesKey, wrappingInfo.isDominatedByLongLines); - const inDiffEditor = this.contextKeyService.createKey(inDiffEditorKey, options.get(EditorOption.inDiffEditor)); + const isWordWrapMinified = this._contextKeyService.createKey(isWordWrapMinifiedKey, wrappingInfo.isWordWrapMinified); + const isDominatedByLongLines = this._contextKeyService.createKey(isDominatedByLongLinesKey, wrappingInfo.isDominatedByLongLines); let currentlyApplyingEditorConfig = false; - this._register(editor.onDidChangeConfiguration((e) => { - if (!e.hasChanged(EditorOption.wrappingInfo) && !e.hasChanged(EditorOption.inDiffEditor)) { + this._register(_editor.onDidChangeConfiguration((e) => { + if (!e.hasChanged(EditorOption.wrappingInfo)) { return; } - const options = this.editor.getOptions(); + const options = this._editor.getOptions(); const wrappingInfo = options.get(EditorOption.wrappingInfo); isWordWrapMinified.set(wrappingInfo.isWordWrapMinified); isDominatedByLongLines.set(wrappingInfo.isDominatedByLongLines); - inDiffEditor.set(options.get(EditorOption.inDiffEditor)); if (!currentlyApplyingEditorConfig) { // I am not the cause of the word wrap getting changed ensureWordWrapSettings(); } })); - this._register(editor.onDidChangeModel((e) => { + this._register(_editor.onDidChangeModel((e) => { ensureWordWrapSettings(); })); - this._register(codeEditorService.onDidChangeTransientModelProperty(() => { + this._register(_codeEditorService.onDidChangeTransientModelProperty(() => { ensureWordWrapSettings(); })); const ensureWordWrapSettings = () => { - if (this.editor.getContribution(DefaultSettingsEditorContribution.ID)) { + if (this._editor.getContribution(DefaultSettingsEditorContribution.ID)) { // in the settings editor... return; } - if (this.editor.isSimpleWidget) { + if (this._editor.isSimpleWidget) { // in a simple widget... return; } // Ensure correct word wrap settings - const newModel = this.editor.getModel(); + const newModel = this._editor.getModel(); if (!newModel) { return; } - if (this.editor.getOption(EditorOption.inDiffEditor)) { - return; - } - if (!canToggleWordWrap(newModel.uri)) { return; } - // Read current configured values and toggle state - const desiredState = readWordWrapState(newModel, this.configurationService, this.codeEditorService); + const transientState = readTransientState(newModel, this._codeEditorService); // Apply the state try { currentlyApplyingEditorConfig = true; - this._applyWordWrapState(desiredState); + this._applyWordWrapState(transientState); } finally { currentlyApplyingEditorConfig = false; } }; } - private _applyWordWrapState(state: IWordWrapState): void { - if (state.transientState) { - // toggle is on - this.editor.updateOptions({ - wordWrap: state.transientState.forceWordWrap, - wordWrapMinified: state.transientState.forceWordWrapMinified - }); - return; - } - - // toggle is off - this.editor.updateOptions({ - wordWrap: state.configuredWordWrap, - wordWrapMinified: state.configuredWordWrapMinified + private _applyWordWrapState(state: IWordWrapTransientState | null): void { + const wordWrapOverride2 = state ? state.wordWrapOverride : 'inherit'; + this._editor.updateOptions({ + wordWrapOverride2: wordWrapOverride2 }); } } @@ -275,14 +191,11 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_WORD_WRAP_ID, title: nls.localize('unwrapMinified', "Disable wrapping for this file"), - icon: { - id: 'codicon/word-wrap' - } + icon: Codicon.wordWrap }, group: 'navigation', order: 1, when: ContextKeyExpr.and( - ContextKeyExpr.not(inDiffEditorKey), ContextKeyExpr.has(isDominatedByLongLinesKey), ContextKeyExpr.has(isWordWrapMinifiedKey) ) @@ -291,14 +204,12 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_WORD_WRAP_ID, title: nls.localize('wrapMinified', "Enable wrapping for this file"), - icon: { - id: 'codicon/word-wrap' - } + icon: Codicon.wordWrap }, group: 'navigation', order: 1, when: ContextKeyExpr.and( - ContextKeyExpr.not(inDiffEditorKey), + EditorContextKeys.inDiffEditor.negate(), ContextKeyExpr.has(isDominatedByLongLinesKey), ContextKeyExpr.not(isWordWrapMinifiedKey) ) diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts b/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts index c764c2c42..7718e5056 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts @@ -70,7 +70,8 @@ class StartDebugTextMate extends Action { }; await this._hostService.openWindow([{ fileUri: URI.file(pathInTemp) }], { forceNewWindow: true }); const textEditorPane = await this._editorService.openEditor({ - resource: model.uri + resource: model.uri, + options: { pinned: true } }); if (!textEditorPane) { return; diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.ts index 087637541..d544a687a 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import './displayChangeRemeasureFonts'; import './inputClipboardActions'; import './selectionClipboard'; import './sleepResumeRepaintMinimap'; diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts new file mode 100644 index 000000000..efcdd8981 --- /dev/null +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { IDisplayMainService } from 'vs/platform/display/common/displayMainService'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; + +class DisplayChangeRemeasureFonts extends Disposable implements IWorkbenchContribution { + + constructor( + @IMainProcessService mainProcessService: IMainProcessService + ) { + super(); + const displayMainService = createChannelSender(mainProcessService.getChannel('display')); + displayMainService.onDidDisplayChanged(() => { + clearAllFontInfos(); + }); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisplayChangeRemeasureFonts, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 249fd9b10..a059627b0 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -34,6 +34,7 @@ import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commen import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { Codicon } from 'vs/base/common/codicons'; export class CommentNode extends Disposable { private _domNode: HTMLElement; @@ -156,7 +157,7 @@ export class CommentNode extends Disposable { { actionViewItemProvider: action => this.actionViewItemProvider(action as Action), actionRunner: this.actionRunner, - classNames: ['toolbar-toggle-pickReactions', 'codicon', 'codicon-reactions'], + classNames: ['toolbar-toggle-pickReactions', ...Codicon.reactions.classNamesArray], anchorAlignmentProvider: () => AnchorAlignment.RIGHT } ); diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 9e66d94a6..ffab7e07c 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -6,7 +6,6 @@ import * as dom from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action, IAction } from 'vs/base/common/actions'; -import * as arrays from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -32,7 +31,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { contrastBorder, editorForeground, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; -import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions'; import { CommentGlyphWidget } from 'vs/workbench/contrib/comments/browser/commentGlyphWidget'; import { CommentMenus } from 'vs/workbench/contrib/comments/browser/commentMenus'; @@ -48,9 +47,14 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { PANEL_BORDER } from 'vs/workbench/common/theme'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { Codicon } from 'vs/base/common/codicons'; + + +const collapseIcon = registerIcon('review-comment-collapse', Codicon.chevronUp, nls.localize('collapseIcon', 'Icon to collapse a review comment.')); export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration'; -const COLLAPSE_ACTION_CLASS = 'expand-review-action codicon-chevron-up'; +const COLLAPSE_ACTION_CLASS = 'expand-review-action ' + ThemeIcon.asClassName(collapseIcon); const COMMENT_SCHEME = 'comment'; @@ -712,10 +716,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget label = this._commentThread.label; if (label === undefined) { - if (this._commentThread.comments && this._commentThread.comments.length) { - const participantsList = this._commentThread.comments.filter(arrays.uniqueFilter(comment => comment.userName)).map(comment => `@${comment.userName}`).join(', '); - label = nls.localize('commentThreadParticipants', "Participants: {0}", participantsList); - } else { + if (!(this._commentThread.comments && this._commentThread.comments.length)) { label = nls.localize('startThread', "Start discussion"); } } @@ -884,13 +885,18 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } private _applyTheme(theme: IColorTheme) { - const borderColor = theme.getColor(peekViewBorder) || Color.transparent; + const borderColor = theme.getColor(peekViewBorder); this.style({ - arrowColor: borderColor, - frameColor: borderColor + arrowColor: borderColor || Color.transparent, + frameColor: borderColor || Color.transparent }); const content: string[] = []; + + if (borderColor) { + content.push(`.monaco-editor .review-widget > .body { border-top: 1px solid ${borderColor} }`); + } + const linkColor = theme.getColor(textLinkForeground); if (linkColor) { content.push(`.monaco-editor .review-widget .body .comment-body a { color: ${linkColor} }`); @@ -945,7 +951,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget const fontInfo = this.editor.getOption(EditorOption.fontInfo); content.push(`.monaco-editor .review-widget .body code { - font-family: ${fontInfo.fontFamily}; + font-family: '${fontInfo.fontFamily}'; font-size: ${fontInfo.fontSize}px; font-weight: ${fontInfo.fontWeight}; }`); diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index 8d77ef60e..5ed011a2c 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -34,7 +34,6 @@ export class CommentsPanel extends ViewPane { private tree!: CommentsList; private treeContainer!: HTMLElement; private messageBoxContainer!: HTMLElement; - private messageBox!: HTMLElement; private commentsModel!: CommentsModel; private collapseAllAction?: IAction; @@ -85,6 +84,18 @@ export class CommentsPanel extends ViewPane { this.renderComments(); } + public focus(): void { + if (this.tree && this.tree.getHTMLElement() === document.activeElement) { + return; + } + + if (!this.commentsModel.hasCommentThreads() && this.messageBoxContainer) { + this.messageBoxContainer.focus(); + } else if (this.tree) { + this.tree.domFocus(); + } + } + private applyStyles(styleElement: HTMLStyleElement) { const content: string[] = []; @@ -114,8 +125,8 @@ export class CommentsPanel extends ViewPane { private async renderComments(): Promise { this.treeContainer.classList.toggle('hidden', !this.commentsModel.hasCommentThreads()); - await this.tree.setInput(this.commentsModel); this.renderMessage(); + await this.tree.setInput(this.commentsModel); } public getActions(): IAction[] { @@ -138,12 +149,11 @@ export class CommentsPanel extends ViewPane { private createMessageBox(parent: HTMLElement): void { this.messageBoxContainer = dom.append(parent, dom.$('.message-box-container')); - this.messageBox = dom.append(this.messageBoxContainer, dom.$('span')); - this.messageBox.setAttribute('tabindex', '0'); + this.messageBoxContainer.setAttribute('tabIndex', '0'); } private renderMessage(): void { - this.messageBox.textContent = this.commentsModel.getMessage(); + this.messageBoxContainer.textContent = this.commentsModel.getMessage(); this.messageBoxContainer.classList.toggle('hidden', this.commentsModel.hasCommentThreads()); } @@ -231,18 +241,23 @@ export class CommentsPanel extends ViewPane { return true; } - private refresh(): void { + private async refresh(): Promise { if (this.isVisible()) { if (this.collapseAllAction) { this.collapseAllAction.enabled = this.commentsModel.hasCommentThreads(); } this.treeContainer.classList.toggle('hidden', !this.commentsModel.hasCommentThreads()); - this.tree.updateChildren().then(() => { - this.renderMessage(); - }, (e) => { - console.log(e); - }); + this.renderMessage(); + await this.tree.updateChildren(); + + if (this.tree.getSelection().length === 0 && this.commentsModel.hasCommentThreads()) { + const firstComment = this.commentsModel.resourceCommentThreads[0].commentThreads[0]; + if (firstComment) { + this.tree.setFocus([firstComment]); + this.tree.setSelection([firstComment]); + } + } } } diff --git a/src/vs/workbench/contrib/comments/browser/media/panel.css b/src/vs/workbench/contrib/comments/browser/media/panel.css index 1633bd41c..b931b6cdb 100644 --- a/src/vs/workbench/contrib/comments/browser/media/panel.css +++ b/src/vs/workbench/contrib/comments/browser/media/panel.css @@ -48,10 +48,7 @@ .comments-panel .comments-panel-container .message-box-container { line-height: 22px; padding-left: 20px; -} - -.comments-panel .comments-panel-container .message-box-container span:focus { - outline: none; + height: inherit; } .comments-panel .comments-panel-container .tree-container .count-badge-wrapper { @@ -61,4 +58,4 @@ .comments-panel .comments-panel-container .tree-container .comment-container { line-height: 22px; margin-right: 5px; -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/comments/common/commentModel.ts b/src/vs/workbench/contrib/comments/common/commentModel.ts index 0dc8a2390..e56c22677 100644 --- a/src/vs/workbench/contrib/comments/common/commentModel.ts +++ b/src/vs/workbench/contrib/comments/common/commentModel.ts @@ -134,7 +134,7 @@ export class CommentsModel { public getMessage(): string { if (!this.resourceCommentThreads.length) { - return localize('noComments', "There are no comments on this review."); + return localize('noComments', "There are no comments in this workspace yet."); } else { return ''; } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 3bcaaacdb..c57aa6dd3 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { coalesce, distinct } from 'vs/base/common/arrays'; +import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; import { Lazy } from 'vs/base/common/lazy'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -173,7 +174,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ : undefined, detail: editorDescriptor.providerDisplayName, buttons: resourceExt ? [{ - iconClass: 'codicon-settings-gear', + iconClass: Codicon.settingsGear.classNames, tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) }] : undefined })); @@ -447,6 +448,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo constructor( @IEditorService private readonly editorService: IEditorService, @ICustomEditorService private readonly customEditorService: ICustomEditorService, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); @@ -651,7 +653,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo if (modifiedOverride || originalOverride) { return { override: (async () => { - const input = new DiffEditorInput(editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true); + const input = this.instantiationService.createInstance(DiffEditorInput, editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true); return this.editorService.openEditor(input, { ...options, override: false }, group); })(), }; diff --git a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts index d61f9874f..98fcdb9b9 100644 --- a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts +++ b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts @@ -8,12 +8,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Memento } from 'vs/workbench/common/memento'; import { CustomEditorDescriptor, CustomEditorInfo, CustomEditorPriority } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { customEditorsExtensionPoint, ICustomEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/common/extensionPoint'; -import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { DEFAULT_EDITOR_ID } from 'vs/workbench/services/editor/common/editorOpenWith'; +import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in"); @@ -40,7 +40,7 @@ export class ContributedCustomEditors extends Disposable { this._memento = new Memento(ContributedCustomEditors.CUSTOM_EDITORS_STORAGE_ID, storageService); - const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); for (const info of (mementoObject[ContributedCustomEditors.CUSTOM_EDITORS_ENTRY_ID] || []) as CustomEditorDescriptor[]) { this.add(new CustomEditorInfo(info)); } @@ -68,7 +68,7 @@ export class ContributedCustomEditors extends Disposable { } } - const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); mementoObject[ContributedCustomEditors.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._editors.values()); this._memento.saveMemento(); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index bd7d28ee0..966dc83e5 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -22,7 +22,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { MarkdownString } from 'vs/base/common/htmlContent'; -import { getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView'; +import { getBreakpointMessageAndIcon } from 'vs/workbench/contrib/debug/browser/breakpointsView'; import { generateUuid } from 'vs/base/common/uuid'; import { memoize } from 'vs/base/common/decorators'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -32,9 +32,10 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { isSafari } from 'vs/base/browser/browser'; -import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { ILabelService } from 'vs/platform/label/common/label'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; @@ -46,7 +47,7 @@ interface IBreakpointDecoration { } const breakpointHelperDecoration: IModelDecorationOptions = { - glyphMarginClassName: 'codicon-debug-hint', + glyphMarginClassName: ThemeIcon.asClassName(icons.debugBreakpointHint), stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges }; @@ -72,7 +73,7 @@ export function createBreakpointDecorations(model: ITextModel, breakpoints: Read } function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoint, state: State, breakpointsActivated: boolean, showBreakpointsInOverviewRuler: boolean): IModelDecorationOptions { - const { className, message } = getBreakpointMessageAndClassName(state, breakpointsActivated, breakpoint, undefined); + const { icon, message } = getBreakpointMessageAndIcon(state, breakpointsActivated, breakpoint, undefined); let glyphMarginHoverMessage: MarkdownString | undefined; if (message) { @@ -94,7 +95,7 @@ function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoi const renderInline = breakpoint.column && (breakpoint.column > model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber)); return { - glyphMarginClassName: `${className}`, + glyphMarginClassName: ThemeIcon.asClassName(icon), glyphMarginHoverMessage, stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, beforeContentClassName: renderInline ? `debug-breakpoint-placeholder` : undefined, @@ -174,7 +175,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi private registerListeners(): void { this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => { - if (!this.debugService.getConfigurationManager().hasDebuggers()) { + if (!this.debugService.getAdapterManager().hasDebuggers()) { return; } @@ -183,7 +184,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { return; } - const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(model); + const canSetBreakpoints = this.debugService.canSetBreakpointsIn(model); const lineNumber = e.target.position.lineNumber; const uri = model.uri; @@ -252,13 +253,13 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi * 2. When users click on line numbers, the breakpoint hint displays immediately, however it doesn't create the breakpoint unless users click on the left gutter. On a touch screen, it's hard to click on that small area. */ this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => { - if (!this.debugService.getConfigurationManager().hasDebuggers()) { + if (!this.debugService.getAdapterManager().hasDebuggers()) { return; } let showBreakpointHintAtLineNumber = -1; const model = this.editor.getModel(); - if (model && e.target.position && (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) && this.debugService.getConfigurationManager().canSetBreakpointsIn(model) && + if (model && e.target.position && (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) && this.debugService.canSetBreakpointsIn(model) && this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { const data = e.target.detail as IMarginData; if (!data.isAfterLines) { @@ -453,9 +454,9 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi // Candidate decoration has a breakpoint attached when a breakpoint is already at that location and we did not yet set a decoration there // In practice this happens for the first breakpoint that was set on a line // We could have also rendered this first decoration as part of desiredBreakpointDecorations however at that moment we have no location information - const cssClass = candidate.breakpoint ? getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), candidate.breakpoint, this.labelService).className : 'codicon-debug-breakpoint-disabled'; + const icon = candidate.breakpoint ? getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), candidate.breakpoint, this.labelService).icon : icons.debugBreakpointDisabled; const contextMenuActions = () => this.getContextMenuActions(candidate.breakpoint ? [candidate.breakpoint] : [], activeCodeEditor.getModel().uri, candidate.range.startLineNumber, candidate.range.startColumn); - const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, cssClass, candidate.breakpoint, this.debugService, this.contextMenuService, contextMenuActions); + const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, ThemeIcon.asClassName(icon), candidate.breakpoint, this.debugService, this.contextMenuService, contextMenuActions); return { decorationId, @@ -575,9 +576,8 @@ class InlineBreakpointWidget implements IContentWidget, IDisposable { private create(cssClass: string | null | undefined): void { this.domNode = $('.inline-breakpoint-widget'); - this.domNode.classList.add('codicon'); if (cssClass) { - this.domNode.classList.add(cssClass); + this.domNode.classList.add(...cssClass.split(' ')); } this.toDispose.push(dom.addDisposableListener(this.domNode, dom.EventType.CLICK, async e => { if (this.breakpoint) { @@ -645,15 +645,15 @@ registerThemingParticipant((theme, collector) => { const debugIconBreakpointColor = theme.getColor(debugIconBreakpointForeground); if (debugIconBreakpointColor) { collector.addRule(` - .monaco-workbench .codicon-debug-breakpoint, - .monaco-workbench .codicon-debug-breakpoint-conditional, - .monaco-workbench .codicon-debug-breakpoint-log, - .monaco-workbench .codicon-debug-breakpoint-function, - .monaco-workbench .codicon-debug-breakpoint-data, - .monaco-workbench .codicon-debug-breakpoint-unsupported, - .monaco-workbench .codicon-debug-hint:not([class*='codicon-debug-breakpoint']):not([class*='codicon-debug-stackframe']), - .monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe-focused::after, - .monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe::after { + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpoint)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointConditional)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointLog)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointFunction)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointData)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointUnsupported)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointHint)}:not([class*='codicon-debug-breakpoint']):not([class*='codicon-debug-stackframe']), + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpoint)}${ThemeIcon.asCSSSelector(icons.debugStackframeFocused)}::after, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpoint)}${ThemeIcon.asCSSSelector(icons.debugStackframe)}::after { color: ${debugIconBreakpointColor} !important; } `); @@ -680,7 +680,7 @@ registerThemingParticipant((theme, collector) => { const debugIconBreakpointCurrentStackframeForegroundColor = theme.getColor(debugIconBreakpointCurrentStackframeForeground); if (debugIconBreakpointCurrentStackframeForegroundColor) { collector.addRule(` - .monaco-workbench .codicon-debug-stackframe, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStackframe)}, .monaco-editor .debug-top-stack-frame-column::before { color: ${debugIconBreakpointCurrentStackframeForegroundColor} !important; } @@ -690,7 +690,7 @@ registerThemingParticipant((theme, collector) => { const debugIconBreakpointStackframeFocusedColor = theme.getColor(debugIconBreakpointStackframeForeground); if (debugIconBreakpointStackframeFocusedColor) { collector.addRule(` - .monaco-workbench .codicon-debug-stackframe-focused { + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStackframeFocused)} { color: ${debugIconBreakpointStackframeFocusedColor} !important; } `); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 97636b20c..7fa72c43f 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -13,7 +13,7 @@ import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAl import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Constants } from 'vs/base/common/uint'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IListVirtualDelegate, IListContextMenuEvent, IListRenderer } from 'vs/base/browser/ui/list/list'; @@ -37,6 +37,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; @@ -93,6 +94,7 @@ export class BreakpointsView extends ViewPane { this.list = >this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [ this.instantiationService.createInstance(BreakpointsRenderer), new ExceptionBreakpointsRenderer(this.debugService), + new ExceptionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService), this.instantiationService.createInstance(FunctionBreakpointsRenderer), this.instantiationService.createInstance(DataBreakpointsRenderer), new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService, this.labelService) @@ -122,7 +124,7 @@ export class BreakpointsView extends ViewPane { const resourceNavigator = this._register(new ListResourceNavigator(this.list, { configurationService: this.configurationService })); this._register(resourceNavigator.onDidOpen(async e => { - if (e.element === null) { + if (!e.element) { return; } @@ -130,14 +132,12 @@ export class BreakpointsView extends ViewPane { return; } - const element = this.list.element(e.element); - - if (element instanceof Breakpoint) { - openBreakpointSource(element, e.sideBySide, e.editorOptions.preserveFocus || false, this.debugService, this.editorService); + if (e.element instanceof Breakpoint) { + openBreakpointSource(e.element, e.sideBySide, e.editorOptions.preserveFocus || false, e.editorOptions.pinned || !e.editorOptions.preserveFocus, this.debugService, this.editorService); } - if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) { + if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && e.element instanceof FunctionBreakpoint && e.element !== this.debugService.getViewModel().getSelectedBreakpoint()) { // double click - this.debugService.getViewModel().setSelectedFunctionBreakpoint(element); + this.debugService.getViewModel().setSelectedBreakpoint(e.element); this.onBreakpointsChange(); } })); @@ -188,38 +188,48 @@ export class BreakpointsView extends ViewPane { const actions: IAction[] = []; const element = e.element; - const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint"); - if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) { - actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, async () => { - if (element instanceof Breakpoint) { - const editor = await openBreakpointSource(element, false, false, this.debugService, this.editorService); - if (editor) { - const codeEditor = editor.getControl(); - if (isCodeEditor(codeEditor)) { - codeEditor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column); - } - } - } else { - this.debugService.getViewModel().setSelectedFunctionBreakpoint(element); + if (element instanceof ExceptionBreakpoint) { + if (element.supportsCondition) { + actions.push(new Action('workbench.action.debug.editExceptionBreakpointCondition', nls.localize('editCondition', "Edit Condition"), '', true, async () => { + this.debugService.getViewModel().setSelectedBreakpoint(element); this.onBreakpointsChange(); - } - })); + })); + } + } else { + const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint"); + if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) { + actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, async () => { + if (element instanceof Breakpoint) { + const editor = await openBreakpointSource(element, false, false, true, this.debugService, this.editorService); + if (editor) { + const codeEditor = editor.getControl(); + if (isCodeEditor(codeEditor)) { + codeEditor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column); + } + } + } else { + this.debugService.getViewModel().setSelectedBreakpoint(element); + this.onBreakpointsChange(); + } + })); + actions.push(new Separator()); + } + + + actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService)); + + if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length >= 1) { + actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); + actions.push(new Separator()); + + actions.push(new EnableAllBreakpointsAction(EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); + actions.push(new DisableAllBreakpointsAction(DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); + } + actions.push(new Separator()); + actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService)); } - actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService)); - - if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) { - actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); - actions.push(new Separator()); - - actions.push(new EnableAllBreakpointsAction(EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); - actions.push(new DisableAllBreakpointsAction(DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); - } - - actions.push(new Separator()); - actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService)); - this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => actions, @@ -285,7 +295,7 @@ class BreakpointsDelegate implements IListVirtualDelegate { return BreakpointsRenderer.ID; } if (element instanceof FunctionBreakpoint) { - const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint(); + const selected = this.debugService.getViewModel().getSelectedBreakpoint(); if (!element.name || (selected && selected.getId() === element.getId())) { return FunctionBreakpointInputRenderer.ID; } @@ -293,6 +303,10 @@ class BreakpointsDelegate implements IListVirtualDelegate { return FunctionBreakpointsRenderer.ID; } if (element instanceof ExceptionBreakpoint) { + const selected = this.debugService.getViewModel().getSelectedBreakpoint(); + if (selected && selected.getId() === element.getId()) { + return ExceptionBreakpointInputRenderer.ID; + } return ExceptionBreakpointsRenderer.ID; } if (element instanceof DataBreakpoint) { @@ -320,7 +334,11 @@ interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData { filePath: HTMLElement; } -interface IInputTemplateData { +interface IExceptionBreakpointTemplateData extends IBaseBreakpointTemplateData { + condition: HTMLElement; +} + +interface IFunctionBreakpointInputTemplateData { inputBox: InputBox; checkbox: HTMLInputElement; icon: HTMLElement; @@ -329,6 +347,14 @@ interface IInputTemplateData { toDispose: IDisposable[]; } +interface IExceptionBreakpointInputTemplateData { + inputBox: InputBox; + checkbox: HTMLInputElement; + breakpoint: IExceptionBreakpoint; + reactedOnEvent: boolean; + toDispose: IDisposable[]; +} + class BreakpointsRenderer implements IListRenderer { constructor( @@ -379,8 +405,8 @@ class BreakpointsRenderer implements IListRenderer { +class ExceptionBreakpointsRenderer implements IListRenderer { constructor( private debugService: IDebugService @@ -408,8 +434,8 @@ class ExceptionBreakpointsRenderer implements IListRenderer { +class FunctionBreakpointInputRenderer implements IListRenderer { constructor( private debugService: IDebugService, @@ -567,8 +596,8 @@ class FunctionBreakpointInputRenderer implements IListRenderer { if (!template.reactedOnEvent) { template.reactedOnEvent = true; - this.debugService.getViewModel().setSelectedFunctionBreakpoint(undefined); + this.debugService.getViewModel().setSelectedBreakpoint(undefined); if (inputBox.value && (renamed || template.breakpoint.name)) { this.debugService.renameFunctionBreakpoint(template.breakpoint.getId(), renamed ? inputBox.value : template.breakpoint.name); } else { @@ -619,12 +648,12 @@ class FunctionBreakpointInputRenderer implements IListRenderer { + + constructor( + private debugService: IDebugService, + private contextViewService: IContextViewService, + private themeService: IThemeService + ) { + // noop + } + + static readonly ID = 'exceptionbreakpointinput'; + + get templateId() { + return ExceptionBreakpointInputRenderer.ID; + } + + renderTemplate(container: HTMLElement): IExceptionBreakpointInputTemplateData { + const template: IExceptionBreakpointInputTemplateData = Object.create(null); + + const breakpoint = dom.append(container, $('.breakpoint')); + breakpoint.classList.add('exception'); + template.checkbox = createCheckbox(); + + dom.append(breakpoint, template.checkbox); + const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer')); + const inputBox = new InputBox(inputBoxContainer, this.contextViewService, { + placeholder: nls.localize('exceptionBreakpointPlaceholder', "Break when expression evaluates to true"), + ariaLabel: nls.localize('exceptionBreakpointAriaLabel', "Type exception breakpoint condition") + }); + const styler = attachInputBoxStyler(inputBox, this.themeService); + const toDispose: IDisposable[] = [inputBox, styler]; + + const wrapUp = (success: boolean) => { + if (!template.reactedOnEvent) { + template.reactedOnEvent = true; + this.debugService.getViewModel().setSelectedBreakpoint(undefined); + let newCondition = template.breakpoint.condition; + if (success) { + newCondition = inputBox.value !== '' ? inputBox.value : undefined; + } + this.debugService.setExceptionBreakpointCondition(template.breakpoint, newCondition); + } + }; + + toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => { + const isEscape = e.equals(KeyCode.Escape); + const isEnter = e.equals(KeyCode.Enter); + if (isEscape || isEnter) { + e.preventDefault(); + e.stopPropagation(); + wrapUp(isEnter); + } + })); + toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => { + // Need to react with a timeout on the blur event due to possible concurent splices #56443 + setTimeout(() => { + wrapUp(true); + }); + })); + + template.inputBox = inputBox; + template.toDispose = toDispose; + return template; + } + + renderElement(exceptionBreakpoint: ExceptionBreakpoint, _index: number, data: IExceptionBreakpointInputTemplateData): void { + data.breakpoint = exceptionBreakpoint; + data.reactedOnEvent = false; + data.checkbox.checked = exceptionBreakpoint.enabled; + data.checkbox.disabled = true; + data.inputBox.value = exceptionBreakpoint.condition || ''; + setTimeout(() => { + data.inputBox.focus(); + data.inputBox.select(); + }, 0); + } + + disposeTemplate(templateData: IExceptionBreakpointInputTemplateData): void { dispose(templateData.toDispose); } } @@ -664,14 +775,14 @@ class BreakpointsAccessibilityProvider implements IListAccessibilityProvider { +export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, pinned: boolean, debugService: IDebugService, editorService: IEditorService): Promise { if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) { return Promise.resolve(undefined); } @@ -695,17 +806,17 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea selection, revealIfOpened: true, selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport, - pinned: !preserveFocus + pinned } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } -export function getBreakpointMessageAndClassName(state: State, breakpointsActivated: boolean, breakpoint: IBreakpoint | IFunctionBreakpoint | IDataBreakpoint, labelService?: ILabelService): { message?: string, className: string } { +export function getBreakpointMessageAndIcon(state: State, breakpointsActivated: boolean, breakpoint: IBreakpoint | IFunctionBreakpoint | IDataBreakpoint, labelService?: ILabelService): { message?: string, icon: ThemeIcon } { const debugActive = state === State.Running || state === State.Stopped; if (!breakpoint.enabled || !breakpointsActivated) { return { - className: breakpoint instanceof DataBreakpoint ? 'codicon-debug-breakpoint-data-disabled' : breakpoint instanceof FunctionBreakpoint ? 'codicon-debug-breakpoint-function-disabled' : breakpoint.logMessage ? 'codicon-debug-breakpoint-log-disabled' : 'codicon-debug-breakpoint-disabled', + icon: breakpoint instanceof DataBreakpoint ? icons.debugBreakpointDataDisabled : breakpoint instanceof FunctionBreakpoint ? icons.debugBreakpointFunctionDisabled : breakpoint.logMessage ? icons.debugBreakpointLogDisabled : icons.debugBreakpointDisabled, message: breakpoint.logMessage ? nls.localize('disabledLogpoint', "Disabled Logpoint") : nls.localize('disabledBreakpoint', "Disabled Breakpoint"), }; } @@ -715,7 +826,7 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva }; if (debugActive && !breakpoint.verified) { return { - className: breakpoint instanceof DataBreakpoint ? 'codicon-debug-breakpoint-data-unverified' : breakpoint instanceof FunctionBreakpoint ? 'codicon-debug-breakpoint-function-unverified' : breakpoint.logMessage ? 'codicon-debug-breakpoint-log-unverified' : 'codicon-debug-breakpoint-unverified', + icon: breakpoint instanceof DataBreakpoint ? icons.debugBreakpointDataUnverified : breakpoint instanceof FunctionBreakpoint ? icons.debugBreakpointFunctionUnverified : breakpoint.logMessage ? icons.debugBreakpointLogUnverified : icons.debugBreakpointUnverified, message: ('message' in breakpoint && breakpoint.message) ? breakpoint.message : (breakpoint.logMessage ? nls.localize('unverifiedLogpoint', "Unverified Logpoint") : nls.localize('unverifiedBreakopint', "Unverified Breakpoint")), }; } @@ -723,13 +834,13 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva if (breakpoint instanceof FunctionBreakpoint) { if (!breakpoint.supported) { return { - className: 'codicon-debug-breakpoint-function-unverified', + icon: icons.debugBreakpointFunctionUnverified, message: nls.localize('functionBreakpointUnsupported', "Function breakpoints not supported by this debug type"), }; } return { - className: 'codicon-debug-breakpoint-function', + icon: icons.debugBreakpointFunction, message: breakpoint.message || nls.localize('functionBreakpoint', "Function Breakpoint") }; } @@ -737,13 +848,13 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva if (breakpoint instanceof DataBreakpoint) { if (!breakpoint.supported) { return { - className: 'codicon-debug-breakpoint-data-unverified', + icon: icons.debugBreakpointDataUnverified, message: nls.localize('dataBreakpointUnsupported', "Data breakpoints not supported by this debug type"), }; } return { - className: 'codicon-debug-breakpoint-data', + icon: icons.debugBreakpointData, message: breakpoint.message || nls.localize('dataBreakpoint', "Data Breakpoint") }; } @@ -753,7 +864,7 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva if (!breakpoint.supported) { return { - className: 'codicon-debug-breakpoint-unsupported', + icon: icons.debugBreakpointUnsupported, message: nls.localize('breakpointUnsupported', "Breakpoints of this type are not supported by the debugger"), }; } @@ -762,21 +873,21 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva messages.push(nls.localize('logMessage', "Log Message: {0}", breakpoint.logMessage)); } if (breakpoint.condition) { - messages.push(nls.localize('expression', "Expression: {0}", breakpoint.condition)); + messages.push(nls.localize('expression', "Expression condition: {0}", breakpoint.condition)); } if (breakpoint.hitCondition) { messages.push(nls.localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition)); } return { - className: breakpoint.logMessage ? 'codicon-debug-breakpoint-log' : 'codicon-debug-breakpoint-conditional', + icon: breakpoint.logMessage ? icons.debugBreakpointLog : icons.debugBreakpointConditional, message: appendMessage(messages.join('\n')) }; } const message = ('message' in breakpoint && breakpoint.message) ? breakpoint.message : breakpoint instanceof Breakpoint && labelService ? labelService.getUriLabel(breakpoint.uri) : nls.localize('breakpoint', "Breakpoint"); return { - className: 'codicon-debug-breakpoint', + icon: icons.debugBreakpoint, message }; } diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts index ec6bdd2a5..9fd5d78b0 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -5,9 +5,9 @@ import { Constants } from 'vs/base/common/uint'; import { Range, IRange } from 'vs/editor/common/core/range'; -import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model'; +import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions, OverviewRulerLane } from 'vs/editor/common/model'; import { IDebugService, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; import { Event } from 'vs/base/common/event'; @@ -16,17 +16,28 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { distinct } from 'vs/base/common/arrays'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons'; +const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#ffff0033' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.')); +const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#7abd7a4d' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.')); const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; // we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement. const TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = { - glyphMarginClassName: 'codicon-debug-stackframe', - stickiness + glyphMarginClassName: ThemeIcon.asClassName(debugStackframe), + stickiness, + overviewRuler: { + position: OverviewRulerLane.Full, + color: themeColorFromId(topStackFrameColor) + } }; const FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = { - glyphMarginClassName: 'codicon-debug-stackframe-focused', - stickiness + glyphMarginClassName: ThemeIcon.asClassName(debugStackframeFocused), + stickiness, + overviewRuler: { + position: OverviewRulerLane.Full, + color: themeColorFromId(focusedStackFrameColor) + } }; const TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = { isWholeLine: true, @@ -152,6 +163,3 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`); } }); - -const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#ffff0033' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.')); -const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#7abd7a4d' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.')); diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index c7d8774c8..70851479d 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -36,7 +36,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { attachStylerCallback } from 'vs/platform/theme/common/styler'; @@ -46,6 +46,7 @@ import { posix } from 'vs/base/common/path'; import { ITreeCompressionDelegate } from 'vs/base/browser/ui/tree/asyncDataTree'; import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; @@ -206,7 +207,7 @@ export class CallStackView extends ViewPane { getActions(): IAction[] { if (this.stateMessage.hidden) { - return [new CollapseAction(() => this.tree, true, 'explorer-action codicon-collapse-all')]; + return [new CollapseAction(() => this.tree, true, 'explorer-action ' + ThemeIcon.asClassName(icons.debugCollapseAll))]; } return []; @@ -501,7 +502,7 @@ class SessionsRenderer implements ICompressibleTreeRenderer { + const action = new Action('debug.callStack.restartFrame', nls.localize('restartFrame', "Restart Frame"), ThemeIcon.asClassName(icons.debugRestartFrame), true, async () => { try { await stackFrame.restart(); } catch (e) { @@ -918,7 +919,7 @@ class CallStackDataSource implements IAsyncDataSource callStack.length && callStack.length > 1) { + if (!thread.reachedEndOfCallStack && thread.stoppedDetails) { callStack = callStack.concat([new ThreadAndSessionIds(thread.session.getId(), thread.threadId)]); } @@ -994,7 +995,7 @@ class StopAction extends Action { private readonly session: IDebugSession, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${STOP_ID}`, STOP_LABEL, 'debug-action codicon-debug-stop'); + super(`action.${STOP_ID}`, STOP_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStop)); } public run(): Promise { @@ -1008,7 +1009,7 @@ class DisconnectAction extends Action { private readonly session: IDebugSession, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${DISCONNECT_ID}`, DISCONNECT_LABEL, 'debug-action codicon-debug-disconnect'); + super(`action.${DISCONNECT_ID}`, DISCONNECT_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugDisconnect)); } public run(): Promise { @@ -1022,7 +1023,7 @@ class RestartAction extends Action { private readonly session: IDebugSession, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${RESTART_SESSION_ID}`, RESTART_LABEL, 'debug-action codicon-debug-restart'); + super(`action.${RESTART_SESSION_ID}`, RESTART_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugRestart)); } public run(): Promise { @@ -1036,7 +1037,7 @@ class StepOverAction extends Action { private readonly thread: IThread, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${STEP_OVER_ID}`, STEP_OVER_LABEL, 'debug-action codicon-debug-step-over', thread.stopped); + super(`action.${STEP_OVER_ID}`, STEP_OVER_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStepOver), thread.stopped); } public run(): Promise { @@ -1050,7 +1051,7 @@ class StepIntoAction extends Action { private readonly thread: IThread, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${STEP_INTO_ID}`, STEP_INTO_LABEL, 'debug-action codicon-debug-step-into', thread.stopped); + super(`action.${STEP_INTO_ID}`, STEP_INTO_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStepInto), thread.stopped); } public run(): Promise { @@ -1064,7 +1065,7 @@ class StepOutAction extends Action { private readonly thread: IThread, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${STEP_OUT_ID}`, STEP_OUT_LABEL, 'debug-action codicon-debug-step-out', thread.stopped); + super(`action.${STEP_OUT_ID}`, STEP_OUT_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStepOut), thread.stopped); } public run(): Promise { @@ -1078,7 +1079,7 @@ class PauseAction extends Action { private readonly thread: IThread, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${PAUSE_ID}`, PAUSE_LABEL, 'debug-action codicon-debug-pause', !thread.stopped); + super(`action.${PAUSE_ID}`, PAUSE_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugPause), !thread.stopped); } public run(): Promise { @@ -1092,7 +1093,7 @@ class ContinueAction extends Action { private readonly thread: IThread, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${CONTINUE_ID}`, CONTINUE_LABEL, 'debug-action codicon-debug-continue', thread.stopped); + super(`action.${CONTINUE_ID}`, CONTINUE_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugContinue), thread.stopped); } public run(): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index a26cea18b..9438ac11c 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -49,10 +49,10 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl import { StartDebugQuickAccessProvider } from 'vs/workbench/contrib/debug/browser/debugQuickAccess'; import { DebugProgressContribution } from 'vs/workbench/contrib/debug/browser/debugProgress'; import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debugTitle'; -import { Codicon } from 'vs/base/common/codicons'; import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors'; import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution'; import { FileAccess } from 'vs/base/common/network'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); const debugCategory = nls.localize('debugCategory', "Debug"); @@ -153,16 +153,16 @@ function registerCommandsAndActions(): void { }); }; - registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, 10, { id: 'codicon/debug-continue' }, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 10, { id: 'codicon/debug-pause' }, CONTEXT_DEBUG_STATE.notEqualsTo('stopped'), CONTEXT_DEBUG_STATE.isEqualTo('running')); - registerDebugToolBarItem(STOP_ID, STOP_LABEL, 70, { id: 'codicon/debug-stop' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated()); - registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, 70, { id: 'codicon/debug-disconnect' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH); - registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, { id: 'codicon/debug-step-over' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(STEP_INTO_ID, STEP_INTO_LABEL, 30, { id: 'codicon/debug-step-into' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(STEP_OUT_ID, STEP_OUT_LABEL, 40, { id: 'codicon/debug-step-out' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(RESTART_SESSION_ID, RESTART_LABEL, 60, { id: 'codicon/debug-restart' }); - registerDebugToolBarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), 50, { id: 'codicon/debug-step-back' }, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), 60, { id: 'codicon/debug-reverse-continue' }, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, 10, icons.debugContinue, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 10, icons.debugPause, CONTEXT_DEBUG_STATE.notEqualsTo('stopped'), CONTEXT_DEBUG_STATE.isEqualTo('running')); + registerDebugToolBarItem(STOP_ID, STOP_LABEL, 70, icons.debugStop, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated()); + registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, 70, icons.debugDisconnect, CONTEXT_FOCUSED_SESSION_IS_ATTACH); + registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, icons.debugStepOver, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(STEP_INTO_ID, STEP_INTO_LABEL, 30, icons.debugStepInto, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(STEP_OUT_ID, STEP_OUT_LABEL, 40, icons.debugStepOut, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(RESTART_SESSION_ID, RESTART_LABEL, 60, icons.debugRestart); + registerDebugToolBarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), 50, icons.debugStepBack, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), 60, icons.debugReverseContinue, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); // Debug callstack context menu const registerDebugViewMenuItem = (menuId: MenuId, id: string, title: string, order: number, when?: ContextKeyExpression, precondition?: ContextKeyExpression, group = 'navigation') => { @@ -460,7 +460,7 @@ function registerDebugPanel(): void { const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: DEBUG_PANEL_ID, name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), - icon: Codicon.debugConsole.classNames, + icon: icons.debugConsoleViewIcon, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: DEBUG_PANEL_ID, focusCommand: { id: OpenDebugConsoleAction.ID }, @@ -471,7 +471,7 @@ function registerDebugPanel(): void { Registry.as(ViewExtensions.ViewsRegistry).registerViews([{ id: REPL_VIEW_ID, name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), - containerIcon: Codicon.debugConsole.classNames, + containerIcon: icons.debugConsoleViewIcon, canToggleVisibility: false, canMoveView: true, when: CONTEXT_DEBUGGERS_AVAILABLE, @@ -481,12 +481,13 @@ function registerDebugPanel(): void { registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugConsoleAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y }), 'View: Debug Console', CATEGORIES.View.value, CONTEXT_DEBUGGERS_AVAILABLE); } + function registerDebugView(): void { const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: nls.localize('run', "Run"), ctorDescriptor: new SyncDescriptor(DebugViewPaneContainer), - icon: Codicon.debugAlt.classNames, + icon: icons.runViewIcon, alwaysUseContainerInfo: true, order: 2 }, ViewContainerLocation.Sidebar); @@ -494,12 +495,12 @@ function registerDebugView(): void { // Register default debug views const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); - viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); - viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); - viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); - viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); - viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); - viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); + viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: icons.variablesViewIcon, ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); + viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: icons.watchViewIcon, ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); + viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: icons.callStackViewIcon, ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); + viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: icons.breakpointsViewIcon, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); + viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: icons.runViewIcon, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); + viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: icons.loadedScriptsViewIcon, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); } function registerConfiguration(): void { diff --git a/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts b/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts index 473562714..d3cef4bba 100644 --- a/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts +++ b/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts @@ -7,13 +7,13 @@ import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { RGBA, Color } from 'vs/base/common/color'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ansiColorIdentifiers } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; /** * @param text The content to stylize. * @returns An {@link HTMLSpanElement} that contains the potentially stylized text. */ -export function handleANSIOutput(text: string, linkDetector: LinkDetector, themeService: IThemeService, debugSession: IDebugSession): HTMLSpanElement { +export function handleANSIOutput(text: string, linkDetector: LinkDetector, themeService: IThemeService, workspaceFolder: IWorkspaceFolder | undefined): HTMLSpanElement { const root: HTMLSpanElement = document.createElement('span'); const textLength: number = text.length; @@ -54,7 +54,7 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector, theme if (sequenceFound) { // Flush buffer with previous styles. - appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, debugSession, customFgColor, customBgColor); + appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, workspaceFolder, customFgColor, customBgColor); buffer = ''; @@ -100,7 +100,7 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector, theme // Flush remaining text buffer if not empty. if (buffer) { - appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, debugSession, customFgColor, customBgColor); + appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, workspaceFolder, customFgColor, customBgColor); } return root; @@ -268,7 +268,7 @@ export function appendStylizedStringToContainer( stringContent: string, cssClasses: string[], linkDetector: LinkDetector, - debugSession: IDebugSession, + workspaceFolder: IWorkspaceFolder | undefined, customTextColor?: RGBA, customBackgroundColor?: RGBA ): void { @@ -276,7 +276,7 @@ export function appendStylizedStringToContainer( return; } - const container = linkDetector.linkify(stringContent, true, debugSession.root); + const container = linkDetector.linkify(stringContent, true, workspaceFolder); container.className = cssClasses.join(' '); if (customTextColor) { diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index f7fcea500..3ab907976 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -12,7 +12,7 @@ import { SelectBox, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selec import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IDebugService, IDebugSession, IDebugConfiguration, IConfig, ILaunch } from 'vs/workbench/contrib/debug/common/debug'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { selectBorder, selectBackground } from 'vs/platform/theme/common/colorRegistry'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -20,6 +20,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ADD_CONFIGURATION_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { debugStart } from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; @@ -68,7 +69,7 @@ export class StartDebugActionViewItem implements IActionViewItem { render(container: HTMLElement): void { this.container = container; container.classList.add('start-debug-action-item'); - this.start = dom.append(container, $('.codicon.codicon-debug-start')); + this.start = dom.append(container, $(ThemeIcon.asCSSSelector(debugStart))); this.start.title = this.action.label; this.start.setAttribute('role', 'button'); this.start.tabIndex = 0; @@ -188,23 +189,35 @@ export class StartDebugActionViewItem implements IActionViewItem { }); }); - if (this.options.length === 0) { - this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false }); - } else { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); - disabledIdxs.push(this.options.length - 1); - } - - this.providers.forEach(p => { - if (p.type === manager.selectedConfiguration.type) { + // Only take 3 elements from the recent dynamic configurations to not clutter the dropdown + manager.getRecentDynamicConfigurations().slice(0, 3).forEach(({ name, type }) => { + if (type === manager.selectedConfiguration.type && manager.selectedConfiguration.name === name) { this.selected = this.options.length; } + this.options.push({ + label: name, + handler: async () => { + await manager.selectConfiguration(undefined, name, undefined, { type }); + return true; + } + }); + }); + + if (this.options.length === 0) { + this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false }); + } + + this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); + disabledIdxs.push(this.options.length - 1); + + this.providers.forEach(p => { this.options.push({ - label: `${p.label}...`, handler: async () => { + label: `${p.label}...`, + handler: async () => { const picked = await p.pick(); if (picked) { - await manager.selectConfiguration(picked.launch, picked.config.name, picked.config, p.type); + await manager.selectConfiguration(picked.launch, picked.config.name, picked.config, { type: p.type }); return true; } return false; @@ -212,11 +225,6 @@ export class StartDebugActionViewItem implements IActionViewItem { }); }); - if (this.providers.length > 0) { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); - disabledIdxs.push(this.options.length - 1); - } - manager.getLaunches().filter(l => !l.hidden).forEach(l => { const label = inWorkspace ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration..."); this.options.push({ diff --git a/src/vs/workbench/contrib/debug/browser/debugActions.ts b/src/vs/workbench/contrib/debug/browser/debugActions.ts index 3f3e09c95..3c9472104 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActions.ts @@ -14,6 +14,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { deepClone } from 'vs/base/common/objects'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export abstract class AbstractDebugAction extends Action { @@ -64,7 +66,7 @@ export class ConfigureAction extends AbstractDebugAction { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IQuickInputService private readonly quickInputService: IQuickInputService ) { - super(id, label, 'debug-action codicon codicon-gear', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.debugConfigure), debugService, keybindingService); this._register(debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateClass())); this.updateClass(); } @@ -79,7 +81,7 @@ export class ConfigureAction extends AbstractDebugAction { private updateClass(): void { const configurationManager = this.debugService.getConfigurationManager(); - this.class = configurationManager.selectedConfiguration.name ? 'debug-action codicon codicon-gear' : 'debug-action codicon codicon-gear notification'; + this.class = configurationManager.selectedConfiguration.name ? 'debug-action' + ThemeIcon.asClassName(icons.debugConfigure) : 'debug-action ' + ThemeIcon.asClassName(icons.debugConfigure) + ' notification'; } async run(): Promise { @@ -132,7 +134,8 @@ export class StartAction extends AbstractDebugAction { } async run(): Promise { - let { launch, name, config } = this.debugService.getConfigurationManager().selectedConfiguration; + let { launch, name, getConfig } = this.debugService.getConfigurationManager().selectedConfiguration; + const config = await getConfig(); const clonedConfig = deepClone(config); return this.debugService.startDebugging(launch, clonedConfig || name, { noDebug: this.isNoDebug() }); } @@ -147,10 +150,10 @@ export class StartAction extends AbstractDebugAction { if (debugService.state === State.Initializing) { return false; } - let { name, config } = debugService.getConfigurationManager().selectedConfiguration; - let nameToStart = name || config?.name; + let { name, launch } = debugService.getConfigurationManager().selectedConfiguration; + let nameToStart = name; - if (sessions.some(s => s.configuration.name === nameToStart)) { + if (sessions.some(s => s.configuration.name === nameToStart && s.root === launch?.workspace)) { // There is already a debug session running and we do not have any launch configuration selected return false; } @@ -209,7 +212,7 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction { static readonly LABEL = nls.localize('removeAllBreakpoints', "Remove All Breakpoints"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action codicon-close-all', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.breakpointsRemoveAll), debugService, keybindingService); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } @@ -267,7 +270,7 @@ export class ToggleBreakpointsActivatedAction extends AbstractDebugAction { static readonly DEACTIVATE_LABEL = nls.localize('deactivateBreakpoints', "Deactivate Breakpoints"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action codicon-activate-breakpoints', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.breakpointsActivate), debugService, keybindingService); this.updateLabel(this.debugService.getModel().areBreakpointsActivated() ? ToggleBreakpointsActivatedAction.DEACTIVATE_LABEL : ToggleBreakpointsActivatedAction.ACTIVATE_LABEL); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => { @@ -310,7 +313,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction { static readonly LABEL = nls.localize('addFunctionBreakpoint', "Add Function Breakpoint"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action codicon-add', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.watchExpressionsAddFuncBreakpoint), debugService, keybindingService); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } @@ -319,7 +322,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction { } protected isEnabled(_: State): boolean { - return !this.debugService.getViewModel().getSelectedFunctionBreakpoint() + return !this.debugService.getViewModel().getSelectedBreakpoint() && this.debugService.getModel().getFunctionBreakpoints().every(fbp => !!fbp.name); } } @@ -329,7 +332,7 @@ export class AddWatchExpressionAction extends AbstractDebugAction { static readonly LABEL = nls.localize('addWatchExpression', "Add Expression"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action codicon-add', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.watchExpressionsAdd), debugService, keybindingService); this._register(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); this._register(this.debugService.getViewModel().onDidSelectExpression(() => this.updateEnablement())); } @@ -349,7 +352,7 @@ export class RemoveAllWatchExpressionsAction extends AbstractDebugAction { static readonly LABEL = nls.localize('removeAllWatchExpressions', "Remove All Expressions"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action codicon-close-all', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.watchExpressionsRemoveAll), debugService, keybindingService); this._register(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); } diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts new file mode 100644 index 000000000..249f1e69d --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -0,0 +1,268 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import * as strings from 'vs/base/common/strings'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { ITextModel } from 'vs/editor/common/model'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IDebugConfiguration, IConfig, IDebugAdapterDescriptorFactory, IDebugAdapter, IDebugSession, IAdapterDescriptor, IDebugAdapterFactory, CONTEXT_DEBUGGERS_AVAILABLE, IAdapterManager } from 'vs/workbench/contrib/debug/common/debug'; +import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { launchSchema, debuggersExtPoint, breakpointsExtPoint } from 'vs/workbench/contrib/debug/common/debugSchemas'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export class AdapterManager implements IAdapterManager { + + private debuggers: Debugger[]; + private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[]; + private debugAdapterFactories = new Map(); + private debuggersAvailable: IContextKey; + private readonly _onDidRegisterDebugger = new Emitter(); + private readonly _onDidDebuggersExtPointRead = new Emitter(); + private breakpointModeIdsSet = new Set(); + + constructor( + @IEditorService private readonly editorService: IEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ICommandService private readonly commandService: ICommandService, + @IExtensionService private readonly extensionService: IExtensionService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + this.adapterDescriptorFactories = []; + this.debuggers = []; + this.registerListeners(); + this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); + } + + private registerListeners(): void { + debuggersExtPoint.setHandler((extensions, delta) => { + delta.added.forEach(added => { + added.value.forEach(rawAdapter => { + if (!rawAdapter.type || (typeof rawAdapter.type !== 'string')) { + added.collector.error(nls.localize('debugNoType', "Debugger 'type' can not be omitted and must be of type 'string'.")); + } + if (rawAdapter.enableBreakpointsFor && rawAdapter.enableBreakpointsFor.languageIds) { + rawAdapter.enableBreakpointsFor.languageIds.forEach(modeId => { + this.breakpointModeIdsSet.add(modeId); + }); + } + + if (rawAdapter.type !== '*') { + const existing = this.getDebugger(rawAdapter.type); + if (existing) { + existing.merge(rawAdapter, added.description); + } else { + this.debuggers.push(this.instantiationService.createInstance(Debugger, this, rawAdapter, added.description)); + } + } + }); + }); + + // take care of all wildcard contributions + extensions.forEach(extension => { + extension.value.forEach(rawAdapter => { + if (rawAdapter.type === '*') { + this.debuggers.forEach(dbg => dbg.merge(rawAdapter, extension.description)); + } + }); + }); + + delta.removed.forEach(removed => { + const removedTypes = removed.value.map(rawAdapter => rawAdapter.type); + this.debuggers = this.debuggers.filter(d => removedTypes.indexOf(d.type) === -1); + }); + + // update the schema to include all attributes, snippets and types from extensions. + this.debuggers.forEach(adapter => { + const items = (launchSchema.properties!['configurations'].items); + const schemaAttributes = adapter.getSchemaAttributes(); + if (schemaAttributes && items.oneOf) { + items.oneOf.push(...schemaAttributes); + } + const configurationSnippets = adapter.configurationSnippets; + if (configurationSnippets && items.defaultSnippets) { + items.defaultSnippets.push(...configurationSnippets); + } + }); + + this._onDidDebuggersExtPointRead.fire(); + }); + + breakpointsExtPoint.setHandler((extensions, delta) => { + delta.removed.forEach(removed => { + removed.value.forEach(breakpoints => this.breakpointModeIdsSet.delete(breakpoints.language)); + }); + delta.added.forEach(added => { + added.value.forEach(breakpoints => this.breakpointModeIdsSet.add(breakpoints.language)); + }); + }); + } + + registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { + debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); + this.debuggersAvailable.set(this.debugAdapterFactories.size > 0); + this._onDidRegisterDebugger.fire(); + + return { + dispose: () => { + debugTypes.forEach(debugType => this.debugAdapterFactories.delete(debugType)); + } + }; + } + + hasDebuggers(): boolean { + return this.debugAdapterFactories.size > 0; + } + + createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined { + let factory = this.debugAdapterFactories.get(session.configuration.type); + if (factory) { + return factory.createDebugAdapter(session); + } + return undefined; + } + + substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise { + let factory = this.debugAdapterFactories.get(debugType); + if (factory) { + return factory.substituteVariables(folder, config); + } + return Promise.resolve(config); + } + + runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments): Promise { + let factory = this.debugAdapterFactories.get(debugType); + if (factory) { + return factory.runInTerminal(args); + } + return Promise.resolve(void 0); + } + + registerDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): IDisposable { + this.adapterDescriptorFactories.push(debugAdapterProvider); + return { + dispose: () => { + this.unregisterDebugAdapterDescriptorFactory(debugAdapterProvider); + } + }; + } + + unregisterDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): void { + const ix = this.adapterDescriptorFactories.indexOf(debugAdapterProvider); + if (ix >= 0) { + this.adapterDescriptorFactories.splice(ix, 1); + } + } + + getDebugAdapterDescriptor(session: IDebugSession): Promise { + const config = session.configuration; + const providers = this.adapterDescriptorFactories.filter(p => p.type === config.type && p.createDebugAdapterDescriptor); + if (providers.length === 1) { + return providers[0].createDebugAdapterDescriptor(session); + } else { + // TODO@AW handle n > 1 case + } + return Promise.resolve(undefined); + } + + getDebuggerLabel(type: string): string | undefined { + const dbgr = this.getDebugger(type); + if (dbgr) { + return dbgr.label; + } + + return undefined; + } + + get onDidRegisterDebugger(): Event { + return this._onDidRegisterDebugger.event; + } + + get onDidDebuggersExtPointRead(): Event { + return this._onDidDebuggersExtPointRead.event; + } + + canSetBreakpointsIn(model: ITextModel): boolean { + const modeId = model.getLanguageIdentifier().language; + if (!modeId || modeId === 'jsonc' || modeId === 'log') { + // do not allow breakpoints in our settings files and output + return false; + } + if (this.configurationService.getValue('debug').allowBreakpointsEverywhere) { + return true; + } + + return this.breakpointModeIdsSet.has(modeId); + } + + getDebugger(type: string): Debugger | undefined { + return this.debuggers.find(dbg => strings.equalsIgnoreCase(dbg.type, type)); + } + + isDebuggerInterestedInLanguage(language: string): boolean { + return !!this.debuggers.find(a => language && a.languages && a.languages.indexOf(language) >= 0); + } + + async guessDebugger(type?: string): Promise { + if (type) { + const adapter = this.getDebugger(type); + return Promise.resolve(adapter); + } + + const activeTextEditorControl = this.editorService.activeTextEditorControl; + let candidates: Debugger[] | undefined; + if (isCodeEditor(activeTextEditorControl)) { + const model = activeTextEditorControl.getModel(); + const language = model ? model.getLanguageIdentifier().language : undefined; + const adapters = this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0); + if (adapters.length === 1) { + return adapters[0]; + } + if (adapters.length > 1) { + candidates = adapters; + } + } + + if (!candidates) { + await this.activateDebuggers('onDebugInitialConfigurations'); + candidates = this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider()); + } + + candidates.sort((first, second) => first.label.localeCompare(second.label)); + const picks = candidates.map(c => ({ label: c.label, debugger: c })); + return this.quickInputService.pick<{ label: string, debugger: Debugger | undefined }>([...picks, { type: 'separator' }, { label: nls.localize('more', "More..."), debugger: undefined }], { placeHolder: nls.localize('selectDebug', "Select Environment") }) + .then(picked => { + if (picked && picked.debugger) { + return picked.debugger; + } + if (picked) { + this.commandService.executeCommand('debug.installAdditionalDebuggers'); + } + return undefined; + }); + } + + async activateDebuggers(activationEvent: string, debugType?: string): Promise { + const promises: Promise[] = [ + this.extensionService.activateByEvent(activationEvent), + this.extensionService.activateByEvent('onDebug') + ]; + if (debugType) { + promises.push(this.extensionService.activateByEvent(`${activationEvent}:${debugType}`)); + } + await Promise.all(promises); + } +} diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 098a3eebb..47aaedfb9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -113,9 +113,9 @@ function isSessionContext(obj: any): obj is CallStackContext { export function registerCommands(): void { - // These commands are used in call stack context menu, call stack inline actions, command pallete, debug toolbar, mac native touch bar + // These commands are used in call stack context menu, call stack inline actions, command palette, debug toolbar, mac native touch bar // When the command is exectued in the context of a thread(context menu on a thread, inline call stack action) we pass the thread id - // Otherwise when it is executed "globaly"(using the touch bar, debug toolbar, command pallete) we do not pass any id and just take whatever is the focussed thread + // Otherwise when it is executed "globaly"(using the touch bar, debug toolbar, command palette) we do not pass any id and just take whatever is the focussed thread // Same for stackFrame commands and session commands. CommandsRegistry.registerCommand({ id: COPY_STACK_TRACE_ID, @@ -521,7 +521,7 @@ export function registerCommands(): void { const control = editorService.activeTextEditorControl; if (isCodeEditor(control)) { const position = control.getPosition(); - if (position && control.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(control.getModel())) { + if (position && control.hasModel() && debugService.canSetBreakpointsIn(control.getModel())) { const modelUri = control.getModel().uri; const breakpointAlreadySet = debugService.getModel().getBreakpoints({ lineNumber: position.lineNumber, uri: modelUri }) .some(bp => (bp.sessionAgnosticData.column === position.column || (!bp.column && position.column <= 1))); @@ -564,7 +564,7 @@ export function registerCommands(): void { if (list instanceof List) { const focus = list.getFocusedElements(); if (focus.length && focus[0] instanceof Breakpoint) { - return openBreakpointSource(focus[0], true, false, accessor.get(IDebugService), accessor.get(IEditorService)); + return openBreakpointSource(focus[0], true, false, true, accessor.get(IDebugService), accessor.get(IEditorService)); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 041c97e21..1357826cd 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -6,30 +6,25 @@ import * as nls from 'vs/nls'; import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; import * as json from 'vs/base/common/json'; import { URI as uri } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { ITextModel } from 'vs/editor/common/model'; import { IEditorPane } from 'vs/workbench/common/editor'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IConfigPresentation, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib/debug/common/debug'; -import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; +import { IDebugConfigurationProvider, ICompound, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, CONTEXT_DEBUG_CONFIGURATION_TYPE, IConfigPresentation } from 'vs/workbench/contrib/debug/common/debug'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { Registry } from 'vs/platform/registry/common/platform'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; -import { launchSchema, debuggersExtPoint, breakpointsExtPoint } from 'vs/workbench/contrib/debug/common/debugSchemas'; +import { launchSchema } from 'vs/workbench/contrib/debug/common/debugSchemas'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -37,10 +32,13 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { withUndefinedAsNull } from 'vs/base/common/types'; import { sequence } from 'vs/base/common/async'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { flatten } from 'vs/base/common/arrays'; +import { flatten, distinct } from 'vs/base/common/arrays'; import { getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { AdapterManager } from 'vs/workbench/contrib/debug/browser/debugAdapterManager'; +import { debugConfigure } from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); jsonRegistry.registerSchema(launchSchemaId, launchSchema); @@ -49,33 +47,27 @@ const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname'; const DEBUG_SELECTED_ROOT = 'debug.selectedroot'; // Debug type is only stored if a dynamic configuration is used for better restore const DEBUG_SELECTED_TYPE = 'debug.selectedtype'; +const DEBUG_RECENT_DYNAMIC_CONFIGURATIONS = 'debug.recentdynamicconfigurations'; interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig } export class ConfigurationManager implements IConfigurationManager { - private debuggers: Debugger[]; - private breakpointModeIdsSet = new Set(); private launches!: ILaunch[]; private selectedName: string | undefined; private selectedLaunch: ILaunch | undefined; - private selectedConfig: IConfig | undefined; + private getSelectedConfig: () => Promise = () => Promise.resolve(undefined); private selectedType: string | undefined; private toDispose: IDisposable[]; private readonly _onDidSelectConfigurationName = new Emitter(); private configProviders: IDebugConfigurationProvider[]; - private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[]; - private debugAdapterFactories = new Map(); private debugConfigurationTypeContext: IContextKey; - private debuggersAvailable: IContextKey; - private readonly _onDidRegisterDebugger = new Emitter(); constructor( + private readonly adapterManager: AdapterManager, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @ICommandService private readonly commandService: ICommandService, @IStorageService private readonly storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @IHistoryService private readonly historyService: IHistoryService, @@ -83,8 +75,6 @@ export class ConfigurationManager implements IConfigurationManager { @IContextKeyService contextKeyService: IContextKeyService ) { this.configProviders = []; - this.adapterDescriptorFactories = []; - this.debuggers = []; this.toDispose = []; this.initLaunches(); this.registerListeners(); @@ -93,111 +83,13 @@ export class ConfigurationManager implements IConfigurationManager { const previousSelectedLaunch = this.launches.find(l => l.uri.toString() === previousSelectedRoot); const previousSelectedName = this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE); this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService); - this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); if (previousSelectedLaunch && previousSelectedLaunch.getConfigurationNames().length) { - this.selectConfiguration(previousSelectedLaunch, previousSelectedName, undefined, previousSelectedType); + this.selectConfiguration(previousSelectedLaunch, previousSelectedName, undefined, { type: previousSelectedType }); } else if (this.launches.length > 0) { - this.selectConfiguration(undefined, previousSelectedName, undefined, previousSelectedType); + this.selectConfiguration(undefined, previousSelectedName, undefined, { type: previousSelectedType }); } } - // debuggers - - registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { - debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); - this.debuggersAvailable.set(this.debugAdapterFactories.size > 0); - this._onDidRegisterDebugger.fire(); - - return { - dispose: () => { - debugTypes.forEach(debugType => this.debugAdapterFactories.delete(debugType)); - } - }; - } - - hasDebuggers(): boolean { - return this.debugAdapterFactories.size > 0; - } - - createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined { - let factory = this.debugAdapterFactories.get(session.configuration.type); - if (factory) { - return factory.createDebugAdapter(session); - } - return undefined; - } - - substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise { - let factory = this.debugAdapterFactories.get(debugType); - if (factory) { - return factory.substituteVariables(folder, config); - } - return Promise.resolve(config); - } - - runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments): Promise { - let factory = this.debugAdapterFactories.get(debugType); - if (factory) { - return factory.runInTerminal(args); - } - return Promise.resolve(void 0); - } - - // debug adapter - - registerDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): IDisposable { - this.adapterDescriptorFactories.push(debugAdapterProvider); - return { - dispose: () => { - this.unregisterDebugAdapterDescriptorFactory(debugAdapterProvider); - } - }; - } - - unregisterDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): void { - const ix = this.adapterDescriptorFactories.indexOf(debugAdapterProvider); - if (ix >= 0) { - this.adapterDescriptorFactories.splice(ix, 1); - } - } - - getDebugAdapterDescriptor(session: IDebugSession): Promise { - - const config = session.configuration; - - // first try legacy proposed API: DebugConfigurationProvider.debugAdapterExecutable - const providers0 = this.configProviders.filter(p => p.type === config.type && p.debugAdapterExecutable); - if (providers0.length === 1 && providers0[0].debugAdapterExecutable) { - return providers0[0].debugAdapterExecutable(session.root ? session.root.uri : undefined); - } else { - // TODO@AW handle n > 1 case - } - - // new API - const providers = this.adapterDescriptorFactories.filter(p => p.type === config.type && p.createDebugAdapterDescriptor); - if (providers.length === 1) { - return providers[0].createDebugAdapterDescriptor(session); - } else { - // TODO@AW handle n > 1 case - } - return Promise.resolve(undefined); - } - - getDebuggerLabel(type: string): string | undefined { - const dbgr = this.getDebugger(type); - if (dbgr) { - return dbgr.label; - } - - return undefined; - } - - get onDidRegisterDebugger(): Event { - return this._onDidRegisterDebugger.event; - } - - // debug configurations - registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable { this.configProviders.push(debugConfigurationProvider); return { @@ -298,7 +190,7 @@ export class ConfigurationManager implements IConfigurationManager { return debugDynamicExtensionsTypes.map(type => { return { - label: this.getDebuggerLabel(type)!, + label: this.adapterManager.getDebuggerLabel(type)!, getProvider: async () => { await this.activateDebuggers(onDebugDynamicConfigurationsName, type); return this.configProviders.find(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Dynamic && p.provideDebugConfigurations); @@ -313,7 +205,6 @@ export class ConfigurationManager implements IConfigurationManager { input.placeholder = nls.localize('selectConfiguration', "Select Launch Configuration"); input.show(); - let chosenDidCancel = false; const chosenPromise = new Promise(resolve => { disposables.add(input.onDidAccept(() => resolve(input.activeItems[0]))); disposables.add(input.onDidTriggerItemButton(async (context) => { @@ -324,7 +215,6 @@ export class ConfigurationManager implements IConfigurationManager { await (launch as Launch).writeConfiguration(config); await this.selectConfiguration(launch, config.name); })); - disposables.add(input.onDidHide(() => { chosenDidCancel = true; resolve(undefined); })); }); const token = new CancellationTokenSource(); @@ -337,7 +227,7 @@ export class ConfigurationManager implements IConfigurationManager { description: launch.name, config, buttons: [{ - iconClass: 'codicon-gear', + iconClass: ThemeIcon.asClassName(debugConfigure), tooltip: nls.localize('editLaunchConfig', "Edit Debug Configuration in launch.json") }], launch @@ -348,16 +238,9 @@ export class ConfigurationManager implements IConfigurationManager { const nestedPicks = await Promise.all(picks); const items = flatten(nestedPicks); - let chosen: IDynamicPickItem | undefined; - - // If there's exactly one item to choose from, pick it automatically - if (items.length === 1 && !chosenDidCancel) { - chosen = items[0]; - } else { - input.items = items; - input.busy = false; - chosen = await chosenPromise; - } + input.items = items; + input.busy = false; + const chosen = await chosenPromise; disposables.dispose(); @@ -387,69 +270,11 @@ export class ConfigurationManager implements IConfigurationManager { return getVisibleAndSorted(all); } + getRecentDynamicConfigurations(): { name: string, type: string }[] { + return JSON.parse(this.storageService.get(DEBUG_RECENT_DYNAMIC_CONFIGURATIONS, StorageScope.WORKSPACE, '[]')); + } + private registerListeners(): void { - debuggersExtPoint.setHandler((extensions, delta) => { - delta.added.forEach(added => { - added.value.forEach(rawAdapter => { - if (!rawAdapter.type || (typeof rawAdapter.type !== 'string')) { - added.collector.error(nls.localize('debugNoType', "Debugger 'type' can not be omitted and must be of type 'string'.")); - } - if (rawAdapter.enableBreakpointsFor) { - rawAdapter.enableBreakpointsFor.languageIds.forEach(modeId => { - this.breakpointModeIdsSet.add(modeId); - }); - } - - if (rawAdapter.type !== '*') { - const existing = this.getDebugger(rawAdapter.type); - if (existing) { - existing.merge(rawAdapter, added.description); - } else { - this.debuggers.push(this.instantiationService.createInstance(Debugger, this, rawAdapter, added.description)); - } - } - }); - }); - - // take care of all wildcard contributions - extensions.forEach(extension => { - extension.value.forEach(rawAdapter => { - if (rawAdapter.type === '*') { - this.debuggers.forEach(dbg => dbg.merge(rawAdapter, extension.description)); - } - }); - }); - - delta.removed.forEach(removed => { - const removedTypes = removed.value.map(rawAdapter => rawAdapter.type); - this.debuggers = this.debuggers.filter(d => removedTypes.indexOf(d.type) === -1); - }); - - // update the schema to include all attributes, snippets and types from extensions. - this.debuggers.forEach(adapter => { - const items = (launchSchema.properties!['configurations'].items); - const schemaAttributes = adapter.getSchemaAttributes(); - if (schemaAttributes && items.oneOf) { - items.oneOf.push(...schemaAttributes); - } - const configurationSnippets = adapter.configurationSnippets; - if (configurationSnippets && items.defaultSnippets) { - items.defaultSnippets.push(...configurationSnippets); - } - }); - - this.setCompoundSchemaValues(); - }); - - breakpointsExtPoint.setHandler((extensions, delta) => { - delta.removed.forEach(removed => { - removed.value.forEach(breakpoints => this.breakpointModeIdsSet.delete(breakpoints.language)); - }); - delta.added.forEach(added => { - added.value.forEach(breakpoints => this.breakpointModeIdsSet.add(breakpoints.language)); - }); - }); - this.toDispose.push(Event.any(this.contextService.onDidChangeWorkspaceFolders, this.contextService.onDidChangeWorkbenchState)(() => { this.initLaunches(); this.selectConfiguration(undefined); @@ -462,14 +287,17 @@ export class ConfigurationManager implements IConfigurationManager { this.setCompoundSchemaValues(); } })); + this.toDispose.push(this.adapterManager.onDidDebuggersExtPointRead(() => { + this.setCompoundSchemaValues(); + })); } private initLaunches(): void { - this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder)); + this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, this.adapterManager, folder)); if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch, this)); + this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch, this, this.adapterManager)); } - this.launches.push(this.instantiationService.createInstance(UserLaunch, this)); + this.launches.push(this.instantiationService.createInstance(UserLaunch, this, this.adapterManager)); if (this.selectedLaunch && this.launches.indexOf(this.selectedLaunch) === -1) { this.selectConfiguration(undefined); @@ -501,11 +329,11 @@ export class ConfigurationManager implements IConfigurationManager { return this.launches.find(l => l.workspace && this.uriIdentityService.extUri.isEqual(l.workspace.uri, workspaceUri)); } - get selectedConfiguration(): { launch: ILaunch | undefined, name: string | undefined, config: IConfig | undefined, type: string | undefined } { + get selectedConfiguration(): { launch: ILaunch | undefined, name: string | undefined, getConfig: () => Promise, type: string | undefined } { return { launch: this.selectedLaunch, name: this.selectedName, - config: this.selectedConfig, + getConfig: this.getSelectedConfig, type: this.selectedType }; } @@ -522,7 +350,7 @@ export class ConfigurationManager implements IConfigurationManager { return undefined; } - async selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, type?: string): Promise { + async selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, dynamicConfig?: { type?: string }): Promise { if (typeof launch === 'undefined') { const rootUri = this.historyService.getLastActiveWorkspaceRoot(); launch = this.getLaunch(rootUri); @@ -536,40 +364,56 @@ export class ConfigurationManager implements IConfigurationManager { this.selectedLaunch = launch; if (this.selectedLaunch) { - this.storageService.store(DEBUG_SELECTED_ROOT, this.selectedLaunch.uri.toString(), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_SELECTED_ROOT, this.selectedLaunch.uri.toString(), StorageScope.WORKSPACE, StorageTarget.MACHINE); } else { this.storageService.remove(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); } const names = launch ? launch.getConfigurationNames() : []; - if ((name && names.indexOf(name) >= 0) || config) { + this.getSelectedConfig = () => Promise.resolve(config); + let type = config?.type; + if (name && names.indexOf(name) >= 0) { this.setSelectedLaunchName(name); - } else if (!this.selectedName || names.indexOf(this.selectedName) === -1) { - // We could not find the previously used name. We should get all dynamic configurations from providers + } else if (dynamicConfig && dynamicConfig.type) { + // We could not find the previously used name and config is not passed. We should get all dynamic configurations from providers // And potentially auto select the previously used dynamic configuration #96293 - const providers = (await this.getDynamicProviders()).filter(p => p.type === type); - const activatedProviders = await Promise.all(providers.map(p => p.getProvider())); - const provider = activatedProviders.find(p => p && p.type === type); - let nameToSet = names.length ? names[0] : undefined; - if (provider && launch && launch.workspace) { - const token = new CancellationTokenSource(); - const dynamicConfigs = await provider.provideDebugConfigurations!(launch.workspace.uri, token.token); - const dynamicConfig = dynamicConfigs.find(c => c.name === name); - if (dynamicConfig) { - config = dynamicConfig; - nameToSet = name; - } - } + type = dynamicConfig.type; + if (!config) { + const providers = (await this.getDynamicProviders()).filter(p => p.type === type); + this.getSelectedConfig = async () => { + const activatedProviders = await Promise.all(providers.map(p => p.getProvider())); + const provider = activatedProviders.find(p => p && p.type === type); + if (provider && launch && launch.workspace) { + const token = new CancellationTokenSource(); + const dynamicConfigs = await provider.provideDebugConfigurations!(launch.workspace.uri, token.token); + const dynamicConfig = dynamicConfigs.find(c => c.name === name); + if (dynamicConfig) { + return dynamicConfig; + } + } + return undefined; + }; + } + this.setSelectedLaunchName(name); + + let recentDynamicProviders = this.getRecentDynamicConfigurations(); + if (name && dynamicConfig.type) { + // We need to store the recently used dynamic configurations to be able to show them in UI #110009 + recentDynamicProviders.unshift({ name, type: dynamicConfig.type }); + recentDynamicProviders = distinct(recentDynamicProviders, t => `${t.name} : ${t.type}`); + this.storageService.store(DEBUG_RECENT_DYNAMIC_CONFIGURATIONS, JSON.stringify(recentDynamicProviders), StorageScope.WORKSPACE, StorageTarget.USER); + } + } else if (!this.selectedName || names.indexOf(this.selectedName) === -1) { + // We could not find the configuration to select, pick the first one, or reset the selection if there is no launch configuration + const nameToSet = names.length ? names[0] : undefined; this.setSelectedLaunchName(nameToSet); } - this.selectedConfig = config; - this.selectedType = type || this.selectedConfig?.type; - this.storageService.store(DEBUG_SELECTED_TYPE, this.selectedType, StorageScope.WORKSPACE); - const configForType = this.selectedConfig || (this.selectedLaunch && this.selectedName ? this.selectedLaunch.getConfiguration(this.selectedName) : undefined); - if (configForType) { - this.debugConfigurationTypeContext.set(configForType.type); + this.selectedType = dynamicConfig?.type || config?.type; + this.storageService.store(DEBUG_SELECTED_TYPE, this.selectedType, StorageScope.WORKSPACE, StorageTarget.MACHINE); + if (type) { + this.debugConfigurationTypeContext.set(type); } else { this.debugConfigurationTypeContext.reset(); } @@ -579,66 +423,6 @@ export class ConfigurationManager implements IConfigurationManager { } } - canSetBreakpointsIn(model: ITextModel): boolean { - const modeId = model.getLanguageIdentifier().language; - if (!modeId || modeId === 'jsonc' || modeId === 'log') { - // do not allow breakpoints in our settings files and output - return false; - } - if (this.configurationService.getValue('debug').allowBreakpointsEverywhere) { - return true; - } - - return this.breakpointModeIdsSet.has(modeId); - } - - getDebugger(type: string): Debugger | undefined { - return this.debuggers.find(dbg => strings.equalsIgnoreCase(dbg.type, type)); - } - - isDebuggerInterestedInLanguage(language: string): boolean { - return !!this.debuggers.find(a => language && a.languages && a.languages.indexOf(language) >= 0); - } - - async guessDebugger(type?: string): Promise { - if (type) { - const adapter = this.getDebugger(type); - return Promise.resolve(adapter); - } - - const activeTextEditorControl = this.editorService.activeTextEditorControl; - let candidates: Debugger[] | undefined; - if (isCodeEditor(activeTextEditorControl)) { - const model = activeTextEditorControl.getModel(); - const language = model ? model.getLanguageIdentifier().language : undefined; - const adapters = this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0); - if (adapters.length === 1) { - return adapters[0]; - } - if (adapters.length > 1) { - candidates = adapters; - } - } - - if (!candidates) { - await this.activateDebuggers('onDebugInitialConfigurations'); - candidates = this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider()); - } - - candidates.sort((first, second) => first.label.localeCompare(second.label)); - const picks = candidates.map(c => ({ label: c.label, debugger: c })); - return this.quickInputService.pick<{ label: string, debugger: Debugger | undefined }>([...picks, { type: 'separator' }, { label: nls.localize('more', "More..."), debugger: undefined }], { placeHolder: nls.localize('selectDebug', "Select Environment") }) - .then(picked => { - if (picked && picked.debugger) { - return picked.debugger; - } - if (picked) { - this.commandService.executeCommand('debug.installAdditionalDebuggers'); - } - return undefined; - }); - } - async activateDebuggers(activationEvent: string, debugType?: string): Promise { const promises: Promise[] = [ this.extensionService.activateByEvent(activationEvent), @@ -654,7 +438,7 @@ export class ConfigurationManager implements IConfigurationManager { this.selectedName = selectedName; if (this.selectedName) { - this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE); + this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE, StorageTarget.MACHINE); } else { this.storageService.remove(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE); } @@ -668,8 +452,10 @@ export class ConfigurationManager implements IConfigurationManager { abstract class AbstractLaunch { protected abstract getConfig(): IGlobalConfig | undefined; - constructor(protected configurationManager: ConfigurationManager) { - } + constructor( + protected configurationManager: ConfigurationManager, + private readonly adapterManager: AdapterManager + ) { } getCompound(name: string): ICompound | undefined { const config = this.getConfig(); @@ -722,7 +508,7 @@ abstract class AbstractLaunch { async getInitialConfigurationContent(folderUri?: uri, type?: string, token?: CancellationToken): Promise { let content = ''; - const adapter = await this.configurationManager.guessDebugger(type); + const adapter = await this.adapterManager.guessDebugger(type); if (adapter) { const initialConfigs = await this.configurationManager.provideDebugConfigurations(folderUri, adapter.type, token || CancellationToken.None); content = await adapter.getInitialConfigurationContent(initialConfigs); @@ -739,13 +525,14 @@ class Launch extends AbstractLaunch implements ILaunch { constructor( configurationManager: ConfigurationManager, + adapterManager: AdapterManager, public workspace: IWorkspaceFolder, @IFileService private readonly fileService: IFileService, @ITextFileService private readonly textFileService: ITextFileService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService ) { - super(configurationManager); + super(configurationManager, adapterManager); } get uri(): uri { @@ -822,11 +609,12 @@ class Launch extends AbstractLaunch implements ILaunch { class WorkspaceLaunch extends AbstractLaunch implements ILaunch { constructor( configurationManager: ConfigurationManager, + adapterManager: AdapterManager, @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService ) { - super(configurationManager); + super(configurationManager, adapterManager); } get workspace(): undefined { @@ -873,10 +661,11 @@ class UserLaunch extends AbstractLaunch implements ILaunch { constructor( configurationManager: ConfigurationManager, + adapterManager: AdapterManager, @IConfigurationService private readonly configurationService: IConfigurationService, @IPreferencesService private readonly preferencesService: IPreferencesService ) { - super(configurationManager); + super(configurationManager, adapterManager); } get workspace(): undefined { @@ -900,7 +689,7 @@ class UserLaunch extends AbstractLaunch implements ILaunch { } async openConfigFile(preserveFocus: boolean): Promise<{ editor: IEditorPane | null, created: boolean }> { - const editor = await this.preferencesService.openGlobalSettings(true, { preserveFocus }); + const editor = await this.preferencesService.openGlobalSettings(true, { preserveFocus, revealSetting: { key: 'launch' } }); return ({ editor: withUndefinedAsNull(editor), created: false diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 1d5b88bb0..d8ba1d554 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_EXCEPTION_WIDGET_VISIBLE } from 'vs/workbench/contrib/debug/common/debug'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView'; @@ -20,6 +20,10 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { Action } from 'vs/base/common/actions'; import { getDomNodePagePosition } from 'vs/base/browser/dom'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { Position } from 'vs/editor/common/core/position'; +import { URI } from 'vs/base/common/uri'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { raceTimeout } from 'vs/base/common/async'; export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint'; class ToggleBreakpointAction extends EditorAction { @@ -41,7 +45,7 @@ class ToggleBreakpointAction extends EditorAction { if (editor.hasModel()) { const debugService = accessor.get(IDebugService); const modelUri = editor.getModel().uri; - const canSet = debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel()); + const canSet = debugService.canSetBreakpointsIn(editor.getModel()); // Does not account for multi line selections, Set to remove multiple cursor on the same line const lineNumbers = [...new Set(editor.getSelections().map(s => s.getPosition().lineNumber))]; @@ -75,7 +79,7 @@ class ConditionalBreakpointAction extends EditorAction { const debugService = accessor.get(IDebugService); const position = editor.getPosition(); - if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { + if (position && editor.hasModel() && debugService.canSetBreakpointsIn(editor.getModel())) { editor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, undefined, BreakpointWidgetContext.CONDITION); } } @@ -97,7 +101,7 @@ class LogPointAction extends EditorAction { const debugService = accessor.get(IDebugService); const position = editor.getPosition(); - if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { + if (position && editor.hasModel() && debugService.canSetBreakpointsIn(editor.getModel())) { editor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, position.column, BreakpointWidgetContext.LOG_MESSAGE); } } @@ -123,14 +127,37 @@ export class RunToCursorAction extends EditorAction { async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); - const uriIdentityService = accessor.get(IUriIdentityService); const focusedSession = debugService.getViewModel().focusedSession; if (debugService.state !== State.Stopped || !focusedSession) { return; } - let breakpointToRemove: IBreakpoint; - const oneTimeListener = focusedSession.onDidChangeState(() => { + const position = editor.getPosition(); + if (!(editor.hasModel() && position)) { + return; + } + + const uri = editor.getModel().uri; + const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length); + + let breakpointToRemove: IBreakpoint | undefined; + let threadToContinue = debugService.getViewModel().focusedThread; + if (!bpExists) { + const addResult = await this.addBreakpoints(accessor, uri, position); + if (addResult.thread) { + threadToContinue = addResult.thread; + } + + if (addResult.breakpoint) { + breakpointToRemove = addResult.breakpoint; + } + } + + if (!threadToContinue) { + return; + } + + const oneTimeListener = threadToContinue.session.onDidChangeState(() => { const state = focusedSession.state; if (state === State.Stopped || state === State.Inactive) { if (breakpointToRemove) { @@ -140,27 +167,90 @@ export class RunToCursorAction extends EditorAction { } }); - const position = editor.getPosition(); - if (editor.hasModel() && position) { - const uri = editor.getModel().uri; - const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length); - if (!bpExists) { - let column = 0; - const focusedStackFrame = debugService.getViewModel().focusedStackFrame; - if (focusedStackFrame && uriIdentityService.extUri.isEqual(focusedStackFrame.source.uri, uri) && focusedStackFrame.range.startLineNumber === position.lineNumber) { - // If the cursor is on a line different than the one the debugger is currently paused on, then send the breakpoint at column 0 on the line - // otherwise set it at the precise column #102199 - column = position.column; - } + await threadToContinue.continue(); + } - const breakpoints = await debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column }], false); - if (breakpoints && breakpoints.length) { - breakpointToRemove = breakpoints[0]; + private async addBreakpoints(accessor: ServicesAccessor, uri: URI, position: Position) { + const debugService = accessor.get(IDebugService); + const debugModel = debugService.getModel(); + const viewModel = debugService.getViewModel(); + const uriIdentityService = accessor.get(IUriIdentityService); + + let column = 0; + const focusedStackFrame = viewModel.focusedStackFrame; + if (focusedStackFrame && uriIdentityService.extUri.isEqual(focusedStackFrame.source.uri, uri) && focusedStackFrame.range.startLineNumber === position.lineNumber) { + // If the cursor is on a line different than the one the debugger is currently paused on, then send the breakpoint at column 0 on the line + // otherwise set it at the precise column #102199 + column = position.column; + } + + const breakpoints = await debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column }], false); + const breakpoint = breakpoints?.[0]; + if (!breakpoint) { + return { breakpoint: undefined, thread: viewModel.focusedThread }; + } + + // If the breakpoint was not initially verified, wait up to 2s for it to become so. + // Inherently racey if multiple sessions can verify async, but not solvable... + if (!breakpoint.verified) { + let listener: IDisposable; + await raceTimeout(new Promise(resolve => { + listener = debugModel.onDidChangeBreakpoints(() => { + if (breakpoint.verified) { + resolve(); + } + }); + }), 2000); + listener!.dispose(); + } + + // Look at paused threads for sessions that verified this bp. Prefer, in order: + const enum Score { + /** The focused thread */ + Focused, + /** Any other stopped thread of a session that verified the bp */ + Verified, + /** Any thread that verified and paused in the same file */ + VerifiedAndPausedInFile, + /** The focused thread if it verified the breakpoint */ + VerifiedAndFocused, + } + + let bestThread = viewModel.focusedThread; + let bestScore = Score.Focused; + for (const sessionId of breakpoint.sessionsThatVerified) { + const session = debugModel.getSession(sessionId); + if (!session) { + continue; + } + + const threads = session.getAllThreads().filter(t => t.stopped); + if (bestScore < Score.VerifiedAndFocused) { + if (viewModel.focusedThread && threads.includes(viewModel.focusedThread)) { + bestThread = viewModel.focusedThread; + bestScore = Score.VerifiedAndFocused; } } - await debugService.getViewModel().focusedThread!.continue(); + if (bestScore < Score.VerifiedAndPausedInFile) { + const pausedInThisFile = threads.find(t => { + const top = t.getTopStackFrame(); + return top && uriIdentityService.extUri.isEqual(top.source.uri, uri); + }); + + if (pausedInThisFile) { + bestThread = pausedInThisFile; + bestScore = Score.VerifiedAndPausedInFile; + } + } + + if (bestScore < Score.Verified) { + bestThread = threads[0]; + bestScore = Score.VerifiedAndPausedInFile; + } } + + return { thread: bestThread, breakpoint }; } } @@ -336,7 +426,7 @@ class GoToBreakpointAction extends EditorAction { } if (moveBreakpoint) { - return openBreakpointSource(moveBreakpoint, false, true, debugService, editorService); + return openBreakpointSource(moveBreakpoint, false, true, false, debugService, editorService); } } } @@ -364,6 +454,27 @@ class GoToPreviousBreakpointAction extends GoToBreakpointAction { } } +class CloseExceptionWidgetAction extends EditorAction { + + constructor() { + super({ + id: 'editor.debug.action.closeExceptionWidget', + label: nls.localize('closeExceptionWidget', "Close Exception Widget"), + alias: 'Close Exception Widget', + precondition: CONTEXT_EXCEPTION_WIDGET_VISIBLE, + kbOpts: { + primary: KeyCode.Escape, + weight: KeybindingWeight.EditorContrib + } + }); + } + + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const contribution = editor.getContribution(EDITOR_CONTRIBUTION_ID); + contribution.closeExceptionWidget(); + } +} + export function registerEditorActions(): void { registerEditorAction(ToggleBreakpointAction); registerEditorAction(ConditionalBreakpointAction); @@ -375,4 +486,5 @@ export function registerEditorActions(): void { registerEditorAction(ShowDebugHoverAction); registerEditorAction(GoToNextBreakpointAction); registerEditorAction(GoToPreviousBreakpointAction); + registerEditorAction(CloseExceptionWidgetAction); } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 4e39c57dd..dbd53aa5b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -21,7 +21,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDebugEditorContribution, IDebugService, State, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugEditorContribution, IDebugService, State, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, IDebugSession, CONTEXT_EXCEPTION_WIDGET_VISIBLE } from 'vs/workbench/contrib/debug/common/debug'; import { ExceptionWidget } from 'vs/workbench/contrib/debug/browser/exceptionWidget'; import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets'; import { Position } from 'vs/editor/common/core/position'; @@ -39,6 +39,7 @@ import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { Event } from 'vs/base/common/event'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; const LAUNCH_JSON_REGEX = /\.vscode\/launch\.json$/; const INLINE_VALUE_DECORATION_KEY = 'inlinevaluedecoration'; @@ -173,6 +174,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { private hoverWidget: DebugHoverWidget; private hoverRange: Range | null = null; private mouseDown = false; + private exceptionWidgetVisible: IContextKey; private static readonly MEMOIZER = createMemoizer(); private exceptionWidget: ExceptionWidget | undefined; @@ -189,13 +191,15 @@ export class DebugEditorContribution implements IDebugEditorContribution { @ITelemetryService private readonly telemetryService: ITelemetryService, @IConfigurationService private readonly configurationService: IConfigurationService, @IHostService private readonly hostService: IHostService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @IContextKeyService contextKeyService: IContextKeyService ) { this.hoverWidget = this.instantiationService.createInstance(DebugHoverWidget, this.editor); this.toDispose = []; this.registerListeners(); this.updateConfigurationWidgetVisibility(); this.codeEditorService.registerDecorationType(INLINE_VALUE_DECORATION_KEY, {}); + this.exceptionWidgetVisible = CONTEXT_EXCEPTION_WIDGET_VISIBLE.bindTo(contextKeyService); this.toggleExceptionWidget(); } @@ -355,7 +359,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { } private hideHoverWidget(): void { - if (!this.hideHoverScheduler.isScheduled() && this.hoverWidget.isVisible()) { + if (!this.hideHoverScheduler.isScheduled() && this.hoverWidget.willBeVisible()) { this.hideHoverScheduler.schedule(); } this.showHoverScheduler.cancel(); @@ -441,13 +445,20 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.exceptionWidget = this.instantiationService.createInstance(ExceptionWidget, this.editor, exceptionInfo, debugSession); this.exceptionWidget.show({ lineNumber, column }, 0); + this.exceptionWidget.focus(); this.editor.revealLine(lineNumber); + this.exceptionWidgetVisible.set(true); } - private closeExceptionWidget(): void { + closeExceptionWidget(): void { if (this.exceptionWidget) { + const shouldFocusEditor = this.exceptionWidget.hasfocus(); this.exceptionWidget.dispose(); this.exceptionWidget = undefined; + this.exceptionWidgetVisible.set(false); + if (shouldFocusEditor) { + this.editor.focus(); + } } } diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 4e877402d..d828f5bf4 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -31,7 +31,8 @@ import { coalesce } from 'vs/base/common/arrays'; import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView'; import { EvaluatableExpressionProviderRegistry } from 'vs/editor/common/modes'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { isMacintosh } from 'vs/base/common/platform'; const $ = dom.$; @@ -70,6 +71,7 @@ export class DebugHoverWidget implements IContentWidget { allowEditorOverflow = true; private _isVisible: boolean; + private showCancellationSource?: CancellationTokenSource; private domNode!: HTMLElement; private tree!: AsyncDataTree; private showAtPosition: Position | null; @@ -102,6 +104,8 @@ export class DebugHoverWidget implements IContentWidget { this.complexValueTitle = dom.append(this.complexValueContainer, $('.title')); this.treeContainer = dom.append(this.complexValueContainer, $('.debug-hover-tree')); this.treeContainer.setAttribute('role', 'tree'); + const tip = dom.append(this.complexValueContainer, $('.tip')); + tip.textContent = nls.localize('quickTip', 'Hold {0} key to switch to editor language hover', isMacintosh ? 'Option' : 'Alt'); const dataSource = new DebugHoverDataSource(); this.tree = >this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'DebugHover', this.treeContainer, new DebugHoverDelegate(), [this.instantiationService.createInstance(VariablesRenderer)], @@ -161,13 +165,17 @@ export class DebugHoverWidget implements IContentWidget { } isHovered(): boolean { - return this.domNode.matches(':hover'); + return !!this.domNode?.matches(':hover'); } isVisible(): boolean { return this._isVisible; } + willBeVisible(): boolean { + return !!this.showCancellationSource; + } + getId(): string { return DebugHoverWidget.ID; } @@ -177,6 +185,8 @@ export class DebugHoverWidget implements IContentWidget { } async showAt(range: Range, focus: boolean): Promise { + this.showCancellationSource?.cancel(); + const cancellationSource = this.showCancellationSource = new CancellationTokenSource(); const session = this.debugService.getViewModel().focusedSession; if (!session || !this.editor.hasModel()) { @@ -193,7 +203,7 @@ export class DebugHoverWidget implements IContentWidget { const supports = EvaluatableExpressionProviderRegistry.ordered(model); const promises = supports.map(support => { - return Promise.resolve(support.provideEvaluatableExpression(model, pos, CancellationToken.None)).then(expression => { + return Promise.resolve(support.provideEvaluatableExpression(model, pos, cancellationSource.token)).then(expression => { return expression; }, err => { //onUnexpectedExternalError(err); @@ -236,7 +246,7 @@ export class DebugHoverWidget implements IContentWidget { } } - if (!expression || (expression instanceof Expression && !expression.available)) { + if (cancellationSource.token.isCancellationRequested || !expression || (expression instanceof Expression && !expression.available)) { this.hide(); return; } @@ -315,6 +325,11 @@ export class DebugHoverWidget implements IContentWidget { hide(): void { + if (this.showCancellationSource) { + this.showCancellationSource.cancel(); + this.showCancellationSource = undefined; + } + if (!this._isVisible) { return; } diff --git a/src/vs/workbench/contrib/debug/browser/debugIcons.ts b/src/vs/workbench/contrib/debug/browser/debugIcons.ts new file mode 100644 index 000000000..b2203e381 --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/debugIcons.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const debugConsoleViewIcon = registerIcon('debug-console-view-icon', Codicon.debugConsole, localize('debugConsoleViewIcon', 'View icon of the debug console view.')); +export const runViewIcon = registerIcon('run-view-icon', Codicon.debugAlt, localize('runViewIcon', 'View icon of the run view.')); +export const variablesViewIcon = registerIcon('variables-view-icon', Codicon.debugAlt, localize('variablesViewIcon', 'View icon of the variables view.')); +export const watchViewIcon = registerIcon('watch-view-icon', Codicon.debugAlt, localize('watchViewIcon', 'View icon of the watch view.')); +export const callStackViewIcon = registerIcon('callstack-view-icon', Codicon.debugAlt, localize('callStackViewIcon', 'View icon of the call stack view.')); +export const breakpointsViewIcon = registerIcon('breakpoints-view-icon', Codicon.debugAlt, localize('breakpointsViewIcon', 'View icon of the breakpoints view.')); +export const loadedScriptsViewIcon = registerIcon('loaded-scripts-view-icon', Codicon.debugAlt, localize('loadedScriptsViewIcon', 'View icon of the loaded scripts view.')); + +export const debugBreakpoint = registerIcon('debug-breakpoint', Codicon.debugBreakpoint, localize('debugBreakpoint', 'Icon for breakpoints.')); +export const debugBreakpointDisabled = registerIcon('debug-breakpoint-disabled', Codicon.debugBreakpointDisabled, localize('debugBreakpointDisabled', 'Icon for disabled breakpoints.')); +export const debugBreakpointUnverified = registerIcon('debug-breakpoint-unverified', Codicon.debugBreakpointUnverified, localize('debugBreakpointUnverified', 'Icon for unverified breakpoints.')); +export const debugBreakpointHint = registerIcon('debug-hint', Codicon.debugHint, localize('debugBreakpointHint', 'Icon for breakpoint hints shown on hover in editor glyph margin.')); +export const debugBreakpointFunction = registerIcon('debug-breakpoint-function', Codicon.debugBreakpointFunction, localize('debugBreakpointFunction', 'Icon for function breakpoints.')); +export const debugBreakpointFunctionUnverified = registerIcon('debug-breakpoint-function-unverified', Codicon.debugBreakpointFunctionUnverified, localize('debugBreakpointFunctionUnverified', 'Icon for unverified function breakpoints.')); +export const debugBreakpointFunctionDisabled = registerIcon('debug-breakpoint-function-disabled', Codicon.debugBreakpointFunctionDisabled, localize('debugBreakpointFunctionDisabled', 'Icon for disabled function breakpoints.')); + +export const debugBreakpointUnsupported = registerIcon('debug-breakpoint-unsupported', Codicon.debugBreakpointUnsupported, localize('debugBreakpointUnsupported', 'Icon for unsupported breakpoints.')); + +export const debugBreakpointConditionalUnverified = registerIcon('debug-breakpoint-conditional-unverified', Codicon.debugBreakpointConditionalUnverified, localize('debugBreakpointConditionalUnverified', 'Icon for unverified conditional breakpoints.')); +export const debugBreakpointConditional = registerIcon('debug-breakpoint-conditional', Codicon.debugBreakpointConditional, localize('debugBreakpointConditional', 'Icon for conditional breakpoints.')); +export const debugBreakpointConditionalDisabled = registerIcon('debug-breakpoint-conditional-disabled', Codicon.debugBreakpointConditionalDisabled, localize('debugBreakpointConditionalDisabled', 'Icon for disabled conditional breakpoints.')); +export const debugBreakpointDataUnverified = registerIcon('debug-breakpoint-data-unverified', Codicon.debugBreakpointDataUnverified, localize('debugBreakpointDataUnverified', 'Icon for unverified data breakpoints.')); +export const debugBreakpointData = registerIcon('debug-breakpoint-data', Codicon.debugBreakpointData, localize('debugBreakpointData', 'Icon for data breakpoints.')); +export const debugBreakpointDataDisabled = registerIcon('debug-breakpoint-data-disabled', Codicon.debugBreakpointDataDisabled, localize('debugBreakpointDataDisabled', 'Icon for disabled data breakpoints.')); +export const debugBreakpointLogUnverified = registerIcon('debug-breakpoint-log-unverified', Codicon.debugBreakpointLogUnverified, localize('debugBreakpointLogUnverified', 'Icon for unverified log breakpoints.')); +export const debugBreakpointLog = registerIcon('debug-breakpoint-log', Codicon.debugBreakpointLog, localize('debugBreakpointLog', 'Icon for log breakpoints.')); +export const debugBreakpointLogDisabled = registerIcon('debug-breakpoint-log-disabled', Codicon.debugBreakpointLogDisabled, localize('debugBreakpointLogDisabled', 'Icon for disabled log breakpoint.')); + +export const debugStackframe = registerIcon('debug-stackframe', Codicon.debugStackframe, localize('debugStackframe', 'Icon for a stackframe shown in the editor glyph margin.')); +export const debugStackframeFocused = registerIcon('debug-stackframe-focused', Codicon.debugStackframeFocused, localize('debugStackframeFocused', 'Icon for a focused stackframe shown in the editor glyph margin.')); + +export const debugGripper = registerIcon('debug-gripper', Codicon.gripper, localize('debugGripper', 'Icon for the debug bar gripper.')); + +export const debugRestartFrame = registerIcon('debug-restart-frame', Codicon.debugRestartFrame, localize('debugRestartFrame', 'Icon for the debug restart frame action.')); + +export const debugStop = registerIcon('debug-stop', Codicon.debugStop, localize('debugStop', 'Icon for the debug stop action.')); +export const debugDisconnect = registerIcon('debug-disconnect', Codicon.debugDisconnect, localize('debugDisconnect', 'Icon for the debug disconnect action.')); +export const debugRestart = registerIcon('debug-restart', Codicon.debugRestart, localize('debugRestart', 'Icon for the debug restart action.')); +export const debugStepOver = registerIcon('debug-step-over', Codicon.debugStepOver, localize('debugStepOver', 'Icon for the debug step over action.')); +export const debugStepInto = registerIcon('debug-step-into', Codicon.debugStepInto, localize('debugStepInto', 'Icon for the debug step into action.')); +export const debugStepOut = registerIcon('debug-step-out', Codicon.debugStepOut, localize('debugStepOut', 'Icon for the debug step out action.')); +export const debugStepBack = registerIcon('debug-step-back', Codicon.debugStepBack, localize('debugStepBack', 'Icon for the debug step back action.')); +export const debugPause = registerIcon('debug-pause', Codicon.debugPause, localize('debugPause', 'Icon for the debug pause action.')); +export const debugContinue = registerIcon('debug-continue', Codicon.debugContinue, localize('debugContinue', 'Icon for the debug continue action.')); +export const debugReverseContinue = registerIcon('debug-reverse-continue', Codicon.debugReverseContinue, localize('debugReverseContinue', 'Icon for the debug reverse continue action.')); + +export const debugStart = registerIcon('debug-start', Codicon.debugStart, localize('debugStart', 'Icon for the debug start action.')); +export const debugConfigure = registerIcon('debug-configure', Codicon.gear, localize('debugConfigure', 'Icon for the debug configure action.')); +export const debugConsole = registerIcon('debug-console', Codicon.gear, localize('debugConsole', 'Icon for the debug console open action.')); + +export const debugCollapseAll = registerIcon('debug-collapse-all', Codicon.collapseAll, localize('debugCollapseAll', 'Icon for the collapse all action in the debug views.')); +export const callstackViewSession = registerIcon('callstack-view-session', Codicon.bug, localize('callstackViewSession', 'Icon for the session icon in the call stack view.')); +export const debugConsoleClearAll = registerIcon('debug-console-clear-all', Codicon.clearAll, localize('debugConsoleClearAll', 'Icon for the clear all action in the debug console.')); +export const watchExpressionsRemoveAll = registerIcon('watch-expressions-remove-all', Codicon.closeAll, localize('watchExpressionsRemoveAll', 'Icon for the remove all action in the watch view.')); +export const watchExpressionsAdd = registerIcon('watch-expressions-add', Codicon.add, localize('watchExpressionsAdd', 'Icon for the add action in the watch view.')); +export const watchExpressionsAddFuncBreakpoint = registerIcon('watch-expressions-add-function-breakpoint', Codicon.add, localize('watchExpressionsAddFuncBreakpoint', 'Icon for the add function breakpoint action in the watch view.')); + +export const breakpointsRemoveAll = registerIcon('breakpoints-remove-all', Codicon.closeAll, localize('breakpointsRemoveAll', 'Icon for the remove all action in the breakpoints view.')); +export const breakpointsActivate = registerIcon('breakpoints-activate', Codicon.activateBreakpoints, localize('breakpointsActivate', 'Icon for the activate action in the breakpoints view.')); + +export const debugConsoleEvaluationInput = registerIcon('debug-console-evaluation-input', Codicon.arrowSmallRight, localize('debugConsoleEvaluationInput', 'Icon for the debug evaluation input marker.')); +export const debugConsoleEvaluationPrompt = registerIcon('debug-console-evaluation-prompt', Codicon.chevronRight, localize('debugConsoleEvaluationPrompt', 'Icon for the debug evaluation prompt.')); diff --git a/src/vs/workbench/contrib/debug/browser/debugProgress.ts b/src/vs/workbench/contrib/debug/browser/debugProgress.ts index 6a8ef0397..67c975359 100644 --- a/src/vs/workbench/contrib/debug/browser/debugProgress.ts +++ b/src/vs/workbench/contrib/debug/browser/debugProgress.ts @@ -39,7 +39,7 @@ export class DebugProgressContribution implements IWorkbenchContribution { if (viewsService.isViewContainerVisible(VIEWLET_ID)) { progressService.withProgress({ location: VIEWLET_ID }, () => promise); } - const source = debugService.getConfigurationManager().getDebuggerLabel(session.configuration.type); + const source = debugService.getAdapterManager().getDebuggerLabel(session.configuration.type); progressService.withProgress({ location: ProgressLocation.Notification, title: progressStartEvent.body.title, diff --git a/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts b/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts index b8aac7951..2b6f530c4 100644 --- a/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts +++ b/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts @@ -13,6 +13,8 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { matchesFuzzy } from 'vs/base/common/filters'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ADD_CONFIGURATION_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; +import { debugConfigure } from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { @@ -55,7 +57,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { @@ -64,7 +66,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { - await this.debugService.getConfigurationManager().selectConfiguration(config.launch, config.name); + await configManager.selectConfiguration(config.launch, config.name); try { await this.debugService.startDebugging(config.launch); } catch (error) { @@ -86,6 +88,26 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { + const highlights = matchesFuzzy(filter, name, true); + if (highlights) { + picks.push({ + label: name, + highlights: { label: highlights }, + accept: async () => { + await configManager.selectConfiguration(undefined, name, undefined, { type }); + try { + const { launch, getConfig } = configManager.selectedConfiguration; + const config = await getConfig(); + await this.debugService.startDebugging(launch, config); + } catch (error) { + this.notificationService.error(error); + } + } + }); + } + }); + dynamicProviders.forEach(provider => { picks.push({ label: `$(folder) ${provider.label}...`, @@ -93,6 +115,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { const pick = await provider.pick(); if (pick) { + await configManager.selectConfiguration(pick.launch, pick.config.name, pick.config, { type: pick.config.type }); this.debugService.startDebugging(pick.launch, pick.config); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 6f2078d03..e05a471f5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -32,7 +32,7 @@ import { IAction, Action } from 'vs/base/common/actions'; import { deepClone, equals } from 'vs/base/common/objects'; import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig, CALLSTACK_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig, CALLSTACK_VIEW_ID, IAdapterManager, IExceptionBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { getExtensionHostDebugSession } from 'vs/workbench/contrib/debug/common/debugUtils'; import { isErrorWithActions } from 'vs/base/common/errorsWithActions'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -49,6 +49,8 @@ import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompou import { ICommandService } from 'vs/platform/commands/common/commands'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { AdapterManager } from 'vs/workbench/contrib/debug/browser/debugAdapterManager'; +import { ITextModel } from 'vs/editor/common/model'; export class DebugService implements IDebugService { declare readonly _serviceBrand: undefined; @@ -63,6 +65,7 @@ export class DebugService implements IDebugService { private telemetry: DebugTelemetry; private taskRunner: DebugTaskRunner; private configurationManager: ConfigurationManager; + private adapterManager: AdapterManager; private toDispose: IDisposable[]; private debugType!: IContextKey; private debugState!: IContextKey; @@ -105,7 +108,8 @@ export class DebugService implements IDebugService { this._onWillNewSession = new Emitter(); this._onDidEndSession = new Emitter(); - this.configurationManager = this.instantiationService.createInstance(ConfigurationManager); + this.adapterManager = this.instantiationService.createInstance(AdapterManager); + this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this.adapterManager); this.toDispose.push(this.configurationManager); contextKeyService.bufferChangeEvents(() => { @@ -113,7 +117,7 @@ export class DebugService implements IDebugService { this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService); - this.debugUx.set((this.configurationManager.hasDebuggers() && !!this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); + this.debugUx.set((this.adapterManager.hasDebuggers() && !!this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService); }); @@ -162,8 +166,8 @@ export class DebugService implements IDebugService { this.toDispose.push(this.viewModel.onDidFocusSession(() => { this.onStateChange(); })); - this.toDispose.push(Event.any(this.configurationManager.onDidRegisterDebugger, this.configurationManager.onDidSelectConfiguration)(() => { - this.debugUx.set(!!(this.state !== State.Inactive || (this.configurationManager.selectedConfiguration.name && this.configurationManager.hasDebuggers())) ? 'default' : 'simple'); + this.toDispose.push(Event.any(this.adapterManager.onDidRegisterDebugger, this.configurationManager.onDidSelectConfiguration)(() => { + this.debugUx.set(!!(this.state !== State.Inactive || (this.configurationManager.selectedConfiguration.name && this.adapterManager.hasDebuggers())) ? 'default' : 'simple'); })); this.toDispose.push(this.model.onDidChangeCallStack(() => { const numberOfSessions = this.model.getSessions().filter(s => !s.parentSession).length; @@ -192,6 +196,10 @@ export class DebugService implements IDebugService { return this.configurationManager; } + getAdapterManager(): IAdapterManager { + return this.adapterManager; + } + sourceIsNotAvailable(uri: uri): void { this.model.sourceIsNotAvailable(uri); } @@ -245,7 +253,7 @@ export class DebugService implements IDebugService { this.debugState.set(getStateLabel(state)); this.inDebugMode.set(state !== State.Inactive); // Only show the simple ux if debug is not yet started and if no launch.json exists - this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || (this.configurationManager.hasDebuggers() && this.configurationManager.selectedConfiguration.name)) ? 'default' : 'simple'); + this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || (this.adapterManager.hasDebuggers() && this.configurationManager.selectedConfiguration.name)) ? 'default' : 'simple'); }); this.previousState = state; this._onDidChangeState.fire(state); @@ -297,7 +305,7 @@ export class DebugService implements IDebugService { const sessions = this.model.getSessions(); const alreadyRunningMessage = nls.localize('configurationAlreadyRunning', "There is already a debug configuration \"{0}\" running.", configOrName); - if (sessions.some(s => s.configuration.name === configOrName && (!launch || !launch.workspace || !s.root || this.uriIdentityService.extUri.isEqual(s.root.uri, launch.workspace.uri)))) { + if (sessions.some(s => (s.configuration.name === configOrName && s.root === launch.workspace) && (!launch || !launch.workspace || !s.root || this.uriIdentityService.extUri.isEqual(s.root.uri, launch.workspace.uri)))) { throw new Error(alreadyRunningMessage); } if (compound && compound.configurations && sessions.some(p => compound!.configurations.indexOf(p.configuration.name) !== -1)) { @@ -397,7 +405,7 @@ export class DebugService implements IDebugService { const unresolvedConfig = deepClone(config); if (!type) { - const guess = await this.configurationManager.guessDebugger(); + const guess = await this.adapterManager.guessDebugger(); if (guess) { type = guess.type; } @@ -437,7 +445,7 @@ export class DebugService implements IDebugService { } resolvedConfig = cfg; - if (!this.configurationManager.getDebugger(resolvedConfig.type) || (configByProviders.request !== 'attach' && configByProviders.request !== 'launch')) { + if (!this.adapterManager.getDebugger(resolvedConfig.type) || (configByProviders.request !== 'attach' && configByProviders.request !== 'launch')) { let message: string; if (configByProviders.request !== 'attach' && configByProviders.request !== 'launch') { message = configByProviders.request ? nls.localize('debugRequestNotSupported', "Attribute '{0}' has an unsupported value '{1}' in the chosen debug configuration.", 'request', configByProviders.request) @@ -549,7 +557,7 @@ export class DebugService implements IDebugService { } private async launchOrAttachToSession(session: IDebugSession, forceFocus = false): Promise { - const dbgr = this.configurationManager.getDebugger(session.configuration.type); + const dbgr = this.adapterManager.getDebugger(session.configuration.type); try { await session.initialize(dbgr!); await session.launchOrAttach(session.configuration); @@ -754,7 +762,7 @@ export class DebugService implements IDebugService { } private async substituteVariables(launch: ILaunch | undefined, config: IConfig): Promise { - const dbg = this.configurationManager.getDebugger(config.type); + const dbg = this.adapterManager.getDebugger(config.type); if (dbg) { let folder: IWorkspaceFolder | undefined = undefined; if (launch && launch.workspace) { @@ -842,6 +850,10 @@ export class DebugService implements IDebugService { //---- breakpoints + canSetBreakpointsIn(model: ITextModel): boolean { + return this.adapterManager.canSetBreakpointsIn(model); + } + async enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): Promise { if (breakpoint) { this.model.setEnablement(breakpoint, enable); @@ -906,7 +918,7 @@ export class DebugService implements IDebugService { addFunctionBreakpoint(name?: string, id?: string): void { const newFunctionBreakpoint = this.model.addFunctionBreakpoint(name || '', id); - this.viewModel.setSelectedFunctionBreakpoint(newFunctionBreakpoint); + this.viewModel.setSelectedBreakpoint(newFunctionBreakpoint); } async renameFunctionBreakpoint(id: string, newFunctionName: string): Promise { @@ -934,6 +946,12 @@ export class DebugService implements IDebugService { await this.sendDataBreakpoints(); } + async setExceptionBreakpointCondition(exceptionBreakpoint: IExceptionBreakpoint, condition: string | undefined): Promise { + this.model.setExceptionBreakpointCondition(exceptionBreakpoint, condition); + this.debugStorage.storeBreakpoints(this.model); + await this.sendExceptionBreakpoints(); + } + async sendAllBreakpoints(session?: IDebugSession): Promise { await Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session))); await this.sendFunctionBreakpoints(session); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 663ff7109..9b8c75afa 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -394,7 +394,18 @@ export class DebugSession implements IDebugSession { } if (this.raw.readyForBreakpoints) { - await this.raw.setExceptionBreakpoints({ filters: exbpts.map(exb => exb.filter) }); + const args: DebugProtocol.SetExceptionBreakpointsArguments = this.capabilities.supportsExceptionFilterOptions ? { + filters: [], + filterOptions: exbpts.map(exb => { + if (exb.condition) { + return { filterId: exb.filter, condition: exb.condition }; + } + + return { filterId: exb.filter }; + }) + } : { filters: exbpts.map(exb => exb.filter) }; + + await this.raw.setExceptionBreakpoints(args); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 93635f7ff..8f62ac8d9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -16,9 +16,9 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDebugConfiguration, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { registerThemingParticipant, IThemeService, Themable } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, IThemeService, Themable, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerColor, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -29,6 +29,7 @@ import { IMenu, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from ' import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; const DEBUG_TOOLBAR_POSITION_KEY = 'debug.actionswidgetposition'; const DEBUG_TOOLBAR_Y_KEY = 'debug.actionswidgety'; @@ -64,7 +65,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.$el = dom.$('div.debug-toolbar'); this.$el.style.top = `${layoutService.offset?.top ?? 0}px`; - this.dragArea = dom.append(this.$el, dom.$('div.drag-area.codicon.codicon-gripper')); + this.dragArea = dom.append(this.$el, dom.$('div.drag-area' + ThemeIcon.asCSSSelector(icons.debugGripper))); const actionBarContainer = dom.append(this.$el, dom.$('div.action-bar-container')); this.debugToolBarMenu = menuService.createMenu(MenuId.DebugToolBar, contextKeyService); @@ -169,7 +170,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { const left = dom.getComputedStyle(this.$el).left; if (left) { const position = parseFloat(left) / window.innerWidth; - this.storageService.store(DEBUG_TOOLBAR_POSITION_KEY, position, StorageScope.GLOBAL); + this.storageService.store(DEBUG_TOOLBAR_POSITION_KEY, position, StorageScope.GLOBAL, StorageTarget.MACHINE); } } @@ -180,7 +181,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.$el.style.backgroundColor = this.getColor(debugToolBarBackground) || ''; const widgetShadowColor = this.getColor(widgetShadow); - this.$el.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : ''; + this.$el.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : ''; const contrastBorderColor = this.getColor(contrastBorder); const borderColor = this.getColor(debugToolBarBorder); @@ -220,7 +221,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { if ((y < titleAreaHeight / 2) || (y > titleAreaHeight + titleAreaHeight / 2)) { const moveToTop = y < titleAreaHeight; this.setYCoordinate(moveToTop ? 0 : titleAreaHeight); - this.storageService.store(DEBUG_TOOLBAR_Y_KEY, moveToTop ? 0 : 2 * titleAreaHeight, StorageScope.GLOBAL); + this.storageService.store(DEBUG_TOOLBAR_Y_KEY, moveToTop ? 0 : 2 * titleAreaHeight, StorageScope.GLOBAL, StorageTarget.MACHINE); } } @@ -351,51 +352,51 @@ registerThemingParticipant((theme, collector) => { const debugIconStartColor = theme.getColor(debugIconStartForeground); if (debugIconStartColor) { - collector.addRule(`.monaco-workbench .codicon-debug-start { color: ${debugIconStartColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStart)} { color: ${debugIconStartColor} !important; }`); } const debugIconPauseColor = theme.getColor(debugIconPauseForeground); if (debugIconPauseColor) { - collector.addRule(`.monaco-workbench .codicon-debug-pause { color: ${debugIconPauseColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugPause)} { color: ${debugIconPauseColor} !important; }`); } const debugIconStopColor = theme.getColor(debugIconStopForeground); if (debugIconStopColor) { - collector.addRule(`.monaco-workbench .codicon-debug-stop, .monaco-workbench .debug-view-content .codicon-record { color: ${debugIconStopColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStop)} { color: ${debugIconStopColor} !important; }`); } const debugIconDisconnectColor = theme.getColor(debugIconDisconnectForeground); if (debugIconDisconnectColor) { - collector.addRule(`.monaco-workbench .codicon-debug-disconnect { color: ${debugIconDisconnectColor} !important; }`); + collector.addRule(`.monaco-workbench .debug-view-content ${ThemeIcon.asCSSSelector(icons.debugDisconnect)}, .monaco-workbench .debug-toolbar ${ThemeIcon.asCSSSelector(icons.debugDisconnect)} { color: ${debugIconDisconnectColor} !important; }`); } const debugIconRestartColor = theme.getColor(debugIconRestartForeground); if (debugIconRestartColor) { - collector.addRule(`.monaco-workbench .codicon-debug-restart, .monaco-workbench .codicon-debug-restart-frame { color: ${debugIconRestartColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugRestart)}, .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugRestartFrame)} { color: ${debugIconRestartColor} !important; }`); } const debugIconStepOverColor = theme.getColor(debugIconStepOverForeground); if (debugIconStepOverColor) { - collector.addRule(`.monaco-workbench .codicon-debug-step-over { color: ${debugIconStepOverColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepOver)} { color: ${debugIconStepOverColor} !important; }`); } const debugIconStepIntoColor = theme.getColor(debugIconStepIntoForeground); if (debugIconStepIntoColor) { - collector.addRule(`.monaco-workbench .codicon-debug-step-into { color: ${debugIconStepIntoColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepInto)} { color: ${debugIconStepIntoColor} !important; }`); } const debugIconStepOutColor = theme.getColor(debugIconStepOutForeground); if (debugIconStepOutColor) { - collector.addRule(`.monaco-workbench .codicon-debug-step-out { color: ${debugIconStepOutColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepOut)} { color: ${debugIconStepOutColor} !important; }`); } const debugIconContinueColor = theme.getColor(debugIconContinueForeground); if (debugIconContinueColor) { - collector.addRule(`.monaco-workbench .codicon-debug-continue,.monaco-workbench .codicon-debug-reverse-continue { color: ${debugIconContinueColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugContinue)}, .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugReverseContinue)} { color: ${debugIconContinueColor} !important; }`); } const debugIconStepBackColor = theme.getColor(debugIconStepBackForeground); if (debugIconStepBackColor) { - collector.addRule(`.monaco-workbench .codicon-debug-step-back { color: ${debugIconStepBackColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepBack)} { color: ${debugIconStepBackColor} !important; }`); } }); diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index f13479c4e..91187dd3a 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -15,7 +15,7 @@ import { IProgressService } from 'vs/platform/progress/common/progress'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -33,6 +33,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { debugConsole } from 'vs/workbench/contrib/debug/browser/debugIcons'; export class DebugViewPaneContainer extends ViewPaneContainer { @@ -247,7 +248,7 @@ export class OpenDebugConsoleAction extends ToggleViewAction { @IContextKeyService contextKeyService: IContextKeyService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService ) { - super(id, label, REPL_VIEW_ID, viewsService, viewDescriptorService, contextKeyService, layoutService, 'codicon-debug-console'); + super(id, label, REPL_VIEW_ID, viewsService, viewDescriptorService, contextKeyService, layoutService, ThemeIcon.asClassName(debugConsole)); } } diff --git a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts index 93f702fa3..67a12c489 100644 --- a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts @@ -8,14 +8,17 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IExceptionInfo, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IExceptionInfo, IDebugSession, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID } from 'vs/workbench/contrib/debug/common/debug'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; const $ = dom.$; // theming @@ -25,18 +28,19 @@ export const debugExceptionWidgetBackground = registerColor('debugExceptionWidge export class ExceptionWidget extends ZoneWidget { - private _backgroundColor?: Color; + private backgroundColor: Color | undefined; - constructor(editor: ICodeEditor, private exceptionInfo: IExceptionInfo, private debugSession: IDebugSession | undefined, + constructor( + editor: ICodeEditor, + private exceptionInfo: IExceptionInfo, + private debugSession: IDebugSession | undefined, @IThemeService themeService: IThemeService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { - super(editor, { showFrame: true, showArrow: true, frameWidth: 1, className: 'exception-widget-container' }); + super(editor, { showFrame: true, showArrow: true, isAccessible: true, frameWidth: 1, className: 'exception-widget-container' }); - this._backgroundColor = Color.white; - - this._applyTheme(themeService.getColorTheme()); - this._disposables.add(themeService.onDidColorThemeChange(this._applyTheme.bind(this))); + this.applyTheme(themeService.getColorTheme()); + this._disposables.add(themeService.onDidColorThemeChange(this.applyTheme.bind(this))); this.create(); const onDidLayoutChangeScheduler = new RunOnceScheduler(() => this._doLayout(undefined, undefined), 50); @@ -44,8 +48,8 @@ export class ExceptionWidget extends ZoneWidget { this._disposables.add(onDidLayoutChangeScheduler); } - private _applyTheme(theme: IColorTheme): void { - this._backgroundColor = theme.getColor(debugExceptionWidgetBackground); + private applyTheme(theme: IColorTheme): void { + this.backgroundColor = theme.getColor(debugExceptionWidgetBackground); const frameColor = theme.getColor(debugExceptionWidgetBorder); this.style({ arrowColor: frameColor, @@ -55,7 +59,7 @@ export class ExceptionWidget extends ZoneWidget { protected _applyStyles(): void { if (this.container) { - this.container.style.backgroundColor = this._backgroundColor ? this._backgroundColor.toString() : ''; + this.container.style.backgroundColor = this.backgroundColor ? this.backgroundColor.toString() : ''; } super._applyStyles(); } @@ -66,14 +70,27 @@ export class ExceptionWidget extends ZoneWidget { const fontInfo = this.editor.getOption(EditorOption.fontInfo); container.style.fontSize = `${fontInfo.fontSize}px`; container.style.lineHeight = `${fontInfo.lineHeight}px`; + container.tabIndex = 0; + const title = $('.title'); + const label = $('.label'); + dom.append(title, label); + const actions = $('.actions'); + dom.append(title, actions); + label.textContent = this.exceptionInfo.id ? nls.localize('exceptionThrownWithId', 'Exception has occurred: {0}', this.exceptionInfo.id) : nls.localize('exceptionThrown', 'Exception has occurred.'); + let ariaLabel = label.textContent; + + const actionBar = new ActionBar(actions); + actionBar.push(new Action('editor.closeExceptionWidget', nls.localize('close', "Close"), ThemeIcon.asClassName(widgetClose), true, async () => { + const contribution = this.editor.getContribution(EDITOR_CONTRIBUTION_ID); + contribution.closeExceptionWidget(); + }), { label: false, icon: true }); - let title = $('.title'); - title.textContent = this.exceptionInfo.id ? nls.localize('exceptionThrownWithId', 'Exception has occurred: {0}', this.exceptionInfo.id) : nls.localize('exceptionThrown', 'Exception has occurred.'); dom.append(container, title); if (this.exceptionInfo.description) { let description = $('.description'); description.textContent = this.exceptionInfo.description; + ariaLabel += ', ' + this.exceptionInfo.description; dom.append(container, description); } @@ -83,7 +100,9 @@ export class ExceptionWidget extends ZoneWidget { const linkedStackTrace = linkDetector.linkify(this.exceptionInfo.details.stackTrace, true, this.debugSession ? this.debugSession.root : undefined); stackTrace.appendChild(linkedStackTrace); dom.append(container, stackTrace); + ariaLabel += ', ' + this.exceptionInfo.details.stackTrace; } + container.setAttribute('aria-label', ariaLabel); } protected _doLayout(_heightInPixel: number | undefined, _widthInPixel: number | undefined): void { @@ -96,4 +115,13 @@ export class ExceptionWidget extends ZoneWidget { this._relayout(computedLinesNumber); } + + focus(): void { + // Focus into the container for accessibility purposes so the exception and stack trace gets read + this.container?.focus(); + } + + hasfocus(): boolean { + return dom.isAncestor(document.activeElement, this.container); + } } diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index e115fe0be..198da9108 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -16,7 +16,7 @@ import { IDebugSession, IDebugService, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from ' import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { tildify } from 'vs/base/common/labels'; +import { normalizeDriveLetter, tildify } from 'vs/base/common/labels'; import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { ltrim } from 'vs/base/common/strings'; @@ -350,7 +350,9 @@ class SessionTreeItem extends BaseTreeItem { } else { // on unix try to tildify absolute paths path = normalize(path); - if (!isWindows) { + if (isWindows) { + path = normalizeDriveLetter(path); + } else { path = tildify(path, (await this._pathService.userHome()).fsPath); } } diff --git a/src/vs/workbench/contrib/debug/browser/media/debugHover.css b/src/vs/workbench/contrib/debug/browser/media/debugHover.css index 6c48d940b..8f5ae2c96 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugHover.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugHover.css @@ -19,7 +19,8 @@ max-width: 700px; } -.monaco-editor .debug-hover-widget .complex-value .title { +.monaco-editor .debug-hover-widget .complex-value .title, +.monaco-editor .debug-hover-widget .complex-value .tip { padding-left: 15px; padding-right: 2px; font-size: 11px; @@ -29,9 +30,17 @@ height: 18px; overflow: hidden; white-space: pre; +} + +.monaco-editor .debug-hover-widget .complex-value .title { border-bottom: 1px solid rgba(128, 128, 128, 0.35); } +.monaco-editor .debug-hover-widget .complex-value .tip { + border-top: 1px solid rgba(128, 128, 128, 0.35); + opacity: 0.5; +} + .monaco-editor .debug-hover-widget .debug-hover-tree { line-height: 18px; cursor: pointer; diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index e0b6acb32..47cafc6ae 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -84,6 +84,7 @@ /* Make icons and text the same color as the list foreground on focus selection */ .debug-pane .monaco-list:focus .monaco-list-row.selected .state.label, +.debug-pane .monaco-list:focus .monaco-list-row.selected .load-all, .debug-pane .monaco-list:focus .monaco-list-row.selected.focused .state.label, .debug-pane .monaco-list:focus .monaco-list-row.selected .codicon, .debug-pane .monaco-list:focus .monaco-list-row.selected.focused .codicon { @@ -317,10 +318,10 @@ justify-content: center; } -.debug-pane .debug-breakpoints .breakpoint > .file-path { +.debug-pane .debug-breakpoints .breakpoint > .file-path, +.debug-pane .debug-breakpoints .breakpoint.exception > .condition { opacity: 0.7; - font-size: 0.9em; - margin-left: 0.8em; + margin-left: 0.9em; flex: 1; text-overflow: ellipsis; overflow: hidden; diff --git a/src/vs/workbench/contrib/debug/browser/media/exceptionWidget.css b/src/vs/workbench/contrib/debug/browser/media/exceptionWidget.css index df149d3b4..f9d7846d9 100644 --- a/src/vs/workbench/contrib/debug/browser/media/exceptionWidget.css +++ b/src/vs/workbench/contrib/debug/browser/media/exceptionWidget.css @@ -15,9 +15,17 @@ } .monaco-editor .zone-widget .zone-widget-container.exception-widget .title { + display: flex; +} + +.monaco-editor .zone-widget .zone-widget-container.exception-widget .title .label { font-weight: bold; } +.monaco-editor .zone-widget .zone-widget-container.exception-widget .title .actions { + flex: 1; +} + .monaco-editor .zone-widget .zone-widget-container.exception-widget .description, .monaco-editor .zone-widget .zone-widget-container.exception-widget .stack-trace { font-family: var(--monaco-monospace-font); @@ -27,7 +35,7 @@ margin-top: 0.5em; } -.monaco-editor .zone-widget .zone-widget-container.exception-widget a { +.monaco-editor .zone-widget .zone-widget-container.exception-widget .stack-trace a { text-decoration: underline; cursor: pointer; } diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index b2a474076..04de3a219 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -38,6 +38,15 @@ margin-right: 4px; } +.monaco-workbench .repl .repl-tree .output.expression.value-and-source .count-badge-wrapper { + margin-right: 4px; +} + +/* Allow the badge to be a bit shorter so it does not look cut off */ +.monaco-workbench .repl .repl-tree .output.expression.value-and-source .count-badge-wrapper .monaco-count-badge { + min-height: 16px; +} + .monaco-workbench .repl .repl-tree .monaco-tl-contents .arrow { position:absolute; left: 2px; diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index dce391245..a70036599 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -620,10 +620,10 @@ export class RawDebugSession implements IDisposable { } } - let env: IProcessEnvironment = {}; - if (vscodeArgs.env) { + let env: IProcessEnvironment = processEnv; + if (vscodeArgs.env && Object.keys(vscodeArgs.env).length > 0) { // merge environment variables into a copy of the process.env - env = objects.mixin(processEnv, vscodeArgs.env); + env = objects.mixin(objects.deepClone(processEnv), vscodeArgs.env); // and delete some if necessary Object.keys(env).filter(k => env[k] === null).forEach(key => delete env[key]); } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index cbc9401ec..74467fa80 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -19,8 +19,8 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { memoize } from 'vs/base/common/decorators'; import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle'; @@ -59,10 +59,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; import { ReplFilter, ReplFilterState, ReplFilterActionViewItem } from 'vs/workbench/contrib/debug/browser/replFilter'; +import { debugConsoleClearAll, debugConsoleEvaluationPrompt } from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; const HISTORY_STORAGE_KEY = 'debug.repl.history'; +const FILTER_HISTORY_STORAGE_KEY = 'debug.repl.filterHistory'; const DECORATION_KEY = 'replinputdecoration'; const FILTER_ACTION_ID = `workbench.actions.treeView.repl.filter`; @@ -303,7 +305,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { if (this.styleElement) { const debugConsole = this.configurationService.getValue('debug').console; const fontSize = debugConsole.fontSize; - const fontFamily = debugConsole.fontFamily === 'default' ? 'var(--monaco-monospace-font)' : debugConsole.fontFamily; + const fontFamily = debugConsole.fontFamily === 'default' ? 'var(--monaco-monospace-font)' : `'${debugConsole.fontFamily}'`; const lineHeight = debugConsole.lineHeight ? `${debugConsole.lineHeight}px` : '1.4em'; const backgroundColor = this.themeService.getColorTheme().getColor(this.getBackgroundColor()); @@ -462,7 +464,9 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { const session = (this.tree ? this.tree.getInput() : undefined) ?? this.debugService.getViewModel().focusedSession; return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction, session); } else if (action.id === FILTER_ACTION_ID) { - this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action, localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), this.filterState); + const filterHistory = JSON.parse(this.storageService.get(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')) as string[]; + this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action, + localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), this.filterState, filterHistory); return this.filterActionViewItem; } @@ -592,7 +596,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { private createReplInput(container: HTMLElement): void { this.replInputContainer = dom.append(container, $('.repl-input-wrapper')); - dom.append(this.replInputContainer, $('.repl-input-chevron.codicon.codicon-chevron-right')); + dom.append(this.replInputContainer, $('.repl-input-chevron' + ThemeIcon.asCSSSelector(debugConsoleEvaluationPrompt))); const { scopedContextKeyService, historyNavigationEnablement } = createAndBindHistoryNavigationWidgetScopedContextKeyService(this.contextKeyService, { target: this.replInputContainer, historyNavigator: this }); this.historyNavigationEnablement = historyNavigationEnablement; @@ -708,10 +712,18 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { saveState(): void { const replHistory = this.history.getHistory(); if (replHistory.length) { - this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE); + this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); } + if (this.filterActionViewItem) { + const filterHistory = this.filterActionViewItem.getHistory(); + if (filterHistory.length) { + this.storageService.store(FILTER_HISTORY_STORAGE_KEY, JSON.stringify(filterHistory), StorageScope.WORKSPACE, StorageTarget.USER); + } else { + this.storageService.remove(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); + } + } super.saveState(); } @@ -845,7 +857,7 @@ export class ClearReplAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'debug-action codicon-clear-all'); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(debugConsoleClearAll)); } async run(): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts index 143d555a5..3cb5e45d8 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilter.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -136,6 +136,7 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { action: IAction, private placeholder: string, private filters: ReplFilterState, + private history: string[], @IInstantiationService private readonly instantiationService: IInstantiationService, @IThemeService private readonly themeService: IThemeService, @IContextViewService private readonly contextViewService: IContextViewService) { @@ -159,6 +160,10 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { this.filterInputBox.focus(); } + getHistory(): string[] { + return this.filterInputBox.getHistory(); + } + private clearFilterText(): void { this.filterInputBox.value = ''; } @@ -166,7 +171,7 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { private createInput(container: HTMLElement): void { this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { placeholder: this.placeholder, - history: [] + history: this.history })); this._register(attachInputBoxStyler(this.filterInputBox, this.themeService)); this.filterInputBox.value = this.filters.filterText; diff --git a/src/vs/workbench/contrib/debug/browser/replViewer.ts b/src/vs/workbench/contrib/debug/browser/replViewer.ts index 4fbb33ebc..0d432417f 100644 --- a/src/vs/workbench/contrib/debug/browser/replViewer.ts +++ b/src/vs/workbench/contrib/debug/browser/replViewer.ts @@ -21,8 +21,11 @@ import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IReplElementSource, IDebugService, IExpression, IReplElement, IDebugConfiguration, IDebugSession, IExpressionContainer } from 'vs/workbench/contrib/debug/common/debug'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { localize } from 'vs/nls'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; +import { debugConsoleEvaluationInput } from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; @@ -40,10 +43,13 @@ interface IReplEvaluationResultTemplateData { interface ISimpleReplElementTemplateData { container: HTMLElement; + count: CountBadge; + countContainer: HTMLElement; value: HTMLElement; source: HTMLElement; getReplElementSource(): IReplElementSource | undefined; toDispose: IDisposable[]; + elementListener: IDisposable; } interface IRawObjectReplTemplateData { @@ -62,7 +68,7 @@ export class ReplEvaluationInputsRenderer implements ITreeRenderer { +export class ReplEvaluationResultsRenderer implements ITreeRenderer { static readonly ID = 'replEvaluationResult'; get templateId(): string { @@ -117,7 +123,7 @@ export class ReplEvaluationResultsRenderer implements ITreeRenderer, index: number, templateData: IReplEvaluationResultTemplateData): void { + renderElement(element: ITreeNode, index: number, templateData: IReplEvaluationResultTemplateData): void { const expression = element.element; renderExpressionValue(expression, templateData.value, { showHover: false, @@ -151,9 +157,12 @@ export class ReplSimpleElementsRenderer implements ITreeRenderer { e.preventDefault(); e.stopPropagation(); @@ -172,11 +181,13 @@ export class ReplSimpleElementsRenderer implements ITreeRenderer, index: number, templateData: ISimpleReplElementTemplateData): void { + this.setElementCount(element, templateData); + templateData.elementListener = element.onDidChangeCount(() => this.setElementCount(element, templateData)); // value dom.clearNode(templateData.value); // Reset classes to clear ansi decorations since templates are reused templateData.value.className = 'value'; - const result = handleANSIOutput(element.value, this.linkDetector, this.themeService, element.session); + const result = handleANSIOutput(element.value, this.linkDetector, this.themeService, element.session.root); templateData.value.appendChild(result); templateData.value.classList.add((element.severity === severity.Warning) ? 'warn' : (element.severity === severity.Error) ? 'error' : (element.severity === severity.Ignore) ? 'ignore' : 'info'); @@ -185,9 +196,22 @@ export class ReplSimpleElementsRenderer implements ITreeRenderer element.sourceData; } + private setElementCount(element: SimpleReplElement, templateData: ISimpleReplElementTemplateData): void { + if (element.count >= 2) { + templateData.count.setCount(element.count); + templateData.countContainer.hidden = false; + } else { + templateData.countContainer.hidden = true; + } + } + disposeTemplate(templateData: ISimpleReplElementTemplateData): void { dispose(templateData.toDispose); } + + disposeElement(_element: ITreeNode, _index: number, templateData: ISimpleReplElementTemplateData): void { + templateData.elementListener.dispose(); + } } export class ReplVariablesRenderer extends AbstractExpressionsRenderer { @@ -296,14 +320,14 @@ export class ReplDelegate extends CachedListVirtualDelegate { if (element instanceof Variable && element.name) { return ReplVariablesRenderer.ID; } - if (element instanceof ReplEvaluationResult) { + if (element instanceof ReplEvaluationResult || (element instanceof Variable && !element.name)) { + // Variable with no name is a top level variable which should be rendered like a repl element #17404 return ReplEvaluationResultsRenderer.ID; } if (element instanceof ReplEvaluationInput) { return ReplEvaluationInputsRenderer.ID; } - if (element instanceof SimpleReplElement || (element instanceof Variable && !element.name)) { - // Variable with no name is a top level variable which should be rendered like a repl element #17404 + if (element instanceof SimpleReplElement) { return ReplSimpleElementsRenderer.ID; } if (element instanceof ReplGroup) { @@ -359,13 +383,13 @@ export class ReplAccessibilityProvider implements IListAccessibilityProvider 1 ? localize('occurred', ", occured {0} times", element.count) : ''); } if (element instanceof RawObjectReplElement) { return localize('replRawObjectAriaLabel', "Debug console variable {0}, value {1}", element.name, element.value); } if (element instanceof ReplGroup) { - return localize('replGroup', "Debug console group {0}, read eval print loop, debug", element.name); + return localize('replGroup', "Debug console group {0}", element.name); } return ''; diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 9a4560c03..7c8acdc41 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -30,12 +30,13 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c import { dispose } from 'vs/base/common/lifecycle'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { debugCollapseAll } from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; let forgetScopes = true; @@ -179,7 +180,7 @@ export class VariablesView extends ViewPane { } getActions(): IAction[] { - return [new CollapseAction(() => this.tree, true, 'explorer-action codicon-collapse-all')]; + return [new CollapseAction(() => this.tree, true, 'explorer-action ' + ThemeIcon.asClassName(debugCollapseAll))]; } layoutBody(width: number, height: number): void { @@ -207,8 +208,8 @@ export class VariablesView extends ViewPane { this.variableEvaluateName.set(!!variable.evaluateName); this.breakWhenValueChangesSupported.reset(); if (session && session.capabilities.supportsDataBreakpoints) { - const response = await session.dataBreakpointInfo(variable.name, variable.parent.reference); - const dataBreakpointId = response?.dataId; + dataBreakpointInfoResponse = await session.dataBreakpointInfo(variable.name, variable.parent.reference); + const dataBreakpointId = dataBreakpointInfoResponse?.dataId; this.breakWhenValueChangesSupported.set(!!dataBreakpointId); } diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index ac56c4791..2e18414c9 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -30,8 +30,9 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { dispose } from 'vs/base/common/lifecycle'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { debugCollapseAll } from 'vs/workbench/contrib/debug/browser/debugIcons'; const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; let ignoreViewUpdates = false; @@ -160,7 +161,7 @@ export class WatchExpressionsView extends ViewPane { getActions(): IAction[] { return [ new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService), - new CollapseAction(() => this.tree, true, 'explorer-action codicon-collapse-all'), + new CollapseAction(() => this.tree, true, 'explorer-action ' + ThemeIcon.asClassName(debugCollapseAll)), new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService) ]; } diff --git a/src/vs/workbench/contrib/debug/browser/welcomeView.ts b/src/vs/workbench/contrib/debug/browser/welcomeView.ts index 37f5d7975..c88b28e8b 100644 --- a/src/vs/workbench/contrib/debug/browser/welcomeView.ts +++ b/src/vs/workbench/contrib/debug/browser/welcomeView.ts @@ -22,7 +22,7 @@ import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { OpenFolderAction, OpenFileAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { isMacintosh } from 'vs/base/common/platform'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -65,10 +65,10 @@ export class WelcomeView extends ViewPane { if (isCodeEditor(editorControl)) { const model = editorControl.getModel(); const language = model ? model.getLanguageIdentifier().language : undefined; - if (language && this.debugService.getConfigurationManager().isDebuggerInterestedInLanguage(language)) { + if (language && this.debugService.getAdapterManager().isDebuggerInterestedInLanguage(language)) { this.debugStartLanguageContext.set(language); this.debuggerInterestedContext.set(true); - storageSevice.store(debugStartLanguageKey, language, StorageScope.WORKSPACE); + storageSevice.store(debugStartLanguageKey, language, StorageScope.WORKSPACE, StorageTarget.MACHINE); return; } } @@ -88,7 +88,7 @@ export class WelcomeView extends ViewPane { setContextKey(); })); - this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey)); + this._register(this.debugService.getAdapterManager().onDidRegisterDebugger(setContextKey)); this._register(this.onDidChangeBodyVisibility(visible => { if (visible) { setContextKey(); @@ -117,7 +117,6 @@ let debugKeybindingLabel = ''; viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'runAndDebugAction', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID), - preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR], when: CONTEXT_DEBUGGERS_AVAILABLE, group: ViewContentGroups.Debug }); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 782bb5af4..8f1715543 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -64,6 +64,7 @@ export const CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT = new RawContextKey('debugSetVariableSupported', false); export const CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED = new RawContextKey('breakWhenValueChangesSupported', false); export const CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT = new RawContextKey('variableEvaluateNamePresent', false); +export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey('exceptionWidgetVisible', false); export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug'; export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint'; @@ -379,6 +380,7 @@ export interface IBaseBreakpoint extends IEnablement { readonly logMessage?: string; readonly verified: boolean; readonly supported: boolean; + readonly sessionsThatVerified: string[]; getIdFromAdapter(sessionId: string): number | undefined; } @@ -400,6 +402,7 @@ export interface IFunctionBreakpoint extends IBaseBreakpoint { export interface IExceptionBreakpoint extends IEnablement { readonly filter: string; readonly label: string; + readonly condition: string | undefined; } export interface IDataBreakpoint extends IBaseBreakpoint { @@ -434,9 +437,9 @@ export interface IViewModel extends ITreeElement { readonly focusedStackFrame: IStackFrame | undefined; getSelectedExpression(): IExpression | undefined; - getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined; + getSelectedBreakpoint(): IFunctionBreakpoint | IExceptionBreakpoint | undefined; setSelectedExpression(expression: IExpression | undefined): void; - setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void; + setSelectedBreakpoint(functionBreakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined): void; updateViews(): void; isMultiSessionView(): boolean; @@ -615,8 +618,6 @@ export interface IPlatformSpecificAdapterContribution { export interface IDebuggerContribution extends IPlatformSpecificAdapterContribution { type: string; label?: string; - // debug adapter executable - adapterExecutableCommand?: string; win?: IPlatformSpecificAdapterContribution; winx86?: IPlatformSpecificAdapterContribution; windows?: IPlatformSpecificAdapterContribution; @@ -628,7 +629,7 @@ export interface IDebuggerContribution extends IPlatformSpecificAdapterContribut // supported languages languages?: string[]; - enableBreakpointsFor?: { languageIds: string[] }; + enableBreakpointsFor?: { languageIds?: string[] }; // debug configuration support configurationAttributes?: any; @@ -643,7 +644,6 @@ export interface IDebugConfigurationProvider { resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise; resolveDebugConfigurationWithSubstitutedVariables?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise; provideDebugConfigurations?(folderUri: uri | undefined, token: CancellationToken): Promise; - debugAdapterExecutable?(folderUri: uri | undefined): Promise; // TODO@AW legacy } export interface IDebugAdapterDescriptorFactory { @@ -660,57 +660,54 @@ export interface ITerminalLauncher { } export interface IConfigurationManager { - /** - * Returns true if breakpoints can be set for a given editor model. Depends on mode. - */ - canSetBreakpointsIn(model: EditorIModel): boolean; /** * Returns an object containing the selected launch configuration and the selected configuration name. Both these fields can be null (no folder workspace). */ readonly selectedConfiguration: { launch: ILaunch | undefined; - config: IConfig | undefined; + // Potentially activates extensions + getConfig: () => Promise; name: string | undefined; // Type is used when matching dynamic configurations to their corresponding provider type: string | undefined; }; - selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, type?: string): Promise; + selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, dynamicConfigOptions?: { type?: string }): Promise; getLaunches(): ReadonlyArray; - - hasDebuggers(): boolean; - getLaunch(workspaceUri: uri | undefined): ILaunch | undefined; - getAllConfigurations(): { launch: ILaunch, name: string, presentation?: IConfigPresentation }[]; + getRecentDynamicConfigurations(): { name: string, type: string }[]; /** * Allows to register on change of selected debug configuration. */ onDidSelectConfiguration: Event; - onDidRegisterDebugger: Event; - - activateDebuggers(activationEvent: string, debugType?: string): Promise; - - isDebuggerInterestedInLanguage(language: string): boolean; hasDebugConfigurationProvider(debugType: string): boolean; getDynamicProviders(): Promise<{ label: string, type: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]>; registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable; unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void; - registerDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): IDisposable; - unregisterDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): void; - resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any, token: CancellationToken): Promise; +} + +export interface IAdapterManager { + + onDidRegisterDebugger: Event; + + hasDebuggers(): boolean; getDebugAdapterDescriptor(session: IDebugSession): Promise; getDebuggerLabel(type: string): string | undefined; + isDebuggerInterestedInLanguage(language: string): boolean; + activateDebuggers(activationEvent: string, debugType?: string): Promise; registerDebugAdapterFactory(debugTypes: string[], debugAdapterFactory: IDebugAdapterFactory): IDisposable; createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined; + registerDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): IDisposable; + unregisterDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): void; substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise; runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments): Promise; @@ -795,15 +792,25 @@ export interface IDebugService { onDidEndSession: Event; /** - * Gets the current configuration manager. + * Gets the configuration manager. */ getConfigurationManager(): IConfigurationManager; + /** + * Gets the adapter manager. + */ + getAdapterManager(): IAdapterManager; + /** * Sets the focused stack frame and evaluates all expressions against the newly focused stack frame, */ focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise; + /** + * Returns true if breakpoints can be set for a given editor model. Depends on mode. + */ + canSetBreakpointsIn(model: EditorIModel): boolean; + /** * Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes. */ @@ -860,6 +867,8 @@ export interface IDebugService { */ removeDataBreakpoints(id?: string): Promise; + setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string | undefined): Promise; + /** * Sends all breakpoints to the passed session. * If session is not passed, sends all breakpoints to each session. @@ -932,6 +941,7 @@ export const enum BreakpointWidgetContext { export interface IDebugEditorContribution extends editorCommon.IEditorContribution { showHover(range: Range, focus: boolean): Promise; addLaunchConfiguration(): Promise; + closeExceptionWidget(): void; } export interface IBreakpointEditorContribution extends editorCommon.IEditorContribution { diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 1d203c90b..b98b3b897 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -393,6 +393,7 @@ export class Thread implements IThread { private callStackCancellationTokens: CancellationTokenSource[] = []; public stoppedDetails: IRawStoppedDetails | undefined; public stopped: boolean; + public reachedEndOfCallStack = false; constructor(public session: IDebugSession, public name: string, public threadId: number) { this.callStack = []; @@ -445,6 +446,7 @@ export class Thread implements IThread { if (this.stopped) { const start = this.callStack.length; const callStack = await this.getCallStackImpl(start, levels); + this.reachedEndOfCallStack = callStack.length < levels; if (start < this.callStack.length) { // Set the stack frames for exact position we requested. To make sure no concurrent requests create duplicate stack frames #30660 this.callStack.splice(start, this.callStack.length - start); @@ -613,6 +615,17 @@ export abstract class BaseBreakpoint extends Enablement implements IBaseBreakpoi return this.data ? this.data.verified : true; } + get sessionsThatVerified() { + const sessionIds: string[] = []; + for (const [sessionId, data] of this.sessionData) { + if (data.verified) { + sessionIds.push(sessionId); + } + } + + return sessionIds; + } + abstract get supported(): boolean; getIdFromAdapter(sessionId: string): number | undefined { @@ -847,7 +860,7 @@ export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint { export class ExceptionBreakpoint extends Enablement implements IExceptionBreakpoint { - constructor(public filter: string, public label: string, enabled: boolean) { + constructor(public filter: string, public label: string, enabled: boolean, public supportsCondition: boolean, public condition: string | undefined) { super(enabled, generateUuid()); } @@ -856,6 +869,8 @@ export class ExceptionBreakpoint extends Enablement implements IExceptionBreakpo result.filter = this.filter; result.label = this.label; result.enabled = this.enabled; + result.supportsCondition = this.supportsCondition; + result.condition = this.condition; return result; } @@ -1049,19 +1064,24 @@ export class DebugModel implements IDebugModel { setExceptionBreakpoints(data: DebugProtocol.ExceptionBreakpointsFilter[]): void { if (data) { - if (this.exceptionBreakpoints.length === data.length && this.exceptionBreakpoints.every((exbp, i) => exbp.filter === data[i].filter && exbp.label === data[i].label)) { + if (this.exceptionBreakpoints.length === data.length && this.exceptionBreakpoints.every((exbp, i) => exbp.filter === data[i].filter && exbp.label === data[i].label && exbp.supportsCondition === data[i].supportsCondition)) { // No change return; } this.exceptionBreakpoints = data.map(d => { const ebp = this.exceptionBreakpoints.filter(ebp => ebp.filter === d.filter).pop(); - return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : !!d.default); + return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : !!d.default, !!d.supportsCondition, ebp?.condition); }); this._onDidChangeBreakpoints.fire(undefined); } } + setExceptionBreakpointCondition(exceptionBreakpoint: IExceptionBreakpoint, condition: string | undefined): void { + (exceptionBreakpoint as ExceptionBreakpoint).condition = condition; + this._onDidChangeBreakpoints.fire(undefined); + } + areBreakpointsActivated(): boolean { return this.breakpointsActivated; } diff --git a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts index 0f0374d54..f11bab4fb 100644 --- a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts +++ b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts @@ -16,7 +16,7 @@ declare module DebugProtocol { /** Message type. Values: 'request', 'response', 'event', etc. */ - type: string; + type: 'request' | 'response' | 'event' | string; } /** A client or debug adapter initiated request. */ @@ -56,7 +56,7 @@ declare module DebugProtocol { 'cancelled': request was cancelled. etc. */ - message?: string; + message?: 'cancelled' | string; /** Contains request result if success is true and optional error details if success is false. */ body?: any; } @@ -129,7 +129,7 @@ declare module DebugProtocol { For backward compatibility this string is shown in the UI if the 'description' attribute is missing (but it must not be translated). Values: 'step', 'breakpoint', 'exception', 'pause', 'entry', 'goto', 'function breakpoint', 'data breakpoint', 'instruction breakpoint', etc. */ - reason: string; + reason: 'step' | 'breakpoint' | 'exception' | 'pause' | 'entry' | 'goto' | 'function breakpoint' | 'data breakpoint' | 'instruction breakpoint' | string; /** The full reason for the event, e.g. 'Paused on exception'. This string is shown in the UI as is and must be translated. */ description?: string; /** The thread which was stopped. */ @@ -194,7 +194,7 @@ declare module DebugProtocol { /** The reason for the event. Values: 'started', 'exited', etc. */ - reason: string; + reason: 'started' | 'exited' | string; /** The identifier of the thread. */ threadId: number; }; @@ -209,7 +209,7 @@ declare module DebugProtocol { /** The output category. If not specified, 'console' is assumed. Values: 'console', 'stdout', 'stderr', 'telemetry', etc. */ - category?: string; + category?: 'console' | 'stdout' | 'stderr' | 'telemetry' | string; /** The output to report. */ output: string; /** Support for keeping an output log organized by grouping related messages. @@ -221,7 +221,7 @@ declare module DebugProtocol { A non empty 'output' attribute is shown as the unindented end of the group. */ group?: 'start' | 'startCollapsed' | 'end'; - /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31 - 1). */ + /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31-1). */ variablesReference?: number; /** An optional source location where the output was produced. */ source?: Source; @@ -243,7 +243,7 @@ declare module DebugProtocol { /** The reason for the event. Values: 'changed', 'new', 'removed', etc. */ - reason: string; + reason: 'changed' | 'new' | 'removed' | string; /** The 'id' attribute is used to find the target breakpoint and the other attributes are used as the new values. */ breakpoint: Breakpoint; }; @@ -383,7 +383,7 @@ declare module DebugProtocol { export interface InvalidatedEvent extends Event { // event: 'invalidated'; body: { - /** Optional set of logical areas that got invalidated. If this property is missing or empty, a single value 'all' is assumed. */ + /** Optional set of logical areas that got invalidated. This property has a hint characteristic: a client can only be expected to make a 'best effort' in honouring the areas but there are no guarantees. If this property is missing, empty, or if values are not understand the client should assume a single value 'all'. */ areas?: InvalidatedAreas[]; /** If specified, the client only needs to refetch data related to this thread. */ threadId?: number; @@ -408,7 +408,7 @@ declare module DebugProtocol { kind?: 'integrated' | 'external'; /** Optional title of the terminal. */ title?: string; - /** Working directory of the command. */ + /** Working directory for the command. For non-empty, valid paths this typically results in execution of a change directory command. */ cwd: string; /** List of arguments. The first argument is the command to run. */ args: string[]; @@ -419,9 +419,9 @@ declare module DebugProtocol { /** Response to 'runInTerminal' request. */ export interface RunInTerminalResponse extends Response { body: { - /** The process ID. The value should be less than or equal to 2147483647 (2^31 - 1). */ + /** The process ID. The value should be less than or equal to 2147483647 (2^31-1). */ processId?: number; - /** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31 - 1). */ + /** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31-1). */ shellProcessId?: number; }; } @@ -455,7 +455,7 @@ declare module DebugProtocol { /** Determines in what format paths are specified. The default is 'path', which is the native format. Values: 'path', 'uri', etc. */ - pathFormat?: string; + pathFormat?: 'path' | 'uri' | string; /** Client supports the optional type attribute for variables. */ supportsVariableType?: boolean; /** Client supports the paging of variables. */ @@ -712,8 +712,10 @@ declare module DebugProtocol { /** Arguments for 'setExceptionBreakpoints' request. */ export interface SetExceptionBreakpointsArguments { - /** IDs of checked exception options. The set of IDs is returned via the 'exceptionBreakpointFilters' capability. */ + /** Set of exception filters specified by their ID. The set of all possible exception filters is defined by the 'exceptionBreakpointFilters' capability. The 'filter' and 'filterOptions' sets are additive. */ filters: string[]; + /** Set of exception filters and their options. The set of all possible exception filters is defined by the 'exceptionBreakpointFilters' capability. This attribute is only honored by a debug adapter if the capability 'supportsExceptionFilterOptions' is true. The 'filter' and 'filterOptions' sets are additive. */ + filterOptions?: ExceptionFilterOptions[]; /** Configuration options for selected exceptions. The attribute is only honored by a debug adapter if the capability 'supportsExceptionOptions' is true. */ @@ -1009,7 +1011,8 @@ declare module DebugProtocol { } /** StackTrace request; value of command field is 'stackTrace'. - The request returns a stacktrace from the current execution state. + The request returns a stacktrace from the current execution state of a given thread. + A client can request all stack frames by omitting the startFrame and levels arguments. For performance conscious clients stack frames can be retrieved in a piecemeal way with the startFrame and levels arguments. The response of the stackTrace request may contain a totalFrames property that hints at the total number of frames in the stack. If a client needs this total number upfront, it can issue a request for a single (first) frame and depending on the value of totalFrames decide how to proceed. In any case a client should be prepared to receive less frames than requested, which is an indication that the end of the stack has been reached. */ export interface StackTraceRequest extends Request { // command: 'stackTrace'; @@ -1037,7 +1040,7 @@ declare module DebugProtocol { This means that there is no location information available. */ stackFrames: StackFrame[]; - /** The total number of frames available. */ + /** The total number of frames available in the stack. If omitted or if totalFrames is larger than the available frames, a client is expected to request frames until a request returns less frames than requested (which indicates the end of the stack). Returning monotonically increasing totalFrames values for subsequent requests can be used to enforce paging in the client. */ totalFrames?: number; }; } @@ -1125,17 +1128,17 @@ declare module DebugProtocol { /** The type of the new value. Typically shown in the UI when hovering over the value. */ type?: string; /** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ variablesReference?: number; /** The number of named child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ namedVariables?: number; /** The number of indexed child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ indexedVariables?: number; }; @@ -1275,7 +1278,7 @@ declare module DebugProtocol { The attribute is only honored by a debug adapter if the capability 'supportsClipboardContext' is true. etc. */ - context?: string; + context?: 'watch' | 'repl' | 'hover' | 'clipboard' | string; /** Specifies details on how to format the Evaluate result. The attribute is only honored by a debug adapter if the capability 'supportsValueFormattingOptions' is true. */ @@ -1294,17 +1297,17 @@ declare module DebugProtocol { /** Properties of a evaluate result that can be used to determine how to render the result in the UI. */ presentationHint?: VariablePresentationHint; /** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ variablesReference: number; /** The number of named child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ namedVariables?: number; /** The number of indexed child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ indexedVariables?: number; /** Optional memory reference to a location appropriate for this result. @@ -1349,17 +1352,17 @@ declare module DebugProtocol { /** Properties of a value that can be used to determine how to render the result in the UI. */ presentationHint?: VariablePresentationHint; /** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ variablesReference?: number; /** The number of named child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ namedVariables?: number; /** The number of indexed child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ indexedVariables?: number; }; @@ -1556,7 +1559,7 @@ declare module DebugProtocol { supportsHitConditionalBreakpoints?: boolean; /** The debug adapter supports a (side effect free) evaluate request for data hovers. */ supportsEvaluateForHovers?: boolean; - /** Available filters or options for the setExceptionBreakpoints request. */ + /** Available exception filter options for the 'setExceptionBreakpoints' request. */ exceptionBreakpointFilters?: ExceptionBreakpointsFilter[]; /** The debug adapter supports stepping back via the 'stepBack' and 'reverseContinue' requests. */ supportsStepBack?: boolean; @@ -1616,16 +1619,20 @@ declare module DebugProtocol { supportsSteppingGranularity?: boolean; /** The debug adapter supports adding breakpoints based on instruction references. */ supportsInstructionBreakpoints?: boolean; + /** The debug adapter supports 'filterOptions' as an argument on the 'setExceptionBreakpoints' request. */ + supportsExceptionFilterOptions?: boolean; } - /** An ExceptionBreakpointsFilter is shown in the UI as an option for configuring how exceptions are dealt with. */ + /** An ExceptionBreakpointsFilter is shown in the UI as an filter option for configuring how exceptions are dealt with. */ export interface ExceptionBreakpointsFilter { - /** The internal ID of the filter. This value is passed to the setExceptionBreakpoints request. */ + /** The internal ID of the filter option. This value is passed to the 'setExceptionBreakpoints' request. */ filter: string; - /** The name of the filter. This will be shown in the UI. */ + /** The name of the filter option. This will be shown in the UI. */ label: string; - /** Initial value of the filter. If not specified a value 'false' is assumed. */ + /** Initial value of the filter option. If not specified a value 'false' is assumed. */ default?: boolean; + /** Controls whether a condition can be specified for this filter option. If false or missing, a condition can not be set. */ + supportsCondition?: boolean; } /** A structured message object. Used to return errors from requests. */ @@ -1730,7 +1737,7 @@ declare module DebugProtocol { path?: string; /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ sourceReference?: number; /** An optional hint for how to present the source in the UI. @@ -1788,7 +1795,7 @@ declare module DebugProtocol { 'registers': Scope contains registers. Only a single 'registers' scope should be returned from a 'scopes' request. etc. */ - presentationHint?: string; + presentationHint?: 'arguments' | 'locals' | 'registers' | string; /** The variables of this scope can be retrieved by passing the value of variablesReference to the VariablesRequest. */ variablesReference: number; /** The number of named variables in this scope. @@ -1867,7 +1874,7 @@ declare module DebugProtocol { 'dataBreakpoint': Indicates that a data breakpoint is registered for the object. etc. */ - kind?: string; + kind?: 'property' | 'method' | 'class' | 'data' | 'event' | 'baseClass' | 'innerClass' | 'interface' | 'mostDerivedClass' | 'virtual' | 'dataBreakpoint' | string; /** Set of attributes represented as an array of strings. Before introducing additional values, try to use the listed values. Values: 'static': Indicates that the object is static. @@ -1879,11 +1886,11 @@ declare module DebugProtocol { 'hasSideEffects': Indicates that the evaluation had side effects. etc. */ - attributes?: string[]; + attributes?: ('static' | 'constant' | 'readOnly' | 'rawString' | 'hasObjectId' | 'canHaveObjectId' | 'hasSideEffects' | string)[]; /** Visibility of variable. Before introducing additional values, try to use the listed values. Values: 'public', 'private', 'protected', 'internal', 'final', etc. */ - visibility?: string; + visibility?: 'public' | 'private' | 'protected' | 'internal' | 'final' | string; } /** Properties of a breakpoint location returned from the 'breakpointLocations' request. */ @@ -2108,6 +2115,16 @@ declare module DebugProtocol { includeAll?: boolean; } + /** An ExceptionFilterOptions is used to specify an exception filter together with a condition for the setExceptionsFilter request. */ + export interface ExceptionFilterOptions { + /** ID of an exception filter returned by the 'exceptionBreakpointFilters' capability. */ + filterId: string; + /** An optional expression for conditional exceptions. + The exception will break into the debugger if the result of the condition is true. + */ + condition?: string; + } + /** An ExceptionOptions assigns configuration options to a set of exceptions. */ export interface ExceptionOptions { /** A path that selects a single or multiple exceptions in a tree. If 'path' is missing, the whole tree is selected. @@ -2179,11 +2196,13 @@ declare module DebugProtocol { } /** Logical areas that can be invalidated by the 'invalidated' event. + Values: 'all': All previously fetched data has become invalid and needs to be refetched. 'stacks': Previously fetched stack related data has become invalid and needs to be refetched. 'threads': Previously fetched thread related data has become invalid and needs to be refetched. 'variables': Previously fetched variable data has become invalid and needs to be refetched. + etc. */ - export type InvalidatedAreas = 'all' | 'stacks' | 'threads' | 'variables'; + export type InvalidatedAreas = 'all' | 'stacks' | 'threads' | 'variables' | string; } diff --git a/src/vs/workbench/contrib/debug/common/debugStorage.ts b/src/vs/workbench/contrib/debug/common/debugStorage.ts index 6cabc0ba9..42583a616 100644 --- a/src/vs/workbench/contrib/debug/common/debugStorage.ts +++ b/src/vs/workbench/contrib/debug/common/debugStorage.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; +import { StorageScope, IStorageService, StorageTarget } from 'vs/platform/storage/common/storage'; import { ExceptionBreakpoint, Expression, Breakpoint, FunctionBreakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IEvaluate, IExpression, IDebugModel } from 'vs/workbench/contrib/debug/common/debug'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -49,7 +49,7 @@ export class DebugStorage { let result: ExceptionBreakpoint[] | undefined; try { result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => { - return new ExceptionBreakpoint(exBreakpoint.filter, exBreakpoint.label, exBreakpoint.enabled); + return new ExceptionBreakpoint(exBreakpoint.filter, exBreakpoint.label, exBreakpoint.enabled, exBreakpoint.supportsCondition, exBreakpoint.condition); }); } catch (e) { } @@ -80,7 +80,7 @@ export class DebugStorage { storeWatchExpressions(watchExpressions: (IExpression & IEvaluate)[]): void { if (watchExpressions.length) { - this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(watchExpressions.map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(watchExpressions.map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE); } @@ -89,28 +89,28 @@ export class DebugStorage { storeBreakpoints(debugModel: IDebugModel): void { const breakpoints = debugModel.getBreakpoints(); if (breakpoints.length) { - this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(breakpoints), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(breakpoints), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE); } const functionBreakpoints = debugModel.getFunctionBreakpoints(); if (functionBreakpoints.length) { - this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(functionBreakpoints), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(functionBreakpoints), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE); } const dataBreakpoints = debugModel.getDataBreakpoints().filter(dbp => dbp.canPersist); if (dataBreakpoints.length) { - this.storageService.store(DEBUG_DATA_BREAKPOINTS_KEY, JSON.stringify(dataBreakpoints), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_DATA_BREAKPOINTS_KEY, JSON.stringify(dataBreakpoints), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE); } const exceptionBreakpoints = debugModel.getExceptionBreakpoints(); if (exceptionBreakpoints.length) { - this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE); } diff --git a/src/vs/workbench/contrib/debug/common/debugViewModel.ts b/src/vs/workbench/contrib/debug/common/debugViewModel.ts index 7a4b1fbac..e7541cd89 100644 --- a/src/vs/workbench/contrib/debug/common/debugViewModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugViewModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; +import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, IExceptionBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils'; @@ -16,7 +16,7 @@ export class ViewModel implements IViewModel { private _focusedSession: IDebugSession | undefined; private _focusedThread: IThread | undefined; private selectedExpression: IExpression | undefined; - private selectedFunctionBreakpoint: IFunctionBreakpoint | undefined; + private selectedBreakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined; private readonly _onDidFocusSession = new Emitter(); private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>(); private readonly _onDidSelectExpression = new Emitter(); @@ -112,8 +112,8 @@ export class ViewModel implements IViewModel { return this._onDidSelectExpression.event; } - getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined { - return this.selectedFunctionBreakpoint; + getSelectedBreakpoint(): IFunctionBreakpoint | IExceptionBreakpoint | undefined { + return this.selectedBreakpoint; } updateViews(): void { @@ -124,9 +124,9 @@ export class ViewModel implements IViewModel { return this._onWillUpdateViews.event; } - setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void { - this.selectedFunctionBreakpoint = functionBreakpoint; - this.breakpointSelectedContextKey.set(!!functionBreakpoint); + setSelectedBreakpoint(breakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined): void { + this.selectedBreakpoint = breakpoint; + this.breakpointSelectedContextKey.set(!!breakpoint); } isMultiSessionView(): boolean { diff --git a/src/vs/workbench/contrib/debug/common/debugger.ts b/src/vs/workbench/contrib/debug/common/debugger.ts index ae8bc52db..b60cda04e 100644 --- a/src/vs/workbench/contrib/debug/common/debugger.ts +++ b/src/vs/workbench/contrib/debug/common/debugger.ts @@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects'; import { isObject } from 'vs/base/common/types'; import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { IConfig, IDebuggerContribution, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, IDebugger, IDebugSession, IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug'; +import { IConfig, IDebuggerContribution, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IDebugAdapter, IDebugger, IDebugSession, IDebugHelperService, IAdapterManager, IDebugService } from 'vs/workbench/contrib/debug/common/debug'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import * as ConfigurationResolverUtils from 'vs/workbench/services/configurationResolver/common/configurationResolverUtils'; @@ -30,14 +30,15 @@ export class Debugger implements IDebugger { private mainExtensionDescription: IExtensionDescription | undefined; constructor( - private configurationManager: IConfigurationManager, + private adapterManager: IAdapterManager, dbgContribution: IDebuggerContribution, extensionDescription: IExtensionDescription, @IConfigurationService private readonly configurationService: IConfigurationService, @ITextResourcePropertiesService private readonly resourcePropertiesService: ITextResourcePropertiesService, @IConfigurationResolverService private readonly configurationResolverService: IConfigurationResolverService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IDebugHelperService private readonly debugHelperService: IDebugHelperService + @IDebugHelperService private readonly debugHelperService: IDebugHelperService, + @IDebugService private readonly debugService: IDebugService ) { this.debuggerContribution = { type: dbgContribution.type }; this.merge(dbgContribution, extensionDescription); @@ -97,8 +98,8 @@ export class Debugger implements IDebugger { } createDebugAdapter(session: IDebugSession): Promise { - return this.configurationManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type).then(_ => { - const da = this.configurationManager.createDebugAdapter(session); + return this.adapterManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type).then(_ => { + const da = this.adapterManager.createDebugAdapter(session); if (da) { return Promise.resolve(da); } @@ -107,13 +108,13 @@ export class Debugger implements IDebugger { } substituteVariables(folder: IWorkspaceFolder | undefined, config: IConfig): Promise { - return this.configurationManager.substituteVariables(this.type, folder, config).then(config => { + return this.adapterManager.substituteVariables(this.type, folder, config).then(config => { return this.configurationResolverService.resolveWithInteractionReplace(folder, config, 'launch', this.variables, config.__configurationTarget); }); } runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise { - return this.configurationManager.runInTerminal(this.type, args); + return this.adapterManager.runInTerminal(this.type, args); } get label(): string { @@ -141,7 +142,7 @@ export class Debugger implements IDebugger { } hasConfigurationProvider(): boolean { - return this.configurationManager.hasDebugConfigurationProvider(this.type); + return this.debugService.getConfigurationManager().hasDebugConfigurationProvider(this.type); } getInitialConfigurationContent(initialConfigs?: IConfig[]): Promise { diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index 04297b0e9..f10608a01 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -11,12 +11,16 @@ import { isString, isUndefinedOrNull, isObject } from 'vs/base/common/types'; import { basenameOrAuthority } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; const MAX_REPL_LENGTH = 10000; let topReplElementCounter = 0; export class SimpleReplElement implements IReplElement { + + private _count = 1; + private _onDidChangeCount = new Emitter(); + constructor( public session: IDebugSession, private id: string, @@ -33,6 +37,19 @@ export class SimpleReplElement implements IReplElement { getId(): string { return this.id; } + + set count(value: number) { + this._count = value; + this._onDidChangeCount.fire(); + } + + get count(): number { + return this._count; + } + + get onDidChangeCount(): Event { + return this._onDidChangeCount.event; + } } export class RawObjectReplElement implements IExpression { @@ -202,13 +219,21 @@ export class ReplModel { if (typeof data === 'string') { const previousElement = this.replElements.length ? this.replElements[this.replElements.length - 1] : undefined; - if (previousElement instanceof SimpleReplElement && previousElement.severity === sev && !previousElement.value.endsWith('\n') && !previousElement.value.endsWith('\r\n')) { - previousElement.value += data; - this._onDidChangeElements.fire(); - } else { - const element = new SimpleReplElement(session, `topReplElement:${topReplElementCounter++}`, data, sev, source); - this.addReplElement(element); + if (previousElement instanceof SimpleReplElement && previousElement.severity === sev) { + if (previousElement.value === data) { + previousElement.count++; + // No need to fire an event, just the count updates and badge will adjust automatically + return; + } + if (!previousElement.value.endsWith('\n') && !previousElement.value.endsWith('\r\n') && previousElement.count === 1) { + previousElement.value += data; + this._onDidChangeElements.fire(); + return; + } } + + const element = new SimpleReplElement(session, `topReplElement:${topReplElementCounter++}`, data, sev, source); + this.addReplElement(element); } else { // TODO@Isidor hack, we should introduce a new type which is an output that can fetch children like an expression (data).severity = sev; diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index dc6d941a0..d8ba9f71e 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -199,9 +199,9 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { "Cannot determine executable for debug adapter '{0}'.", this.debugType)); } - let env = objects.mixin({}, process.env); - if (options.env) { - env = objects.mixin(env, options.env); + let env = process.env; + if (options.env && Object.keys(options.env).length > 0) { + env = objects.mixin(objects.deepClone(process.env), options.env); } if (command === 'node') { @@ -263,6 +263,8 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { channel.append(sanitize(data)); } }); + } else { + this.serverProcess.stderr!.resume(); } // finally connect to the DA diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index 0c27cb6d5..45a8fc654 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -9,6 +9,7 @@ import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExtern import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; +import { extractDriveLetter } from 'vs/base/common/labels'; let externalTerminalService: IExternalTerminalService | undefined = undefined; @@ -111,7 +112,11 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env? }; if (cwd) { - command += `cd '${cwd}'; `; + const driveLetter = extractDriveLetter(cwd); + if (driveLetter) { + command += `${driveLetter}:; `; + } + command += `cd ${quote(cwd)}; `; } if (env) { for (let key in env) { @@ -140,6 +145,10 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env? }; if (cwd) { + const driveLetter = extractDriveLetter(cwd); + if (driveLetter) { + command += `${driveLetter}: && `; + } command += `cd ${quote(cwd)} && `; } if (env) { diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index aece705d6..1a2bed974 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { URI as uri } from 'vs/base/common/uri'; import { DebugModel, Breakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; -import { getExpandedBodySize, getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView'; +import { getExpandedBodySize, getBreakpointMessageAndIcon } from 'vs/workbench/contrib/debug/browser/breakpointsView'; import { dispose } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; import { IBreakpointData, IBreakpointUpdateData, State } from 'vs/workbench/contrib/debug/common/debug'; @@ -258,40 +258,40 @@ suite('Debug - Breakpoints', () => { ]); const breakpoints = model.getBreakpoints(); - let result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[0]); - assert.equal(result.message, 'Expression: x > 5'); - assert.equal(result.className, 'codicon-debug-breakpoint-conditional'); + let result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[0]); + assert.equal(result.message, 'Expression condition: x > 5'); + assert.equal(result.icon.id, 'debug-breakpoint-conditional'); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[1]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[1]); assert.equal(result.message, 'Disabled Breakpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint-disabled'); + assert.equal(result.icon.id, 'debug-breakpoint-disabled'); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[2]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[2]); assert.equal(result.message, 'Log Message: hello'); - assert.equal(result.className, 'codicon-debug-breakpoint-log'); + assert.equal(result.icon.id, 'debug-breakpoint-log'); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[3]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[3]); assert.equal(result.message, 'Hit Count: 12'); - assert.equal(result.className, 'codicon-debug-breakpoint-conditional'); + assert.equal(result.icon.id, 'debug-breakpoint-conditional'); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[4]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[4]); assert.equal(result.message, 'Breakpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint'); + assert.equal(result.icon.id, 'debug-breakpoint'); - result = getBreakpointMessageAndClassName(State.Stopped, false, breakpoints[2]); + result = getBreakpointMessageAndIcon(State.Stopped, false, breakpoints[2]); assert.equal(result.message, 'Disabled Logpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint-log-disabled'); + assert.equal(result.icon.id, 'debug-breakpoint-log-disabled'); model.addDataBreakpoint('label', 'id', true, ['read']); const dataBreakpoints = model.getDataBreakpoints(); - result = getBreakpointMessageAndClassName(State.Stopped, true, dataBreakpoints[0]); + result = getBreakpointMessageAndIcon(State.Stopped, true, dataBreakpoints[0]); assert.equal(result.message, 'Data Breakpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint-data'); + assert.equal(result.icon.id, 'debug-breakpoint-data'); const functionBreakpoint = model.addFunctionBreakpoint('foo', '1'); - result = getBreakpointMessageAndClassName(State.Stopped, true, functionBreakpoint); + result = getBreakpointMessageAndIcon(State.Stopped, true, functionBreakpoint); assert.equal(result.message, 'Function Breakpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint-function'); + assert.equal(result.icon.id, 'debug-breakpoint-function'); const data = new Map(); data.set(breakpoints[0].getId(), { verified: false, line: 10 }); @@ -300,17 +300,17 @@ suite('Debug - Breakpoints', () => { data.set(functionBreakpoint.getId(), { verified: true }); model.setBreakpointSessionData('mocksessionid', { supportsFunctionBreakpoints: false, supportsDataBreakpoints: true, supportsLogPoints: true }, data); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[0]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[0]); assert.equal(result.message, 'Unverified Breakpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint-unverified'); + assert.equal(result.icon.id, 'debug-breakpoint-unverified'); - result = getBreakpointMessageAndClassName(State.Stopped, true, functionBreakpoint); + result = getBreakpointMessageAndIcon(State.Stopped, true, functionBreakpoint); assert.equal(result.message, 'Function breakpoints not supported by this debug type'); - assert.equal(result.className, 'codicon-debug-breakpoint-function-unverified'); + assert.equal(result.icon.id, 'debug-breakpoint-function-unverified'); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[2]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[2]); assert.equal(result.message, 'Log Message: hello, world'); - assert.equal(result.className, 'codicon-debug-breakpoint-log'); + assert.equal(result.icon.id, 'debug-breakpoint-log'); }); test('decorations', () => { @@ -337,7 +337,7 @@ suite('Debug - Breakpoints', () => { assert.equal(decorations[0].options.beforeContentClassName, undefined); assert.equal(decorations[1].options.beforeContentClassName, `debug-breakpoint-placeholder`); assert.equal(decorations[0].options.overviewRuler?.position, OverviewRulerLane.Left); - const expected = new MarkdownString().appendCodeblock(languageIdentifier.language, 'Expression: x > 5'); + const expected = new MarkdownString().appendCodeblock(languageIdentifier.language, 'Expression condition: x > 5'); assert.deepEqual(decorations[0].options.glyphMarginHoverMessage, expected); decorations = createBreakpointDecorations(textModel, breakpoints, State.Running, true, false); diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index ba1d0fa31..ca7c0db50 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -17,6 +17,8 @@ import { Constants } from 'vs/base/common/uint'; import { getContext, getContextForContributedActions, getSpecificSourceName } from 'vs/workbench/contrib/debug/browser/callStackView'; import { getStackFrameThreadAndSessionToFocus } from 'vs/workbench/contrib/debug/browser/debugService'; import { generateUuid } from 'vs/base/common/uuid'; +import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession { return new DebugSession(generateUuid(), { resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, { @@ -308,7 +310,7 @@ suite('Debug - CallStack', () => { let decorations = createDecorationsForStackFrame(firstStackFrame, firstStackFrame.range, true); assert.equal(decorations.length, 2); assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1)); - assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe'); + assert.equal(decorations[0].options.glyphMarginClassName, ThemeIcon.asClassName(debugStackframe)); assert.deepEqual(decorations[1].range, new Range(1, Constants.MAX_SAFE_SMALL_INTEGER, 1, 1)); assert.equal(decorations[1].options.className, 'debug-top-stack-frame-line'); assert.equal(decorations[1].options.isWholeLine, true); @@ -316,7 +318,7 @@ suite('Debug - CallStack', () => { decorations = createDecorationsForStackFrame(secondStackFrame, firstStackFrame.range, true); assert.equal(decorations.length, 2); assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1)); - assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe-focused'); + assert.equal(decorations[0].options.glyphMarginClassName, ThemeIcon.asClassName(debugStackframeFocused)); assert.deepEqual(decorations[1].range, new Range(1, Constants.MAX_SAFE_SMALL_INTEGER, 1, 1)); assert.equal(decorations[1].options.className, 'debug-focused-stack-frame-line'); assert.equal(decorations[1].options.isWholeLine, true); @@ -324,7 +326,7 @@ suite('Debug - CallStack', () => { decorations = createDecorationsForStackFrame(firstStackFrame, new Range(1, 5, 1, 6), true); assert.equal(decorations.length, 3); assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1)); - assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe'); + assert.equal(decorations[0].options.glyphMarginClassName, ThemeIcon.asClassName(debugStackframe)); assert.deepEqual(decorations[1].range, new Range(1, Constants.MAX_SAFE_SMALL_INTEGER, 1, 1)); assert.equal(decorations[1].options.className, 'debug-top-stack-frame-line'); assert.equal(decorations[1].options.isWholeLine, true); diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts index 4a885f892..96a346ab9 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts @@ -7,7 +7,7 @@ import { URI as uri } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Position, IPosition } from 'vs/editor/common/core/position'; -import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions, IEvaluate } from 'vs/workbench/contrib/debug/common/debug'; +import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions, IEvaluate, IAdapterManager } from 'vs/workbench/contrib/debug/common/debug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import Severity from 'vs/base/common/severity'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; @@ -17,6 +17,7 @@ import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompou import { CancellationToken } from 'vs/base/common/cancellation'; import { TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { ITextModel } from 'vs/editor/common/model'; const fileService = new TestFileService(); export const mockUriIdentityService = new UriIdentityService(fileService); @@ -49,6 +50,14 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } + getAdapterManager(): IAdapterManager { + throw new Error('Method not implemented.'); + } + + canSetBreakpointsIn(model: ITextModel): boolean { + throw new Error('Method not implemented.'); + } + public focusStackFrame(focusedStackFrame: IStackFrame): Promise { throw new Error('not implemented'); } @@ -77,6 +86,10 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } + setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string): Promise { + throw new Error('Method not implemented.'); + } + public addFunctionBreakpoint(): void { } public moveWatchExpression(id: string, position: number): void { } diff --git a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts index 815ec28a7..288fa962a 100644 --- a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts @@ -64,6 +64,41 @@ suite('Debug - REPL', () => { assert.equal(elements[0], '1\n'); assert.equal(elements[1], '23\n45\n'); assert.equal(elements[2], '6'); + + repl.removeReplExpressions(); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'second line', severity.Info); + repl.appendToRepl(session, 'second line', severity.Info); + repl.appendToRepl(session, 'third line', severity.Info); + elements = repl.getReplElements(); + assert.equal(elements.length, 3); + assert.equal(elements[0], 'first line\n'); + assert.equal(elements[0].count, 3); + assert.equal(elements[1], 'second line'); + assert.equal(elements[1].count, 2); + assert.equal(elements[2], 'third line'); + assert.equal(elements[2].count, 1); + }); + + test('repl output count', () => { + const session = createMockSession(model); + const repl = new ReplModel(); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'second line', severity.Info); + repl.appendToRepl(session, 'second line', severity.Info); + repl.appendToRepl(session, 'third line', severity.Info); + const elements = repl.getReplElements(); + assert.equal(elements.length, 3); + assert.equal(elements[0], 'first line\n'); + assert.equal(elements[0].count, 3); + assert.equal(elements[1], 'second line'); + assert.equal(elements[1].count, 2); + assert.equal(elements[2], 'third line'); + assert.equal(elements[2].count, 1); }); test('repl merging', () => { @@ -85,7 +120,7 @@ suite('Debug - REPL', () => { assert.equal(grandChild.getReplElements().length, 1); assert.equal(child3.getReplElements().length, 0); - grandChild.appendToRepl('1\n', severity.Info); + grandChild.appendToRepl('2\n', severity.Info); assert.equal(parentChanges, 2); assert.equal(parent.getReplElements().length, 2); assert.equal(child1.getReplElements().length, 0); @@ -93,7 +128,7 @@ suite('Debug - REPL', () => { assert.equal(grandChild.getReplElements().length, 2); assert.equal(child3.getReplElements().length, 0); - child3.appendToRepl('1\n', severity.Info); + child3.appendToRepl('3\n', severity.Info); assert.equal(parentChanges, 2); assert.equal(parent.getReplElements().length, 2); assert.equal(child1.getReplElements().length, 0); @@ -101,7 +136,7 @@ suite('Debug - REPL', () => { assert.equal(grandChild.getReplElements().length, 2); assert.equal(child3.getReplElements().length, 1); - child1.appendToRepl('1\n', severity.Info); + child1.appendToRepl('4\n', severity.Info); assert.equal(parentChanges, 2); assert.equal(parent.getReplElements().length, 2); assert.equal(child1.getReplElements().length, 1); diff --git a/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts b/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts index b56194b26..2a915d524 100644 --- a/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts +++ b/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts @@ -49,8 +49,8 @@ suite('Debug - ANSI Handling', () => { assert.equal(0, root.children.length); - appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector, session); - appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector, session); + appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector, session.root); + appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector, session.root); assert.equal(2, root.children.length); @@ -80,7 +80,7 @@ suite('Debug - ANSI Handling', () => { * @returns An {@link HTMLSpanElement} that contains the stylized text. */ function getSequenceOutput(sequence: string): HTMLSpanElement { - const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session); + const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session.root); assert.equal(1, root.children.length); const child: Node = root.lastChild!; if (child instanceof HTMLSpanElement) { @@ -320,7 +320,7 @@ suite('Debug - ANSI Handling', () => { if (elementsExpected === undefined) { elementsExpected = assertions.length; } - const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session); + const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session.root); assert.equal(elementsExpected, root.children.length); for (let i = 0; i < elementsExpected; i++) { const child: Node = root.children[i]; diff --git a/src/vs/workbench/contrib/debug/test/node/debugger.test.ts b/src/vs/workbench/contrib/debug/test/node/debugger.test.ts index 6fda912c7..68a34b4b1 100644 --- a/src/vs/workbench/contrib/debug/test/node/debugger.test.ts +++ b/src/vs/workbench/contrib/debug/test/node/debugger.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { join, normalize } from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; -import { IDebugAdapterExecutable, IConfigurationManager, IConfig, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugAdapterExecutable, IConfig, IDebugSession, IAdapterManager } from 'vs/workbench/contrib/debug/common/debug'; import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { URI } from 'vs/base/common/uri'; @@ -22,7 +22,6 @@ suite('Debug - Debugger', () => { const debuggerContribution = { type: 'mock', label: 'Mock Debug', - enableBreakpointsFor: { 'languageIds': ['markdown'] }, program: './out/mock/mockDebug.js', args: ['arg1', 'arg2'], configurationAttributes: { @@ -123,7 +122,7 @@ suite('Debug - Debugger', () => { }; - const configurationManager = { + const adapterManager = { getDebugAdapterDescriptor(session: IDebugSession, config: IConfig): Promise { return Promise.resolve(undefined); } @@ -133,7 +132,7 @@ suite('Debug - Debugger', () => { const testResourcePropertiesService = new TestTextResourcePropertiesService(configurationService); setup(() => { - _debugger = new Debugger(configurationManager, debuggerContribution, extensionDescriptor0, configurationService, testResourcePropertiesService, undefined!, undefined!, undefined!); + _debugger = new Debugger(adapterManager, debuggerContribution, extensionDescriptor0, configurationService, testResourcePropertiesService, undefined!, undefined!, undefined!, undefined!); }); teardown(() => { diff --git a/src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts b/src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts deleted file mode 100644 index 1d9314233..000000000 --- a/src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; - -import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { MenuId } from 'vs/platform/actions/common/actions'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; - -const EMMET_COMMANDS_PREFIX = '>Emmet: '; - -class ShowEmmetCommandsAction extends EditorAction { - - constructor() { - super({ - id: 'workbench.action.showEmmetCommands', - label: nls.localize('showEmmetCommands', "Show Emmet Commands"), - alias: 'Show Emmet Commands', - precondition: EditorContextKeys.writable, - menuOpts: { - menuId: MenuId.MenubarEditMenu, - group: '5_insert', - title: nls.localize({ key: 'miShowEmmetCommands', comment: ['&& denotes a mnemonic'] }, "E&&mmet..."), - order: 4 - } - }); - } - - public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { - const quickInputService = accessor.get(IQuickInputService); - quickInputService.quickAccess.show(EMMET_COMMANDS_PREFIX); - } -} - -registerEditorAction(ShowEmmetCommandsAction); diff --git a/src/vs/workbench/contrib/emmet/browser/emmet.contribution.ts b/src/vs/workbench/contrib/emmet/browser/emmet.contribution.ts index b603e987b..f29d6b2c0 100644 --- a/src/vs/workbench/contrib/emmet/browser/emmet.contribution.ts +++ b/src/vs/workbench/contrib/emmet/browser/emmet.contribution.ts @@ -3,6 +3,5 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import './actions/showEmmetCommands'; import './actions/expandAbbreviation'; diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index ee883ca58..a544ac4c6 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -5,7 +5,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -222,7 +222,7 @@ export class ExperimentService extends Disposable implements IExperimentService const storageKey = 'experiments.' + experimentId; const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); experimentState.state = ExperimentState.Complete; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); } protected async getExperiments(): Promise { @@ -280,7 +280,7 @@ export class ExperimentService extends Disposable implements IExperimentService }); } if (enabledExperiments.length) { - this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL); + this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL, StorageTarget.MACHINE); } else { this.storageService.remove('allExperiments', StorageScope.GLOBAL); } @@ -350,7 +350,7 @@ export class ExperimentService extends Disposable implements IExperimentService return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => { experimentState.state = processedExperiment.state = state; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); if (state === ExperimentState.Run) { this.fireRunExperiment(processedExperiment); @@ -370,7 +370,7 @@ export class ExperimentService extends Disposable implements IExperimentService // Ensure we dont store duplicates const distinctExperiments = distinct(runExperimentIdsFromStorage); if (runExperimentIdsFromStorage.length !== distinctExperiments.length) { - this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL); + this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL, StorageTarget.MACHINE); } } @@ -399,7 +399,7 @@ export class ExperimentService extends Disposable implements IExperimentService const key = experimentEventStorageKey(event); const record = getCurrentActivationRecord(safeParse(this.storageService.get(key, StorageScope.GLOBAL), undefined)); record.count[0]++; - this.storageService.store(key, JSON.stringify(record), StorageScope.GLOBAL); + this.storageService.store(key, JSON.stringify(record), StorageScope.GLOBAL, StorageTarget.MACHINE); this._experiments .filter(e => e.state === ExperimentState.Evaluating && e.raw?.condition?.activationEvent?.event === event) @@ -558,12 +558,12 @@ export class ExperimentService extends Disposable implements IExperimentService if (filePathCheck && workspaceCheck) { latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1; latestExperimentState.lastEditedDate = date; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); } }); if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) { processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); if (latestExperimentState.state === ExperimentState.Run && processedExperiment.action && ExperimentActionType[processedExperiment.action.type] === ExperimentActionType.Prompt) { this.fireRunExperiment(processedExperiment); } diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts index b0e18778a..584f71b24 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts @@ -64,7 +64,7 @@ suite('Experimental Prompts', () => { storageData = {}; instantiationService.stub(IStorageService, >{ get: (a: string, b: StorageScope, c?: string) => a === 'experiments.experiment1' ? JSON.stringify(storageData) : c, - store: (a, b, c) => { + store: (a, b, c, d) => { if (a === 'experiments.experiment1') { storageData = JSON.parse(b + ''); } diff --git a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts new file mode 100644 index 000000000..96c54f0a8 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts @@ -0,0 +1,474 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/runtimeExtensionsEditor'; +import * as nls from 'vs/nls'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; +import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; +import { WorkbenchList } from 'vs/platform/list/browser/listService'; +import { append, $, reset, Dimension, clearNode } from 'vs/base/browser/dom'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { memoize } from 'vs/base/common/decorators'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { Event } from 'vs/base/common/event'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { renderCodicons } from 'vs/base/browser/codicons'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { Schemas } from 'vs/base/common/network'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { domEvent } from 'vs/base/browser/event'; +import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; + +interface IExtensionProfileInformation { + /** + * segment when the extension was running. + * 2*i = segment start time + * 2*i+1 = segment end time + */ + segments: number[]; + /** + * total time when the extension was running. + * (sum of all segment lengths). + */ + totalTime: number; +} + +export interface IRuntimeExtension { + originalIndex: number; + description: IExtensionDescription; + marketplaceInfo: IExtension; + status: IExtensionsStatus; + profileInfo?: IExtensionProfileInformation; + unresponsiveProfile?: IExtensionHostProfile; +} + +export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { + + public static readonly ID: string = 'workbench.editor.runtimeExtensions'; + + private _list: WorkbenchList | null; + private _elements: IRuntimeExtension[] | null; + private _updateSoon: RunOnceScheduler; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IContextKeyService contextKeyService: IContextKeyService, + @IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionService private readonly _extensionService: IExtensionService, + @INotificationService private readonly _notificationService: INotificationService, + @IContextMenuService private readonly _contextMenuService: IContextMenuService, + @IInstantiationService protected readonly _instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @ILabelService private readonly _labelService: ILabelService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + ) { + super(AbstractRuntimeExtensionsEditor.ID, telemetryService, themeService, storageService); + + this._list = null; + this._elements = null; + this._updateSoon = this._register(new RunOnceScheduler(() => this._updateExtensions(), 200)); + + this._register(this._extensionService.onDidChangeExtensionsStatus(() => this._updateSoon.schedule())); + this._updateExtensions(); + } + + protected async _updateExtensions(): Promise { + this._elements = await this._resolveExtensions(); + if (this._list) { + this._list.splice(0, this._list.length, this._elements); + } + } + + private async _resolveExtensions(): Promise { + // We only deal with extensions with source code! + const extensionsDescriptions = (await this._extensionService.getExtensions()).filter((extension) => { + return Boolean(extension.main) || Boolean(extension.browser); + }); + let marketplaceMap: { [id: string]: IExtension; } = Object.create(null); + const marketPlaceExtensions = await this._extensionsWorkbenchService.queryLocal(); + for (let extension of marketPlaceExtensions) { + marketplaceMap[ExtensionIdentifier.toKey(extension.identifier.id)] = extension; + } + + let statusMap = this._extensionService.getExtensionsStatus(); + + // group profile segments by extension + let segments: { [id: string]: number[]; } = Object.create(null); + + const profileInfo = this._getProfileInfo(); + if (profileInfo) { + let currentStartTime = profileInfo.startTime; + for (let i = 0, len = profileInfo.deltas.length; i < len; i++) { + const id = profileInfo.ids[i]; + const delta = profileInfo.deltas[i]; + + let extensionSegments = segments[ExtensionIdentifier.toKey(id)]; + if (!extensionSegments) { + extensionSegments = []; + segments[ExtensionIdentifier.toKey(id)] = extensionSegments; + } + + extensionSegments.push(currentStartTime); + currentStartTime = currentStartTime + delta; + extensionSegments.push(currentStartTime); + } + } + + let result: IRuntimeExtension[] = []; + for (let i = 0, len = extensionsDescriptions.length; i < len; i++) { + const extensionDescription = extensionsDescriptions[i]; + + let profileInfo: IExtensionProfileInformation | null = null; + if (profileInfo) { + let extensionSegments = segments[ExtensionIdentifier.toKey(extensionDescription.identifier)] || []; + let extensionTotalTime = 0; + for (let j = 0, lenJ = extensionSegments.length / 2; j < lenJ; j++) { + const startTime = extensionSegments[2 * j]; + const endTime = extensionSegments[2 * j + 1]; + extensionTotalTime += (endTime - startTime); + } + profileInfo = { + segments: extensionSegments, + totalTime: extensionTotalTime + }; + } + + result[i] = { + originalIndex: i, + description: extensionDescription, + marketplaceInfo: marketplaceMap[ExtensionIdentifier.toKey(extensionDescription.identifier)], + status: statusMap[extensionDescription.identifier.value], + profileInfo: profileInfo || undefined, + unresponsiveProfile: this._getUnresponsiveProfile(extensionDescription.identifier) + }; + } + + result = result.filter(element => element.status.activationTimes); + + // bubble up extensions that have caused slowness + + const isUnresponsive = (extension: IRuntimeExtension): boolean => + extension.unresponsiveProfile === profileInfo; + + const profileTime = (extension: IRuntimeExtension): number => + extension.profileInfo?.totalTime ?? 0; + + const activationTime = (extension: IRuntimeExtension): number => + (extension.status.activationTimes?.codeLoadingTime ?? 0) + + (extension.status.activationTimes?.activateCallTime ?? 0); + + result = result.sort((a, b) => { + if (isUnresponsive(a) || isUnresponsive(b)) { + return +isUnresponsive(b) - +isUnresponsive(a); + } else if (profileTime(a) || profileTime(b)) { + return profileTime(b) - profileTime(a); + } else if (activationTime(a) || activationTime(b)) { + return activationTime(b) - activationTime(a); + } + return a.originalIndex - b.originalIndex; + }); + + return result; + } + + protected createEditor(parent: HTMLElement): void { + parent.classList.add('runtime-extensions-editor'); + + const TEMPLATE_ID = 'runtimeExtensionElementTemplate'; + + const delegate = new class implements IListVirtualDelegate{ + getHeight(element: IRuntimeExtension): number { + return 62; + } + getTemplateId(element: IRuntimeExtension): string { + return TEMPLATE_ID; + } + }; + + interface IRuntimeExtensionTemplateData { + root: HTMLElement; + element: HTMLElement; + icon: HTMLImageElement; + name: HTMLElement; + version: HTMLElement; + msgContainer: HTMLElement; + actionbar: ActionBar; + activationTime: HTMLElement; + profileTime: HTMLElement; + disposables: IDisposable[]; + elementDisposables: IDisposable[]; + } + + const renderer: IListRenderer = { + templateId: TEMPLATE_ID, + renderTemplate: (root: HTMLElement): IRuntimeExtensionTemplateData => { + const element = append(root, $('.extension')); + const iconContainer = append(element, $('.icon-container')); + const icon = append(iconContainer, $('img.icon')); + + const desc = append(element, $('div.desc')); + const headerContainer = append(desc, $('.header-container')); + const header = append(headerContainer, $('.header')); + const name = append(header, $('div.name')); + const version = append(header, $('span.version')); + + const msgContainer = append(desc, $('div.msg')); + + const actionbar = new ActionBar(desc, { animated: false }); + actionbar.onDidRun(({ error }) => error && this._notificationService.error(error)); + + + const timeContainer = append(element, $('.time')); + const activationTime = append(timeContainer, $('div.activation-time')); + const profileTime = append(timeContainer, $('div.profile-time')); + + const disposables = [actionbar]; + + return { + root, + element, + icon, + name, + version, + actionbar, + activationTime, + profileTime, + msgContainer, + disposables, + elementDisposables: [], + }; + }, + + renderElement: (element: IRuntimeExtension, index: number, data: IRuntimeExtensionTemplateData): void => { + + data.elementDisposables = dispose(data.elementDisposables); + + data.root.classList.toggle('odd', index % 2 === 1); + + const onError = Event.once(domEvent(data.icon, 'error')); + onError(() => data.icon.src = element.marketplaceInfo.iconUrlFallback, null, data.elementDisposables); + data.icon.src = element.marketplaceInfo.iconUrl; + + if (!data.icon.complete) { + data.icon.style.visibility = 'hidden'; + data.icon.onload = () => data.icon.style.visibility = 'inherit'; + } else { + data.icon.style.visibility = 'inherit'; + } + data.name.textContent = element.marketplaceInfo.displayName; + data.version.textContent = element.description.version; + + const activationTimes = element.status.activationTimes!; + let syncTime = activationTimes.codeLoadingTime + activationTimes.activateCallTime; + data.activationTime.textContent = activationTimes.activationReason.startup ? `Startup Activation: ${syncTime}ms` : `Activation: ${syncTime}ms`; + + data.actionbar.clear(); + const slowExtensionAction = this._createSlowExtensionAction(element); + if (slowExtensionAction) { + data.actionbar.push(slowExtensionAction, { icon: true, label: true }); + } + if (isNonEmptyArray(element.status.runtimeErrors)) { + const reportExtensionIssueAction = this._createReportExtensionIssueAction(element); + if (reportExtensionIssueAction) { + data.actionbar.push(reportExtensionIssueAction, { icon: true, label: true }); + } + } + + let title: string; + const activationId = activationTimes.activationReason.extensionId.value; + const activationEvent = activationTimes.activationReason.activationEvent; + if (activationEvent === '*') { + title = nls.localize('starActivation', "Activated by {0} on start-up", activationId); + } else if (/^workspaceContains:/.test(activationEvent)) { + let fileNameOrGlob = activationEvent.substr('workspaceContains:'.length); + if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) { + title = nls.localize({ + key: 'workspaceContainsGlobActivation', + comment: [ + '{0} will be a glob pattern' + ] + }, "Activated by {1} because a file matching {1} exists in your workspace", fileNameOrGlob, activationId); + } else { + title = nls.localize({ + key: 'workspaceContainsFileActivation', + comment: [ + '{0} will be a file name' + ] + }, "Activated by {1} because file {0} exists in your workspace", fileNameOrGlob, activationId); + } + } else if (/^workspaceContainsTimeout:/.test(activationEvent)) { + const glob = activationEvent.substr('workspaceContainsTimeout:'.length); + title = nls.localize({ + key: 'workspaceContainsTimeout', + comment: [ + '{0} will be a glob pattern' + ] + }, "Activated by {1} because searching for {0} took too long", glob, activationId); + } else if (activationEvent === 'onStartupFinished') { + title = nls.localize({ + key: 'startupFinishedActivation', + comment: [ + 'This refers to an extension. {0} will be an activation event.' + ] + }, "Activated by {0} after start-up finished", activationId); + } else if (/^onLanguage:/.test(activationEvent)) { + let language = activationEvent.substr('onLanguage:'.length); + title = nls.localize('languageActivation', "Activated by {1} because you opened a {0} file", language, activationId); + } else { + title = nls.localize({ + key: 'workspaceGenericActivation', + comment: [ + 'The {0} placeholder will be an activation event, like e.g. \'language:typescript\', \'debug\', etc.' + ] + }, "Activated by {1} on {0}", activationEvent, activationId); + } + data.activationTime.title = title; + + clearNode(data.msgContainer); + + if (this._getUnresponsiveProfile(element.description.identifier)) { + const el = $('span', undefined, ...renderCodicons(` $(alert) Unresponsive`)); + el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); + data.msgContainer.appendChild(el); + } + + if (isNonEmptyArray(element.status.runtimeErrors)) { + const el = $('span', undefined, ...renderCodicons(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`)); + data.msgContainer.appendChild(el); + } + + if (element.status.messages && element.status.messages.length > 0) { + const el = $('span', undefined, ...renderCodicons(`$(alert) ${element.status.messages[0].message}`)); + data.msgContainer.appendChild(el); + } + + if (element.description.extensionLocation.scheme === Schemas.vscodeRemote) { + const el = $('span', undefined, ...renderCodicons(`$(remote) ${element.description.extensionLocation.authority}`)); + data.msgContainer.appendChild(el); + + const hostLabel = this._labelService.getHostLabel(Schemas.vscodeRemote, this._environmentService.remoteAuthority); + if (hostLabel) { + reset(el, ...renderCodicons(`$(remote) ${hostLabel}`)); + } + } + + if (element.profileInfo) { + data.profileTime.textContent = `Profile: ${(element.profileInfo.totalTime / 1000).toFixed(2)}ms`; + } else { + data.profileTime.textContent = ''; + } + + }, + + disposeTemplate: (data: IRuntimeExtensionTemplateData): void => { + data.disposables = dispose(data.disposables); + } + }; + + this._list = >this._instantiationService.createInstance(WorkbenchList, + 'RuntimeExtensions', + parent, delegate, [renderer], { + multipleSelectionSupport: false, + setRowLineHeight: false, + horizontalScrolling: false, + overrideStyles: { + listBackground: editorBackground + }, + accessibilityProvider: new class implements IListAccessibilityProvider { + getWidgetAriaLabel(): string { + return nls.localize('runtimeExtensions', "Runtime Extensions"); + } + getAriaLabel(element: IRuntimeExtension): string | null { + return element.description.name; + } + } + }); + + this._list.splice(0, this._list.length, this._elements || undefined); + + this._list.onContextMenu((e) => { + if (!e.element) { + return; + } + + const actions: IAction[] = []; + + const reportExtensionIssueAction = this._createReportExtensionIssueAction(e.element); + if (reportExtensionIssueAction) { + actions.push(reportExtensionIssueAction); + actions.push(new Separator()); + } + + actions.push(new Action('runtimeExtensionsEditor.action.disableWorkspace', nls.localize('disable workspace', "Disable (Workspace)"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledWorkspace))); + actions.push(new Action('runtimeExtensionsEditor.action.disable', nls.localize('disable', "Disable"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledGlobally))); + actions.push(new Separator()); + + const profileAction = this._createProfileAction(); + if (profileAction) { + actions.push(profileAction); + } + const saveExtensionHostProfileAction = this.saveExtensionHostProfileAction; + if (saveExtensionHostProfileAction) { + actions.push(saveExtensionHostProfileAction); + } + + this._contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => actions + }); + }); + } + + @memoize + private get saveExtensionHostProfileAction(): IAction | null { + return this._createSaveExtensionHostProfileAction(); + } + + public layout(dimension: Dimension): void { + if (this._list) { + this._list.layout(dimension.height); + } + } + + protected abstract _getProfileInfo(): IExtensionHostProfile | null; + protected abstract _getUnresponsiveProfile(extensionId: ExtensionIdentifier): IExtensionHostProfile | undefined; + protected abstract _createSlowExtensionAction(element: IRuntimeExtension): Action | null; + protected abstract _createReportExtensionIssueAction(element: IRuntimeExtension): Action | null; + protected abstract _createSaveExtensionHostProfileAction(): Action | null; + protected abstract _createProfileAction(): Action | null; +} + +export class ShowRuntimeExtensionsAction extends Action { + static readonly ID = 'workbench.action.showRuntimeExtensions'; + static readonly LABEL = nls.localize('showRuntimeExtensions', "Show Running Extensions"); + + constructor( + id: string, label: string, + @IEditorService private readonly _editorService: IEditorService + ) { + super(id, label); + } + + public async run(e?: any): Promise { + await this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true, pinned: true }); + } +} diff --git a/src/vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor.ts new file mode 100644 index 000000000..a3b1e8090 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Action } from 'vs/base/common/actions'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IExtensionService, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { AbstractRuntimeExtensionsEditor, IRuntimeExtension } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor'; + +export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor { + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IContextKeyService contextKeyService: IContextKeyService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionService extensionService: IExtensionService, + @INotificationService notificationService: INotificationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IInstantiationService instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @ILabelService labelService: ILabelService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + ) { + super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService); + } + + protected _getProfileInfo(): IExtensionHostProfile | null { + return null; + } + + protected _getUnresponsiveProfile(extensionId: ExtensionIdentifier): IExtensionHostProfile | undefined { + return undefined; + } + + protected _createSlowExtensionAction(element: IRuntimeExtension): Action | null { + return null; + } + + protected _createReportExtensionIssueAction(element: IRuntimeExtension): Action | null { + return null; + } + + protected _createSaveExtensionHostProfileAction(): Action | null { + return null; + } + + protected _createProfileAction(): Action | null { + return null; + } +} diff --git a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts index 87780840a..87e238bf2 100644 --- a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -81,7 +81,7 @@ export class DynamicWorkspaceRecommendations extends ExtensionRecommendations { const workspaceTip = workspacesTips.filter(workspaceTip => isNonEmptyArray(workspaceTip.remoteSet) && workspaceTip.remoteSet.indexOf(hashedRemote) > -1)[0]; if (workspaceTip) { this._recommendations = workspaceTip.recommendations.map(id => this.toExtensionRecommendation(id, folder)); - this.storageService.store(dynamicWorkspaceRecommendationsStorageKey, JSON.stringify({ recommendations: workspaceTip.recommendations, timestamp: Date.now() }), StorageScope.WORKSPACE); + this.storageService.store(dynamicWorkspaceRecommendationsStorageKey, JSON.stringify({ recommendations: workspaceTip.recommendations, timestamp: Date.now() }), StorageScope.WORKSPACE, StorageTarget.MACHINE); this.telemetryService.publicLog2<{ count: number, cache: number }, DynamicWorkspaceRecommendationsClassification>('dynamicWorkspaceRecommendations', { count: this._recommendations.length, cache: 0 }); return; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 16dac4771..7a58b96da 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -14,7 +14,7 @@ import { Action, IAction } from 'vs/base/common/actions'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { dispose, toDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { domEvent } from 'vs/base/browser/event'; -import { append, $, finalHandler, join, hide, show, addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { append, $, finalHandler, join, hide, show, addDisposableListener, EventType, setParentFlowTo } from 'vs/base/browser/dom'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -27,7 +27,12 @@ import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { UpdateAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, SyncIgnoredIconAction, SetProductIconThemeAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { + UpdateAction, ReloadAction, MaliciousStatusLabelAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, + RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction, + ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, + InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction +} from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; @@ -38,7 +43,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Color } from 'vs/base/common/color'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { ExtensionsTree, ExtensionData, ExtensionsGridView, getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer'; import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; @@ -251,6 +256,9 @@ export class ExtensionEditor extends EditorPane { const extensionActionBar = this._register(new ActionBar(extensionActions, { animated: false, actionViewItemProvider: (action: IAction) => { + if (action instanceof ExtensionDropDownAction) { + return action.createActionViewItem(); + } if (action instanceof ActionWithDropDownAction) { return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); } @@ -276,6 +284,7 @@ export class ExtensionEditor extends EditorPane { const navbar = new NavBar(body); const content = append(body, $('.content')); + content.id = generateUuid(); // An id is needed for the webview parent flow to this.template = { builtin, @@ -323,8 +332,6 @@ export class ExtensionEditor extends EditorPane { } private async updateTemplate(input: ExtensionsInput, template: IExtensionEditorTemplate, preserveFocus: boolean): Promise { - const runningExtensions = await this.extensionService.getExtensions(); - this.activeElement = null; this.editorLoadComplete = false; const extension = input.extension; @@ -405,7 +412,6 @@ export class ExtensionEditor extends EditorPane { const systemDisabledWarningAction = this.instantiationService.createInstance(SystemDisabledWarningAction); const actions = [ reloadAction, - this.instantiationService.createInstance(SyncIgnoredIconAction), this.instantiationService.createInstance(StatusLabelAction), this.instantiationService.createInstance(UpdateAction), this.instantiationService.createInstance(SetColorThemeAction, await this.workbenchThemeService.getColorThemes()), @@ -413,13 +419,19 @@ export class ExtensionEditor extends EditorPane { this.instantiationService.createInstance(SetProductIconThemeAction, await this.workbenchThemeService.getProductIconThemes()), this.instantiationService.createInstance(EnableDropDownAction), - this.instantiationService.createInstance(DisableDropDownAction, runningExtensions), + this.instantiationService.createInstance(DisableDropDownAction), this.instantiationService.createInstance(RemoteInstallAction, false), this.instantiationService.createInstance(LocalInstallAction), + this.instantiationService.createInstance(WebInstallAction), combinedInstallAction, this.instantiationService.createInstance(InstallingLabelAction), - this.instantiationService.createInstance(UninstallAction), + this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.uninstall', UninstallAction.UninstallLabel, [ + this.instantiationService.createInstance(UninstallAction), + this.instantiationService.createInstance(InstallAnotherVersionAction), + ]), + this.instantiationService.createInstance(ToggleSyncExtensionAction), systemDisabledWarningAction, + this.instantiationService.createInstance(ExtensionEditorManageExtensionAction), this.instantiationService.createInstance(ExtensionToolTipAction, systemDisabledWarningAction, reloadAction), this.instantiationService.createInstance(MaliciousStatusLabelAction, true), ]; @@ -432,7 +444,7 @@ export class ExtensionEditor extends EditorPane { this.transientDisposables.add(disposable); } - this.setSubText(extension, reloadAction, template); + this.setSubText(extension, template); template.content.innerText = ''; // Clear content before setting navbar actions. template.navbar.clear(); @@ -463,56 +475,24 @@ export class ExtensionEditor extends EditorPane { this.editorLoadComplete = true; } - private setSubText(extension: IExtension, reloadAction: ReloadAction, template: IExtensionEditorTemplate): void { + private setSubText(extension: IExtension, template: IExtensionEditorTemplate): void { hide(template.subtextContainer); - const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction, extension); - const undoIgnoreAction = this.instantiationService.createInstance(UndoIgnoreExtensionRecommendationAction, extension); - ignoreAction.enabled = false; - undoIgnoreAction.enabled = false; - - template.ignoreActionbar.clear(); - template.ignoreActionbar.push([ignoreAction, undoIgnoreAction], { icon: true, label: true }); - this.transientDisposables.add(ignoreAction); - this.transientDisposables.add(undoIgnoreAction); - const updateRecommendationFn = () => { const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); if (extRecommendations[extension.identifier.id.toLowerCase()]) { - ignoreAction.enabled = true; - undoIgnoreAction.enabled = false; template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; show(template.subtextContainer); } else if (this.extensionIgnoredRecommendationsService.globalIgnoredRecommendations.indexOf(extension.identifier.id.toLowerCase()) !== -1) { - ignoreAction.enabled = false; - undoIgnoreAction.enabled = true; template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); show(template.subtextContainer); } else { - ignoreAction.enabled = false; - undoIgnoreAction.enabled = false; template.subtext.textContent = ''; hide(template.subtextContainer); } }; updateRecommendationFn(); this.transientDisposables.add(this.extensionRecommendationsService.onDidChangeRecommendations(() => updateRecommendationFn())); - - this.transientDisposables.add(reloadAction.onDidChange(e => { - if (e.tooltip) { - template.subtext.textContent = reloadAction.tooltip; - show(template.subtextContainer); - ignoreAction.enabled = false; - undoIgnoreAction.enabled = false; - } - if (e.enabled === true) { - show(template.subtextContainer); - } - if (e.enabled === false) { - hide(template.subtextContainer); - } - this.layout(); - })); } clearInput(): void { @@ -558,8 +538,13 @@ export class ExtensionEditor extends EditorPane { template.content.innerText = ''; this.activeElement = null; if (id) { - this.open(id, extension, template) + const cts = new CancellationTokenSource(); + this.contentDisposables.add(toDisposable(() => cts.dispose(true))); + this.open(id, extension, template, cts.token) .then(activeElement => { + if (cts.token.isCancellationRequested) { + return; + } this.activeElement = activeElement; if (focus) { this.focus(); @@ -568,26 +553,31 @@ export class ExtensionEditor extends EditorPane { } } - private open(id: string, extension: IExtension, template: IExtensionEditorTemplate): Promise { + private open(id: string, extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise { switch (id) { - case NavbarSection.Readme: return this.openReadme(template); - case NavbarSection.Contributions: return this.openContributions(template); - case NavbarSection.Changelog: return this.openChangelog(template); - case NavbarSection.Dependencies: return this.openDependencies(extension, template); + case NavbarSection.Readme: return this.openReadme(template, token); + case NavbarSection.Contributions: return this.openContributions(template, token); + case NavbarSection.Changelog: return this.openChangelog(template, token); + case NavbarSection.Dependencies: return this.openDependencies(extension, template, token); } return Promise.resolve(null); } - private async openMarkdown(cacheResult: CacheResult, noContentCopy: string, template: IExtensionEditorTemplate): Promise { + private async openMarkdown(cacheResult: CacheResult, noContentCopy: string, template: IExtensionEditorTemplate, token: CancellationToken): Promise { try { const body = await this.renderMarkdown(cacheResult, template); + if (token.isCancellationRequested) { + return Promise.resolve(null); + } const webview = this.contentDisposables.add(this.webviewService.createWebviewOverlay('extensionEditor', { enableFindWidget: true, }, {}, undefined)); - webview.claim(this); + webview.claim(this, this.scopedContextKeyService); + setParentFlowTo(webview.container, template.content); webview.layoutWebviewOverElement(template.content); + webview.html = body; this.contentDisposables.add(webview.onDidFocus(() => this.fireOnDidFocus())); @@ -828,15 +818,19 @@ export class ExtensionEditor extends EditorPane { `; } - private async openReadme(template: IExtensionEditorTemplate): Promise { + private async openReadme(template: IExtensionEditorTemplate, token: CancellationToken): Promise { const manifest = await this.extensionManifest!.get().promise; if (manifest && manifest.extensionPack && manifest.extensionPack.length) { - return this.openExtensionPackReadme(manifest, template); + return this.openExtensionPackReadme(manifest, template, token); } - return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), template); + return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), template, token); } - private async openExtensionPackReadme(manifest: IExtensionManifest, template: IExtensionEditorTemplate): Promise { + private async openExtensionPackReadme(manifest: IExtensionManifest, template: IExtensionEditorTemplate, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return Promise.resolve(null); + } + const extensionPackReadme = append(template.content, $('div', { class: 'extension-pack-readme' })); extensionPackReadme.style.margin = '0 auto'; extensionPackReadme.style.maxWidth = '882px'; @@ -860,21 +854,25 @@ export class ExtensionEditor extends EditorPane { const readmeContent = append(extensionPackReadme, $('div.readme-content')); await Promise.all([ - this.renderExtensionPack(manifest, extensionPackContent), - this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), { ...template, ...{ content: readmeContent } }), + this.renderExtensionPack(manifest, extensionPackContent, token), + this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), { ...template, ...{ content: readmeContent } }, token), ]); return { focus: () => extensionPackContent.focus() }; } - private openChangelog(template: IExtensionEditorTemplate): Promise { - return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template); + private openChangelog(template: IExtensionEditorTemplate, token: CancellationToken): Promise { + return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template, token); } - private openContributions(template: IExtensionEditorTemplate): Promise { + private openContributions(template: IExtensionEditorTemplate, token: CancellationToken): Promise { const content = $('div', { class: 'subcontent', tabindex: '0' }); return this.loadContents(() => this.extensionManifest!.get(), template) .then(manifest => { + if (token.isCancellationRequested) { + return null; + } + if (!manifest) { return content; } @@ -900,6 +898,7 @@ export class ExtensionEditor extends EditorPane { this.renderLocalizations(content, manifest, layout), this.renderCustomEditors(content, manifest, layout), this.renderAuthentication(content, manifest, layout), + this.renderActivationEvents(content, manifest, layout), ]; scrollableContent.scanDomNode(); @@ -914,13 +913,21 @@ export class ExtensionEditor extends EditorPane { } return content; }, () => { + if (token.isCancellationRequested) { + return null; + } + append(content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions"); append(template.content, content); return content; }); } - private openDependencies(extension: IExtension, template: IExtensionEditorTemplate): Promise { + private openDependencies(extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return Promise.resolve(null); + } + if (arrays.isFalsyOrEmpty(extension.dependencies)) { append(template.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies"); return Promise.resolve(template.content); @@ -949,7 +956,11 @@ export class ExtensionEditor extends EditorPane { return Promise.resolve({ focus() { dependenciesTree.domFocus(); } }); } - private async renderExtensionPack(manifest: IExtensionManifest, parent: HTMLElement): Promise { + private async renderExtensionPack(manifest: IExtensionManifest, parent: HTMLElement, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return; + } + const content = $('div', { class: 'subcontent' }); const scrollableContent = new DomScrollableElement(content, { useShadows: false }); append(parent, scrollableContent.getDomNode()); @@ -1414,6 +1425,21 @@ export class ExtensionEditor extends EditorPane { return true; } + private renderActivationEvents(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const activationEvents = manifest.activationEvents || []; + if (!activationEvents.length) { + return false; + } + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', { tabindex: '0' }, localize('activation events', "Activation Events ({0})", activationEvents.length)), + $('ul', undefined, ...activationEvents.map(activationEvent => $('li', undefined, $('code', undefined, activationEvent)))) + ); + + append(container, details); + return true; + } + private resolveKeybinding(rawKeyBinding: IKeyBinding): ResolvedKeybinding | null { let key: string | undefined; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index f6aa5db5a..ab902f413 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -11,14 +11,13 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; -import { INotificationHandle, INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { INotificationHandle, INotificationService, IPromptChoice, IPromptChoiceWithMenu, Severity } from 'vs/platform/notification/common/notification'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -26,14 +25,6 @@ import { ITASExperimentService } from 'vs/workbench/services/experiment/common/e import { EnablementState, IWorkbenchExtensioManagementService, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; -interface IExtensionsConfiguration { - autoUpdate: boolean; - autoCheckUpdates: boolean; - ignoreRecommendations: boolean; - showRecommendationsOnlyOnDemand: boolean; - closeExtensionDetailsOnViewChange: boolean; -} - type ExtensionRecommendationsNotificationClassification = { userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; extensionId?: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; @@ -142,18 +133,16 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec @IWorkbenchExtensioManagementService private readonly extensionManagementService: IWorkbenchExtensioManagementService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IExtensionIgnoredRecommendationsService private readonly extensionIgnoredRecommendationsService: IExtensionIgnoredRecommendationsService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, ) { - storageKeysSyncRegistryService.registerStorageKey({ key: ignoreImportantExtensionRecommendationStorageKey, version: 1 }); this.tasExperimentService = tasExperimentService; } hasToIgnoreRecommendationNotifications(): boolean { - const config = this.configurationService.getValue('extensions'); - return config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand; + const config = this.configurationService.getValue<{ ignoreRecommendations: boolean, showRecommendationsOnlyOnDemand?: boolean }>('extensions'); + return config.ignoreRecommendations || !!config.showRecommendationsOnlyOnDemand; } async promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string, source: RecommendationSource): Promise { @@ -207,7 +196,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec }); if (result === RecommendationsNotificationResult.Accepted) { - this.storageService.store(donotShowWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); + this.storageService.store(donotShowWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE, StorageTarget.USER); } } @@ -252,7 +241,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec { onDidInstallRecommendedExtensions, onDidShowRecommendedExtensions, onDidCancelRecommendedExtensions, onDidNeverShowRecommendedExtensionsAgain }: RecommendationsNotificationActions): CancelablePromise { return createCancelablePromise(async token => { let accepted = false; - const choices: IPromptChoice[] = []; + const choices: (IPromptChoice | IPromptChoiceWithMenu)[] = []; const installExtensions = async (isMachineScoped?: boolean) => { this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); onDidInstallRecommendedExtensions(extensions); @@ -263,14 +252,12 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec }; choices.push({ label: localize('install', "Install"), - run: () => installExtensions() - }); - if (this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions)) { - choices.push({ + run: () => installExtensions(), + menu: this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions) ? [{ label: localize('install and do no sync', "Install (Do not sync)"), run: () => installExtensions(true) - }); - } + }] : undefined, + }); choices.push(...[{ label: localize('show recommendations', "Show Recommendations"), run: async () => { @@ -423,11 +410,11 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec const importantRecommendationsIgnoreList = [...this.ignoredRecommendations]; if (!importantRecommendationsIgnoreList.includes(id.toLowerCase())) { importantRecommendationsIgnoreList.push(id.toLowerCase()); - this.storageService.store(ignoreImportantExtensionRecommendationStorageKey, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL); + this.storageService.store(ignoreImportantExtensionRecommendationStorageKey, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL, StorageTarget.USER); } } private setIgnoreRecommendationsConfig(configVal: boolean) { - this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER); + this.configurationService.updateValue('extensions.ignoreRecommendations', configVal); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 7df6cf1b9..60fa348e8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -7,8 +7,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, IExtensionGalleryService, InstallOperation, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService, ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { distinct, shuffle } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; @@ -52,7 +50,6 @@ export class ExtensionRecommendationsService extends Disposable implements IExte @IInstantiationService instantiationService: IInstantiationService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, - @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @@ -92,15 +89,9 @@ export class ExtensionRecommendationsService extends Disposable implements IExte this.fileBasedRecommendations.activate(), this.experimentalRecommendations.activate(), this.keymapRecommendations.activate(), - this.lifecycleService.when(LifecyclePhase.Eventually) - .then(async () => { - if (!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)) { - await this.activateProactiveRecommendations(); - } - }) ]); - this._register(this.extensionRecommendationsManagementService.onDidChangeIgnoredRecommendations(() => this._onDidChangeRecommendations.fire())); + this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations, this.extensionRecommendationsManagementService.onDidChangeIgnoredRecommendations)(() => this._onDidChangeRecommendations.fire())); this._register(this.extensionRecommendationsManagementService.onDidChangeGlobalIgnoredRecommendation(({ extensionId, isRecommended }) => { if (!isRecommended) { const reason = this.getAllRecommendationsWithReason()[extensionId]; @@ -111,7 +102,6 @@ export class ExtensionRecommendationsService extends Disposable implements IExte })); await this.promptWorkspaceRecommendations(); - this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations)(() => this.promptWorkspaceRecommendations())); } private isEnabled(): boolean { diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index f4b33985a..839c3ffbe 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -6,11 +6,11 @@ import { localize } from 'vs/nls'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; -import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, registerAction2, Action2, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionsLabel, ExtensionsLocalizedLabel, ExtensionsChannelId, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; @@ -18,7 +18,7 @@ import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction, - EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction + EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, RefreshExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; @@ -26,7 +26,7 @@ import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContrib import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import * as jsonContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeymapExtensions } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -54,7 +54,7 @@ import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { ExtensionRecommendationNotificationService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService'; import { IExtensionService, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; @@ -62,6 +62,10 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { IAction } from 'vs/base/common/actions'; +import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; +import { Schemas } from 'vs/base/common/network'; +import { ShowRuntimeExtensionsAction } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor'; +import { extensionsViewIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); @@ -100,12 +104,13 @@ Registry.as(EditorExtensions.Editors).registerEditor( new SyncDescriptor(ExtensionsInput) ]); + Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer( { id: VIEWLET_ID, name: localize('extensions', "Extensions"), ctorDescriptor: new SyncDescriptor(ExtensionsViewPaneContainer), - icon: 'codicon-extensions', + icon: extensionsViewIcon, order: 4, rejectAddedViews: true, alwaysUseContainerInfo: true @@ -140,7 +145,7 @@ Registry.as(ConfigurationExtensions.Configuration) }, 'extensions.showRecommendationsOnlyOnDemand': { type: 'boolean', - description: localize('extensionsShowRecommendationsOnlyOnDemand', "When enabled, recommendations will not be fetched or shown unless specifically requested by the user. Some recommendations are fetched from a Microsoft online service."), + deprecationMessage: localize('extensionsShowRecommendationsOnlyOnDemand_Deprecated', "This setting is deprecated. Use extensions.ignoreRecommendations setting to control recommendation notifications. Use Extensions view's visibility actions to hide Recommended view by default."), default: false, tags: ['usesOnlineServices'] }, @@ -234,8 +239,8 @@ CommandsRegistry.registerCommand({ .then(async (extensions) => { for (const extension of extensions) { const requireReload = !(extension.local && extensionService.canAddExtension(toExtensionDescription(extension.local))); - const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.displayName || extension.name) - : localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.displayName || extension.name); + const message = requireReload ? localize('InstallVSIXAction.successReload', "Completed installing {0} extension from VSIX. Please reload Visual Studio Code to enable it.", extension.displayName || extension.name) + : localize('InstallVSIXAction.success', "Completed installing {0} extension from VSIX.", extension.displayName || extension.name); const actions = requireReload ? [{ label: localize('InstallVSIXAction.reloadNow', "Reload Now"), run: () => hostService.reload() @@ -279,7 +284,7 @@ CommandsRegistry.registerCommand({ } try { - await extensionManagementService.uninstall(extensionToUninstall, true); + await extensionManagementService.uninstall(extensionToUninstall); } catch (e) { onUnexpectedError(e); throw e; @@ -771,6 +776,22 @@ class ExtensionsContributions implements IWorkbenchContribution { } }); + registerAction2(class extends Action2 { + constructor() { + super({ + id: RefreshExtensionsAction.ID, + title: { value: RefreshExtensionsAction.LABEL, original: 'Refresh' }, + category: ExtensionsLocalizedLabel, + menu: { + id: MenuId.CommandPalette, + } + }); + } + run(accessor: ServicesAccessor) { + return runAction(accessor.get(IInstantiationService).createInstance(RefreshExtensionsAction, RefreshExtensionsAction.ID, RefreshExtensionsAction.LABEL)); + } + }); + registerAction2(class extends Action2 { constructor() { super({ @@ -916,7 +937,7 @@ class ExtensionsContributions implements IWorkbenchContribution { menu: { id: MenuId.ExtensionContext, group: '2_configure', - when: CONTEXT_SYNC_ENABLEMENT + when: ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, ContextKeyExpr.has('inExtensionEditor').negate()) }, }); } @@ -929,6 +950,220 @@ class ExtensionsContributions implements IWorkbenchContribution { } } }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.ignoreRecommendation', + title: { value: localize('workbench.extensions.action.ignoreRecommendation', "Ignore Recommendation"), original: `Ignore Recommendation` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.has('isExtensionRecommended'), + order: 1 + }, + }); + } + + async run(accessor: ServicesAccessor, id: string): Promise { + accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, true); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.undoIgnoredRecommendation', + title: { value: localize('workbench.extensions.action.undoIgnoredRecommendation', "Undo Ignored Recommendation"), original: `Undo Ignored Recommendation` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.has('isUserIgnoredRecommendation'), + order: 1 + }, + }); + } + + async run(accessor: ServicesAccessor, id: string): Promise { + accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, false); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addExtensionToWorkspaceRecommendations', + title: { value: localize('workbench.extensions.action.addExtensionToWorkspaceRecommendations', "Add to Workspace Recommendations"), original: `Add to Workspace Recommendations` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended').negate(), ContextKeyExpr.has('isUserIgnoredRecommendation').negate()), + order: 2 + }, + }); + } + + run(accessor: ServicesAccessor, id: string): Promise { + return accessor.get(IWorkpsaceExtensionsConfigService).toggleRecommendation(id); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', + title: { value: localize('workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', "Remove from Workspace Recommendations"), original: `Remove from Workspace Recommendations` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended')), + order: 2 + }, + }); + } + + run(accessor: ServicesAccessor, id: string): Promise { + return accessor.get(IWorkpsaceExtensionsConfigService).toggleRecommendation(id); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceRecommendations', "Add Extension to Workspace Recommendations"), original: `Add Extension to Workspace Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const workpsaceExtensionsConfigService = accessor.get(IWorkpsaceExtensionsConfigService); + if (!(editorService.activeEditor instanceof ExtensionsInput)) { + return; + } + const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase(); + const recommendations = await workpsaceExtensionsConfigService.getRecommendations(); + if (recommendations.includes(extensionId)) { + return; + } + await workpsaceExtensionsConfigService.toggleRecommendation(extensionId); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceFolderRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceFolderRecommendations', "Add Extension to Workspace Folder Recommendations"), original: `Add Extension to Workspace Folder Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + async run(accessor: ServicesAccessor): Promise { + return accessor.get(ICommandService).executeCommand('workbench.extensions.action.addToWorkspaceRecommendations'); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceIgnoredRecommendations', "Add Extension to Workspace Ignored Recommendations"), original: `Add Extension to Workspace Ignored Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const workpsaceExtensionsConfigService = accessor.get(IWorkpsaceExtensionsConfigService); + if (!(editorService.activeEditor instanceof ExtensionsInput)) { + return; + } + const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase(); + const unwatedRecommendations = await workpsaceExtensionsConfigService.getUnwantedRecommendations(); + if (unwatedRecommendations.includes(extensionId)) { + return; + } + await workpsaceExtensionsConfigService.toggleUnwantedRecommendation(extensionId); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', "Add Extension to Workspace Folder Ignored Recommendations"), original: `Add Extension to Workspace Folder Ignored Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + run(accessor: ServicesAccessor): Promise { + return accessor.get(ICommandService).executeCommand('workbench.extensions.action.addToWorkspaceIgnoredRecommendations'); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: ConfigureWorkspaceRecommendedExtensionsAction.ID, + title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: WorkbenchStateContext.isEqualTo('workspace'), + }, + }); + } + + run(accessor: ServicesAccessor): Promise { + return accessor.get(IInstantiationService).createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL).run(); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, + title: { value: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace Folder)' }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: WorkbenchStateContext.notEqualsTo('empty'), + }, + }); + } + + run(accessor: ServicesAccessor): Promise { + return accessor.get(IInstantiationService).createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL).run(); + } + }); } } @@ -936,9 +1171,12 @@ const workbenchRegistry = Registry.as(Workbench workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Starting); workbenchRegistry.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(MaliciousExtensionChecker, LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(ConfigureRecommendedExtensionsCommandsContributor, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Starting); workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(ExtensionDependencyChecker, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInstaller, LifecyclePhase.Eventually); + +// Running Extensions +const actionRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ShowRuntimeExtensionsAction), 'Show Running Extensions', CATEGORIES.Developer.value); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts new file mode 100644 index 000000000..f5d4587b7 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; +import { RuntimeExtensionsEditor } from 'vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor'; +import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; + +// Running Extensions +Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create(RuntimeExtensionsEditor, RuntimeExtensionsEditor.ID, localize('runtimeExtension', "Running Extensions")), + [new SyncDescriptor(RuntimeExtensionsInput)] +); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index c46dbde91..36dbf7c75 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -11,12 +11,12 @@ import * as DOM from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { dispose, Disposable } from 'vs/base/common/lifecycle'; +import { dispose } from 'vs/base/common/lifecycle'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; -import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IExtensionIgnoredRecommendationsService, IExtensionsConfigContent } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensioManagementService, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -30,22 +30,19 @@ import { IExtensionService, toExtension, toExtensionDescription } from 'vs/workb import { URI } from 'vs/base/common/uri'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, IColorTheme, ICssStyleCollector, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { buttonBackground, buttonForeground, buttonHoverBackground, contrastBorder, registerColor, foreground } from 'vs/platform/theme/common/colorRegistry'; -import { Color } from 'vs/base/common/color'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { MenuRegistry, MenuId, IMenuService } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { MenuId, IMenuService } from 'vs/platform/actions/common/actions'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { IQuickPickItem, IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -58,51 +55,16 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IProductService } from 'vs/platform/product/common/productService'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { Codicon } from 'vs/base/common/codicons'; import { IViewsService } from 'vs/workbench/common/views'; import { IActionViewItemOptions, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { EXTENSIONS_CONFIG } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; -import { isPromiseCanceledError } from 'vs/base/common/errors'; +import { EXTENSIONS_CONFIG, IExtensionsConfigContent } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; +import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { ActionWithDropdownActionViewItem, IActionWithDropdownActionViewItemOptions } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; - -const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error, instantiationService: IInstantiationService): Promise => { - return instantiationService.invokeFunction(accessor => { - if (isPromiseCanceledError(error)) { - return Promise.resolve(); - } - - const productService = accessor.get(IProductService); - const openerService = accessor.get(IOpenerService); - const notificationService = accessor.get(INotificationService); - const dialogService = accessor.get(IDialogService); - const erorrsToShows = [INSTALL_ERROR_INCOMPATIBLE, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_NOT_SUPPORTED]; - if (!extension || erorrsToShows.indexOf(error.name) !== -1 || !productService.extensionsGallery) { - return dialogService.show(Severity.Error, error.message, []); - } else { - const downloadUrl = `${productService.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; - notificationService.prompt(Severity.Error, message, [{ - label: localize('download', "Download Manually"), - run: () => openerService.open(URI.parse(downloadUrl)).then(() => { - notificationService.prompt( - Severity.Info, - localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', extension.identifier.id), - [{ - label: InstallVSIXAction.LABEL, - run: () => { - const action = instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); - action.run(); - action.dispose(); - } - }] - ); - }) - }]); - return Promise.resolve(); - } - }); -}; +import { ILogService } from 'vs/platform/log/common/log'; +import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; +import { clearSearchResultsIcon, infoIcon, manageExtensionIcon, refreshIcon, syncEnabledIcon, syncIgnoredIcon, warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; function getRelativeDateLabel(date: Date): string { const delta = new Date().getTime() - date.getTime(); @@ -138,6 +100,61 @@ function getRelativeDateLabel(date: Date): string { return ''; } +class PromptExtensionInstallFailureAction extends Action { + + constructor( + private readonly extension: IExtension, + private readonly installOperation: InstallOperation, + private readonly error: Error, + @IProductService private readonly productService: IProductService, + @IOpenerService private readonly openerService: IOpenerService, + @INotificationService private readonly notificationService: INotificationService, + @IDialogService private readonly dialogService: IDialogService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ILogService private readonly logService: ILogService, + ) { + super('extension.promptExtensionInstallFailure'); + } + + async run(): Promise { + if (isPromiseCanceledError(this.error)) { + return; + } + + this.logService.error(this.error); + const operationMessage = this.installOperation === InstallOperation.Update ? localize('update operation', "Error while updating '{0}' extension.", this.extension.displayName || this.extension.identifier.id) + : localize('install operation', "Error while installing '{0}' extension.", this.extension.displayName || this.extension.identifier.id); + + if ([INSTALL_ERROR_INCOMPATIBLE, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_NOT_SUPPORTED].includes(this.error.name)) { + await this.dialogService.show(Severity.Error, `${operationMessage}\n${getErrorMessage(this.error)}`, []); + return; + } + + const promptChoices: IPromptChoice[] = []; + if (this.extension.gallery && this.productService.extensionsGallery) { + promptChoices.push({ + label: localize('download', "Try Downloading Manually..."), + run: () => this.openerService.open(URI.parse(`${this.productService.extensionsGallery!.serviceUrl}/publishers/${this.extension.publisher}/vsextensions/${this.extension.name}/${this.extension.version}/vspackage`)).then(() => { + this.notificationService.prompt( + Severity.Info, + localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', this.extension.identifier.id), + [{ + label: InstallVSIXAction.LABEL, + run: () => { + const action = this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); + action.run(); + action.dispose(); + } + }] + ); + }) + }); + } + const checkLogsMessage = localize('check logs', "Please check the [log]({0}) for more details.", `command:${Constants.showWindowLogActionId}`); + this.notificationService.prompt(Severity.Error, `${operationMessage} ${checkLogsMessage}`, promptChoices); + } +} + export abstract class ExtensionAction extends Action implements IExtensionContainer { static readonly EXTENSION_ACTION_CLASS = 'extension-action'; static readonly TEXT_ACTION_CLASS = `${ExtensionAction.EXTENSION_ACTION_CLASS} text`; @@ -149,13 +166,17 @@ export abstract class ExtensionAction extends Action implements IExtensionContai abstract update(): void; } -export abstract class ActionWithDropDownAction extends ExtensionAction { +export class ActionWithDropDownAction extends ExtensionAction { private action: IAction | undefined; private _menuActions: IAction[] = []; get menuActions(): IAction[] { return [...this._menuActions]; } + get extension(): IExtension | null { + return super.extension; + } + set extension(extension: IExtension | null) { this.actions.forEach(a => a.extension = extension); super.extension = extension; @@ -168,6 +189,7 @@ export abstract class ActionWithDropDownAction extends ExtensionAction { super(id, label); this.update(); this._register(Event.any(...actions.map(a => a.onDidChange))(() => this.update(true))); + actions.forEach(a => this._register(a)); } update(donotUpdateActions?: boolean): void { @@ -213,7 +235,6 @@ export abstract class AbstractInstallAction extends ExtensionAction { id: string, label: string, cssClass: string, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @INotificationService private readonly notificationService: INotificationService, @IExtensionService private readonly runtimeExtensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, @ILabelService private readonly labelService: ILabelService, @@ -243,11 +264,10 @@ export abstract class AbstractInstallAction extends ExtensionAction { const extension = await this.install(this.extension); - alert(localize('installExtensionComplete', "Installing extension {0} is completed.", this.extension.displayName)); - - if (extension && extension.local) { + if (extension?.local) { + alert(localize('installExtensionComplete', "Installing extension {0} is completed.", this.extension.displayName)); const runningExtension = await this.getRunningExtension(extension.local); - if (runningExtension) { + if (runningExtension && !(runningExtension.activationEvents && runningExtension.activationEvents.some(activationEent => activationEent.startsWith('onLanguage')))) { let action = await SetColorThemeAction.create(this.workbenchThemeService, this.instantiationService, extension) || await SetFileIconThemeAction.create(this.workbenchThemeService, this.instantiationService, extension) || await SetProductIconThemeAction.create(this.workbenchThemeService, this.instantiationService, extension); @@ -263,17 +283,13 @@ export abstract class AbstractInstallAction extends ExtensionAction { } - private install(extension: IExtension): Promise { - return this.extensionsWorkbenchService.install(extension, this.getInstallOptions()) - .then(null, err => { - if (!extension.gallery) { - return this.notificationService.error(err); - } - - console.error(err); - - return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService); - }); + private async install(extension: IExtension): Promise { + try { + return await this.extensionsWorkbenchService.install(extension, this.getInstallOptions()); + } catch (error) { + await this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Install, error).run(); + return undefined; + } } private async getRunningExtension(extension: ILocalExtension): Promise { @@ -304,18 +320,16 @@ export class InstallAction extends AbstractInstallAction { constructor( @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IInstantiationService instantiationService: IInstantiationService, - @INotificationService notificationService: INotificationService, @IExtensionService runtimeExtensionService: IExtensionService, @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @ILabelService labelService: ILabelService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IProductService private readonly productService: IProductService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IWorkbenchExtensioManagementService private readonly workbenchExtensioManagementService: IWorkbenchExtensioManagementService, @IUserDataAutoSyncEnablementService protected readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncResourceEnablementService protected readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, ) { super(`extensions.installAndSync`, localize('install', "Install"), InstallAction.Class, - extensionsWorkbenchService, instantiationService, notificationService, runtimeExtensionService, workbenchThemeService, labelService); + extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService); this.updateLabel(); this._register(labelService.onDidChangeFormatters(() => this.updateLabel(), this)); this._register(Event.any(userDataAutoSyncEnablementService.onDidChangeEnablement, @@ -333,20 +347,17 @@ export class InstallAction extends AbstractInstallAction { // When remote connection exists if (this._manifest && this.extensionManagementServerService.remoteExtensionManagementServer) { - // On Desktop and UI Extension - if (this.extensionManagementServerService.localExtensionManagementServer && prefersExecuteOnUI(this._manifest, this.productService, this.configurationService)) { - this.label = isMachineScoped ? localize('install locally and do not sync', "Install Locally (Do not sync)") : localize('install locally', "Install Locally"); + const server = this.workbenchExtensioManagementService.getExtensionManagementServerToInstall(this._manifest); + + if (server === this.extensionManagementServerService.remoteExtensionManagementServer) { + const host = this.extensionManagementServerService.remoteExtensionManagementServer.label; + this.label = isMachineScoped + ? localize({ key: 'install in remote and do not sync', comment: ['This is the name of the action to install an extension in remote server and do not sync it. Placeholder is for the name of remote server.'] }, "Install in {0} (Do not sync)", host) + : localize({ key: 'install in remote', comment: ['This is the name of the action to install an extension in remote server. Placeholder is for the name of remote server.'] }, "Install in {0}", host); return; } - // On Web and Web Extension - if (this.extensionManagementServerService.webExtensionManagementServer && prefersExecuteOnWeb(this._manifest, this.productService, this.configurationService)) { - this.label = isMachineScoped ? localize('install locally and do not sync', "Install Locally (Do not sync)") : localize('install locally', "Install Locally"); - return; - } - - const host = this.extensionManagementServerService.remoteExtensionManagementServer.label; - this.label = isMachineScoped ? localize('install on remote and do not sync', "Install on {0} (Do not sync)", host) : localize('install on remote', "Install on {0}", host); + this.label = isMachineScoped ? localize('install locally and do not sync', "Install Locally (Do not sync)") : localize('install locally', "Install Locally"); return; } } @@ -362,7 +373,6 @@ export class InstallAndSyncAction extends AbstractInstallAction { constructor( @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IInstantiationService instantiationService: IInstantiationService, - @INotificationService notificationService: INotificationService, @IExtensionService runtimeExtensionService: IExtensionService, @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @ILabelService labelService: ILabelService, @@ -371,8 +381,8 @@ export class InstallAndSyncAction extends AbstractInstallAction { @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, ) { super(`extensions.installAndSync`, localize('install', "Install"), InstallAndSyncAction.Class, - extensionsWorkbenchService, instantiationService, notificationService, runtimeExtensionService, workbenchThemeService, labelService); - this.tooltip = localize('install everywhere tooltip', "Install this extension in all your synced {0} instances", productService.nameLong); + extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService); + this.tooltip = localize({ key: 'install everywhere tooltip', comment: ['Placeholder is the name of the product. Eg: Visual Studio Code or Visual Studio Code - Insiders'] }, "Install this extension in all your synced {0} instances", productService.nameLong); this._register(Event.any(userDataAutoSyncEnablementService.onDidChangeEnablement, Event.filter(userDataSyncResourceEnablementService.onDidChangeResourceEnablement, e => e[0] === SyncResource.Extensions))(() => this.update())); } @@ -469,7 +479,7 @@ export abstract class InstallInOtherServerAction extends ExtensionAction { } } - private canInstall(): boolean { + protected canInstall(): boolean { // Disable if extension is not installed or not an user extension if ( !this.extension @@ -496,6 +506,11 @@ export abstract class InstallInOtherServerAction extends ExtensionAction { return true; } + // Prefers to run on Web + if (this.server === this.extensionManagementServerService.webExtensionManagementServer && prefersExecuteOnWeb(this.extension.local.manifest, this.productService, this.configurationService)) { + return true; + } + if (this.canInstallAnyWhere) { // Can run on UI if (this.server === this.extensionManagementServerService.localExtensionManagementServer && canExecuteOnUI(this.extension.local.manifest, this.productService, this.configurationService)) { @@ -543,7 +558,9 @@ export class RemoteInstallAction extends InstallInOtherServerAction { } protected getInstallLabel(): string { - return this.extensionManagementServerService.remoteExtensionManagementServer ? localize('Install on Server', "Install in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label) : InstallInOtherServerAction.INSTALL_LABEL; + return this.extensionManagementServerService.remoteExtensionManagementServer + ? localize({ key: 'install in remote', comment: ['This is the name of the action to install an extension in remote server. Placeholder is for the name of remote server.'] }, "Install in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label) + : InstallInOtherServerAction.INSTALL_LABEL; } } @@ -565,9 +582,34 @@ export class LocalInstallAction extends InstallInOtherServerAction { } +export class WebInstallAction extends InstallInOtherServerAction { + + constructor( + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService, + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, + @IWebExtensionsScannerService private readonly webExtensionsScannerService: IWebExtensionsScannerService, + ) { + super(`extensions.webInstall`, extensionManagementServerService.webExtensionManagementServer, false, extensionsWorkbenchService, extensionManagementServerService, productService, configurationService); + } + + protected getInstallLabel(): string { + return localize('install browser', "Install in Browser"); + } + + protected canInstall(): boolean { + if (super.canInstall()) { + return !!this.extension?.gallery && this.webExtensionsScannerService.canAddExtension(this.extension.gallery); + } + return false; + } + +} + export class UninstallAction extends ExtensionAction { - private static readonly UninstallLabel = localize('uninstallAction', "Uninstall"); + static readonly UninstallLabel = localize('uninstallAction', "Uninstall"); private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling"); private static readonly UninstallClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall`; @@ -632,7 +674,6 @@ export class UpdateAction extends ExtensionAction { constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @INotificationService private readonly notificationService: INotificationService, ) { super(`extensions.update`, '', UpdateAction.DisabledClass, false); this.update(); @@ -669,18 +710,13 @@ export class UpdateAction extends ExtensionAction { return this.install(this.extension); } - private install(extension: IExtension): Promise { - return this.extensionsWorkbenchService.install(extension).then(() => { + private async install(extension: IExtension): Promise { + try { + await this.extensionsWorkbenchService.install(extension); alert(localize('updateExtensionComplete', "Updating extension {0} to version {1} completed.", extension.displayName, extension.latestVersion)); - }, err => { - if (!extension.gallery) { - return this.notificationService.error(err); - } - - console.error(err); - - return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.identifier.id), err, this.instantiationService); - }); + } catch (err) { + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Update, err).run(); + } } private getUpdateLabel(version?: string): string { @@ -828,44 +864,51 @@ export class DropDownMenuActionViewItem extends ExtensionActionViewItem { } } -export function getContextMenuActions(menuService: IMenuService, contextKeyService: IContextKeyService, instantiationService: IInstantiationService, extension: IExtension | undefined | null): IAction[][] { - const scopedContextKeyService = contextKeyService.createScoped(); - if (extension) { - scopedContextKeyService.createKey('extension', extension.identifier.id); - scopedContextKeyService.createKey('isBuiltinExtension', extension.isBuiltin); - scopedContextKeyService.createKey('extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration); - if (extension.state === ExtensionState.Installed) { - scopedContextKeyService.createKey('extensionStatus', 'installed'); +export function getContextMenuActions(extension: IExtension | undefined | null, inExtensionEditor: boolean, instantiationService: IInstantiationService): IAction[][] { + return instantiationService.invokeFunction(accessor => { + const scopedContextKeyService = accessor.get(IContextKeyService).createScoped(); + const menuService = accessor.get(IMenuService); + const extensionRecommendationsService = accessor.get(IExtensionRecommendationsService); + const extensionIgnoredRecommendationsService = accessor.get(IExtensionIgnoredRecommendationsService); + if (extension) { + scopedContextKeyService.createKey('extension', extension.identifier.id); + scopedContextKeyService.createKey('isBuiltinExtension', extension.isBuiltin); + scopedContextKeyService.createKey('extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration); + scopedContextKeyService.createKey('isExtensionRecommended', !!extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]); + scopedContextKeyService.createKey('isExtensionWorkspaceRecommended', extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]?.reasonId === ExtensionRecommendationReason.Workspace); + scopedContextKeyService.createKey('isUserIgnoredRecommendation', extensionIgnoredRecommendationsService.globalIgnoredRecommendations.some(e => e === extension.identifier.id.toLowerCase())); + scopedContextKeyService.createKey('inExtensionEditor', inExtensionEditor); + if (extension.state === ExtensionState.Installed) { + scopedContextKeyService.createKey('extensionStatus', 'installed'); + } } - } - const groups: IAction[][] = []; - const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService); - menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => { - if (action instanceof SubmenuAction) { - return action; - } - return instantiationService.createInstance(MenuItemExtensionAction, action); - }))); - menu.dispose(); - scopedContextKeyService.dispose(); + const groups: IAction[][] = []; + const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService); + menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => { + if (action instanceof SubmenuAction) { + return action; + } + return instantiationService.createInstance(MenuItemExtensionAction, action); + }))); + menu.dispose(); + scopedContextKeyService.dispose(); - return groups; + return groups; + }); } export class ManageExtensionAction extends ExtensionDropDownAction { static readonly ID = 'extensions.manage'; - private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage codicon-gear`; + private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage ` + ThemeIcon.asClassName(manageExtensionIcon); private static readonly HideManageExtensionClass = `${ManageExtensionAction.Class} hide`; constructor( @IInstantiationService instantiationService: IInstantiationService, @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, - @IMenuService private readonly menuService: IMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(ManageExtensionAction.ID, '', '', true, true, instantiationService); @@ -902,10 +945,12 @@ export class ManageExtensionAction extends ExtensionDropDownAction { this.instantiationService.createInstance(DisableGloballyAction, runningExtensions), this.instantiationService.createInstance(DisableForWorkspaceAction, runningExtensions) ]); - groups.push([this.instantiationService.createInstance(UninstallAction)]); - groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]); + groups.push([ + this.instantiationService.createInstance(UninstallAction), + this.instantiationService.createInstance(InstallAnotherVersionAction) + ]); - getContextMenuActions(this.menuService, this.contextKeyService, this.instantiationService, this.extension).forEach(actions => groups.push(actions)); + getContextMenuActions(this.extension, false, this.instantiationService).forEach(actions => groups.push(actions)); groups.forEach(group => group.forEach(extensionAction => { if (extensionAction instanceof ExtensionAction) { @@ -933,6 +978,30 @@ export class ManageExtensionAction extends ExtensionDropDownAction { } } +export class ExtensionEditorManageExtensionAction extends ExtensionDropDownAction { + + constructor( + @IInstantiationService instantiationService: IInstantiationService + ) { + super('extensionEditor.manageExtension', '', `${ExtensionAction.ICON_ACTION_CLASS} manage ${ThemeIcon.asClassName(manageExtensionIcon)}`, true, true, instantiationService); + this.tooltip = localize('manage', "Manage"); + } + + update(): void { } + + run(): Promise { + const actionGroups: IAction[][] = []; + getContextMenuActions(this.extension, true, this.instantiationService).forEach(actions => actionGroups.push(actions)); + actionGroups.forEach(group => group.forEach(extensionAction => { + if (extensionAction instanceof ExtensionAction) { + extensionAction.extension = this.extension; + } + })); + return super.run({ actionGroups, disposeActionsOnHide: true }); + } + +} + export class MenuItemExtensionAction extends ExtensionAction { constructor( @@ -968,14 +1037,13 @@ export class InstallAnotherVersionAction extends ExtensionAction { @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @INotificationService private readonly notificationService: INotificationService, ) { - super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL); + super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); this.update(); } update(): void { - this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery; + this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery && this.extension.state === ExtensionState.Installed; } run(): Promise { @@ -983,22 +1051,20 @@ export class InstallAnotherVersionAction extends ExtensionAction { return Promise.resolve(); } return this.quickInputService.pick(this.getVersionEntries(), { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true }) - .then(pick => { + .then(async pick => { if (pick) { if (this.extension!.version === pick.id) { return Promise.resolve(); } - const promise: Promise = pick.latest ? this.extensionsWorkbenchService.install(this.extension!) : this.extensionsWorkbenchService.installVersion(this.extension!, pick.id); - return promise - .then(null, err => { - if (!this.extension!.gallery) { - return this.notificationService.error(err); - } - - console.error(err); - - return promptDownloadManually(this.extension!.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", this.extension!.identifier.id), err, this.instantiationService); - }); + try { + if (pick.latest) { + await this.extensionsWorkbenchService.install(this.extension!); + } else { + await this.extensionsWorkbenchService.installVersion(this.extension!, pick.id); + } + } catch (error) { + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension!, InstallOperation.Install, error).run(); + } } return null; }); @@ -1020,6 +1086,7 @@ export class EnableForWorkspaceAction extends ExtensionAction { @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService ) { super(EnableForWorkspaceAction.ID, EnableForWorkspaceAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); + this.tooltip = localize('enableForWorkspaceActionToolTip', "Enable this extension only in this workspace"); this.update(); } @@ -1050,6 +1117,7 @@ export class EnableGloballyAction extends ExtensionAction { @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService ) { super(EnableGloballyAction.ID, EnableGloballyAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); + this.tooltip = localize('enableGloballyActionToolTip', "Enable this extension"); this.update(); } @@ -1075,18 +1143,24 @@ export class DisableForWorkspaceAction extends ExtensionAction { static readonly ID = 'extensions.disableForWorkspace'; static readonly LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)"); - constructor(readonly runningExtensions: IExtensionDescription[], + constructor(private _runningExtensions: IExtensionDescription[], @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService ) { super(DisableForWorkspaceAction.ID, DisableForWorkspaceAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); + this.tooltip = localize('disableForWorkspaceActionToolTip', "Disable this extension only in this workspace"); + this.update(); + } + + set runningExtensions(runningExtensions: IExtensionDescription[]) { + this._runningExtensions = runningExtensions; this.update(); } update(): void { this.enabled = false; - if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { + if (this.extension && this.extension.local && this._runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace) && this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local); @@ -1106,17 +1180,24 @@ export class DisableGloballyAction extends ExtensionAction { static readonly ID = 'extensions.disableGlobally'; static readonly LABEL = localize('disableGloballyAction', "Disable"); - constructor(readonly runningExtensions: IExtensionDescription[], + constructor( + private _runningExtensions: IExtensionDescription[], @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService ) { super(DisableGloballyAction.ID, DisableGloballyAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); + this.tooltip = localize('disableGloballyActionToolTip', "Disable this extension"); + this.update(); + } + + set runningExtensions(runningExtensions: IExtensionDescription[]) { + this._runningExtensions = runningExtensions; this.update(); } update(): void { this.enabled = false; - if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))) { + if (this.extension && this.extension.local && this._runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))) { this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace) && this.extensionEnablementService.canChangeEnablement(this.extension.local); @@ -1146,13 +1227,21 @@ export class EnableDropDownAction extends ActionWithDropDownAction { export class DisableDropDownAction extends ActionWithDropDownAction { constructor( - runningExtensions: IExtensionDescription[], + @IExtensionService extensionService: IExtensionService, @IInstantiationService instantiationService: IInstantiationService ) { - super('extensions.disable', localize('disableAction', "Disable"), [ - instantiationService.createInstance(DisableGloballyAction, runningExtensions), - instantiationService.createInstance(DisableForWorkspaceAction, runningExtensions) - ]); + const actions = [ + instantiationService.createInstance(DisableGloballyAction, []), + instantiationService.createInstance(DisableForWorkspaceAction, []) + ]; + super('extensions.disable', localize('disableAction', "Disable"), actions); + + const updateRunningExtensions = async () => { + const runningExtensions = await extensionService.getExtensions(); + actions.forEach(a => a.runningExtensions = runningExtensions); + }; + updateRunningExtensions(); + this._register(extensionService.onDidChangeExtensions(() => updateRunningExtensions())); } } @@ -1265,7 +1354,6 @@ export class UpdateAllAction extends Action { constructor( id: string, label: string, isPrimary: boolean, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @INotificationService private readonly notificationService: INotificationService, @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(id, label, '', false); @@ -1283,16 +1371,12 @@ export class UpdateAllAction extends Action { return Promise.all(this.extensionsWorkbenchService.outdated.map(e => this.install(e))); } - private install(extension: IExtension): Promise { - return this.extensionsWorkbenchService.install(extension).then(undefined, err => { - if (!extension.gallery) { - return this.notificationService.error(err); - } - - console.error(err); - - return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.identifier.id), err, this.instantiationService); - }); + private async install(extension: IExtension): Promise { + try { + await this.extensionsWorkbenchService.install(extension); + } catch (err) { + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Update, err).run(); + } } } @@ -1345,11 +1429,12 @@ export class ReloadAction extends ExtensionAction { } const isUninstalled = this.extension.state === ExtensionState.Uninstalled; - const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0]; - const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)); + const runningExtension = this._runningExtensions.find(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier)); if (isUninstalled) { - if (isSameExtensionRunning && !this.extensionService.canRemoveExtension(runningExtension)) { + const canRemoveRunningExtension = runningExtension && this.extensionService.canRemoveExtension(runningExtension); + const isSameExtensionRunning = runningExtension && (!this.extension.server || this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension))); + if (!canRemoveRunningExtension && isSameExtensionRunning) { this.enabled = true; this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('postUninstallTooltip', "Please reload Visual Studio Code to complete the uninstallation of this extension."); @@ -1358,6 +1443,7 @@ export class ReloadAction extends ExtensionAction { return; } if (this.extension.local) { + const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)); const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local); // Extension is running @@ -1693,11 +1779,11 @@ export class ShowInstalledExtensionsAction extends Action { super(id, label, undefined, true); } - run(refresh?: boolean): Promise { + run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { - viewlet.search('@installed ', refresh); + viewlet.search('@installed '); viewlet.focus(); }); } @@ -1736,7 +1822,7 @@ export class ClearExtensionsSearchResultsAction extends Action { label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'codicon-clear-all', true); + super(id, label, ThemeIcon.asClassName(clearSearchResultsIcon), true); } async run(): Promise { @@ -1755,18 +1841,39 @@ export class ClearExtensionsInputAction extends ClearExtensionsSearchResultsActi id: string, label: string, onSearchChange: Event, - value: string, + getValue: () => string, @IViewsService viewsService: IViewsService ) { super(id, label, viewsService); - this.onSearchChange(value); + this.onSearchChange(getValue()); this._register(onSearchChange(this.onSearchChange, this)); } private onSearchChange(value: string): void { this.enabled = !!value; } +} +export class RefreshExtensionsAction extends Action { + + static readonly ID = 'workbench.extensions.action.refreshExtension'; + static readonly LABEL = localize('refreshExtension', "Refresh"); + + constructor( + id: string, + label: string, + @IViewsService private readonly viewsService: IViewsService + ) { + super(id, label, ThemeIcon.asClassName(refreshIcon), true); + } + + async run(): Promise { + const viewPaneContainer = this.viewsService.getActiveViewPaneContainerWithId(VIEWLET_ID); + if (viewPaneContainer) { + const extensionsViewPaneContainer = viewPaneContainer as IExtensionsViewPaneContainer; + return extensionsViewPaneContainer.refresh(); + } + } } export class ShowBuiltInExtensionsAction extends Action { @@ -1899,7 +2006,7 @@ export class ShowRecommendedExtensionsAction extends Action { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { - viewlet.search('@recommended ', true); + viewlet.search('@recommended '); viewlet.focus(); }); } @@ -1956,26 +2063,21 @@ export class InstallRecommendedExtensionAction extends Action { this.extensionId = extensionId; } - run(): Promise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) - .then(viewlet => { - viewlet.search(`@id:${this.extensionId}`); - viewlet.focus(); - return this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None) - .then(pager => { - if (pager && pager.firstPage && pager.firstPage.length) { - const extension = pager.firstPage[0]; - return this.extensionWorkbenchService.install(extension) - .then(() => this.extensionWorkbenchService.open(extension)) - .then(() => null, err => { - console.error(err); - return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService); - }); - } - return null; - }); - }); + async run(): Promise { + const viewlet = await this.viewletService.openViewlet(VIEWLET_ID, true); + const viewPaneContainer = viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer; + viewPaneContainer.search(`@id:${this.extensionId}`); + viewPaneContainer.focus(); + const pager = await this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None); + if (pager && pager.firstPage && pager.firstPage.length) { + const extension = pager.firstPage[0]; + await this.extensionWorkbenchService.open(extension); + try { + await this.extensionWorkbenchService.install(extension); + } catch (err) { + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Install, err).run(); + } + } } } @@ -1983,7 +2085,7 @@ export class IgnoreExtensionRecommendationAction extends Action { static readonly ID = 'extensions.ignore'; - private static readonly Class = 'extension-action ignore'; + private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} ignore`; constructor( private readonly extension: IExtension, @@ -2006,7 +2108,7 @@ export class UndoIgnoreExtensionRecommendationAction extends Action { static readonly ID = 'extensions.ignore'; - private static readonly Class = 'extension-action undo-ignore'; + private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} undo-ignore`; constructor( private readonly extension: IExtension, @@ -2122,13 +2224,15 @@ export class ChangeSortAction extends Action { this.query = Query.parse(''); this.enabled = false; + this.checked = false; this._register(onSearchChange(this.onSearchChange, this)); } private onSearchChange(value: string): void { const query = Query.parse(value); this.query = new Query(query.value, this.sortBy || query.sortBy, query.groupBy); - this.enabled = !!value && this.query.isValid() && !this.query.equals(query); + this.enabled = !!value && this.query.isValid(); + this.checked = this.enabled && this.query.equals(query); } run(): Promise { @@ -2141,124 +2245,6 @@ export class ChangeSortAction extends Action { } } -export class ConfigureRecommendedExtensionsCommandsContributor extends Disposable implements IWorkbenchContribution { - - private workspaceContextKey = new RawContextKey('workspaceRecommendations', true); - private workspaceFolderContextKey = new RawContextKey('workspaceFolderRecommendations', true); - private addToWorkspaceRecommendationsContextKey = new RawContextKey('addToWorkspaceRecommendations', false); - private addToWorkspaceFolderRecommendationsContextKey = new RawContextKey('addToWorkspaceFolderRecommendations', false); - - constructor( - @IContextKeyService contextKeyService: IContextKeyService, - @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @IEditorService editorService: IEditorService - ) { - super(); - const boundWorkspaceContextKey = this.workspaceContextKey.bindTo(contextKeyService); - boundWorkspaceContextKey.set(workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE); - this._register(workspaceContextService.onDidChangeWorkbenchState(() => boundWorkspaceContextKey.set(workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE))); - - const boundWorkspaceFolderContextKey = this.workspaceFolderContextKey.bindTo(contextKeyService); - boundWorkspaceFolderContextKey.set(workspaceContextService.getWorkspace().folders.length > 0); - this._register(workspaceContextService.onDidChangeWorkspaceFolders(() => boundWorkspaceFolderContextKey.set(workspaceContextService.getWorkspace().folders.length > 0))); - - const boundAddToWorkspaceRecommendationsContextKey = this.addToWorkspaceRecommendationsContextKey.bindTo(contextKeyService); - boundAddToWorkspaceRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE); - this._register(editorService.onDidActiveEditorChange(() => boundAddToWorkspaceRecommendationsContextKey.set( - editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE))); - this._register(workspaceContextService.onDidChangeWorkbenchState(() => boundAddToWorkspaceRecommendationsContextKey.set( - editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE))); - - const boundAddToWorkspaceFolderRecommendationsContextKey = this.addToWorkspaceFolderRecommendationsContextKey.bindTo(contextKeyService); - boundAddToWorkspaceFolderRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput); - this._register(editorService.onDidActiveEditorChange(() => boundAddToWorkspaceFolderRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput))); - - this.registerCommands(); - } - - private registerCommands(): void { - CommandsRegistry.registerCommand(ConfigureWorkspaceRecommendedExtensionsAction.ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService).createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL).run(); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: ConfigureWorkspaceRecommendedExtensionsAction.ID, - title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' }, - category: localize('extensions', "Extensions") - }, - when: this.workspaceContextKey - }); - - CommandsRegistry.registerCommand(ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService).createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL).run(); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, - title: { value: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace Folder)' }, - category: localize('extensions', "Extensions") - }, - when: this.workspaceFolderContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceRecommendationsAction.ADD_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceRecommendationsAction, AddToWorkspaceRecommendationsAction.ADD_ID, AddToWorkspaceRecommendationsAction.ADD_LABEL) - .run(AddToWorkspaceRecommendationsAction.ADD); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceRecommendationsAction.ADD_ID, - title: { value: AddToWorkspaceRecommendationsAction.ADD_LABEL, original: 'Add to Recommended Extensions (Workspace)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceRecommendationsContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceFolderRecommendationsAction.ADD_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceFolderRecommendationsAction, AddToWorkspaceFolderRecommendationsAction.ADD_ID, AddToWorkspaceFolderRecommendationsAction.ADD_LABEL) - .run(AddToWorkspaceRecommendationsAction.ADD); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceFolderRecommendationsAction.ADD_ID, - title: { value: AddToWorkspaceFolderRecommendationsAction.ADD_LABEL, original: 'Extensions: Add to Recommended Extensions (Workspace Folder)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceFolderRecommendationsContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceRecommendationsAction.IGNORE_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceRecommendationsAction, AddToWorkspaceRecommendationsAction.IGNORE_ID, AddToWorkspaceRecommendationsAction.IGNORE_LABEL) - .run(AddToWorkspaceRecommendationsAction.IGNORE); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceRecommendationsAction.IGNORE_ID, - title: { value: AddToWorkspaceRecommendationsAction.IGNORE_LABEL, original: 'Extensions: Ignore Recommended Extension (Workspace)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceRecommendationsContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceFolderRecommendationsAction, AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, AddToWorkspaceFolderRecommendationsAction.IGNORE_LABEL) - .run(AddToWorkspaceRecommendationsAction.IGNORE); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, - title: { value: AddToWorkspaceFolderRecommendationsAction.IGNORE_LABEL, original: 'Extensions: Ignore Recommended Extension (Workspace Folder)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceFolderRecommendationsContextKey - }); - } -} - export abstract class AbstractConfigureRecommendedExtensionsAction extends Action { constructor( @@ -2300,83 +2286,6 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio })); } - protected addExtensionToWorkspaceConfig(workspaceConfigurationFile: URI, extensionId: string, shouldRecommend: boolean) { - return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile) - .then(content => { - const extensionIdLowerCase = extensionId.toLowerCase(); - const workspaceExtensionsConfigContent: IExtensionsConfigContent = (json.parse(content.value.toString()) || {})['extensions'] || {}; - let insertInto = shouldRecommend ? workspaceExtensionsConfigContent.recommendations || [] : workspaceExtensionsConfigContent.unwantedRecommendations || []; - let removeFrom = shouldRecommend ? workspaceExtensionsConfigContent.unwantedRecommendations || [] : workspaceExtensionsConfigContent.recommendations || []; - - if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) { - return Promise.resolve(null); - } - - insertInto.push(extensionId); - removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase); - - return this.jsonEditingService.write(workspaceConfigurationFile, - [{ - path: ['extensions'], - value: { - recommendations: shouldRecommend ? insertInto : removeFrom, - unwantedRecommendations: shouldRecommend ? removeFrom : insertInto - } - }], - true); - }); - } - - protected addExtensionToWorkspaceFolderConfig(extensionsFileResource: URI, extensionId: string, shouldRecommend: boolean): Promise { - return this.getOrCreateExtensionsFile(extensionsFileResource) - .then(({ content }) => { - const extensionIdLowerCase = extensionId.toLowerCase(); - const extensionsConfigContent: IExtensionsConfigContent = json.parse(content) || {}; - let insertInto = shouldRecommend ? extensionsConfigContent.recommendations || [] : extensionsConfigContent.unwantedRecommendations || []; - let removeFrom = shouldRecommend ? extensionsConfigContent.unwantedRecommendations || [] : extensionsConfigContent.recommendations || []; - - if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) { - return Promise.resolve(null); - } - - insertInto.push(extensionId); - - let removeFromPromise: Promise = Promise.resolve(); - if (removeFrom.some(e => e.toLowerCase() === extensionIdLowerCase)) { - removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase); - removeFromPromise = this.jsonEditingService.write(extensionsFileResource, - [{ - path: shouldRecommend ? ['unwantedRecommendations'] : ['recommendations'], - value: removeFrom - }], - true); - } - - return removeFromPromise.then(() => - this.jsonEditingService.write(extensionsFileResource, - [{ - path: shouldRecommend ? ['recommendations'] : ['unwantedRecommendations'], - value: insertInto - }], - true) - ); - }); - } - - protected getWorkspaceExtensionsConfigContent(extensionsFileResource: URI): Promise { - return Promise.resolve(this.fileService.readFile(extensionsFileResource)) - .then(content => { - return (json.parse(content.value.toString()) || {})['extensions'] || {}; - }, err => ({ recommendations: [], unwantedRecommendations: [] })); - } - - protected getWorkspaceFolderExtensionsConfigContent(extensionsFileResource: URI): Promise { - return Promise.resolve(this.fileService.readFile(extensionsFileResource)) - .then(content => { - return (json.parse(content.value.toString()) || {}); - }, err => ({ recommendations: [], unwantedRecommendations: [] })); - } - private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise { return Promise.resolve(this.fileService.readFile(workspaceConfigurationFile)) .then(content => { @@ -2495,161 +2404,6 @@ export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends Abstrac } } -export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigureRecommendedExtensionsAction { - static readonly ADD = true; - static readonly IGNORE = false; - static readonly ADD_ID = 'workbench.extensions.action.addToWorkspaceFolderRecommendations'; - static readonly ADD_LABEL = localize('addToWorkspaceFolderRecommendations', "Add to Recommended Extensions (Workspace Folder)"); - static readonly IGNORE_ID = 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations'; - static readonly IGNORE_LABEL = localize('addToWorkspaceFolderIgnoredRecommendations', "Ignore Recommended Extension (Workspace Folder)"); - - constructor( - id: string, - label: string, - @IFileService fileService: IFileService, - @ITextFileService textFileService: ITextFileService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IEditorService editorService: IEditorService, - @IJSONEditingService jsonEditingService: IJSONEditingService, - @ITextModelService textModelResolverService: ITextModelService, - @ICommandService private readonly commandService: ICommandService, - @INotificationService private readonly notificationService: INotificationService - ) { - super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService); - } - - run(shouldRecommend: boolean): Promise { - if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension) { - return Promise.resolve(); - } - const folders = this.contextService.getWorkspace().folders; - if (!folders || !folders.length) { - this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.noWorkspace', 'There are no workspace folders open to add recommendations.')); - return Promise.resolve(); - } - - const extensionId = this.editorService.activeEditor.extension.identifier; - const pickFolderPromise = folders.length === 1 - ? Promise.resolve(folders[0]) - : this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID); - return Promise.resolve(pickFolderPromise) - .then(workspaceFolder => { - if (!workspaceFolder) { - return Promise.resolve(); - } - const configurationFile = workspaceFolder.toResource(EXTENSIONS_CONFIG); - return this.getWorkspaceFolderExtensionsConfigContent(configurationFile).then(content => { - const extensionIdLowerCase = extensionId.id.toLowerCase(); - if (shouldRecommend) { - if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceFolderRecommendations.success', 'The extension was successfully added to this workspace folder\'s recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openExtensionsFile(configurationFile) - }]); - }, err => { - this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err)); - }); - } - else { - if ((content.unwantedRecommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceFolderIgnoredRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s unwanted recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceFolderIgnoredRecommendations.success', 'The extension was successfully added to this workspace folder\'s unwanted recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openExtensionsFile(configurationFile) - }]); - }, err => { - this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err)); - }); - } - }); - }); - } -} - -export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecommendedExtensionsAction { - static readonly ADD = true; - static readonly IGNORE = false; - static readonly ADD_ID = 'workbench.extensions.action.addToWorkspaceRecommendations'; - static readonly ADD_LABEL = localize('addToWorkspaceRecommendations', "Add to Recommended Extensions (Workspace)"); - static readonly IGNORE_ID = 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations'; - static readonly IGNORE_LABEL = localize('addToWorkspaceIgnoredRecommendations', "Ignore Recommended Extension (Workspace)"); - - constructor( - id: string, - label: string, - @IFileService fileService: IFileService, - @ITextFileService textFileService: ITextFileService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IEditorService editorService: IEditorService, - @IJSONEditingService jsonEditingService: IJSONEditingService, - @ITextModelService textModelResolverService: ITextModelService, - @INotificationService private readonly notificationService: INotificationService - ) { - super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService); - } - - run(shouldRecommend: boolean): Promise { - const workspaceConfig = this.contextService.getWorkspace().configuration; - - if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension || !workspaceConfig) { - return Promise.resolve(); - } - - const extensionId = this.editorService.activeEditor.extension.identifier; - - return this.getWorkspaceExtensionsConfigContent(workspaceConfig).then(content => { - const extensionIdLowerCase = extensionId.id.toLowerCase(); - if (shouldRecommend) { - if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceRecommendations.alreadyExists', 'This extension is already present in workspace recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceRecommendations.success', 'The extension was successfully added to this workspace\'s recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openWorkspaceConfigurationFile(workspaceConfig) - }]); - - }, err => { - this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err)); - }); - } else { - if ((content.unwantedRecommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceUnwantedRecommendations.alreadyExists', 'This extension is already present in workspace unwanted recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceUnwantedRecommendations.success', 'The extension was successfully added to this workspace\'s unwanted recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openWorkspaceConfigurationFile(workspaceConfig) - }]); - }, err => { - this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err)); - }); - } - }); - } -} - export class StatusLabelAction extends Action implements IExtensionContainer { private static readonly ENABLED_CLASS = `${ExtensionAction.TEXT_ACTION_CLASS} extension-status-label`; @@ -2776,30 +2530,43 @@ export class MaliciousStatusLabelAction extends ExtensionAction { } } -export class SyncIgnoredIconAction extends ExtensionAction { +export class ToggleSyncExtensionAction extends ExtensionDropDownAction { - private static readonly ENABLE_CLASS = `${ExtensionAction.ICON_ACTION_CLASS} codicon-sync-ignored`; - private static readonly DISABLE_CLASS = `${SyncIgnoredIconAction.ENABLE_CLASS} hide`; + private static readonly IGNORED_SYNC_CLASS = `${ExtensionAction.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncIgnoredIcon)}`; + private static readonly SYNC_CLASS = `${ToggleSyncExtensionAction.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncEnabledIcon)}`; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, + @IInstantiationService instantiationService: IInstantiationService, ) { - super('extensions.syncignore', '', SyncIgnoredIconAction.DISABLE_CLASS, false); + super('extensions.sync', '', ToggleSyncExtensionAction.SYNC_CLASS, false, true, instantiationService); this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectedKeys.includes('settingsSync.ignoredExtensions'))(() => this.update())); + this._register(userDataAutoSyncEnablementService.onDidChangeEnablement(() => this.update())); this.update(); - this.tooltip = localize('syncingore.label', "This extension is ignored during sync."); } update(): void { - this.class = SyncIgnoredIconAction.DISABLE_CLASS; - if (this.extension && this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension)) { - this.class = SyncIgnoredIconAction.ENABLE_CLASS; + this.enabled = !!this.extension && this.userDataAutoSyncEnablementService.isEnabled() && this.extension.state === ExtensionState.Installed; + if (this.extension) { + const isIgnored = this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension); + this.class = isIgnored ? ToggleSyncExtensionAction.IGNORED_SYNC_CLASS : ToggleSyncExtensionAction.SYNC_CLASS; + this.tooltip = isIgnored ? localize('ignored', "This extension is ignored during sync") : localize('synced', "This extension is synced"); } } - run(): Promise { - return Promise.resolve(); + async run(): Promise { + return super.run({ + actionGroups: [ + [ + new Action( + 'extensions.syncignore', + this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension!) ? localize('sync', "Sync this extension") : localize('do not sync', "Do not sync this extension") + , undefined, true, () => this.extensionsWorkbenchService.toggleExtensionIgnoredToSync(this.extension!)) + ] + ], disposeActionsOnHide: true + }); } } @@ -2883,8 +2650,8 @@ export class ExtensionToolTipAction extends ExtensionAction { export class SystemDisabledWarningAction extends ExtensionAction { private static readonly CLASS = `${ExtensionAction.ICON_ACTION_CLASS} system-disable`; - private static readonly WARNING_CLASS = `${SystemDisabledWarningAction.CLASS} ${Codicon.warning.classNames}`; - private static readonly INFO_CLASS = `${SystemDisabledWarningAction.CLASS} ${Codicon.info.classNames}`; + private static readonly WARNING_CLASS = `${SystemDisabledWarningAction.CLASS} ${ThemeIcon.asClassName(warningIcon)}`; + private static readonly INFO_CLASS = `${SystemDisabledWarningAction.CLASS} ${ThemeIcon.asClassName(infoIcon)}`; updateWhenCounterExtensionChanges: boolean = true; private _runningExtensions: IExtensionDescription[] | null = null; @@ -3105,6 +2872,7 @@ export class InstallVSIXAction extends Action { title: localize('installFromVSIX', "Install from VSIX"), filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }], canSelectFiles: true, + canSelectMany: true, openLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install")) }); @@ -3273,21 +3041,18 @@ interface IExtensionPickItem extends IQuickPickItem { extension?: IExtension; } -export class InstallLocalExtensionsInRemoteAction extends Action { +export abstract class AbstractInstallExtensionsInServerAction extends Action { private extensions: IExtension[] | undefined = undefined; constructor( - @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + id: string, + @IExtensionsWorkbenchService protected readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IQuickInputService private readonly quickInputService: IQuickInputService, @INotificationService private readonly notificationService: INotificationService, - @IHostService private readonly hostService: IHostService, @IProgressService private readonly progressService: IProgressService, - @IInstantiationService private readonly instantiationService: IInstantiationService ) { - super('workbench.extensions.actions.installLocalExtensionsInRemote'); + super(id); this.update(); this.extensionsWorkbenchService.queryLocal().then(() => this.updateExtensions()); this._register(this.extensionsWorkbenchService.onChange(() => { @@ -3297,13 +3062,6 @@ export class InstallLocalExtensionsInRemoteAction extends Action { })); } - get label(): string { - if (this.extensionManagementServerService.remoteExtensionManagementServer) { - return localize('select and install local extensions', "Install Local Extensions in '{0}'...", this.extensionManagementServerService.remoteExtensionManagementServer.label); - } - return ''; - } - private updateExtensions(): void { this.extensions = this.extensionsWorkbenchService.local; this.update(); @@ -3315,7 +3073,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action { } async run(): Promise { - return this.selectAndInstallLocalExtensions(); + return this.selectAndInstallExtensions(); } private async queryExtensionsToInstall(): Promise { @@ -3323,15 +3081,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action { return this.getExtensionsToInstall(local); } - private getExtensionsToInstall(local: IExtension[]): IExtension[] { - return local.filter(extension => { - const action = this.instantiationService.createInstance(RemoteInstallAction, true); - action.extension = extension; - return action.enabled; - }); - } - - private async selectAndInstallLocalExtensions(): Promise { + private async selectAndInstallExtensions(): Promise { const quickPick = this.quickInputService.createQuickPick(); quickPick.busy = true; const disposable = quickPick.onDidAccept(() => { @@ -3344,7 +3094,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action { const localExtensionsToInstall = await this.queryExtensionsToInstall(); quickPick.busy = false; if (localExtensionsToInstall.length) { - quickPick.title = localize('install local extensions title', "Install Local Extensions in '{0}'", this.extensionManagementServerService.remoteExtensionManagementServer!.label); + quickPick.title = this.getQuickPickTitle(); quickPick.placeholder = localize('select extensions to install', "Select extensions to install"); quickPick.canSelectMany = true; localExtensionsToInstall.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)); @@ -3359,21 +3109,60 @@ export class InstallLocalExtensionsInRemoteAction extends Action { } } - private onDidAccept(selectedItems: ReadonlyArray): void { + private async onDidAccept(selectedItems: ReadonlyArray): Promise { if (selectedItems.length) { const localExtensionsToInstall = selectedItems.filter(r => !!r.extension).map(r => r.extension!); if (localExtensionsToInstall.length) { - this.progressService.withProgress( + await this.progressService.withProgress( { location: ProgressLocation.Notification, title: localize('installing extensions', "Installing Extensions...") }, - () => this.installLocalExtensions(localExtensionsToInstall)); + () => this.installExtensions(localExtensionsToInstall)); + this.notificationService.info(localize('finished installing', "Successfully installed extensions.")); } } } - private async installLocalExtensions(localExtensionsToInstall: IExtension[]): Promise { + protected abstract getQuickPickTitle(): string; + protected abstract getExtensionsToInstall(local: IExtension[]): IExtension[]; + protected abstract installExtensions(extensions: IExtension[]): Promise; +} + +export class InstallLocalExtensionsInRemoteAction extends AbstractInstallExtensionsInServerAction { + + constructor( + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IQuickInputService quickInputService: IQuickInputService, + @IProgressService progressService: IProgressService, + @INotificationService notificationService: INotificationService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super('workbench.extensions.actions.installLocalExtensionsInRemote', extensionsWorkbenchService, quickInputService, notificationService, progressService); + } + + get label(): string { + if (this.extensionManagementServerService && this.extensionManagementServerService.remoteExtensionManagementServer) { + return localize('select and install local extensions', "Install Local Extensions in '{0}'...", this.extensionManagementServerService.remoteExtensionManagementServer.label); + } + return ''; + } + + protected getQuickPickTitle(): string { + return localize('install local extensions title', "Install Local Extensions in '{0}'", this.extensionManagementServerService.remoteExtensionManagementServer!.label); + } + + protected getExtensionsToInstall(local: IExtension[]): IExtension[] { + return local.filter(extension => { + const action = this.instantiationService.createInstance(RemoteInstallAction, true); + action.extension = extension; + return action.enabled; + }); + } + + protected async installExtensions(localExtensionsToInstall: IExtension[]): Promise { const galleryExtensions: IGalleryExtension[] = []; const vsixs: URI[] = []; await Promise.all(localExtensionsToInstall.map(async extension => { @@ -3390,15 +3179,54 @@ export class InstallLocalExtensionsInRemoteAction extends Action { await Promise.all(galleryExtensions.map(gallery => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(gallery))); await Promise.all(vsixs.map(vsix => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.install(vsix))); + } +} - this.notificationService.notify({ - severity: Severity.Info, - message: localize('finished installing', "Successfully installed extensions in {0}. Please reload the window to enable them.", this.extensionManagementServerService.remoteExtensionManagementServer!.label), - actions: { - primary: [new Action('realod', localize('reload', "Reload Window"), '', true, - () => this.hostService.reload())] +export class InstallRemoteExtensionsInLocalAction extends AbstractInstallExtensionsInServerAction { + + constructor( + id: string, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IQuickInputService quickInputService: IQuickInputService, + @IProgressService progressService: IProgressService, + @INotificationService notificationService: INotificationService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + ) { + super(id, extensionsWorkbenchService, quickInputService, notificationService, progressService); + } + + get label(): string { + return localize('select and install remote extensions', "Install Remote Extensions Locally..."); + } + + protected getQuickPickTitle(): string { + return localize('install remote extensions', "Install Remote Extensions Locally"); + } + + protected getExtensionsToInstall(local: IExtension[]): IExtension[] { + return local.filter(extension => + extension.type === ExtensionType.User && extension.server !== this.extensionManagementServerService.localExtensionManagementServer + && !this.extensionsWorkbenchService.installed.some(e => e.server === this.extensionManagementServerService.localExtensionManagementServer && areSameExtensions(e.identifier, extension.identifier))); + } + + protected async installExtensions(extensions: IExtension[]): Promise { + const galleryExtensions: IGalleryExtension[] = []; + const vsixs: URI[] = []; + await Promise.all(extensions.map(async extension => { + if (this.extensionGalleryService.isEnabled()) { + const gallery = await this.extensionGalleryService.getCompatibleExtension(extension.identifier, extension.version); + if (gallery) { + galleryExtensions.push(gallery); + return; + } } - }); + const vsix = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.zip(extension.local!); + vsixs.push(vsix); + })); + + await Promise.all(galleryExtensions.map(gallery => this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.installFromGallery(gallery))); + await Promise.all(vsixs.map(vsix => this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.install(vsix))); } } @@ -3428,20 +3256,20 @@ CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsWith }); export const extensionButtonProminentBackground = registerColor('extensionButton.prominentBackground', { - dark: '#327e36', - light: '#327e36', + dark: buttonBackground, + light: buttonBackground, hc: null }, localize('extensionButtonProminentBackground', "Button background color for actions extension that stand out (e.g. install button).")); export const extensionButtonProminentForeground = registerColor('extensionButton.prominentForeground', { - dark: Color.white, - light: Color.white, + dark: buttonForeground, + light: buttonForeground, hc: null }, localize('extensionButtonProminentForeground', "Button foreground color for actions extension that stand out (e.g. install button).")); export const extensionButtonProminentHoverBackground = registerColor('extensionButton.prominentHoverBackground', { - dark: '#28632b', - light: '#28632b', + dark: buttonHoverBackground, + light: buttonHoverBackground, hc: null }, localize('extensionButtonProminentHoverBackground', "Button background hover color for actions extension that stand out (e.g. install button).")); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts b/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts new file mode 100644 index 000000000..55fea5ddf --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const extensionsViewIcon = registerIcon('extensions-view-icon', Codicon.extensions, localize('extensionsViewIcon', 'View icon of the extensions view.')); + +export const manageExtensionIcon = registerIcon('extensions-manage', Codicon.gear, localize('manageExtensionIcon', 'Icon for the \'Manage\' action in the extensions view.')); + +export const clearSearchResultsIcon = registerIcon('extensions-clear-search-results', Codicon.clearAll, localize('clearSearchResultsIcon', 'Icon for the \'Clear Search Result\' action in the extensions view.')); +export const refreshIcon = registerIcon('extensions-refresh', Codicon.refresh, localize('refreshIcon', 'Icon for the \'Refresh\' action in the extensions view.')); +export const filterIcon = registerIcon('extensions-filter', Codicon.filter, localize('filterIcon', 'Icon for the \'Filter\' action in the extensions view.')); + +export const installLocalInRemoteIcon = registerIcon('extensions-install-local-in-remote', Codicon.cloudDownload, localize('installLocalInRemoteIcon', 'Icon for the \'Install Local Extension in Remote\' action in the extensions view.')); +export const installWorkspaceRecommendedIcon = registerIcon('extensions-install-workspace-recommended', Codicon.cloudDownload, localize('installWorkspaceRecommendedIcon', 'Icon for the \'Install Workspace Recommended Extensions\' action in the extensions view.')); +export const configureRecommendedIcon = registerIcon('extensions-configure-recommended', Codicon.pencil, localize('configureRecommendedIcon', 'Icon for the \'Configure Recommended Extensions\' action in the extensions view.')); + +export const syncEnabledIcon = registerIcon('extensions-sync-enabled', Codicon.sync, localize('syncEnabledIcon', 'Icon to indicate that an extension is synced.')); +export const syncIgnoredIcon = registerIcon('extensions-sync-ignored', Codicon.syncIgnored, localize('syncIgnoredIcon', 'Icon to indicate that an extension is ignored when syncing.')); +export const remoteIcon = registerIcon('extensions-remote', Codicon.remote, localize('remoteIcon', 'Icon to indicate that an extension is remote in the extensions view and editor.')); +export const installCountIcon = registerIcon('extensions-install-count', Codicon.cloudDownload, localize('installCountIcon', 'Icon shown along with the install count in the extensions view and editor.')); +export const ratingIcon = registerIcon('extensions-rating', Codicon.star, localize('ratingIcon', 'Icon shown along with the rating in the extensions view and editor.')); + +export const starFullIcon = registerIcon('extensions-star-full', Codicon.starFull, localize('starFullIcon', 'Full star icon used for the rating in the extensions editor.')); +export const starHalfIcon = registerIcon('extensions-star-half', Codicon.starHalf, localize('starHalfIcon', 'Half star icon used for the rating in the extensions editor.')); +export const starEmptyIcon = registerIcon('extensions-star-empty', Codicon.starEmpty, localize('starEmptyIcon', 'Empty star icon used for the rating in the extensions editor.')); + +export const warningIcon = registerIcon('extensions-warning-message', Codicon.warning, localize('warningIcon', 'Icon shown with a warning message in the extensions editor.')); +export const infoIcon = registerIcon('extensions-info-message', Codicon.info, localize('infoIcon', 'Icon shown with an info message in the extensions editor.')); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index f1aede07b..563a5534a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -14,9 +14,9 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { Event } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; -import { UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionViewItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, ExtensionToolTipAction, LocalInstallAction, SyncIgnoredIconAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionViewItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, ExtensionToolTipAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; +import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -26,6 +26,8 @@ import { foreground, listActiveSelectionForeground, listActiveSelectionBackgroun import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +export const EXTENSION_LIST_ELEMENT_HEIGHT = 62; + export interface IExtensionsViewState { onFocus: Event; onBlur: Event; @@ -47,7 +49,7 @@ export interface ITemplateData { } export class Delegate implements IListVirtualDelegate { - getHeight() { return 62; } + getHeight() { return EXTENSION_LIST_ELEMENT_HEIGHT; } getTemplateId() { return 'extension'; } } @@ -81,6 +83,7 @@ export class Renderer implements IPagedRenderer { const version = append(header, $('span.version')); const installCount = append(header, $('span.install-count')); const ratings = append(header, $('span.ratings')); + const syncIgnore = append(header, $('span.sync-ignored')); const headerRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, header, false); const description = append(details, $('.description.ellipsis')); const footer = append(details, $('.footer')); @@ -91,8 +94,8 @@ export class Renderer implements IPagedRenderer { if (action instanceof ActionWithDropDownAction) { return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); } - if (action.id === ManageExtensionAction.ID) { - return (action).createActionViewItem(); + if (action instanceof ExtensionDropDownAction) { + return action.createActionViewItem(); } return new ExtensionActionViewItem(null, action, actionOptions); } @@ -103,13 +106,13 @@ export class Renderer implements IPagedRenderer { const reloadAction = this.instantiationService.createInstance(ReloadAction); const actions = [ this.instantiationService.createInstance(StatusLabelAction), - this.instantiationService.createInstance(SyncIgnoredIconAction), this.instantiationService.createInstance(UpdateAction), reloadAction, this.instantiationService.createInstance(InstallDropdownAction), this.instantiationService.createInstance(InstallingLabelAction), this.instantiationService.createInstance(RemoteInstallAction, false), this.instantiationService.createInstance(LocalInstallAction), + this.instantiationService.createInstance(WebInstallAction), this.instantiationService.createInstance(MaliciousStatusLabelAction, false), systemDisabledWarningAction, this.instantiationService.createInstance(ManageExtensionAction) @@ -123,6 +126,7 @@ export class Renderer implements IPagedRenderer { headerRemoteBadgeWidget, tooltipWidget, this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version), + this.instantiationService.createInstance(SyncIgnoredWidget, syncIgnore), this.instantiationService.createInstance(InstallCountWidget, installCount, true), this.instantiationService.createInstance(RatingsWidget, ratings, true) ]; @@ -220,10 +224,14 @@ export class Renderer implements IPagedRenderer { }); } }, this, data.extensionDisposables); + } + disposeElement(extension: IExtension, index: number, data: ITemplateData): void { + data.extensionDisposables = dispose(data.extensionDisposables); } disposeTemplate(data: ITemplateData): void { + data.extensionDisposables = dispose(data.extensionDisposables); data.disposables = dispose(data.disposables); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 9cd1effb5..afe9ebc8a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -9,7 +9,7 @@ import { timeout, Delayer } from 'vs/base/common/async'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; -import { Event as EventOf, Emitter } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { IAction, Action, Separator, SubmenuAction } from 'vs/base/common/actions'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -22,20 +22,20 @@ import { ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, SearchCategoryAction, RecentlyPublishedExtensionsAction, ShowInstalledExtensionsAction, ShowOutdatedExtensionsAction, ShowDisabledExtensionsAction, - ShowEnabledExtensionsAction, PredefinedExtensionFilterAction + ShowEnabledExtensionsAction, PredefinedExtensionFilterAction, RefreshExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; -import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInFeatureExtensionsView, BuiltInThemesExtensionsView, BuiltInProgrammingLanguageExtensionsView, ServerExtensionsView, DefaultRecommendedExtensionsView, OutdatedExtensionsView, InstalledExtensionsView, SearchBuiltInExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews'; +import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInFeatureExtensionsView, BuiltInThemesExtensionsView, BuiltInProgrammingLanguageExtensionsView, ServerInstalledExtensionsView, DefaultRecommendedExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import Severity from 'vs/base/common/severity'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewDescriptorService, IAddedViewDescriptorRef } from 'vs/workbench/common/views'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -61,6 +61,9 @@ import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { isWeb } from 'vs/base/common/platform'; +import { memoize } from 'vs/base/common/decorators'; +import { filterIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; const DefaultViewsContext = new RawContextKey('defaultExtensionViews', true); const SearchMarketplaceExtensionsContext = new RawContextKey('searchMarketplaceExtensions', false); @@ -69,6 +72,7 @@ const SearchOutdatedExtensionsContext = new RawContextKey('searchOutdat const SearchEnabledExtensionsContext = new RawContextKey('searchEnabledExtensions', false); const SearchDisabledExtensionsContext = new RawContextKey('searchDisabledExtensions', false); const HasInstalledExtensionsContext = new RawContextKey('hasInstalledExtensions', true); +const HasInstalledWebExtensionsContext = new RawContextKey('hasInstalledWebExtensions', false); const BuiltInExtensionsContext = new RawContextKey('builtInExtensions', false); const SearchBuiltInExtensionsContext = new RawContextKey('searchBuiltInExtensions', false); const RecommendedExtensionsContext = new RawContextKey('recommendedExtensions', false); @@ -80,7 +84,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio constructor( @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @ILabelService private readonly labelService: ILabelService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IContextKeyService private readonly contextKeyService: IContextKeyService ) { this.container = viewDescriptorService.getViewContainerById(VIEWLET_ID)!; this.registerViews(); @@ -107,20 +112,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio private createDefaultExtensionsViewDescriptors(): IViewDescriptor[] { const viewDescriptors: IViewDescriptor[] = []; - /* - * Default popular extensions view - * Separate view for popular extensions required as we need to show popular and recommended sections - * in the default view when there is no search text, and user has no installed extensions. - */ - viewDescriptors.push({ - id: 'workbench.views.extensions.popular', - name: localize('popularExtensions', "Popular"), - ctorDescriptor: new SyncDescriptor(ExtensionsListView), - when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')), - weight: 60, - order: 1, - }); - /* * Default installed extensions views - Shows all user installed extensions. */ @@ -135,27 +126,70 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio servers.push(this.extensionManagementServerService.remoteExtensionManagementServer); } const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => { - if (servers.length) { - const serverLabel = server === this.extensionManagementServerService.webExtensionManagementServer && !this.extensionManagementServerService.localExtensionManagementServer ? localize('local', "Local") : server.label; - return servers.length > 1 ? `${serverLabel} - ${viewTitle}` : viewTitle; + if (servers.length > 1) { + // In Web, use view title as is for remote server, when web extension server is enabled and no web extensions are installed + if (isWeb && server === this.extensionManagementServerService.remoteExtensionManagementServer && + this.extensionManagementServerService.webExtensionManagementServer && !this.contextKeyService.getContextKeyValue('hasInstalledWebExtensions')) { + return viewTitle; + } + return `${server.label} - ${viewTitle}`; } return viewTitle; }; + let installedWebExtensionsContextChangeEvent = Event.None; + if (this.extensionManagementServerService.webExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { + const interestingContextKeys = new Set(); + interestingContextKeys.add('hasInstalledWebExtensions'); + installedWebExtensionsContextChangeEvent = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(interestingContextKeys)); + } + const serverLabelChangeEvent = Event.any(this.labelService.onDidChangeFormatters, installedWebExtensionsContextChangeEvent); for (const server of servers) { const getInstalledViewName = (): string => getViewName(localize('installed', "Installed"), server); - const onDidChangeServerLabel: EventOf = EventOf.map(this.labelService.onDidChangeFormatters, () => undefined); + const onDidChangeTitle = Event.map(serverLabelChangeEvent, () => getInstalledViewName()); + const id = servers.length > 1 ? `workbench.views.extensions.${server.id}.installed` : `workbench.views.extensions.installed`; + const isWebServer = server === this.extensionManagementServerService.webExtensionManagementServer; + if (!isWebServer) { + /* Empty installed extensions view */ + viewDescriptors.push({ + id: `${id}.empty`, + get name() { return getInstalledViewName(); }, + weight: 100, + order: 1, + when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')), + /* Empty installed extensions view shall have fixed height */ + ctorDescriptor: new SyncDescriptor(ServerInstalledExtensionsView, [{ server, fixedHeight: true, onDidChangeTitle }]), + /* Empty installed extensions views shall not be allowed to hidden */ + canToggleVisibility: false + }); + } + /* Installed extensions view */ viewDescriptors.push({ - id: servers.length > 1 ? `workbench.views.extensions.${server.id}.installed` : `workbench.views.extensions.installed`, + id, get name() { return getInstalledViewName(); }, - ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())]), - when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')), weight: 100, - order: 2, - /* Installed extensions views shall not be hidden when there are more than one server */ + order: 1, + when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), isWebServer ? ContextKeyExpr.has('hasInstalledWebExtensions') : ContextKeyExpr.has('hasInstalledExtensions')), + ctorDescriptor: new SyncDescriptor(ServerInstalledExtensionsView, [{ server, onDidChangeTitle }]), + /* Installed extensions views shall not be allowed to hidden when there are more than one server */ canToggleVisibility: servers.length === 1 }); } + /* + * Default popular extensions view + * Separate view for popular extensions required as we need to show popular and recommended sections + * in the default view when there is no search text, and user has no installed extensions. + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.popular', + name: localize('popularExtensions', "Popular"), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), + when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')), + weight: 60, + order: 2, + canToggleVisibility: false + }); + /* * Default recommended extensions view * When user has installed extensions, this is shown along with the views for enabled & disabled extensions @@ -164,7 +198,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'extensions.recommendedList', name: localize('recommendedExtensions', "Recommended"), - ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView), + ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('config.extensions.showRecommendationsOnlyOnDemand')), weight: 40, order: 3, @@ -180,7 +214,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.enabled', name: localize('enabledExtensions', "Enabled"), - ctorDescriptor: new SyncDescriptor(EnabledExtensionsView), + ctorDescriptor: new SyncDescriptor(EnabledExtensionsView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')), hideByDefault: true, weight: 40, @@ -195,7 +229,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.disabled', name: localize('disabledExtensions', "Disabled"), - ctorDescriptor: new SyncDescriptor(DisabledExtensionsView), + ctorDescriptor: new SyncDescriptor(DisabledExtensionsView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')), hideByDefault: true, weight: 10, @@ -217,7 +251,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.marketplace', name: localize('marketPlace', "Marketplace"), - ctorDescriptor: new SyncDescriptor(ExtensionsListView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchMarketplaceExtensions')), }); @@ -227,7 +261,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.searchInstalled', name: localize('installed', "Installed"), - ctorDescriptor: new SyncDescriptor(InstalledExtensionsView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')), }); @@ -237,7 +271,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.searchEnabled', name: localize('enabled', "Enabled"), - ctorDescriptor: new SyncDescriptor(EnabledExtensionsView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')), }); @@ -247,7 +281,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.searchDisabled', name: localize('disabled', "Disabled"), - ctorDescriptor: new SyncDescriptor(DisabledExtensionsView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchDisabledExtensions')), }); @@ -257,7 +291,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.searchOutdated', name: localize('outdated', "Outdated"), - ctorDescriptor: new SyncDescriptor(OutdatedExtensionsView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')), }); @@ -267,7 +301,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.searchBuiltin', name: localize('builtin', "Builtin"), - ctorDescriptor: new SyncDescriptor(SearchBuiltInExtensionsView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchBuiltInExtensions')), }); @@ -280,7 +314,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.workspaceRecommendations', name: localize('workspaceRecommendedExtensions', "Workspace Recommendations"), - ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView), + ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), WorkbenchStateContext.notEqualsTo('empty')), order: 1 }); @@ -288,7 +322,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.otherRecommendations', name: localize('otherRecommendedExtensions', "Other Recommendations"), - ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView), + ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView, [{}]), when: ContextKeyExpr.has('recommendedExtensions'), order: 2 }); @@ -302,21 +336,21 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.builtinFeatureExtensions', name: localize('builtinFeatureExtensions', "Features"), - ctorDescriptor: new SyncDescriptor(BuiltInFeatureExtensionsView), + ctorDescriptor: new SyncDescriptor(BuiltInFeatureExtensionsView, [{}]), when: ContextKeyExpr.has('builtInExtensions'), }); viewDescriptors.push({ id: 'workbench.views.extensions.builtinThemeExtensions', name: localize('builtInThemesExtensions', "Themes"), - ctorDescriptor: new SyncDescriptor(BuiltInThemesExtensionsView), + ctorDescriptor: new SyncDescriptor(BuiltInThemesExtensionsView, [{}]), when: ContextKeyExpr.has('builtInExtensions'), }); viewDescriptors.push({ id: 'workbench.views.extensions.builtinProgrammingLanguageExtensions', name: localize('builtinProgrammingLanguageExtensions', "Programming Languages"), - ctorDescriptor: new SyncDescriptor(BuiltInProgrammingLanguageExtensionsView), + ctorDescriptor: new SyncDescriptor(BuiltInProgrammingLanguageExtensionsView, [{}]), when: ContextKeyExpr.has('builtInExtensions'), }); @@ -328,7 +362,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IExtensionsViewPaneContainer { private readonly _onSearchChange: Emitter = this._register(new Emitter()); - private readonly onSearchChange: EventOf = this._onSearchChange.event; + private readonly onSearchChange: Event = this._onSearchChange.event; private defaultViewsContextKey: IContextKey; private searchMarketplaceExtensionsContextKey: IContextKey; private searchInstalledExtensionsContextKey: IContextKey; @@ -336,6 +370,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE private searchEnabledExtensionsContextKey: IContextKey; private searchDisabledExtensionsContextKey: IContextKey; private hasInstalledExtensionsContextKey: IContextKey; + private hasInstalledWebExtensionsContextKey: IContextKey; private builtInExtensionsContextKey: IContextKey; private searchBuiltInExtensionsContextKey: IContextKey; private recommendedExtensionsContextKey: IContextKey; @@ -345,6 +380,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE private searchBox: SuggestEnabledInput | undefined; private readonly searchViewletState: MementoObject; private readonly sortActions: ChangeSortAction[]; + private secondaryActions: IAction[] | undefined = undefined; constructor( @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @@ -352,7 +388,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE @IProgressService private readonly progressService: IProgressService, @IInstantiationService instantiationService: IInstantiationService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @INotificationService private readonly notificationService: INotificationService, @IViewletService private readonly viewletService: IViewletService, @@ -377,22 +414,30 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.searchEnabledExtensionsContextKey = SearchEnabledExtensionsContext.bindTo(contextKeyService); this.searchDisabledExtensionsContextKey = SearchDisabledExtensionsContext.bindTo(contextKeyService); this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService); + this.hasInstalledWebExtensionsContextKey = HasInstalledWebExtensionsContext.bindTo(contextKeyService); this.builtInExtensionsContextKey = BuiltInExtensionsContext.bindTo(contextKeyService); this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService); this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService); this._register(this.viewletService.onDidViewletOpen(this.onViewletOpen, this)); - this.searchViewletState = this.getMemento(StorageScope.WORKSPACE); - - this.extensionManagementService.getInstalled().then(result => { - this.hasInstalledExtensionsContextKey.set(result.some(r => !r.isBuiltin)); - }); + this.searchViewletState = this.getMemento(StorageScope.WORKSPACE, StorageTarget.USER); this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(AutoUpdateConfigurationKey)) { + this.secondaryActions = undefined; this.updateTitleArea(); } }, this)); + if (extensionManagementServerService.webExtensionManagementServer) { + this._register(extensionsWorkbenchService.onChange(() => { + // show installed web extensions view only when it is not visible + // Do not hide the view automatically when it is visible + if (!this.hasInstalledWebExtensionsContextKey.get()) { + this.updateInstalledWebExtensionsContext(); + } + })); + } + this.sortActions = [ this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Install Count"), this.onSearchChange, 'installs')), this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Rating"), this.onSearchChange, 'rating')), @@ -425,6 +470,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE provideResults: (query: string) => Query.suggestions(query) }, placeholder, 'extensions:searchinput', { placeholderText: placeholder, value: searchValue })); + this.updateInstalledExtensionsContexts(); if (this.searchBox.getValue()) { this.triggerSearch(); } @@ -510,71 +556,80 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE return 400; } + @memoize getActions(): IAction[] { - const filterActions: IAction[] = []; - // Local extensions filters - filterActions.push(...[ - this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, localize('builtin filter', "Built-in")), - this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, localize('installed filter', "Installed")), - this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, localize('enabled filter', "Enabled")), - this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, localize('disabled filter', "Disabled")), - this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, localize('outdated filter', "Outdated")), - ]); + let filterActions: IAction[] = [ + this._register(this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, localize('builtin filter', "Built-in"))), + this._register(this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, localize('installed filter', "Installed"))), + this._register(this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, localize('enabled filter', "Enabled"))), + this._register(this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, localize('disabled filter', "Disabled"))), + this._register(this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, localize('outdated filter', "Outdated"))), + ]; if (this.extensionGalleryService.isEnabled()) { - const galleryFilterActions = [ - this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.featured', localize('featured filter', "Featured"), '@featured'), - this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.popular', localize('most popular filter', "Most Popular"), '@popular'), - this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.recommended', localize('most popular recommended', "Recommended"), '@recommended'), - this.instantiationService.createInstance(RecentlyPublishedExtensionsAction, RecentlyPublishedExtensionsAction.ID, localize('recently published filter', "Recently Published")), - new Separator(), - new SubmenuAction('workbench.extensions.action.filterExtensionsByCategory', localize('filter by category', "Category"), EXTENSION_CATEGORIES.map(category => this.instantiationService.createInstance(SearchCategoryAction, `extensions.actions.searchByCategory.${category}`, category, category))), - new Separator(), + filterActions = [ + this._register(this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.featured', localize('featured filter', "Featured"), '@featured')), + this._register(this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.popular', localize('most popular filter', "Most Popular"), '@popular')), + this._register(this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.recommended', localize('most popular recommended', "Recommended"), '@recommended')), + this._register(this.instantiationService.createInstance(RecentlyPublishedExtensionsAction, RecentlyPublishedExtensionsAction.ID, localize('recently published filter', "Recently Published"))), + this._register(new Separator()), + this._register(new SubmenuAction('workbench.extensions.action.filterExtensionsByCategory', localize('filter by category', "Category"), EXTENSION_CATEGORIES.map(category => this.instantiationService.createInstance(SearchCategoryAction, `extensions.actions.searchByCategory.${category}`, category, category)))), + this._register(new Separator()), + ...filterActions, + this._register(new Separator()), + this._register(new SubmenuAction('workbench.extensions.action.sortBy', localize('sorty by', "Sort By"), this.sortActions)), ]; - filterActions.splice(0, 0, ...galleryFilterActions); - filterActions.push(...[ - new Separator(), - new SubmenuAction('workbench.extensions.action.sortBy', localize('sorty by', "Sort By"), this.sortActions), - ]); } return [ - new SubmenuAction('workbench.extensions.action.filterExtensions', localize('filterExtensions', "Filter Extensions..."), filterActions, 'codicon-filter'), - this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : ''), + this._register(new SubmenuAction('workbench.extensions.action.filterExtensions', localize('filterExtensions', "Filter Extensions..."), filterActions, ThemeIcon.asClassName(filterIcon))), + this._register(this.instantiationService.createInstance(RefreshExtensionsAction, RefreshExtensionsAction.ID, RefreshExtensionsAction.LABEL)), + this._register(this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, () => this.searchBox!.getValue() || '')), ]; } getSecondaryActions(): IAction[] { - const actions: IAction[] = []; + if (!this.secondaryActions) { + this.secondaryActions = []; + this.secondaryActions.push(this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL)); + if (this.configurationService.getValue(AutoUpdateConfigurationKey)) { + this.secondaryActions.push(this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)); + } else { + this.secondaryActions.push(this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL, false), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)); + } - actions.push(this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL)); - if (this.configurationService.getValue(AutoUpdateConfigurationKey)) { - actions.push(this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)); - } else { - actions.push(this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL, false), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)); + this.secondaryActions.push(new Separator()); + this.secondaryActions.push(this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL, false)); + this.secondaryActions.push(this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL, false)); + + this.secondaryActions.push(new Separator()); + this.secondaryActions.push(this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL)); } - - actions.push(new Separator()); - actions.push(this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL, false)); - actions.push(this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL, false)); - - actions.push(new Separator()); - actions.push(this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL)); - - return actions; + return this.secondaryActions; } - search(value: string, refresh: boolean = false): void { - if (this.searchBox) { - if (this.searchBox.getValue() !== value) { - this.searchBox.setValue(value); - } else if (refresh) { - this.doSearch(); - } + search(value: string): void { + if (this.searchBox && this.searchBox.getValue() !== value) { + this.searchBox.setValue(value); } } + async refresh(): Promise { + await this.updateInstalledExtensionsContexts(); + this.doSearch(true); + } + + private async updateInstalledExtensionsContexts(): Promise { + const result = await this.extensionsWorkbenchService.queryLocal(); + this.hasInstalledExtensionsContextKey.set(result.some(r => !r.isBuiltin)); + this.updateInstalledWebExtensionsContext(); + } + + private updateInstalledWebExtensionsContext(): void { + this.hasInstalledWebExtensionsContextKey.set(!!this.extensionManagementServerService.webExtensionManagementServer && this.extensionsWorkbenchService.installed.some(r => r.server === this.extensionManagementServerService.webExtensionManagementServer)); + } + private triggerSearch(): void { this.searchDelayer.trigger(() => this.doSearch(), this.searchBox && this.searchBox.getValue() ? 500 : 0).then(undefined, err => this.onError(err)); } @@ -601,7 +656,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE super.saveState(); } - private doSearch(): Promise { + private doSearch(refresh?: boolean): Promise { const value = this.normalizedQuery(); const isRecommendedExtensionsQuery = ExtensionsListView.isRecommendedExtensionsQuery(value); this.searchInstalledExtensionsContextKey.set(ExtensionsListView.isInstalledExtensionsQuery(value)); @@ -613,9 +668,10 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery); this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery); this.defaultViewsContextKey.set(!value); + this.updateInstalledWebExtensionsContext(); return this.progress(Promise.all(this.panes.map(view => - (view).show(this.normalizedQuery()) + (view).show(this.normalizedQuery(), refresh) .then(model => this.alertSearchResult(model.length, view.id)) ))).then(() => undefined); } @@ -765,7 +821,7 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { .filter(e => maliciousSet.has(e.identifier.id)); if (maliciousExtensions.length) { - return Promise.all(maliciousExtensions.map(e => this.extensionsManagementService.uninstall(e, true).then(() => { + return Promise.all(maliciousExtensions.map(e => this.extensionsManagementService.uninstall(e).then(() => { this.notificationService.prompt( Severity.Warning, localize('malicious warning', "We have uninstalled '{0}' which was reported to be problematic.", e.identifier.id), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 8dc0c1069..932a76b68 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError, getErrorMessage } from 'vs/base/common/errors'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; @@ -16,11 +16,11 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { append, $ } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/contrib/extensions/browser/extensionsList'; -import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; +import { Delegate, Renderer, IExtensionsViewState, EXTENSION_LIST_ELEMENT_HEIGHT } from 'vs/workbench/contrib/extensions/browser/extensionsList'; +import { ExtensionState, IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -44,11 +44,12 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { IMenuService } from 'vs/platform/actions/common/actions'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { configureRecommendedIcon, installLocalInRemoteIcon, installWorkspaceRecommendedIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; // Extensions that are automatically classified as Programming Language extensions, but should be Feature extensions const FORCE_FEATURE_EXTENSIONS = ['vscode.git', 'vscode.search-result']; @@ -74,15 +75,22 @@ class ExtensionsViewState extends Disposable implements IExtensionsViewState { } } -export interface ExtensionsListViewOptions extends IViewletViewOptions { +export interface ExtensionsListViewOptions { server?: IExtensionManagementServer; + fixedHeight?: boolean; + onDidChangeTitle?: Event; } class ExtensionListViewWarning extends Error { } +interface IQueryResult { + readonly model: IPagedModel; + readonly onDidChangeModel?: Event>; + readonly disposables: DisposableStore; +} + export class ExtensionsListView extends ViewPane { - protected readonly server: IExtensionManagementServer | undefined; private bodyTemplate: { messageContainer: HTMLElement; messageSeverityIcon: HTMLElement; @@ -92,9 +100,11 @@ export class ExtensionsListView extends ViewPane { private badge: CountBadge | undefined; private list: WorkbenchPagedList | null = null; private queryRequest: { query: string, request: CancelablePromise> } | null = null; + private queryResult: IQueryResult | undefined; constructor( - options: ExtensionsListViewOptions, + protected readonly options: ExtensionsListViewOptions, + viewletViewOptions: IViewletViewOptions, @INotificationService protected notificationService: INotificationService, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @@ -112,12 +122,18 @@ export class ExtensionsListView extends ViewPane { @IProductService protected readonly productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IMenuService private readonly menuService: IMenuService, @IOpenerService openerService: IOpenerService, @IPreferencesService private readonly preferencesService: IPreferencesService, + @IStorageService private readonly storageService: IStorageService, ) { - super({ ...(options as IViewPaneOptions), showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - this.server = options.server; + super({ + ...(viewletViewOptions as IViewPaneOptions), + showActionsAlways: true, + maximumBodySize: options.fixedHeight ? storageService.getNumber(viewletViewOptions.id, StorageScope.GLOBAL, 0) : undefined + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + if (this.options.onDidChangeTitle) { + this._register(this.options.onDidChangeTitle(title => this.updateTitle(title))); + } } protected renderHeader(container: HTMLElement): void { @@ -161,7 +177,7 @@ export class ExtensionsListView extends ViewPane { const resourceNavigator = this._register(new ListResourceNavigator(this.list, { openOnSingleClick: true })); this._register(Event.debounce(Event.filter(resourceNavigator.onDidOpen, e => e.element !== null), (_, event) => event, 75, true)(options => { - this.openExtension(this.list!.model.get(options.element!), { sideByside: options.sideBySide, ...options.editorOptions }); + this.openExtension(options.element!, { sideByside: options.sideBySide, ...options.editorOptions }); })); this.bodyTemplate = { @@ -182,15 +198,20 @@ export class ExtensionsListView extends ViewPane { } } - async show(query: string): Promise> { + async show(query: string, refresh?: boolean): Promise> { if (this.queryRequest) { - if (this.queryRequest.query === query) { + if (!refresh && this.queryRequest.query === query) { return this.queryRequest.request; } this.queryRequest.request.cancel(); this.queryRequest = null; } + if (this.queryResult) { + this.queryResult.disposables.dispose(); + this.queryResult = undefined; + } + const parsedQuery = Query.parse(query); let options: IQueryOptions = { @@ -204,23 +225,25 @@ export class ExtensionsListView extends ViewPane { case 'publishedDate': options.sortBy = SortBy.PublishedDate; break; } - const successCallback = (model: IPagedModel) => { - this.queryRequest = null; - this.setModel(model); - return model; - }; - - - const errorCallback = (e: any) => { - const model = new PagedModel([]); - if (!isPromiseCanceledError(e)) { - this.queryRequest = null; - this.setModel(model, e); + const request = createCancelablePromise(async token => { + try { + this.queryResult = await this.query(parsedQuery, options, token); + const model = this.queryResult.model; + this.setModel(model); + if (this.queryResult.onDidChangeModel) { + this.queryResult.disposables.add(this.queryResult.onDidChangeModel(model => this.updateModel(model))); + } + return model; + } catch (e) { + const model = new PagedModel([]); + if (!isPromiseCanceledError(e)) { + this.setModel(model, e); + } + return this.list ? this.list.model : model; } - return this.list ? this.list.model : model; - }; + }); - const request = createCancelablePromise(token => this.query(parsedQuery, options, token).then(successCallback).catch(errorCallback)); + request.finally(() => this.queryRequest = null); this.queryRequest = { query, request }; return request; } @@ -251,7 +274,7 @@ export class ExtensionsListView extends ViewPane { getActions: () => actions.slice(0, actions.length - 1) }); } else if (e.element) { - const groups = getContextMenuActions(this.menuService, this.contextKeyService.createScoped(), this.instantiationService, e.element); + const groups = getContextMenuActions(e.element, false, this.instantiationService); groups.forEach(group => group.forEach(extensionAction => { if (extensionAction instanceof ExtensionAction) { extensionAction.extension = e.element!; @@ -269,7 +292,7 @@ export class ExtensionsListView extends ViewPane { } } - private async query(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { + private async query(query: Query, options: IQueryOptions, token: CancellationToken): Promise { const idRegex = /@id:(([a-z0-9A-Z][a-z0-9\-A-Z]*)\.([a-z0-9A-Z][a-z0-9\-A-Z]*))/g; const ids: string[] = []; let idMatch; @@ -278,21 +301,26 @@ export class ExtensionsListView extends ViewPane { ids.push(name); } if (ids.length) { - return this.queryByIds(ids, options, token); + const model = await this.queryByIds(ids, options, token); + return { model, disposables: new DisposableStore() }; } - if (ExtensionsListView.isLocalExtensionsQuery(query.value) || /@builtin/.test(query.value)) { + + if (ExtensionsListView.isLocalExtensionsQuery(query.value)) { return this.queryLocal(query, options); } - return this.queryGallery(query, options, token) - .then(null, e => { - console.warn('Error querying extensions gallery', getErrorMessage(e)); - return Promise.reject(new ExtensionListViewWarning(localize('galleryError', "We cannot connect to the Extensions Marketplace at this time, please try again later."))); - }); + + try { + const model = await this.queryGallery(query, options, token); + return { model, disposables: new DisposableStore() }; + } catch (e) { + console.warn('Error querying extensions gallery', getErrorMessage(e)); + return Promise.reject(new ExtensionListViewWarning(localize('galleryError', "We cannot connect to the Extensions Marketplace at this time, please try again later."))); + } } private async queryByIds(ids: string[], options: IQueryOptions, token: CancellationToken): Promise> { const idsSet: Set = ids.reduce((result, id) => { result.add(id.toLowerCase()); return result; }, new Set()); - const result = (await this.extensionsWorkbenchService.queryLocal(this.server)) + const result = (await this.extensionsWorkbenchService.queryLocal(this.options.server)) .filter(e => idsSet.has(e.identifier.id.toLowerCase())); if (result.length) { @@ -303,32 +331,70 @@ export class ExtensionsListView extends ViewPane { .then(pager => this.getPagedModel(pager)); } - private async queryLocal(query: Query, options: IQueryOptions): Promise> { - let value = query.value; - if (/@builtin/i.test(value)) { - return this.queryBuiltinExtensions(query, options); + private async queryLocal(query: Query, options: IQueryOptions): Promise { + const local = await this.extensionsWorkbenchService.queryLocal(this.options.server); + const runningExtensions = await this.extensionService.getExtensions(); + let { extensions, canIncludeInstalledExtensions } = this.filterLocal(local, runningExtensions, query, options); + const disposables = new DisposableStore(); + const onDidChangeModel = disposables.add(new Emitter>()); + + if (canIncludeInstalledExtensions) { + let isDisposed: boolean = false; + disposables.add(toDisposable(() => isDisposed = true)); + disposables.add(Event.debounce(Event.any( + Event.filter(this.extensionsWorkbenchService.onChange, e => e?.state === ExtensionState.Installed), + this.extensionService.onDidChangeExtensions + ), () => undefined)(async () => { + const local = this.options.server ? this.extensionsWorkbenchService.installed.filter(e => e.server === this.options.server) : this.extensionsWorkbenchService.local; + const runningExtensions = await this.extensionService.getExtensions(); + const { extensions: newExtensions } = this.filterLocal(local, runningExtensions, query, options); + if (!isDisposed) { + const mergedExtensions = this.mergeAddedExtensions(extensions, newExtensions); + if (mergedExtensions) { + extensions = mergedExtensions; + onDidChangeModel.fire(new PagedModel(extensions)); + } + } + })); } - if (/@installed/i.test(value)) { - return this.queryInstalledExtensions(query, options); - } - - if (/@outdated/i.test(value)) { - return this.queryOutdatedExtensions(query, options); - } - - if (/@disabled/i.test(value)) { - return this.queryDisabledExtensions(query, options); - } - - if (/@enabled/i.test(value)) { - return this.queryEnabledExtensions(query, options); - } - - return new PagedModel([]); + return { + model: new PagedModel(extensions), + onDidChangeModel: onDidChangeModel.event, + disposables + }; } - private async queryBuiltinExtensions(query: Query, options: IQueryOptions): Promise> { + private filterLocal(local: IExtension[], runningExtensions: IExtensionDescription[], query: Query, options: IQueryOptions): { extensions: IExtension[], canIncludeInstalledExtensions: boolean } { + let value = query.value; + let extensions: IExtension[] = []; + let canIncludeInstalledExtensions = true; + + if (/@builtin/i.test(value)) { + extensions = this.filterBuiltinExtensions(local, query, options); + canIncludeInstalledExtensions = false; + } + + else if (/@installed/i.test(value)) { + extensions = this.filterInstalledExtensions(local, runningExtensions, query, options); + } + + else if (/@outdated/i.test(value)) { + extensions = this.filterOutdatedExtensions(local, query, options); + } + + else if (/@disabled/i.test(value)) { + extensions = this.filterDisabledExtensions(local, runningExtensions, query, options); + } + + else if (/@enabled/i.test(value)) { + extensions = this.filterEnabledExtensions(local, runningExtensions, query, options); + } + + return { extensions, canIncludeInstalledExtensions }; + } + + private filterBuiltinExtensions(local: IExtension[], query: Query, options: IQueryOptions): IExtension[] { let value = query.value; const showThemesOnly = /@builtin:themes/i.test(value); if (showThemesOnly) { @@ -344,9 +410,8 @@ export class ExtensionsListView extends ViewPane { } value = value.replace(/@builtin/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); - let result = await this.extensionsWorkbenchService.queryLocal(this.server); - result = result + let result = local .filter(e => e.isBuiltin && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1)); const isThemeExtension = (e: IExtension): boolean => { @@ -355,7 +420,7 @@ export class ExtensionsListView extends ViewPane { }; if (showThemesOnly) { const themesExtensions = result.filter(isThemeExtension); - return this.getPagedModel(this.sortExtensions(themesExtensions, options)); + return this.sortExtensions(themesExtensions, options); } const isLangaugeBasicExtension = (e: IExtension): boolean => { @@ -364,7 +429,7 @@ export class ExtensionsListView extends ViewPane { }; if (showBasicsOnly) { const basics = result.filter(isLangaugeBasicExtension); - return this.getPagedModel(this.sortExtensions(basics, options)); + return this.sortExtensions(basics, options); } if (showFeaturesOnly) { const others = result.filter(e => { @@ -373,10 +438,10 @@ export class ExtensionsListView extends ViewPane { && !isThemeExtension(e) && !isLangaugeBasicExtension(e); }); - return this.getPagedModel(this.sortExtensions(others, options)); + return this.sortExtensions(others, options); } - return this.getPagedModel(this.sortExtensions(result, options)); + return this.sortExtensions(result, options); } private parseCategories(value: string): { value: string, categories: string[] } { @@ -391,14 +456,12 @@ export class ExtensionsListView extends ViewPane { return { value, categories }; } - private async queryInstalledExtensions(query: Query, options: IQueryOptions): Promise> { + private filterInstalledExtensions(local: IExtension[], runningExtensions: IExtensionDescription[], query: Query, options: IQueryOptions): IExtension[] { let { value, categories } = this.parseCategories(query.value); value = value.replace(/@installed/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); - let result = await this.extensionsWorkbenchService.queryLocal(this.server); - - result = result + let result = local .filter(e => !e.isBuiltin && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); @@ -406,7 +469,6 @@ export class ExtensionsListView extends ViewPane { if (options.sortBy !== undefined) { result = this.sortExtensions(result, options); } else { - const runningExtensions = await this.extensionService.getExtensions(); const runningExtensionsById = runningExtensions.reduce((result, e) => { result.set(ExtensionIdentifier.toKey(e.identifier.value), e); return result; }, new Map()); result = result.sort((e1, e2) => { const running1 = runningExtensionsById.get(ExtensionIdentifier.toKey(e1.identifier.id)); @@ -433,56 +495,76 @@ export class ExtensionsListView extends ViewPane { return isE1Running ? -1 : 1; }); } - return this.getPagedModel(result); + return result; } - private async queryOutdatedExtensions(query: Query, options: IQueryOptions): Promise> { + private filterOutdatedExtensions(local: IExtension[], query: Query, options: IQueryOptions): IExtension[] { let { value, categories } = this.parseCategories(query.value); value = value.replace(/@outdated/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); - const local = await this.extensionsWorkbenchService.queryLocal(this.server); const result = local .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) .filter(extension => extension.outdated && (extension.name.toLowerCase().indexOf(value) > -1 || extension.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => !!extension.local && extension.local.manifest.categories!.some(c => c.toLowerCase() === category)))); - return this.getPagedModel(this.sortExtensions(result, options)); + return this.sortExtensions(result, options); } - private async queryDisabledExtensions(query: Query, options: IQueryOptions): Promise> { + private filterDisabledExtensions(local: IExtension[], runningExtensions: IExtensionDescription[], query: Query, options: IQueryOptions): IExtension[] { let { value, categories } = this.parseCategories(query.value); value = value.replace(/@disabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); - const local = await this.extensionsWorkbenchService.queryLocal(this.server); - const runningExtensions = await this.extensionService.getExtensions(); - const result = local .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) .filter(e => runningExtensions.every(r => !areSameExtensions({ id: r.identifier.value, uuid: r.uuid }, e.identifier)) && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); - return this.getPagedModel(this.sortExtensions(result, options)); + return this.sortExtensions(result, options); } - private async queryEnabledExtensions(query: Query, options: IQueryOptions): Promise> { + private filterEnabledExtensions(local: IExtension[], runningExtensions: IExtensionDescription[], query: Query, options: IQueryOptions): IExtension[] { let { value, categories } = this.parseCategories(query.value); value = value ? value.replace(/@enabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : ''; - const local = (await this.extensionsWorkbenchService.queryLocal(this.server)).filter(e => !e.isBuiltin); - const runningExtensions = await this.extensionService.getExtensions(); - + local = local.filter(e => !e.isBuiltin); const result = local .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) .filter(e => runningExtensions.some(r => areSameExtensions({ id: r.identifier.value, uuid: r.uuid }, e.identifier)) && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); - return this.getPagedModel(this.sortExtensions(result, options)); + return this.sortExtensions(result, options); + } + + private mergeAddedExtensions(extensions: IExtension[], newExtensions: IExtension[]): IExtension[] | undefined { + const oldExtensions = [...extensions]; + const findPreviousExtensionIndex = (from: number): number => { + let index = -1; + const previousExtensionInNew = newExtensions[from]; + if (previousExtensionInNew) { + index = oldExtensions.findIndex(e => areSameExtensions(e.identifier, previousExtensionInNew.identifier)); + if (index === -1) { + return findPreviousExtensionIndex(from - 1); + } + } + return index; + }; + + let hasChanged: boolean = false; + for (let index = 0; index < newExtensions.length; index++) { + const extension = newExtensions[index]; + if (extensions.every(r => !areSameExtensions(r.identifier, extension.identifier))) { + hasChanged = true; + extensions.splice(findPreviousExtensionIndex(index - 1) + 1, 0, extension); + } + } + + return hasChanged ? extensions : undefined; } private async queryGallery(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { @@ -675,7 +757,7 @@ export class ExtensionsListView extends ViewPane { private async getOtherRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { const value = query.value.replace(/@recommended/g, '').trim().toLowerCase(); - const local = (await this.extensionsWorkbenchService.queryLocal(this.server)) + const local = (await this.extensionsWorkbenchService.queryLocal(this.options.server)) .map(e => e.identifier.id.toLowerCase()); const workspaceRecommendations = (await this.getWorkspaceRecommendations()) .map(extensionId => extensionId.toLowerCase()); @@ -698,7 +780,7 @@ export class ExtensionsListView extends ViewPane { // Get All types of recommendations, trimmed to show a max of 8 at any given time private async getAllRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { - const local = (await this.extensionsWorkbenchService.queryLocal(this.server)).map(e => e.identifier.id.toLowerCase()); + const local = (await this.extensionsWorkbenchService.queryLocal(this.options.server)).map(e => e.identifier.id.toLowerCase()); const allRecommendations = distinct( flatten(await Promise.all([ @@ -727,31 +809,51 @@ export class ExtensionsListView extends ViewPane { if (this.list) { this.list.model = new DelayedPagedModel(model); this.list.scrollTop = 0; - const count = this.count(); + this.updateBody(error); + } + } - if (this.bodyTemplate && this.badge) { + private updateBody(error?: any): void { + const count = this.count(); + if (this.bodyTemplate && this.badge) { - this.bodyTemplate.extensionsList.classList.toggle('hidden', count === 0); - this.bodyTemplate.messageContainer.classList.toggle('hidden', count > 0); - this.badge.setCount(count); + this.bodyTemplate.extensionsList.classList.toggle('hidden', count === 0); + this.bodyTemplate.messageContainer.classList.toggle('hidden', count > 0); + this.badge.setCount(count); - if (count === 0 && this.isBodyVisible()) { - if (error) { - if (error instanceof ExtensionListViewWarning) { - this.bodyTemplate.messageSeverityIcon.className = `codicon ${SeverityIcon.className(Severity.Warning)}`; - this.bodyTemplate.messageBox.textContent = getErrorMessage(error); - } else { - this.bodyTemplate.messageSeverityIcon.className = `codicon ${SeverityIcon.className(Severity.Error)}`; - this.bodyTemplate.messageBox.textContent = localize('error', "Error while loading extensions. {0}", getErrorMessage(error)); - } + if (count === 0 && this.isBodyVisible()) { + if (error) { + if (error instanceof ExtensionListViewWarning) { + this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(Severity.Warning); + this.bodyTemplate.messageBox.textContent = getErrorMessage(error); } else { - this.bodyTemplate.messageSeverityIcon.className = ''; - this.bodyTemplate.messageBox.textContent = localize('no extensions found', "No extensions found."); + this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(Severity.Error); + this.bodyTemplate.messageBox.textContent = localize('error', "Error while loading extensions. {0}", getErrorMessage(error)); } - alert(this.bodyTemplate.messageBox.textContent); + } else { + this.bodyTemplate.messageSeverityIcon.className = ''; + this.bodyTemplate.messageBox.textContent = localize('no extensions found', "No extensions found."); } + alert(this.bodyTemplate.messageBox.textContent); } } + this.updateSize(); + } + + protected updateSize() { + if (this.options.fixedHeight) { + const length = this.list?.model.length || 0; + this.minimumBodySize = Math.min(length, 3) * EXTENSION_LIST_ELEMENT_HEIGHT; + this.maximumBodySize = length * EXTENSION_LIST_ELEMENT_HEIGHT; + this.storageService.store(this.id, this.maximumBodySize, StorageScope.GLOBAL, StorageTarget.MACHINE); + } + } + + private updateModel(model: IPagedModel) { + if (this.list) { + this.list.model = new DelayedPagedModel(model); + this.updateBody(); + } } private openExtension(extension: IExtension, options: { sideByside?: boolean, preserveFocus?: boolean, pinned?: boolean }): void { @@ -799,6 +901,10 @@ export class ExtensionsListView extends ViewPane { this.queryRequest.request.cancel(); this.queryRequest = null; } + if (this.queryResult) { + this.queryResult.disposables.dispose(); + this.queryResult = undefined; + } this.list = null; } @@ -808,7 +914,8 @@ export class ExtensionsListView extends ViewPane { || this.isEnabledExtensionsQuery(query) || this.isDisabledExtensionsQuery(query) || this.isBuiltInExtensionsQuery(query) - || this.isSearchBuiltInExtensionsQuery(query); + || this.isSearchBuiltInExtensionsQuery(query) + || this.isBuiltInGroupExtensionsQuery(query); } static isSearchBuiltInExtensionsQuery(query: string): boolean { @@ -819,6 +926,10 @@ export class ExtensionsListView extends ViewPane { return /^\s*@builtin$/i.test(query.trim()); } + static isBuiltInGroupExtensionsQuery(query: string): boolean { + return /^\s*@builtin:.+$/i.test(query.trim()); + } + static isInstalledExtensionsQuery(query: string): boolean { return /@installed/i.test(query); } @@ -868,39 +979,7 @@ export class ExtensionsListView extends ViewPane { } } -export class ServerExtensionsView extends ExtensionsListView { - - constructor( - server: IExtensionManagementServer, - onDidChangeTitle: Event, - options: ExtensionsListViewOptions, - @INotificationService notificationService: INotificationService, - @IKeybindingService keybindingService: IKeybindingService, - @IContextMenuService contextMenuService: IContextMenuService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IInstantiationService instantiationService: IInstantiationService, - @IExtensionService extensionService: IExtensionService, - @IExtensionRecommendationsService tipsService: IExtensionRecommendationsService, - @ITelemetryService telemetryService: ITelemetryService, - @IConfigurationService configurationService: IConfigurationService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IExperimentService experimentService: IExperimentService, - @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService, - @IWorkbenchExtensioManagementService extensionManagementService: IWorkbenchExtensioManagementService, - @IProductService productService: IProductService, - @IContextKeyService contextKeyService: IContextKeyService, - @IMenuService menuService: IMenuService, - @IOpenerService openerService: IOpenerService, - @IThemeService themeService: IThemeService, - @IPreferencesService preferencesService: IPreferencesService, - ) { - options.server = server; - super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, tipsService, - telemetryService, configurationService, contextService, experimentService, extensionManagementServerService, extensionManagementService, productService, - contextKeyService, viewDescriptorService, menuService, openerService, preferencesService); - this._register(onDidChangeTitle(title => this.updateTitle(title))); - } +export class ServerInstalledExtensionsView extends ExtensionsListView { async show(query: string): Promise> { query = query ? query : '@installed'; @@ -911,9 +990,9 @@ export class ServerExtensionsView extends ExtensionsListView { } getActions(): IAction[] { - if (this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === this.server) { + if (this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === this.options.server) { const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction)); - installLocalExtensionsInRemoteAction.class = 'codicon codicon-cloud-download'; + installLocalExtensionsInRemoteAction.class = ThemeIcon.asClassName(installLocalInRemoteIcon); return [installLocalExtensionsInRemoteAction]; } return []; @@ -936,28 +1015,6 @@ export class DisabledExtensionsView extends ExtensionsListView { } } -export class OutdatedExtensionsView extends ExtensionsListView { - - async show(query: string): Promise> { - query = query || '@outdated'; - return ExtensionsListView.isOutdatedExtensionsQuery(query) ? super.show(query) : this.showEmptyModel(); - } -} - -export class InstalledExtensionsView extends ExtensionsListView { - - async show(query: string): Promise> { - query = query || '@installed'; - return ExtensionsListView.isInstalledExtensionsQuery(query) ? super.show(query) : this.showEmptyModel(); - } -} - -export class SearchBuiltInExtensionsView extends ExtensionsListView { - async show(query: string): Promise> { - return ExtensionsListView.isSearchBuiltInExtensionsQuery(query) ? super.show(query) : this.showEmptyModel(); - } -} - export class BuiltInFeatureExtensionsView extends ExtensionsListView { async show(query: string): Promise> { return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:features'); @@ -1030,11 +1087,11 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { getActions(): IAction[] { if (!this.installAllAction) { - this.installAllAction = this._register(new Action('workbench.extensions.action.installWorkspaceRecommendedExtensions', localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), 'codicon codicon-cloud-download', false, () => this.installWorkspaceRecommendations())); + this.installAllAction = this._register(new Action('workbench.extensions.action.installWorkspaceRecommendedExtensions', localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), ThemeIcon.asClassName(installWorkspaceRecommendedIcon), false, () => this.installWorkspaceRecommendations())); } const configureWorkspaceFolderAction = this._register(this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL)); - configureWorkspaceFolderAction.class = 'codicon codicon-pencil'; + configureWorkspaceFolderAction.class = ThemeIcon.asClassName(configureRecommendedIcon); return [this.installAllAction, configureWorkspaceFolderAction]; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 8718d0b9b..55607b038 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/extensionsWidgets'; import { Disposable, toDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; -import { IExtension, IExtensionsWorkbenchService, IExtensionContainer } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtension, IExtensionsWorkbenchService, IExtensionContainer, ExtensionState } from 'vs/workbench/contrib/extensions/common/extensions'; import { append, $ } from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; import { localize } from 'vs/nls'; @@ -13,11 +13,14 @@ import { IExtensionManagementServerService } from 'vs/workbench/services/extensi import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ILabelService } from 'vs/platform/label/common/label'; import { extensionButtonProminentBackground, extensionButtonProminentForeground, ExtensionToolTipAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; -import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { EXTENSION_BADGE_REMOTE_BACKGROUND, EXTENSION_BADGE_REMOTE_FOREGROUND } from 'vs/workbench/common/theme'; import { Emitter, Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { installCountIcon, ratingIcon, remoteIcon, starEmptyIcon, starFullIcon, starHalfIcon, syncIgnoredIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension | null = null; @@ -83,7 +86,7 @@ export class InstallCountWidget extends ExtensionWidget { installLabel = installCount.toLocaleString(platform.locale); } - append(this.container, $('span.codicon.codicon-cloud-download')); + append(this.container, $('span' + ThemeIcon.asCSSSelector(installCountIcon))); const count = append(this.container, $('span.count')); count.textContent = installLabel; } @@ -123,18 +126,18 @@ export class RatingsWidget extends ExtensionWidget { const rating = Math.round(this.extension.rating * 2) / 2; if (this.small) { - append(this.container, $('span.codicon.codicon-star-full')); + append(this.container, $('span' + ThemeIcon.asCSSSelector(starFullIcon))); const count = append(this.container, $('span.count')); count.textContent = String(rating); } else { for (let i = 1; i <= 5; i++) { if (rating >= i) { - append(this.container, $('span.codicon.codicon-star-full')); + append(this.container, $('span' + ThemeIcon.asCSSSelector(starFullIcon))); } else if (rating >= i - 0.5) { - append(this.container, $('span.codicon.codicon-star-half')); + append(this.container, $('span' + ThemeIcon.asCSSSelector(starHalfIcon))); } else { - append(this.container, $('span.codicon.codicon-star-empty')); + append(this.container, $('span' + ThemeIcon.asCSSSelector(starEmptyIcon))); } } } @@ -220,7 +223,7 @@ export class RecommendationWidget extends ExtensionWidget { if (extRecommendations[this.extension.identifier.id.toLowerCase()]) { this.element = append(this.parent, $('div.extension-bookmark')); const recommendation = append(this.element, $('.recommendation')); - append(recommendation, $('span.codicon.codicon-star')); + append(recommendation, $('span' + ThemeIcon.asCSSSelector(ratingIcon))); const applyBookmarkStyle = (theme: IColorTheme) => { const bgColor = theme.getColor(extensionButtonProminentBackground); const fgColor = theme.getColor(extensionButtonProminentForeground); @@ -286,7 +289,7 @@ class RemoteBadge extends Disposable { } private render(): void { - append(this.element, $('span.codicon.codicon-remote')); + append(this.element, $('span' + ThemeIcon.asCSSSelector(remoteIcon))); const applyBadgeStyle = () => { if (!this.element) { @@ -340,3 +343,28 @@ export class ExtensionPackCountWidget extends ExtensionWidget { countBadge.setCount(this.extension.extensionPack.length); } } + +export class SyncIgnoredWidget extends ExtensionWidget { + + private element: HTMLElement; + + constructor( + container: HTMLElement, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, + ) { + super(); + this.element = append(container, $('span.extension-sync-ignored' + ThemeIcon.asCSSSelector(syncIgnoredIcon))); + this.element.title = localize('syncingore.label', "This extension is ignored during sync."); + this.element.classList.add(...ThemeIcon.asClassNameArray(syncIgnoredIcon)); + this.element.classList.add('hide'); + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectedKeys.includes('settingsSync.ignoredExtensions'))(() => this.render())); + this._register(userDataAutoSyncEnablementService.onDidChangeEnablement(() => this.update())); + this.render(); + } + + render(): void { + this.element.classList.toggle('hide', !(this.extension && this.extension.state === ExtensionState.Installed && this.userDataAutoSyncEnablementService.isEnabled() && this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension))); + } +} diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 5bbd4d88d..3566ca415 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -8,7 +8,7 @@ import * as semver from 'vs/base/common/semver/semver'; import { Event, Emitter } from 'vs/base/common/event'; import { index, distinct } from 'vs/base/common/arrays'; import { ThrottledDelayer } from 'vs/base/common/async'; -import { isPromiseCanceledError } from 'vs/base/common/errors'; +import { canceled, isPromiseCanceledError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -28,10 +28,10 @@ import { IURLService, IURLHandler, IOpenURLOptions } from 'vs/platform/url/commo import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import * as resources from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension } from 'vs/platform/extensions/common/extensions'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -1073,7 +1073,23 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension for (const extension of extensions) { let dependents = this.getDependentsAfterDisablement(extension, allExtensions, this.local); if (dependents.length) { - return Promise.reject(new Error(this.getDependentsErrorMessage(extension, allExtensions, dependents))); + return new Promise((resolve, reject) => { + this.notificationService.prompt(Severity.Error, this.getDependentsErrorMessage(extension, allExtensions, dependents), [ + { + label: nls.localize('disable all', 'Disable All'), + run: async () => { + try { + await this.checkAndSetEnablement(dependents, [extension], enablementState); + resolve(); + } catch (error) { + reject(error); + } + } + } + ], { + onCancel: () => reject(canceled()) + }); + }); } } } @@ -1139,13 +1155,13 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private getErrorMessageForDisablingAnExtensionWithDependents(extension: IExtension, dependents: IExtension[]): string { if (dependents.length === 1) { - return nls.localize('singleDependentError', "Cannot disable extension '{0}'. Extension '{1}' depends on this.", extension.displayName, dependents[0].displayName); + return nls.localize('singleDependentError', "Cannot disable '{0}' extension alone. '{1}' extension depends on this. Do you want to disable all these extensions?", extension.displayName, dependents[0].displayName); } if (dependents.length === 2) { - return nls.localize('twoDependentsError', "Cannot disable extension '{0}'. Extensions '{1}' and '{2}' depend on this.", + return nls.localize('twoDependentsError', "Cannot disable '{0}' extension alone. '{1}' and '{2}' extensions depend on this. Do you want to disable all these extensions?", extension.displayName, dependents[0].displayName, dependents[1].displayName); } - return nls.localize('multipleDependentsError', "Cannot disable extension '{0}'. Extensions '{1}', '{2}' and others depend on this.", + return nls.localize('multipleDependentsError', "Cannot disable '{0}' extension alone. '{1}', '{2}' and other extensions depend on this. Do you want to disable all these extensions?", extension.displayName, dependents[0].displayName, dependents[1].displayName); } @@ -1253,7 +1269,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private set ignoredAutoUpdateExtensions(extensionIds: string[]) { this._ignoredAutoUpdateExtensions = distinct(extensionIds.map(id => id.toLowerCase())); - this.storageService.store('extensions.ignoredAutoUpdateExtension', JSON.stringify(this._ignoredAutoUpdateExtensions), StorageScope.GLOBAL); + this.storageService.store('extensions.ignoredAutoUpdateExtension', JSON.stringify(this._ignoredAutoUpdateExtensions), StorageScope.GLOBAL, StorageTarget.MACHINE); } private ignoreAutoUpdate(identifierWithVersion: ExtensionIdentifierWithVersion): void { diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 0de614759..cab0ed9a7 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -11,7 +11,7 @@ import { ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService import { IExtensionsViewPaneContainer, IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; -import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; +import { StorageScope, IStorageService, StorageTarget } from 'vs/platform/storage/common/storage'; import { ImportantExtensionTip, IProductService } from 'vs/platform/product/common/productService'; import { forEach, IStringDictionary } from 'vs/base/common/collections'; import { ITextModel } from 'vs/editor/common/model'; @@ -26,6 +26,8 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { setImmediate } from 'vs/base/common/platform'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; +import { distinct } from 'vs/base/common/arrays'; +import { DisposableStore } from 'vs/base/common/lifecycle'; type FileExtensionSuggestionClassification = { userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -33,6 +35,7 @@ type FileExtensionSuggestionClassification = { }; const promptedRecommendationsStorageKey = 'fileBasedRecommendations/promptedRecommendations'; +const promptedFileExtensionsStorageKey = 'fileBasedRecommendations/promptedFileExtensions'; const recommendationsStorageKey = 'extensionsAssistant/recommendations'; const searchMarketplace = localize('searchMarketplace', "Search Marketplace"); const milliSecondsInADay = 1000 * 60 * 60 * 24; @@ -148,8 +151,16 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } private onModelAdded(model: ITextModel): void { + const uri = model.uri; + const supportedSchemes = [Schemas.untitled, Schemas.file, Schemas.vscodeRemote]; + if (!uri || !supportedSchemes.includes(uri.scheme)) { + return; + } + this.promptRecommendationsForModel(model); - this._register(model.onDidChangeLanguage(() => this.promptRecommendationsForModel(model))); + const disposables = new DisposableStore(); + disposables.add(model.onDidChangeLanguage(() => this.promptRecommendationsForModel(model))); + disposables.add(model.onWillDispose(() => disposables.dispose())); } /** @@ -158,13 +169,8 @@ export class FileBasedRecommendations extends ExtensionRecommendations { */ private promptRecommendationsForModel(model: ITextModel): void { const uri = model.uri; - const supportedSchemes = [Schemas.untitled, Schemas.file, Schemas.vscodeRemote]; - if (!uri || !supportedSchemes.includes(uri.scheme)) { - return; - } - const language = model.getLanguageIdentifier().language; - const fileExtension = extname(uri); + const fileExtension = extname(uri).toLowerCase(); if (this.processedLanguages.includes(language) && this.processedFileExtensions.includes(fileExtension)) { return; } @@ -269,7 +275,17 @@ export class FileBasedRecommendations extends ExtensionRecommendations { private addToPromptedRecommendations(exeName: string, extensions: string[]) { const promptedRecommendations = this.getPromptedRecommendations(); promptedRecommendations[exeName] = extensions; - this.storageService.store(promptedRecommendationsStorageKey, JSON.stringify(promptedRecommendations), StorageScope.GLOBAL); + this.storageService.store(promptedRecommendationsStorageKey, JSON.stringify(promptedRecommendations), StorageScope.GLOBAL, StorageTarget.USER); + } + + private getPromptedFileExtensions(): string[] { + return JSON.parse(this.storageService.get(promptedFileExtensionsStorageKey, StorageScope.GLOBAL, '[]')); + } + + private addToPromptedFileExtensions(fileExtension: string) { + const promptedFileExtensions = this.getPromptedFileExtensions(); + promptedFileExtensions.push(fileExtension); + this.storageService.store(promptedFileExtensionsStorageKey, JSON.stringify(distinct(promptedFileExtensions)), StorageScope.GLOBAL, StorageTarget.USER); } private async promptRecommendedExtensionForFileExtension(fileExtension: string, installed: IExtension[]): Promise { @@ -278,6 +294,11 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return; } + const promptedFileExtensions = this.getPromptedFileExtensions(); + if (promptedFileExtensions.includes(fileExtension)) { + return; + } + const text = `ext:${fileExtension}`; const pager = await this.extensionsWorkbenchService.queryGallery({ text, pageSize: 100 }, CancellationToken.None); if (pager.firstPage.length === 0) { @@ -295,6 +316,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { [{ label: searchMarketplace, run: () => { + this.addToPromptedFileExtensions(fileExtension); this.telemetryService.publicLog2<{ userReaction: string, fileExtension: string }, FileExtensionSuggestionClassification>('fileExtensionSuggestion:popup', { userReaction: 'ok', fileExtension }); this.viewletService.openViewlet('workbench.view.extensions', true) .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) @@ -310,8 +332,8 @@ export class FileBasedRecommendations extends ExtensionRecommendations { this.storageService.store( 'extensionsAssistant/fileExtensionsSuggestionIgnore', JSON.stringify(fileExtensionSuggestionIgnoreList), - StorageScope.GLOBAL - ); + StorageScope.GLOBAL, + StorageTarget.USER); this.telemetryService.publicLog2<{ userReaction: string, fileExtension: string }, FileExtensionSuggestionClassification>('fileExtensionSuggestion:popup', { userReaction: 'neverShowAgain', fileExtension }); } }], @@ -356,7 +378,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { private storeCachedRecommendations(): void { const storedRecommendations: IStringDictionary = {}; this.fileBasedRecommendations.forEach((value, key) => storedRecommendations[key] = value.recommendedTime); - this.storageService.store(recommendationsStorageKey, JSON.stringify(storedRecommendations), StorageScope.GLOBAL); + this.storageService.store(recommendationsStorageKey, JSON.stringify(storedRecommendations), StorageScope.GLOBAL, StorageTarget.MACHINE); } } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extension.css b/src/vs/workbench/contrib/extensions/browser/media/extension.css index 1911ed1d4..0502ce085 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extension.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extension.css @@ -139,6 +139,15 @@ color: currentColor; } +.extension-list-item > .details > .header-container > .header .sync-ignored { + display: flex; + margin-left: 6px; +} + +.extension-list-item > .details > .header-container > .header .sync-ignored > .codicon { + font-size: 100%; +} + .extension-list-item > .details > .description { padding-right: 11px; } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css index 340a9ad24..bb1107d06 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css @@ -13,7 +13,10 @@ .monaco-action-bar .action-item > .action-label.extension-action.label, .monaco-action-bar .action-dropdown-item > .action-label.extension-action.label { padding: 0 5px; - outline-offset: 2px; +} + +.monaco-action-bar .action-item .action-label.extension-action.label { + outline-offset: 1px; } .monaco-action-bar .action-item .action-label.extension-action.text, @@ -42,6 +45,7 @@ .monaco-action-bar .action-item.disabled .action-label.extension-action.uninstall:not(.uninstalling), .monaco-action-bar .action-item.disabled .action-label.extension-action.update, .monaco-action-bar .action-item.disabled .action-label.extension-action.theme, +.monaco-action-bar .action-item.disabled .action-label.extension-action.extension-sync, .monaco-action-bar .action-item.action-dropdown-item.disabled, .monaco-action-bar .action-item.action-dropdown-item .action-label.extension-action.hide, .monaco-action-bar .action-item.disabled .action-label.extension-action.reload, diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 2acadd17b..7a08b2edb 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -76,7 +76,6 @@ .extension-editor > .header > .details > .title > .identifier { margin-left: 10px; font-size: 14px; - opacity: 0.6; background: rgba(173, 173, 173, 0.31); padding: 0px 4px; border-radius: 4px; @@ -165,6 +164,10 @@ max-width: 300px; } +.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .extension-action.icon { + padding-top: 1px; +} + .extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .extension-action.label { padding: 1px 6px; } @@ -212,8 +215,7 @@ .extension-editor > .header > .details > .subtext-container > .monaco-action-bar .action-label { margin-top: 4px; margin-left: 4px; - padding-top: 0; - padding-bottom: 2px; + padding-bottom: 1px; } .extension-editor > .header.recommended > .details > .recommendation > .monaco-action-bar .actions-container { @@ -397,7 +399,7 @@ word-break: keep-all; } -.extension-editor > .body > .content table code:not(:empty) { +.extension-editor > .body > .content code:not(:empty) { font-family: var(--monaco-monospace-font); font-size: 90%; background-color: rgba(128, 128, 128, 0.17); diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css b/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css index 91cf3cefd..737320d94 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.extension-sync-ignored.hide { + display: none; +} + .extension-ratings { display: inline-block; } @@ -11,7 +15,7 @@ font-size: 80%; } -.extension-ratings > .codicon[class*='codicon-star']:not(:first-child) { +.extension-ratings > .codicon[class*='codicon-extensions-rating']:not(:first-child) { margin-left: 3px; } @@ -24,17 +28,17 @@ } /* TODO @misolori make this a color token */ -.extension-ratings .codicon-star-full, -.extension-ratings .codicon-star-half { +.extension-ratings .codicon-extensions-star-full, +.extension-ratings .codicon-extensions-star-half { color: #FF8E00 !important; } .extension-install-count .codicon, -.extension-action.codicon-gear { +.extension-action.codicon-extensions-manage { color: inherit; } -.extension-ratings .codicon-star-empty { +.extension-ratings .codicon-extensions-star-empty { opacity: .4; } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/media/runtimeExtensionsEditor.css b/src/vs/workbench/contrib/extensions/browser/media/runtimeExtensionsEditor.css similarity index 91% rename from src/vs/workbench/contrib/extensions/electron-browser/media/runtimeExtensionsEditor.css rename to src/vs/workbench/contrib/extensions/browser/media/runtimeExtensionsEditor.css index 8403039d7..050ddc80b 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/media/runtimeExtensionsEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/runtimeExtensionsEditor.css @@ -57,11 +57,6 @@ color: currentColor; } -.monaco-workbench.vs .runtime-extensions-editor .extension > .icon-container > .icon, -.monaco-workbench.vs-dark .runtime-extensions-editor .extension > .icon-container > .icon { - opacity: 0.5; -} - .runtime-extensions-editor .extension > .desc > .header-container { display: flex; overflow: hidden; diff --git a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts index 3ce9d79e5..3a8caf10a 100644 --- a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts +++ b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts @@ -5,13 +5,14 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILabelService } from 'vs/platform/label/common/label'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { InstallLocalExtensionsInRemoteAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { InstallLocalExtensionsInRemoteAction, InstallRemoteExtensionsInLocalAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchContribution { @@ -38,6 +39,20 @@ export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchC appendMenuItem(); this._register(labelService.onDidChangeFormatters(e => appendMenuItem())); this._register(toDisposable(() => disposable.dispose())); + + this._register(registerAction2(class InstallRemoteExtensionsInLocalAction2 extends Action2 { + constructor() { + super({ + id: 'workbench.extensions.actions.installLocalExtensionsInRemote', + title: { value: localize('install remote in local', "Install Remote Extensions Locally..."), original: 'Install Remote Extensions Locally...' }, + category: localize({ key: 'remote', comment: ['Remote as in remote machine'] }, "Remote"), + f1: true + }); + } + run(accessor: ServicesAccessor): Promise { + return accessor.get(IInstantiationService).createInstance(InstallRemoteExtensionsInLocalAction, 'workbench.extensions.actions.installLocalExtensionsInRemote').run(); + } + })); } } diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index 231aa8f4b..013e981bf 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -7,12 +7,12 @@ import { EXTENSION_IDENTIFIER_PATTERN, IExtensionGalleryService } from 'vs/platf import { distinct, flatten } from 'vs/base/common/arrays'; import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IExtensionsConfigContent, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; -import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; +import { IExtensionsConfigContent, IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; export class WorkspaceRecommendations extends ExtensionRecommendations { @@ -51,23 +51,28 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { this.notificationService.warn(`The ${invalidRecommendations.length} extension(s) below, in workspace recommendations have issues:\n${message}`); } + this._recommendations = []; this._ignoredRecommendations = []; for (const extensionsConfig of extensionsConfigs) { - for (const unwantedRecommendation of extensionsConfig.unwantedRecommendations) { - if (invalidRecommendations.indexOf(unwantedRecommendation) === -1) { - this._ignoredRecommendations.push(unwantedRecommendation); + if (extensionsConfig.unwantedRecommendations) { + for (const unwantedRecommendation of extensionsConfig.unwantedRecommendations) { + if (invalidRecommendations.indexOf(unwantedRecommendation) === -1) { + this._ignoredRecommendations.push(unwantedRecommendation); + } } } - for (const extensionId of extensionsConfig.recommendations) { - if (invalidRecommendations.indexOf(extensionId) === -1) { - this._recommendations.push({ - extensionId, - reason: { - reasonId: ExtensionRecommendationReason.Workspace, - reasonText: localize('workspaceRecommendation', "This extension is recommended by users of the current workspace.") - } - }); + if (extensionsConfig.recommendations) { + for (const extensionId of extensionsConfig.recommendations) { + if (invalidRecommendations.indexOf(extensionId) === -1) { + this._recommendations.push({ + extensionId, + reason: { + reasonId: ExtensionRecommendationReason.Workspace, + reasonText: localize('workspaceRecommendation', "This extension is recommended by users of the current workspace.") + } + }); + } } } } @@ -114,12 +119,8 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { } private async onDidChangeExtensionsConfigs(): Promise { - const oldWorkspaceRecommended = this._recommendations; await this.fetch(); - // Suggest only if at least one of the newly added recommendations was not suggested before - if (this._recommendations.some(current => oldWorkspaceRecommended.every(old => current.extensionId !== old.extensionId))) { - this._onDidChangeRecommendations.fire(); - } + this._onDidChangeRecommendations.fire(); } } diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index f4586ae31..3cfe2796c 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -18,7 +18,8 @@ import { IViewPaneContainer } from 'vs/workbench/common/views'; export const VIEWLET_ID = 'workbench.view.extensions'; export interface IExtensionsViewPaneContainer extends IViewPaneContainer { - search(text: string, refresh?: boolean): void; + search(text: string): void; + refresh(): Promise; } export const enum ExtensionState { @@ -96,14 +97,12 @@ export interface IExtensionsWorkbenchService { export const ConfigurationKey = 'extensions'; export const AutoUpdateConfigurationKey = 'extensions.autoUpdate'; export const AutoCheckUpdatesConfigurationKey = 'extensions.autoCheckUpdates'; -export const ShowRecommendationsOnlyOnDemandKey = 'extensions.showRecommendationsOnlyOnDemand'; export const CloseExtensionDetailsOnViewChangeKey = 'extensions.closeExtensionDetailsOnViewChange'; export interface IExtensionsConfiguration { autoUpdate: boolean; autoCheckUpdates: boolean; ignoreRecommendations: boolean; - showRecommendationsOnlyOnDemand: boolean; closeExtensionDetailsOnViewChange: boolean; } @@ -131,10 +130,12 @@ export class ExtensionContainers extends Disposable { for (const container of this.containers) { if (extension && container.extension) { if (areSameExtensions(container.extension.identifier, extension.identifier)) { - if (!container.extension.server || !extension.server || container.extension.server === extension.server) { + if (container.extension.server && extension.server && container.extension.server !== extension.server) { + if (container.updateWhenCounterExtensionChanges) { + container.update(); + } + } else { container.extension = extension; - } else if (container.updateWhenCounterExtensionChanges) { - container.update(); } } } else { diff --git a/src/vs/workbench/contrib/extensions/common/extensionsInput.ts b/src/vs/workbench/contrib/extensions/common/extensionsInput.ts index b1ba1de3a..eef812021 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionsInput.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsInput.ts @@ -9,6 +9,7 @@ import { localize } from 'vs/nls'; import { EditorInput } from 'vs/workbench/common/editor'; import { IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { join } from 'vs/base/common/path'; export class ExtensionsInput extends EditorInput { @@ -17,7 +18,7 @@ export class ExtensionsInput extends EditorInput { get resource() { return URI.from({ scheme: Schemas.extension, - path: this.extension.identifier.id + path: join(this.extension.identifier.id, 'extension') }); } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput.ts b/src/vs/workbench/contrib/extensions/common/runtimeExtensionsInput.ts similarity index 100% rename from src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput.ts rename to src/vs/workbench/contrib/extensions/common/runtimeExtensionsInput.ts diff --git a/src/vs/workbench/contrib/extensions/electron-browser/debugExtensionHostAction.ts b/src/vs/workbench/contrib/extensions/electron-browser/debugExtensionHostAction.ts new file mode 100644 index 000000000..bd3ea4886 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/electron-browser/debugExtensionHostAction.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Action } from 'vs/base/common/actions'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { randomPort } from 'vs/base/node/ports'; + +export class DebugExtensionHostAction extends Action { + static readonly ID = 'workbench.extensions.action.debugExtensionHost'; + static readonly LABEL = nls.localize('debugExtensionHost', "Start Debugging Extension Host"); + static readonly CSS_CLASS = 'debug-extension-host'; + + constructor( + @IDebugService private readonly _debugService: IDebugService, + @INativeHostService private readonly _nativeHostService: INativeHostService, + @IDialogService private readonly _dialogService: IDialogService, + @IExtensionService private readonly _extensionService: IExtensionService, + @IProductService private readonly productService: IProductService + ) { + super(DebugExtensionHostAction.ID, DebugExtensionHostAction.LABEL, DebugExtensionHostAction.CSS_CLASS); + } + + async run(): Promise { + + const inspectPort = await this._extensionService.getInspectPort(false); + if (!inspectPort) { + const res = await this._dialogService.confirm({ + type: 'info', + message: nls.localize('restart1', "Profile Extensions"), + detail: nls.localize('restart2', "In order to profile extensions a restart is required. Do you want to restart '{0}' now?", this.productService.nameLong), + primaryButton: nls.localize('restart3', "&&Restart"), + secondaryButton: nls.localize('cancel', "&&Cancel") + }); + if (res.confirmed) { + await this._nativeHostService.relaunch({ addArgs: [`--inspect-extensions=${randomPort()}`] }); + } + + return; + } + + return this._debugService.startDebugging(undefined, { + type: 'node', + name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"), + request: 'attach', + port: inspectPort + }); + } +} diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts index 34cdcea7e..ff347a59d 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts @@ -16,7 +16,7 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { randomPort } from 'vs/base/node/ports'; import { IProductService } from 'vs/platform/product/common/productService'; -import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput'; +import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -56,9 +56,9 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio this._profileSession = null; this._setState(ProfileSessionState.None); - CommandsRegistry.registerCommand('workbench.action.extensionHostProfilder.stop', () => { + CommandsRegistry.registerCommand('workbench.action.extensionHostProfiler.stop', () => { this.stopProfiling(); - this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true }); + this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true, pinned: true }); }); } @@ -86,7 +86,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio showProgress: true, ariaLabel: nls.localize('profilingExtensionHost', "Profiling Extension Host"), tooltip: nls.localize('selectAndStartDebug', "Click to stop profiling."), - command: 'workbench.action.extensionHostProfilder.stop' + command: 'workbench.action.extensionHostProfiler.stop' }; const timeStarted = Date.now(); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index f7fb0e3bd..a472654f0 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -7,17 +7,18 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; +import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { RuntimeExtensionsEditor, ShowRuntimeExtensionsAction, IExtensionHostProfileService, DebugExtensionHostAction, StartExtensionHostProfileAction, StopExtensionHostProfileAction, CONTEXT_PROFILE_SESSION_STATE, SaveExtensionHostProfileAction, CONTEXT_EXTENSION_HOST_PROFILE_RECORDED } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor'; +import { RuntimeExtensionsEditor, IExtensionHostProfileService, StartExtensionHostProfileAction, StopExtensionHostProfileAction, CONTEXT_PROFILE_SESSION_STATE, CONTEXT_EXTENSION_HOST_PROFILE_RECORDED, SaveExtensionHostProfileAction } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor'; +import { DebugExtensionHostAction } from 'vs/workbench/contrib/extensions/electron-browser/debugExtensionHostAction'; import { EditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, ActiveEditorContext } from 'vs/workbench/common/editor'; import { ExtensionHostProfileService } from 'vs/workbench/contrib/extensions/electron-browser/extensionProfileService'; -import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput'; +import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; @@ -26,6 +27,7 @@ import { ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensio import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { ExtensionRecommendationNotificationServiceChannel } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc'; +import { Codicon } from 'vs/base/common/codicons'; // Singletons registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true); @@ -34,16 +36,11 @@ const workbenchRegistry = Registry.as(Workbench workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, LifecyclePhase.Eventually); // Running Extensions Editor - -const runtimeExtensionsEditorDescriptor = EditorDescriptor.create( - RuntimeExtensionsEditor, - RuntimeExtensionsEditor.ID, - localize('runtimeExtension', "Running Extensions") +Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create(RuntimeExtensionsEditor, RuntimeExtensionsEditor.ID, localize('runtimeExtension', "Running Extensions")), + [new SyncDescriptor(RuntimeExtensionsInput)] ); -Registry.as(EditorExtensions.Editors) - .registerEditor(runtimeExtensionsEditorDescriptor, [new SyncDescriptor(RuntimeExtensionsInput)]); - class RuntimeExtensionsInputFactory implements IEditorInputFactory { canSerialize(editorInput: EditorInput): boolean { return true; @@ -62,8 +59,6 @@ Registry.as(EditorInputExtensions.EditorInputFactor // Global actions const actionRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ShowRuntimeExtensionsAction), 'Show Running Extensions', CATEGORIES.Developer.value); - class ExtensionsContributions implements IWorkbenchContribution { constructor( @@ -72,10 +67,8 @@ class ExtensionsContributions implements IWorkbenchContribution { @ISharedProcessService sharedProcessService: ISharedProcessService, ) { sharedProcessService.registerChannel('IExtensionRecommendationNotificationService', new ExtensionRecommendationNotificationServiceChannel(extensionRecommendationNotificationService)); - if (environmentService.extensionsPath) { - const openExtensionsFolderActionDescriptor = SyncActionDescriptor.from(OpenExtensionsFolderAction); - actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel); - } + const openExtensionsFolderActionDescriptor = SyncActionDescriptor.from(OpenExtensionsFolderAction); + actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel); } } @@ -109,9 +102,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: DebugExtensionHostAction.ID, title: DebugExtensionHostAction.LABEL, - icon: { - id: 'codicon/debug-start' - } + icon: Codicon.debugStart }, group: 'navigation', when: ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID) @@ -121,9 +112,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: StartExtensionHostProfileAction.ID, title: StartExtensionHostProfileAction.LABEL, - icon: { - id: 'codicon/circle-filled' - } + icon: Codicon.circleFilled }, group: 'navigation', when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID), CONTEXT_PROFILE_SESSION_STATE.notEqualsTo('running')) @@ -133,9 +122,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: StopExtensionHostProfileAction.ID, title: StopExtensionHostProfileAction.LABEL, - icon: { - id: 'codicon/debug-stop' - } + icon: Codicon.debugStop }, group: 'navigation', when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID), CONTEXT_PROFILE_SESSION_STATE.isEqualTo('running')) @@ -145,9 +132,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SaveExtensionHostProfileAction.ID, title: SaveExtensionHostProfileAction.LABEL, - icon: { - id: 'codicon/save-all' - }, + icon: Codicon.saveAll, precondition: CONTEXT_EXTENSION_HOST_PROFILE_RECORDED }, group: 'navigation', diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts index 226a0bc8e..d8f182cff 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts @@ -16,10 +16,10 @@ import { IExtensionHostProfileService } from 'vs/workbench/contrib/extensions/el import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput'; +import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { createSlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions'; +import { createSlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; @@ -187,7 +187,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont ), [{ label: localize('show', 'Show Extensions'), - run: () => this._editorService.openEditor(RuntimeExtensionsInput.instance) + run: () => this._editorService.openEditor(RuntimeExtensionsInput.instance, { pinned: true }) }, action ], diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts similarity index 100% rename from src/vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions.ts rename to src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts diff --git a/src/vs/workbench/contrib/extensions/electron-browser/reportExtensionIssueAction.ts b/src/vs/workbench/contrib/extensions/electron-browser/reportExtensionIssueAction.ts new file mode 100644 index 000000000..faf606a75 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/electron-browser/reportExtensionIssueAction.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Action } from 'vs/base/common/actions'; +import { IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; + +export class ReportExtensionIssueAction extends Action { + + private static readonly _id = 'workbench.extensions.action.reportExtensionIssue'; + private static readonly _label = nls.localize('reportExtensionIssue', "Report Issue"); + + private _url: string | undefined; + + constructor( + private extension: { + description: IExtensionDescription; + marketplaceInfo: IExtension; + status?: IExtensionsStatus; + unresponsiveProfile?: IExtensionHostProfile + }, + @IOpenerService private readonly openerService: IOpenerService, + @IClipboardService private readonly clipboardService: IClipboardService, + @IProductService private readonly productService: IProductService, + @INativeHostService private readonly nativeHostService: INativeHostService + ) { + super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue'); + this.enabled = !!extension.description.repository && !!extension.description.repository.url; + } + + async run(): Promise { + if (!this._url) { + this._url = await this._generateNewIssueUrl(this.extension); + } + this.openerService.open(URI.parse(this._url)); + } + + private async _generateNewIssueUrl(extension: { + description: IExtensionDescription; + marketplaceInfo: IExtension; + status?: IExtensionsStatus; + unresponsiveProfile?: IExtensionHostProfile + }): Promise { + let baseUrl = extension.marketplaceInfo && extension.marketplaceInfo.type === ExtensionType.User && extension.description.repository ? extension.description.repository.url : undefined; + if (!!baseUrl) { + baseUrl = `${baseUrl.indexOf('.git') !== -1 ? baseUrl.substr(0, baseUrl.length - 4) : baseUrl}/issues/new/`; + } else { + baseUrl = this.productService.reportIssueUrl!; + } + + let reason = 'Bug'; + let title = 'Extension issue'; + let message = ':warning: We have written the needed data into your clipboard. Please paste! :warning:'; + this.clipboardService.writeText('```json \n' + JSON.stringify(extension.status, null, '\t') + '\n```'); + + const os = await this.nativeHostService.getOSProperties(); + const osVersion = `${os.type} ${os.arch} ${os.release}`; + const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&'; + const body = encodeURIComponent( + `- Issue Type: \`${reason}\` +- Extension Name: \`${extension.description.name}\` +- Extension Version: \`${extension.description.version}\` +- OS Version: \`${osVersion}\` +- VSCode version: \`${this.productService.version}\`\n\n${message}` + ); + + return `${baseUrl}${queryStringPrefix}body=${body}&title=${encodeURIComponent(title)}`; + } +} diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 2a81e43f6..8aba9dd18 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -3,50 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/runtimeExtensionsEditor'; import * as nls from 'vs/nls'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { Action, IAction, Separator } from 'vs/base/common/actions'; -import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { Action } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; -import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; -import { WorkbenchList } from 'vs/platform/list/browser/listService'; -import { append, $, reset, Dimension, clearNode } from 'vs/base/browser/dom'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionService, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { memoize } from 'vs/base/common/decorators'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Event } from 'vs/base/common/event'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput'; -import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { randomPort } from 'vs/base/node/ports'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ILabelService } from 'vs/platform/label/common/label'; -import { renderCodicons } from 'vs/base/browser/codicons'; -import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { Schemas } from 'vs/base/common/network'; -import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { URI } from 'vs/base/common/uri'; -import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; -import { domEvent } from 'vs/base/browser/event'; -import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IFileService } from 'vs/platform/files/common/files'; +import { ReportExtensionIssueAction } from 'vs/workbench/contrib/extensions/electron-browser/reportExtensionIssueAction'; +import { AbstractRuntimeExtensionsEditor, IRuntimeExtension } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor'; import { VSBuffer } from 'vs/base/common/buffer'; +import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; export const IExtensionHostProfileService = createDecorator('extensionHostProfileService'); export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey('profileSessionState', 'none'); @@ -75,64 +53,31 @@ export interface IExtensionHostProfileService { setUnresponsiveProfile(extensionId: ExtensionIdentifier, profile: IExtensionHostProfile): void; } -interface IExtensionProfileInformation { - /** - * segment when the extension was running. - * 2*i = segment start time - * 2*i+1 = segment end time - */ - segments: number[]; - /** - * total time when the extension was running. - * (sum of all segment lengths). - */ - totalTime: number; -} +export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor { -interface IRuntimeExtension { - originalIndex: number; - description: IExtensionDescription; - marketplaceInfo: IExtension; - status: IExtensionsStatus; - profileInfo?: IExtensionProfileInformation; - unresponsiveProfile?: IExtensionHostProfile; -} - -export class RuntimeExtensionsEditor extends EditorPane { - - public static readonly ID: string = 'workbench.editor.runtimeExtensions'; - - private _list: WorkbenchList | null; private _profileInfo: IExtensionHostProfile | null; - - private _elements: IRuntimeExtension[] | null; - private _extensionsDescriptions: IExtensionDescription[]; - private _updateSoon: RunOnceScheduler; - private _profileSessionState: IContextKey; private _extensionsHostRecorded: IContextKey; + private _profileSessionState: IContextKey; constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @IContextKeyService contextKeyService: IContextKeyService, - @IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionService private readonly _extensionService: IExtensionService, - @INotificationService private readonly _notificationService: INotificationService, - @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionService extensionService: IExtensionService, + @INotificationService notificationService: INotificationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IInstantiationService instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, - @ILabelService private readonly _labelService: ILabelService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, - @IOpenerService private readonly _openerService: IOpenerService, - @IClipboardService private readonly _clipboardService: IClipboardService, - @IProductService private readonly _productService: IProductService, - @INativeHostService private readonly _nativeHostService: INativeHostService + @ILabelService labelService: ILabelService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService, ) { - super(RuntimeExtensionsEditor.ID, telemetryService, themeService, storageService); - - this._list = null; + super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService); this._profileInfo = this._extensionHostProfileService.lastProfile; + this._extensionsHostRecorded = CONTEXT_EXTENSION_HOST_PROFILE_RECORDED.bindTo(contextKeyService); + this._profileSessionState = CONTEXT_PROFILE_SESSION_STATE.bindTo(contextKeyService); + this._register(this._extensionHostProfileService.onDidChangeLastProfile(() => { this._profileInfo = this._extensionHostProfileService.lastProfile; this._extensionsHostRecorded.set(!!this._profileInfo); @@ -142,486 +87,39 @@ export class RuntimeExtensionsEditor extends EditorPane { const state = this._extensionHostProfileService.state; this._profileSessionState.set(ProfileSessionState[state].toLowerCase()); })); - - this._elements = null; - - this._extensionsDescriptions = []; - this._updateExtensions(); - - this._profileSessionState = CONTEXT_PROFILE_SESSION_STATE.bindTo(contextKeyService); - this._extensionsHostRecorded = CONTEXT_EXTENSION_HOST_PROFILE_RECORDED.bindTo(contextKeyService); - - this._updateSoon = this._register(new RunOnceScheduler(() => this._updateExtensions(), 200)); - - this._extensionService.getExtensions().then((extensions) => { - // We only deal with extensions with source code! - this._extensionsDescriptions = extensions.filter((extension) => { - return Boolean(extension.main) || Boolean(extension.browser); - }); - this._updateExtensions(); - }); - this._register(this._extensionService.onDidChangeExtensionsStatus(() => this._updateSoon.schedule())); } - private async _updateExtensions(): Promise { - this._elements = await this._resolveExtensions(); - if (this._list) { - this._list.splice(0, this._list.length, this._elements); - } + protected _getProfileInfo(): IExtensionHostProfile | null { + return this._profileInfo; } - private async _resolveExtensions(): Promise { - let marketplaceMap: { [id: string]: IExtension; } = Object.create(null); - const marketPlaceExtensions = await this._extensionsWorkbenchService.queryLocal(); - for (let extension of marketPlaceExtensions) { - marketplaceMap[ExtensionIdentifier.toKey(extension.identifier.id)] = extension; - } - - let statusMap = this._extensionService.getExtensionsStatus(); - - // group profile segments by extension - let segments: { [id: string]: number[]; } = Object.create(null); - - if (this._profileInfo) { - let currentStartTime = this._profileInfo.startTime; - for (let i = 0, len = this._profileInfo.deltas.length; i < len; i++) { - const id = this._profileInfo.ids[i]; - const delta = this._profileInfo.deltas[i]; - - let extensionSegments = segments[ExtensionIdentifier.toKey(id)]; - if (!extensionSegments) { - extensionSegments = []; - segments[ExtensionIdentifier.toKey(id)] = extensionSegments; - } - - extensionSegments.push(currentStartTime); - currentStartTime = currentStartTime + delta; - extensionSegments.push(currentStartTime); - } - } - - let result: IRuntimeExtension[] = []; - for (let i = 0, len = this._extensionsDescriptions.length; i < len; i++) { - const extensionDescription = this._extensionsDescriptions[i]; - - let profileInfo: IExtensionProfileInformation | null = null; - if (this._profileInfo) { - let extensionSegments = segments[ExtensionIdentifier.toKey(extensionDescription.identifier)] || []; - let extensionTotalTime = 0; - for (let j = 0, lenJ = extensionSegments.length / 2; j < lenJ; j++) { - const startTime = extensionSegments[2 * j]; - const endTime = extensionSegments[2 * j + 1]; - extensionTotalTime += (endTime - startTime); - } - profileInfo = { - segments: extensionSegments, - totalTime: extensionTotalTime - }; - } - - result[i] = { - originalIndex: i, - description: extensionDescription, - marketplaceInfo: marketplaceMap[ExtensionIdentifier.toKey(extensionDescription.identifier)], - status: statusMap[extensionDescription.identifier.value], - profileInfo: profileInfo || undefined, - unresponsiveProfile: this._extensionHostProfileService.getUnresponsiveProfile(extensionDescription.identifier) - }; - } - - result = result.filter(element => element.status.activationTimes); - - // bubble up extensions that have caused slowness - - const isUnresponsive = (extension: IRuntimeExtension): boolean => - extension.unresponsiveProfile === this._profileInfo; - - const profileTime = (extension: IRuntimeExtension): number => - extension.profileInfo?.totalTime ?? 0; - - const activationTime = (extension: IRuntimeExtension): number => - (extension.status.activationTimes?.codeLoadingTime ?? 0) + - (extension.status.activationTimes?.activateCallTime ?? 0); - - result = result.sort((a, b) => { - if (isUnresponsive(a) || isUnresponsive(b)) { - return +isUnresponsive(b) - +isUnresponsive(a); - } else if (profileTime(a) || profileTime(b)) { - return profileTime(b) - profileTime(a); - } else if (activationTime(a) || activationTime(b)) { - return activationTime(b) - activationTime(a); - } - return a.originalIndex - b.originalIndex; - }); - - return result; + protected _getUnresponsiveProfile(extensionId: ExtensionIdentifier): IExtensionHostProfile | undefined { + return this._extensionHostProfileService.getUnresponsiveProfile(extensionId); } - protected createEditor(parent: HTMLElement): void { - parent.classList.add('runtime-extensions-editor'); - - const TEMPLATE_ID = 'runtimeExtensionElementTemplate'; - - const delegate = new class implements IListVirtualDelegate{ - getHeight(element: IRuntimeExtension): number { - return 62; - } - getTemplateId(element: IRuntimeExtension): string { - return TEMPLATE_ID; - } - }; - - interface IRuntimeExtensionTemplateData { - root: HTMLElement; - element: HTMLElement; - icon: HTMLImageElement; - name: HTMLElement; - version: HTMLElement; - msgContainer: HTMLElement; - actionbar: ActionBar; - activationTime: HTMLElement; - profileTime: HTMLElement; - disposables: IDisposable[]; - elementDisposables: IDisposable[]; + protected _createSlowExtensionAction(element: IRuntimeExtension): Action | null { + if (element.unresponsiveProfile) { + return this._instantiationService.createInstance(SlowExtensionAction, element.description, element.unresponsiveProfile); } - - const renderer: IListRenderer = { - templateId: TEMPLATE_ID, - renderTemplate: (root: HTMLElement): IRuntimeExtensionTemplateData => { - const element = append(root, $('.extension')); - const iconContainer = append(element, $('.icon-container')); - const icon = append(iconContainer, $('img.icon')); - - const desc = append(element, $('div.desc')); - const headerContainer = append(desc, $('.header-container')); - const header = append(headerContainer, $('.header')); - const name = append(header, $('div.name')); - const version = append(header, $('span.version')); - - const msgContainer = append(desc, $('div.msg')); - - const actionbar = new ActionBar(desc, { animated: false }); - actionbar.onDidRun(({ error }) => error && this._notificationService.error(error)); - - - const timeContainer = append(element, $('.time')); - const activationTime = append(timeContainer, $('div.activation-time')); - const profileTime = append(timeContainer, $('div.profile-time')); - - const disposables = [actionbar]; - - return { - root, - element, - icon, - name, - version, - actionbar, - activationTime, - profileTime, - msgContainer, - disposables, - elementDisposables: [], - }; - }, - - renderElement: (element: IRuntimeExtension, index: number, data: IRuntimeExtensionTemplateData): void => { - - data.elementDisposables = dispose(data.elementDisposables); - - data.root.classList.toggle('odd', index % 2 === 1); - - const onError = Event.once(domEvent(data.icon, 'error')); - onError(() => data.icon.src = element.marketplaceInfo.iconUrlFallback, null, data.elementDisposables); - data.icon.src = element.marketplaceInfo.iconUrl; - - if (!data.icon.complete) { - data.icon.style.visibility = 'hidden'; - data.icon.onload = () => data.icon.style.visibility = 'inherit'; - } else { - data.icon.style.visibility = 'inherit'; - } - data.name.textContent = element.marketplaceInfo.displayName; - data.version.textContent = element.description.version; - - const activationTimes = element.status.activationTimes!; - let syncTime = activationTimes.codeLoadingTime + activationTimes.activateCallTime; - data.activationTime.textContent = activationTimes.activationReason.startup ? `Startup Activation: ${syncTime}ms` : `Activation: ${syncTime}ms`; - - data.actionbar.clear(); - if (element.unresponsiveProfile) { - data.actionbar.push(this._instantiationService.createInstance(SlowExtensionAction, element.description, element.unresponsiveProfile), { icon: true, label: true }); - } - if (isNonEmptyArray(element.status.runtimeErrors)) { - data.actionbar.push(new ReportExtensionIssueAction(element, this._openerService, this._clipboardService, this._productService, this._nativeHostService), { icon: true, label: true }); - } - - let title: string; - const activationId = activationTimes.activationReason.extensionId.value; - const activationEvent = activationTimes.activationReason.activationEvent; - if (activationEvent === '*') { - title = nls.localize('starActivation', "Activated by {0} on start-up", activationId); - } else if (/^workspaceContains:/.test(activationEvent)) { - let fileNameOrGlob = activationEvent.substr('workspaceContains:'.length); - if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) { - title = nls.localize({ - key: 'workspaceContainsGlobActivation', - comment: [ - '{0} will be a glob pattern' - ] - }, "Activated by {1} because a file matching {1} exists in your workspace", fileNameOrGlob, activationId); - } else { - title = nls.localize({ - key: 'workspaceContainsFileActivation', - comment: [ - '{0} will be a file name' - ] - }, "Activated by {1} because file {0} exists in your workspace", fileNameOrGlob, activationId); - } - } else if (/^workspaceContainsTimeout:/.test(activationEvent)) { - const glob = activationEvent.substr('workspaceContainsTimeout:'.length); - title = nls.localize({ - key: 'workspaceContainsTimeout', - comment: [ - '{0} will be a glob pattern' - ] - }, "Activated by {1} because searching for {0} took too long", glob, activationId); - } else if (activationEvent === 'onStartupFinished') { - title = nls.localize({ - key: 'startupFinishedActivation', - comment: [ - 'This refers to an extension. {0} will be an activation event.' - ] - }, "Activated by {0} after start-up finished", activationId); - } else if (/^onLanguage:/.test(activationEvent)) { - let language = activationEvent.substr('onLanguage:'.length); - title = nls.localize('languageActivation', "Activated by {1} because you opened a {0} file", language, activationId); - } else { - title = nls.localize({ - key: 'workspaceGenericActivation', - comment: [ - 'The {0} placeholder will be an activation event, like e.g. \'language:typescript\', \'debug\', etc.' - ] - }, "Activated by {1} on {0}", activationEvent, activationId); - } - data.activationTime.title = title; - - clearNode(data.msgContainer); - - if (this._extensionHostProfileService.getUnresponsiveProfile(element.description.identifier)) { - const el = $('span', undefined, ...renderCodicons(` $(alert) Unresponsive`)); - el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); - data.msgContainer.appendChild(el); - } - - if (isNonEmptyArray(element.status.runtimeErrors)) { - const el = $('span', undefined, ...renderCodicons(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`)); - data.msgContainer.appendChild(el); - } - - if (element.status.messages && element.status.messages.length > 0) { - const el = $('span', undefined, ...renderCodicons(`$(alert) ${element.status.messages[0].message}`)); - data.msgContainer.appendChild(el); - } - - if (element.description.extensionLocation.scheme !== Schemas.file) { - const el = $('span', undefined, ...renderCodicons(`$(remote) ${element.description.extensionLocation.authority}`)); - data.msgContainer.appendChild(el); - - const hostLabel = this._labelService.getHostLabel(Schemas.vscodeRemote, this._environmentService.remoteAuthority); - if (hostLabel) { - reset(el, ...renderCodicons(`$(remote) ${hostLabel}`)); - } - } - - if (this._profileInfo && element.profileInfo) { - data.profileTime.textContent = `Profile: ${(element.profileInfo.totalTime / 1000).toFixed(2)}ms`; - } else { - data.profileTime.textContent = ''; - } - - }, - - disposeTemplate: (data: IRuntimeExtensionTemplateData): void => { - data.disposables = dispose(data.disposables); - } - }; - - this._list = >this._instantiationService.createInstance(WorkbenchList, - 'RuntimeExtensions', - parent, delegate, [renderer], { - multipleSelectionSupport: false, - setRowLineHeight: false, - horizontalScrolling: false, - overrideStyles: { - listBackground: editorBackground - }, - accessibilityProvider: new RuntimeExtensionsEditorAccessibilityProvider() - }); - - this._list.splice(0, this._list.length, this._elements || undefined); - - this._list.onContextMenu((e) => { - if (!e.element) { - return; - } - - const actions: IAction[] = []; - - actions.push(new ReportExtensionIssueAction(e.element, this._openerService, this._clipboardService, this._productService, this._nativeHostService)); - actions.push(new Separator()); - - actions.push(new Action('runtimeExtensionsEditor.action.disableWorkspace', nls.localize('disable workspace', "Disable (Workspace)"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledWorkspace))); - actions.push(new Action('runtimeExtensionsEditor.action.disable', nls.localize('disable', "Disable"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledGlobally))); - actions.push(new Separator()); - - const state = this._extensionHostProfileService.state; - if (state === ProfileSessionState.Running) { - actions.push(this._instantiationService.createInstance(StopExtensionHostProfileAction, StopExtensionHostProfileAction.ID, StopExtensionHostProfileAction.LABEL)); - } else { - actions.push(this._instantiationService.createInstance(StartExtensionHostProfileAction, StartExtensionHostProfileAction.ID, StartExtensionHostProfileAction.LABEL)); - } - actions.push(this.saveExtensionHostProfileAction); - - this._contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, - getActions: () => actions - }); - }); + return null; } - @memoize - private get saveExtensionHostProfileAction(): IAction { + protected _createReportExtensionIssueAction(element: IRuntimeExtension): Action | null { + return this._instantiationService.createInstance(ReportExtensionIssueAction, element); + } + + protected _createSaveExtensionHostProfileAction(): Action | null { return this._instantiationService.createInstance(SaveExtensionHostProfileAction, SaveExtensionHostProfileAction.ID, SaveExtensionHostProfileAction.LABEL); } - public layout(dimension: Dimension): void { - if (this._list) { - this._list.layout(dimension.height); - } - } -} - -export class ShowRuntimeExtensionsAction extends Action { - static readonly ID = 'workbench.action.showRuntimeExtensions'; - static readonly LABEL = nls.localize('showRuntimeExtensions', "Show Running Extensions"); - - constructor( - id: string, label: string, - @IEditorService private readonly _editorService: IEditorService - ) { - super(id, label); - } - - public async run(e?: any): Promise { - await this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true }); - } -} - -export class ReportExtensionIssueAction extends Action { - - private static readonly _id = 'workbench.extensions.action.reportExtensionIssue'; - private static readonly _label = nls.localize('reportExtensionIssue', "Report Issue"); - - private _url: string | undefined; - - constructor( - private extension: { - description: IExtensionDescription; - marketplaceInfo: IExtension; - status?: IExtensionsStatus; - unresponsiveProfile?: IExtensionHostProfile - }, - @IOpenerService private readonly openerService: IOpenerService, - @IClipboardService private readonly clipboardService: IClipboardService, - @IProductService private readonly productService: IProductService, - @INativeHostService private readonly nativeHostService: INativeHostService - ) { - super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue'); - this.enabled = extension.marketplaceInfo - && extension.marketplaceInfo.type === ExtensionType.User - && !!extension.description.repository && !!extension.description.repository.url; - } - - async run(): Promise { - if (!this._url) { - this._url = await this._generateNewIssueUrl(this.extension); - } - this.openerService.open(URI.parse(this._url)); - } - - private async _generateNewIssueUrl(extension: { - description: IExtensionDescription; - marketplaceInfo: IExtension; - status?: IExtensionsStatus; - unresponsiveProfile?: IExtensionHostProfile - }): Promise { - let baseUrl = extension.marketplaceInfo && extension.marketplaceInfo.type === ExtensionType.User && extension.description.repository ? extension.description.repository.url : undefined; - if (!!baseUrl) { - baseUrl = `${baseUrl.indexOf('.git') !== -1 ? baseUrl.substr(0, baseUrl.length - 4) : baseUrl}/issues/new/`; - } else { - baseUrl = this.productService.reportIssueUrl!; - } - - let reason = 'Bug'; - let title = 'Extension issue'; - let message = ':warning: We have written the needed data into your clipboard. Please paste! :warning:'; - this.clipboardService.writeText('```json \n' + JSON.stringify(extension.status, null, '\t') + '\n```'); - - const os = await this.nativeHostService.getOSProperties(); - const osVersion = `${os.type} ${os.arch} ${os.release}`; - const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&'; - const body = encodeURIComponent( - `- Issue Type: \`${reason}\` -- Extension Name: \`${extension.description.name}\` -- Extension Version: \`${extension.description.version}\` -- OS Version: \`${osVersion}\` -- VSCode version: \`${this.productService.version}\`\n\n${message}` + protected _createProfileAction(): Action | null { + const state = this._extensionHostProfileService.state; + const profileAction = ( + state === ProfileSessionState.Running + ? this._instantiationService.createInstance(StopExtensionHostProfileAction, StopExtensionHostProfileAction.ID, StopExtensionHostProfileAction.LABEL) + : this._instantiationService.createInstance(StartExtensionHostProfileAction, StartExtensionHostProfileAction.ID, StartExtensionHostProfileAction.LABEL) ); - - return `${baseUrl}${queryStringPrefix}body=${body}&title=${encodeURIComponent(title)}`; - } -} - -export class DebugExtensionHostAction extends Action { - static readonly ID = 'workbench.extensions.action.debugExtensionHost'; - static readonly LABEL = nls.localize('debugExtensionHost', "Start Debugging Extension Host"); - static readonly CSS_CLASS = 'debug-extension-host'; - - constructor( - @IDebugService private readonly _debugService: IDebugService, - @INativeHostService private readonly _nativeHostService: INativeHostService, - @IDialogService private readonly _dialogService: IDialogService, - @IExtensionService private readonly _extensionService: IExtensionService, - @IProductService private readonly productService: IProductService - ) { - super(DebugExtensionHostAction.ID, DebugExtensionHostAction.LABEL, DebugExtensionHostAction.CSS_CLASS); - } - - async run(): Promise { - - const inspectPort = await this._extensionService.getInspectPort(false); - if (!inspectPort) { - const res = await this._dialogService.confirm({ - type: 'info', - message: nls.localize('restart1', "Profile Extensions"), - detail: nls.localize('restart2', "In order to profile extensions a restart is required. Do you want to restart '{0}' now?", this.productService.nameLong), - primaryButton: nls.localize('restart3', "&&Restart"), - secondaryButton: nls.localize('cancel', "&&Cancel") - }); - if (res.confirmed) { - await this._nativeHostService.relaunch({ addArgs: [`--inspect-extensions=${randomPort()}`] }); - } - - return; - } - - return this._debugService.startDebugging(undefined, { - type: 'node', - name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"), - request: 'attach', - port: inspectPort - }); + return profileAction; } } @@ -707,7 +205,6 @@ export class SaveExtensionHostProfileAction extends Action { // absolute filenames because we don't want to reveal anything // about users. We also append the `.txt` suffix to make it // easier to attach these files to GH issues - let tmp = profiler.rewriteAbsolutePaths({ profile: dataToWrite as any }, 'piiRemoved'); dataToWrite = tmp.profile; @@ -717,13 +214,3 @@ export class SaveExtensionHostProfileAction extends Action { return this._fileService.writeFile(URI.file(savePath), VSBuffer.fromString(JSON.stringify(profileInfo ? profileInfo.data : {}, null, '\t'))); } } - -class RuntimeExtensionsEditorAccessibilityProvider implements IListAccessibilityProvider { - getWidgetAriaLabel(): string { - return nls.localize('runtimeExtensions', "Runtime Extensions"); - } - - getAriaLabel(element: IRuntimeExtension): string | null { - return element.description.name; - } -} diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/extensionsActions.ts index 369d6a1bf..99cd8d052 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/extensionsActions.ts @@ -27,20 +27,18 @@ export class OpenExtensionsFolderAction extends Action { } async run(): Promise { - if (this.environmentService.extensionsPath) { - const extensionsHome = URI.file(this.environmentService.extensionsPath); - const file = await this.fileService.resolve(extensionsHome); + const extensionsHome = URI.file(this.environmentService.extensionsPath); + const file = await this.fileService.resolve(extensionsHome); - let itemToShow: URI; - if (file.children && file.children.length > 0) { - itemToShow = file.children[0].resource; - } else { - itemToShow = extensionsHome; - } + let itemToShow: URI; + if (file.children && file.children.length > 0) { + itemToShow = file.children[0].resource; + } else { + itemToShow = extensionsHome; + } - if (itemToShow.scheme === Schemas.file) { - return this.nativeHostService.showItemInFolder(itemToShow.fsPath); - } + if (itemToShow.scheme === Schemas.file) { + return this.nativeHostService.showItemInFolder(itemToShow.fsPath); } } } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index 3c38a6761..ed8a92a98 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -42,7 +42,7 @@ import { INotificationService, Severity, IPromptChoice, IPromptOptions } from 'v import { NativeURLService } from 'vs/platform/url/common/urlService'; import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -55,7 +55,6 @@ import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-s import { ExtensionRecommendationsService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationsService'; import { NoOpWorkspaceTagsService } from 'vs/workbench/contrib/tags/browser/workspaceTagsService'; import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkpsaceExtensionsConfigService, WorkspaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; @@ -222,7 +221,6 @@ suite('ExtensionRecommendationsService Test', () => { instantiationService.stub(IWorkspaceTagsService, new NoOpWorkspaceTagsService()); instantiationService.stub(IStorageService, new TestStorageService()); instantiationService.stub(ILogService, new NullLogService()); - instantiationService.stub(IStorageKeysSyncRegistryService, new StorageKeysSyncRegistryService()); instantiationService.stub(IProductService, >{ extensionTips: { 'ms-dotnettools.csharp': '{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln,**/appsettings.json}', @@ -273,7 +271,7 @@ suite('ExtensionRecommendationsService Test', () => { instantiationService.stub(INotificationService, new TestNotificationService2()); - testConfigurationService.setUserConfiguration(ConfigurationKey, { ignoreRecommendations: false, showRecommendationsOnlyOnDemand: false }); + testConfigurationService.setUserConfiguration(ConfigurationKey, { ignoreRecommendations: false }); instantiationService.stub(IModelService, { getModels(): any { return []; }, onModelAdded: onModelAddedEvent.event @@ -306,14 +304,14 @@ suite('ExtensionRecommendationsService Test', () => { }, null, '\t')); const myWorkspace = testWorkspace(URI.from({ scheme: 'file', path: folderDir })); + const fileService = new FileService(new NullLogService()); + fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); + instantiationService.stub(IFileService, fileService); workspaceService = new TestContextService(myWorkspace); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IWorkpsaceExtensionsConfigService, instantiationService.createInstance(WorkspaceExtensionsConfigService)); instantiationService.stub(IExtensionIgnoredRecommendationsService, instantiationService.createInstance(ExtensionIgnoredRecommendationsService)); instantiationService.stub(IExtensionRecommendationNotificationService, instantiationService.createInstance(ExtensionRecommendationNotificationService)); - const fileService = new FileService(new NullLogService()); - fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); - instantiationService.stub(IFileService, fileService); } function testNoPromptForValidRecommendations(recommendations: string[]) { @@ -383,6 +381,16 @@ suite('ExtensionRecommendationsService Test', () => { return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); }); + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set', () => { + testConfigurationService.setUserConfiguration(ConfigurationKey, { ignoreRecommendations: true }); + return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { + testObject = instantiationService.createInstance(ExtensionRecommendationsService); + return testObject.activationPromise.then(() => { + assert.ok(!prompted); + }); + }); + }); + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if showRecommendationsOnlyOnDemand is set', () => { testConfigurationService.setUserConfiguration(ConfigurationKey, { showRecommendationsOnlyOnDemand: true }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { @@ -394,14 +402,14 @@ suite('ExtensionRecommendationsService Test', () => { }); test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set for current workspace', () => { - instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); + instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); }); test('ExtensionRecommendationsService: No Recommendations of globally ignored recommendations', () => { - instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', '["ms-dotnettools.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]', StorageScope.GLOBAL); - instantiationService.get(IStorageService).store('extensionsAssistant/ignored_recommendations', '["ms-dotnettools.csharp", "mockpublisher2.mockextension2"]', StorageScope.GLOBAL); + instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', '["ms-dotnettools.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]', StorageScope.GLOBAL, StorageTarget.MACHINE); + instantiationService.get(IStorageService).store('extensionsAssistant/ignored_recommendations', '["ms-dotnettools.csharp", "mockpublisher2.mockextension2"]', StorageScope.GLOBAL, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); @@ -418,8 +426,8 @@ suite('ExtensionRecommendationsService Test', () => { test('ExtensionRecommendationsService: No Recommendations of workspace ignored recommendations', () => { const ignoredRecommendations = ['ms-dotnettools.csharp', 'mockpublisher2.mockextension2']; // ignore a stored recommendation and a workspace recommendation. const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; - instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); + instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, ignoredRecommendations).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); @@ -433,15 +441,15 @@ suite('ExtensionRecommendationsService Test', () => { }); }); - test('ExtensionRecommendationsService: Able to retrieve collection of all ignored recommendations', async () => { + test.skip('ExtensionRecommendationsService: Able to retrieve collection of all ignored recommendations', async () => { const storageService = instantiationService.get(IStorageService); const workspaceIgnoredRecommendations = ['ms-dotnettools.csharp']; // ignore a stored recommendation and a workspace recommendation. const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; const globallyIgnoredRecommendations = '["mockpublisher2.mockextension2"]'; // ignore a workspace recommendation. - storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - storageService.store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); - storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL); + storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + storageService.store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); await setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, workspaceIgnoredRecommendations); testObject = instantiationService.createInstance(ExtensionRecommendationsService); @@ -458,9 +466,9 @@ suite('ExtensionRecommendationsService Test', () => { const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; const globallyIgnoredRecommendations = '["mockpublisher2.mockextension2"]'; // ignore a workspace recommendation. - storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - storageService.store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); - storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL); + storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + storageService.store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); await setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions); const extensionIgnoredRecommendationsService = instantiationService.get(IExtensionIgnoredRecommendationsService); @@ -492,8 +500,8 @@ suite('ExtensionRecommendationsService Test', () => { const changeHandlerTarget = sinon.spy(); const ignoredExtensionId = 'Some.Extension'; - storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - storageService.store('extensionsAssistant/ignored_recommendations', '["ms-vscode.vscode"]', StorageScope.GLOBAL); + storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + storageService.store('extensionsAssistant/ignored_recommendations', '["ms-vscode.vscode"]', StorageScope.GLOBAL, StorageTarget.MACHINE); await setUpFolderWorkspace('myFolder', []); testObject = instantiationService.createInstance(ExtensionRecommendationsService); @@ -508,7 +516,7 @@ suite('ExtensionRecommendationsService Test', () => { test('ExtensionRecommendationsService: Get file based recommendations from storage (old format)', () => { const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]'; - instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); + instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', []).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); @@ -527,7 +535,7 @@ suite('ExtensionRecommendationsService Test', () => { const now = Date.now(); const tenDaysOld = 10 * milliSecondsInADay; const storedRecommendations = `{"ms-dotnettools.csharp": ${now}, "ms-python.python": ${now}, "ms-vscode.vscode-typescript-tslint-plugin": ${now}, "lukehoban.Go": ${tenDaysOld}}`; - instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); + instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', []).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index c928821f6..40cf65cc2 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -38,12 +38,10 @@ import { ExtensionIdentifier, IExtensionContributions, ExtensionType, IExtension import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ILabelService, IFormatterChangeEvent } from 'vs/platform/label/common/label'; -import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService'; import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { ProgressService } from 'vs/workbench/services/progress/browser/progressService'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService'; @@ -78,7 +76,6 @@ async function setupTest() { instantiationService.stub(IWorkspaceContextService, new TestContextService()); instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(IProgressService, ProgressService); - instantiationService.stub(IStorageKeysSyncRegistryService, new StorageKeysSyncRegistryService()); instantiationService.stub(IProductService, {}); instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); @@ -101,14 +98,18 @@ async function setupTest() { instantiationService.stub(IRemoteAgentService, RemoteAgentService); - instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { - #localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; - constructor() { - super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService), instantiationService.get(ILabelService), instantiationService); + const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; + instantiationService.stub(IExtensionManagementServerService, >{ + get localExtensionManagementServer(): IExtensionManagementServer { + return localExtensionManagementServer; + }, + getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null { + if (extension.location.scheme === Schemas.file) { + return localExtensionManagementServer; + } + throw new Error(`Invalid Extension ${extension.location}`); } - get localExtensionManagementServer(): IExtensionManagementServer { return this.#localExtensionManagementServer; } - set localExtensionManagementServer(server: IExtensionManagementServer) { } - }()); + }); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(ILabelService, { onDidChangeFormatters: new Emitter().event }); @@ -418,7 +419,7 @@ suite('ExtensionsActions', () => { .then(extensions => { testObject.extension = extensions[0]; assert.ok(testObject.enabled); - assert.equal('extension-action icon manage codicon-gear', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage', testObject.class); assert.equal('', testObject.tooltip); }); }); @@ -433,7 +434,7 @@ suite('ExtensionsActions', () => { .then(page => { testObject.extension = page.firstPage[0]; assert.ok(!testObject.enabled); - assert.equal('extension-action icon manage codicon-gear hide', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage hide', testObject.class); assert.equal('', testObject.tooltip); }); }); @@ -450,7 +451,7 @@ suite('ExtensionsActions', () => { installEvent.fire({ identifier: gallery.identifier, gallery }); assert.ok(!testObject.enabled); - assert.equal('extension-action icon manage codicon-gear hide', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage hide', testObject.class); assert.equal('', testObject.tooltip); }); }); @@ -468,7 +469,7 @@ suite('ExtensionsActions', () => { didInstallEvent.fire({ identifier: gallery.identifier, gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery) }); assert.ok(testObject.enabled); - assert.equal('extension-action icon manage codicon-gear', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage', testObject.class); assert.equal('', testObject.tooltip); }); }); @@ -483,7 +484,7 @@ suite('ExtensionsActions', () => { .then(extensions => { testObject.extension = extensions[0]; assert.ok(testObject.enabled); - assert.equal('extension-action icon manage codicon-gear', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage', testObject.class); assert.equal('', testObject.tooltip); }); }); @@ -500,7 +501,7 @@ suite('ExtensionsActions', () => { uninstallEvent.fire(local.identifier); assert.ok(!testObject.enabled); - assert.equal('extension-action icon manage codicon-gear', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage', testObject.class); assert.equal('Uninstalling', testObject.tooltip); }); }); @@ -817,25 +818,19 @@ suite('ExtensionsActions', () => { }); }); - test('Test DisableDropDownAction when there is no extension', () => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, []); - - assert.ok(!testObject.enabled); - }); - - test('Test DisableDropDownAction when extension is installed and enabled', () => { + test('Test DisableGloballyAction when extension is installed and enabled', () => { const local = aLocalExtension('a'); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); testObject.extension = extensions[0]; assert.ok(testObject.enabled); }); }); - test('Test DisableDropDownAction when extension is installed and disabled globally', () => { + test('Test DisableGloballyAction when extension is installed and disabled globally', () => { const local = aLocalExtension('a'); return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally) .then(() => { @@ -843,47 +838,32 @@ suite('ExtensionsActions', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); testObject.extension = extensions[0]; assert.ok(!testObject.enabled); }); }); }); - test('Test DisableDropDownAction when extension is installed and disabled for workspace', () => { - const local = aLocalExtension('a'); - return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledWorkspace) - .then(() => { - instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); - - return instantiationService.get(IExtensionsWorkbenchService).queryLocal() - .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); - testObject.extension = extensions[0]; - assert.ok(!testObject.enabled); - }); - }); - }); - - test('Test DisableDropDownAction when extension is uninstalled', () => { + test('Test DisableGloballyAction when extension is uninstalled', () => { const gallery = aGalleryExtension('a'); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); return instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None) .then(page => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); testObject.extension = page.firstPage[0]; assert.ok(!testObject.enabled); }); }); - test('Test DisableDropDownAction when extension is installing', () => { + test('Test DisableGloballyAction when extension is installing', () => { const gallery = aGalleryExtension('a'); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); return instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None) .then(page => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); testObject.extension = page.firstPage[0]; instantiationService.createInstance(ExtensionContainers, [testObject]); installEvent.fire({ identifier: gallery.identifier, gallery }); @@ -891,13 +871,13 @@ suite('ExtensionsActions', () => { }); }); - test('Test DisableDropDownAction when extension is uninstalling', () => { + test('Test DisableGloballyAction when extension is uninstalling', () => { const local = aLocalExtension('a'); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); testObject.extension = extensions[0]; instantiationService.createInstance(ExtensionContainers, [testObject]); uninstallEvent.fire(local.identifier); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index bc816ccb7..cde013f45 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -19,7 +19,7 @@ import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/ex import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { IPager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -36,15 +36,14 @@ import { SinonStub } from 'sinon'; import { IExperimentService, ExperimentState, ExperimentActionType, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; -import { ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, IExtension, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService'; -import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IMenuService } from 'vs/platform/actions/common/actions'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { Schemas } from 'vs/base/common/network'; suite('ExtensionsListView Tests', () => { @@ -101,14 +100,18 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IMenuService, new TestMenuService()); - instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { - #localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; - constructor() { - super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService), instantiationService.get(ILabelService), instantiationService); + const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; + instantiationService.stub(IExtensionManagementServerService, >{ + get localExtensionManagementServer(): IExtensionManagementServer { + return localExtensionManagementServer; + }, + getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null { + if (extension.location.scheme === Schemas.file) { + return localExtensionManagementServer; + } + throw new Error(`Invalid Extension ${extension.location}`); } - get localExtensionManagementServer(): IExtensionManagementServer { return this.#localExtensionManagementServer; } - set localExtensionManagementServer(server: IExtensionManagementServer) { } - }()); + }); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -165,7 +168,8 @@ suite('ExtensionsListView Tests', () => { } }); - instantiationService.stub(IExtensionService, { + instantiationService.stub(IExtensionService, >{ + onDidChangeExtensions: Event.None, getExtensions: (): Promise => { return Promise.resolve([ toExtensionDescription(localEnabledTheme), @@ -180,7 +184,7 @@ suite('ExtensionsListView Tests', () => { await (instantiationService.get(IWorkbenchExtensionEnablementService)).setEnablement([localDisabledLanguage], EnablementState.DisabledGlobally); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); - testableView = instantiationService.createInstance(ExtensionsListView, {}); + testableView = instantiationService.createInstance(ExtensionsListView, {}, {}); }); teardown(() => { @@ -482,7 +486,7 @@ suite('ExtensionsListView Tests', () => { }]); testableView.dispose(); - testableView = instantiationService.createInstance(ExtensionsListView, {}); + testableView = instantiationService.createInstance(ExtensionsListView, {}, {}); return testableView.show('search-me').then(result => { const options: IQueryOptions = queryTarget.args[0][0]; @@ -509,7 +513,7 @@ suite('ExtensionsListView Tests', () => { const queryTarget = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...realResults)); testableView.dispose(); - testableView = instantiationService.createInstance(ExtensionsListView, {}); + testableView = instantiationService.createInstance(ExtensionsListView, {}, {}); return testableView.show('search-me @sort:installs').then(result => { const options: IQueryOptions = queryTarget.args[0][0]; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index dc1e3e167..f4f5d8ee5 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -39,7 +39,6 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IProductService } from 'vs/platform/product/common/productService'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; @@ -68,7 +67,6 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(ILogService, NullLogService); instantiationService.stub(IProgressService, ProgressService); - instantiationService.stub(IStorageKeysSyncRegistryService, new StorageKeysSyncRegistryService()); instantiationService.stub(IProductService, {}); instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); @@ -224,8 +222,8 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal('1.1.0', actual.version); assert.equal('1.1.0', actual.latestVersion); assert.equal('localDescription1', actual.description); - assert.equal('file:///localPath1/localIcon1', actual.iconUrl); - assert.equal('file:///localPath1/localIcon1', actual.iconUrlFallback); + assert.ok(actual.iconUrl === 'file:///localPath1/localIcon1' || actual.iconUrl === 'vscode-file://vscode-app/localPath1/localIcon1'); + assert.ok(actual.iconUrlFallback === 'file:///localPath1/localIcon1' || actual.iconUrlFallback === 'vscode-file://vscode-app/localPath1/localIcon1'); assert.equal(null, actual.licenseUrl); assert.equal(ExtensionState.Installed, actual.state); assert.equal(null, actual.installCount); @@ -687,6 +685,11 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); + instantiationService.stub(INotificationService, >{ + prompt(severity, message, choices, options) { + options!.onCancel!(); + } + }); return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionA], EnablementState.EnabledGlobally) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionB], EnablementState.EnabledGlobally)) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionC], EnablementState.EnabledGlobally)) @@ -697,6 +700,28 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); }); + test('test disable extension disables all dependents when chosen to disable all', async () => { + const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] }); + const extensionB = aLocalExtension('b'); + const extensionC = aLocalExtension('c'); + + instantiationService.stub(INotificationService, >{ + prompt(severity, message, choices, options) { + choices[0].run(); + } + }); + return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionA], EnablementState.EnabledGlobally) + .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionB], EnablementState.EnabledGlobally)) + .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionC], EnablementState.EnabledGlobally)) + .then(async () => { + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); + testObject = await aWorkbenchService(); + await testObject.setEnablement(testObject.local[1], EnablementState.DisabledGlobally); + assert.equal(testObject.local[0].enablementState, EnablementState.DisabledGlobally); + assert.equal(testObject.local[1].enablementState, EnablementState.DisabledGlobally); + }); + }); + test('test disable extension when extension is part of a pack', async () => { const extensionA = aLocalExtension('a', { extensionPack: ['pub.b'] }); const extensionB = aLocalExtension('b'); @@ -802,6 +827,11 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b', { extensionDependencies: ['pub.a'] }); const extensionC = aLocalExtension('c'); + instantiationService.stub(INotificationService, >{ + prompt(severity, message, choices, options) { + options!.onCancel!(); + } + }); return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionA], EnablementState.EnabledGlobally) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionB], EnablementState.EnabledGlobally)) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionC], EnablementState.EnabledGlobally)) @@ -837,6 +867,11 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b', { extensionDependencies: ['pub.c'] }); const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.a'] }); + instantiationService.stub(INotificationService, >{ + prompt(severity, message, choices, options) { + options!.onCancel!(); + } + }); return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionA], EnablementState.EnabledGlobally) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionB], EnablementState.EnabledGlobally)) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionC], EnablementState.EnabledGlobally)) diff --git a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts index 7b38b373b..148401905 100644 --- a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts +++ b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts @@ -12,14 +12,13 @@ import { ITerminalService as IIntegratedTerminalService } from 'vs/workbench/con import { ResourceContextKey } from 'vs/workbench/common/resources'; import { IFileService } from 'vs/platform/files/common/files'; import { IListService } from 'vs/platform/list/browser/listService'; -import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; +import { getMultiSelectedResources, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { Schemas } from 'vs/base/common/network'; import { distinct } from 'vs/base/common/arrays'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { optional } from 'vs/platform/instantiation/common/instantiation'; -import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/contrib/externalTerminal/node/TerminalHelper.scpt b/src/vs/workbench/contrib/externalTerminal/node/TerminalHelper.scpt index 145345e002542006dddfcc4871306c35a7679093..a151118b142180cb73e20254576ced1e95fb4c25 100644 GIT binary patch delta 5841 zcmbtYcYIVu*FMh#NVu0tK?sB-WC_JU5E2Cuup^-8Yw%OV3T`$F22yZ$Ary5{!~%-t zVn-AZ6ciOfQ4tlpARzWG_TGCt^PRa9h%dkQ`~G zGun1lBv1ZF+VMC+<9HHDh~%*tgA*}UV+^T;$k~;XaI(fpR-(1cN8uE6tofJ5DU@+k zsC|?<2B(^%J#!3o?iXuJ#A_Opv4*L2btW)Hdhn@)`cTsdoR*E#2y1Y99!?kg=4k85 zv9|jck;ch{MhgF#QN9@!bSo@OpGcKd9!Z@UkHqVzq-I2tu|(~ZRHU>t5=+FRsdzFH zjWooYBB^+yzP2GM@e*ZYthF*O598=WicpHqq_}*>sh+0DY=(@J6r%*yhVuxO%3ma0 ziBnW0;_^Hqqez+>WYQ!PyO6xPhn&gaRV@BOUMiP`clBgZ3DLsx(iqtkFuP7l z9aJ$3RXh)Ix<{h4(Ivx3Qy~E9h~RJ0iVHf8@~}o`H8QIc5FCz;vSJaoxV2#IV5%-B3~Q!x@Jmtt zA@w0IMfFMMaNitGnH7`~tFIwyg9gelhoR9N>Y2l+l{a}pt*C2s@~2{)Im9=I2#*WW zZOoxIqm!u*^@zl$$754dk`AGDtg*g6Dk-gP2&ezfV>y&-FeMLD=tuz|PSd5uXljUH z3)VI>_3{^O&W{DEE>t$0Onu1S?PpD4Gp6QsOG2NAr7^Yw*W@*f{I$9$p zsGn_yn86xnlWGuKcI6zLt8osYQsxsd$sFLB0|d}iDkYlEb%4e&!3^@vAPZzjSWK&PJj*$Otmn!kW(f+z?fJ-@}3O5aol zmz5MAI9t?Qgo`yUqE@Gf;A||#C75R_On;3_NLkQli^P0fsxjZ0nvFT8pKtn!f`YWh zrLIEE5qI|S%szsQts)xpTx|2O7?)vz#$^uCdAJUj;|h(-9ij_yo$2eDzOwJABO(!v zD;x&%unGd{a(&6{MSL8)_PZI|h1!Mj&A2LXUv9zmkjmFiEiOaCe^fEn7 z513w7sHg3ESjlUoU2D2)T8bHlVi2i zIl2m~O_6VkEU_CD7K=^sU1OQExfTy&IaX*acQ)5zoe?#K%{C`Q>yjEPT5R5i+cj=; zHrHc=>Ef9#p~>VFjoY2cby#l-d{baec2~GnOiCykE8Righ>fN*?!cWeo$W!tBY3U! z;I?<#gT6WstAnoH>(Z-a&t0aI#$C?Mje^+SxJTn|XJR8BG5Nm9|2Oz|p?^<{yK8W- z#v14DCOl<2dZwdwS41`Lb-EtGCey(;9Vowo+NPojd9MY13$w>6)5#LA)bga89d);4 zsg$mkOCPW;pt&xRwIar5`bFqI= z*>_3XefkeMenv7CuMaj<_6l}ZmS^>G^Zr4*eS4($BJ`KHxwYI?+I_fR<34A38@8Do zlWjCijy0NXJ8dQJm$n`cXsjn3Bdgo+0v^{?HnwYRH9ss(vZS$*RIP}4 z0>-0wOuDcfggaXa@)^Eq6>RTSp57!I9>)_JkB4aq1(a_TwXdMes5@dZ7;CFWlfn}s z{-lA%lP%)p3sCJUs8v)-yYwfdW+(&ee1^@%W8vu&#npI9;VDt@G@j9Tnv~pF)3QDu z&*C|aXDQ<(nYE&E*vtl7G&Z}P`E)X#S9o4_ZpAi@tsyxnp9--ZFKBFc#LK4w?7)i} zJ3``7Cn{j}n1y(*LcZu+&ZkcNx6gl5SE+`<-14qDFYJzm|MK~-V0KyY!3x{N-b?(a z#!F#+q9SqQWxT?F@b4NglahC;td-zZyr%K0Q&B|4{F~3eiHee(*W6k$b>&}u{x!I( ztW)|G7i1}o!R!1B-q3j61v!95<4uJ(#pqjjTjMPkrQ8M01f1yeEx|#SCAK*nn=VOQmJTlu(Mxw&buw){^;|ML9}P!rwq5QSaUyF&of28wFhUw-wKWS8_SuGorObbI6mhu`3sHD z?F&JXUHJlEYJB1FIFyFrD}1f-mBZsO8pfY{{JG#!JvAv8jjx@}L#fSS{F%?61v~l` z5BoAak~EBt;7>jN^dFTT{H1l`3+u#(;W4IBbPOcKZ=9l0bTohB^CzOGu)X5%t@L-o zb^d|K=U=S)L2o$UdQus_h2ZxsVP69EqB8#2&1vwm21;5Ev%TfAmcV*&t@CU!`^E>vkmR9&f_Wa3jY5eJc5)Sg4KEJsKaTLF0(R$nF z?Dt*8=1Bad@Rum~8?5nnSZrkfa7byB(!HzJ9HGE(D5VwLNhsoSlD=f6U$n5lY)SgMjphK=%0W>;MQShAK`VKUTH<%1B7WZE=XVibb#OWh zs0(lLd5hJV;qx2vSz4}C3)MG!ym=SZl~|Nt9poQW#~ju1zo9w{!1?@~R{1W}5p<;L zq*W(pVFZojXFYy)H`SlB1pZa_bXEmgb#9^hGyF6^1%AeY@wDxp5=m7ctqVV?RhJgY zCwS!Jyb1h-m3rKEn=E)w%1;~iOM6Z(Wl$zRrV9B{enb_iV$~J=7*Vb&q}IHVALb4G z5I^YiM!B1J6tEtan|C*C2rBn0#zR5Xe&u-3o|(BkxVu=ja@minP{o|R>YpHbwK`s@!DnfnuUS7lZ zNF~3U4(GdgHQ&j1(CNHNzVgqdD`^?sO`B*deab88SNa_p@^e;4tmE7HHeSKY`Bq+r z(|9QlU&6QW&Agaz;zfL;$M-h7?DLxcaLM$=VonA5O^u1kwG9);*VHB~`|b{gv@7oD z^IewIS@DKxfpHn#eMD+rLo59NxryfV~E;Et7I{mN}T13hr7T22RjD! z6>x8}cj#^&uMipgt<2-)GD=k^Wpt~I?2iH-FAKgcDipw%{y)D2dA!8#lvlIQw@8BW zakKsYw~xn*W!>IOJif_F$d?C?7lotb6pwGTBe^c;@qKt{Ef4g7CxW(v293ESO5G;4 zqsZ_xsHkePj7qD&(jHjYHS(jN+l)CpFF24d1RrRj6ACt?ehivF64|_(b7{yf3 z@*qmRg7XG-9=dnm5mghy-DNV`6pniL9dR;coLMz`Ox2{Es?j;ws@O8=20X^s^L475 zD&=c^zCmu1z0=uzJ*ifFjq0Ak^4%_5br)vy)x6N>tN-I|(&vSHR3XLJS$cJ&HXQI( zd?jDOm-7Ojga2dyRsYzZY44prU)fwtuCTW_N#*7CMJ=BTY#BT-SCx{F@A)#ml;`t2 zzJxF4i}*sG%NKZjnP6_;svch|qsfgi$?<%@s!*Nz$+MZ*O$vK-3eYl8)YCwA;=0i1*1`FuW4^-w)|w$F3y(H))5=UV{N zs#gZ53E8TbJPT6~YQ^X9**uG9`h3nFGCrT(jKr*F1ZG+U_Rm#4 zDNA+a89bevcp6XT6em5FdwX;2(`DphZ?bD)?9*)QB~xYYB2I;)l*E~ok&AA2FmUMD F{{8jb$eq=$Uuo8G3E=?T-@ZtZ2eo}v!dNV^u-Xv022526@cA6%c_qQ(t`^%#(V0l}Q~s|v4^Jp(aFV<3eC%opjwD9{+}0+dit zXf83`H44cq5nFbq2tzcA299eef}Y4U^RY z7^pF#%Cip2bO_pKBwsX4)ZB;?jTPIYJuc4&Vw*!WdL&jBx;u$0XCn zH*I8J&#ny`XjC{zDli^naf`-S2gz8BH?1+wv{I&Z6(kM+iODz#=2ml|#;wlLvAEr| zRHkJZlM5{-tt=+DI7})qRwGn}NuDx!!SsgdmF*=ObCCtPMA~h*UE?-K&Y75LE-)?3 z`7jq)p%%6imUy^b+8riW;|_=8Ol-#gP-&W*W*Yx|77w*vPH?c^hgl}aH#q{&jpZf7H7Z>e&&CGa>ETYXI}wvKCOWidV+kf> zipFHOeKzKpY~N&4v??qutq7Hk8LBZQd=fSjF%@@dOm!zQAB#;B-y}7$Tpv;-M|PKU zbPnd5#>zCdTrcr3No?MYX&QGsn@jO1?!k18dz{UsSZ1<(lXc4Gu!7=pjpW3X`c!rZt)2VVal}xHM+E++T%NrV(Zdpr(<{{aL}( z%nmi~x49oAAPDwk7FXUUdmbJZ4R=j~_LIVeD6r!;p4ht|PI+&2xFOE8Spn~F& z<>7WGJuA6L+F~rxSWM_6vs>{jmTD|@j%>v9>pq+N5iKQ7Ku|PF4)(kPUR|DTy0`CR#z=* zWi-X)Qe=UYcImGQjq^;bbvE7tzcQR^qNWg!d3a0|JdQOQkCT^gb<-d`fwdY>P{cLz z6iEZI&ctB7#yYn#k*>uC4;y6T#@^VZu`$di^mI9@sctWq&2Nw`J9-2nT^KFF50%@%`147ng7yw zg*mG_SzSLM|Nf(_G|2O=yakk zIDms12OK(`sSE$+^KXJq;h1tUde9l|M4kCp#lQZIPVjJxT3xX}oR!pty7DhR|MHJY z5B|eC^SgEC)o>;yZ5Q=m$>4W1-f`*&Qvo0K`KXw+ zHx-Y65clwqw0H5I#=EC06MxU&@wedbEiT{L?prJQp0xMzfyVo;GR0B?KCHw?8Xul6 zP5cc$=C2ihQ*~AO=S}5fQSk|XrSXXaO?b*j6dyT*O&)(G2Kj4yfFJ!`jC$Zx51)#H z&+xg%XQzr$rEuyCe5vup-_@uqj_{Wd8%NwsB6Y;q`~|+z_}b~pqPF<_w9zRrt z_Nn4e!wLJnnEHu7(Gc3mb(5Nj2S4K%{+I>5pIyE*qx11Af5-yxuP#CD%x{qV`OT?p zM$P$s#qXarL4S8L%_*1PQ~ch)5X}ya1Ak}_@&gO$FOD5~bRquG_`_|K62$K+e%JAr zKeo~z*;DvL_{(3oocP8elSC;vjuRTk9ii({3cus?JEwEvgwvTs_4sYYZ(E%aioXme z!Bjzh=aB}U?mNC9`SzdQI+Jr9=wBE&gU1cs>&BA+P?G4hF4hk zp0^G}$zT`nRJ`jSYl?SLR4&O!nMhQVdbfkO^ERpc&(g*G3~%M9`6(K}Td15S(=1v+ zYiSqlrX#$Wey8JzkWcN}Sin#6Cf>*!cs;M4#ZZH8gDE9{5Tz7QdQLa_04K_CTxfqrXVbZ|m?4W91(b?ZN~&5YpoYP$c6D1f z%ITgzEWA5YM!UkJ^K!b&p9@Ch_wAQIDlWfoT&yP)jORzOk{{ufUUe^;9}bST&Pj=7 zxjjbmLtc#tmRCWnS3@YvEBHahE9@mGTk(VcswHw&jXrwYIlds~x2u-LwM<#$oAvqSB+(WG#g7xDt0&+~XL&*9np00(@(&kF@P z`-=2=fs96%7Rlkvw{vpnKF_lxXg^n;9rkl%6q3Ia_&hs23XSvm0U6oLgwKH;m6nQz z`(BPp2|~rGEd@( ud?!!vd8#<>;FuyK2ghVP7lvb!g(GdEJUbBX438`b6J+E9o)L8G*#G|(aA*Vo diff --git a/src/vs/workbench/contrib/externalTerminal/node/iTermHelper.scpt b/src/vs/workbench/contrib/externalTerminal/node/iTermHelper.scpt index 6ef0afa62045b05821a0012384db2c2840031dca..f4341af8c242f935cad9655d05e2b95606ec4440 100644 GIT binary patch delta 1077 zcmaKr%TpUi5XSpsajeX;fSq6gLVz$fSmq&@t77w#c^DfEwy;6QZ?Onj=v|Q%h)>En zx0I4&{sdQ2#W9uq0XZk9RQZ<5ImhJkrN>I;b;wqA@9gY1zv-Fo{$b|(*{?r$uzd1` z^ZLx01$gN6;0X#Ma53N9?)j;KUJpTW)};+046}ef<_!zdJ5_sCs~lvCe)*$fk^Drg zl}r0o4}F4?B0?Bo&S6f_#=Iz?SFN9i9{Qy%p@InJt;j>D^*x3;gfZwL+}dy&v+pv? zml!h4HuIrS1AHm+4u-^GMx!|}%t*W0$$VV*Ff6Dj^eR4J+GSc!$HYmO4pdKd9tOoJ zLNJC2M7U4iLqu>zSLh7)4EL0@o353?eVwFl*9^3n5sWhBGBqw8F$t72$EkXzsz+?& zp;dLZ&U*?0jC5fHhk@pm;97GrXii=Q2hcc=Q4i;Jdp&;~alYVlJ_BFaZJt~A%-+tZ z+67$na6z!CvvDk9%)?j<9>)znb@A6wib>vnB;Pt-{x zsQOEYdbspfFZ%EyliUFxT4~a{9ZQa?UFNoj%YQ2$@IJS|2Uc|7x-CoI7U@*BbajiP z*+}TEPJ)`%ZcM1=p5Z;M5kGTOuQZd+d%;dj%IR+2l|F8A15=ph9mBiLZ~MZHO$o%L z-LGbkEP2B+rgX5*HJ9uE_~&v>PQ?T{tC*G%-sUZ?N|ZMx#uYAeiHowq8{$h%8uFQZ zDPPGq@+0H&v-~2z%5QKm!UfKAjPZ8Y(vS~=_TR@3kAIkzfZF>#M8)k3bE zO&#Q_A}((>BPQJAa^-CaWXeaWBfnY`F5zw(MZ}&tzP}m z)d>D7N5h3?AQVo|>;0_PtEKYsars|8r2XV_?mwkwIQu_6XE^g`^%`E+k?jb>YuYnm z=MGj*J`EYHUXA8EB$TjzV0BNziGq#>6DbLwm2**Fh9=I6r|;pEykG2)NHUpB>k^lez delta 950 zcmZ8g-%}H36g`KP(p=%mwDQ9x-cS_BOW5fwFr5JLha(Onx zJ82*LCpdP7$NmRC*C%IubNbxJwhyOwrPFr0GxzTI-S6D<-Mjbfi_5=U`ssUz(+58| z&w6_=(r1rPAKV7$@j`p~_^XTB_L)FO#zFxvu#f#d`wsU>e!>9;J_q2;ql4%8u_`Ka z&{d`VDhOIs8F+_-aJ0oxC0ASMbXSUBHGPI)%{V8R)>BVUaWoC5u)Aq^l>nasoE?Hm zp5>5=+VnXDerOpMQNdNgm{tpoDpjbUhB$H`RJPU$mNWRNwbqY zGK+=FqaQ zZB38*Jo^?8d-Oo}wMspRp!XxU8ZnQWjp?4xv44?St!SCbMxd3*El14vfF9#UEDpz} zao1**qV585o-@oHS9j3&LB6=xthG-JyqD-<3PZa!P1yZ_!w61GT*Yr6S^cnJKpoQ=7 zBc9`Dyws<7gJ1C*ey77>&FZSIXhzeTa&;4(AGy1YYRS_);%Rj6m#dpK8*sU~V>P&qZL3J>uBPl?2%TG;IruYh zIXjW6bR)<_esFFbVJ3(07*3?}7#cqY-`>MHyuZ_pk!&`bsl+l%G2|PCItc%d*RP-b E1tVzfEC2ui diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index 4a013f414..7a6e14ddd 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -277,7 +277,7 @@ export class FeedbackDropdown extends Dropdown { this.sendButton = new Button(buttonsContainer); this.sendButton.enabled = false; this.sendButton.label = nls.localize('tweet', "Tweet"); - dom.prepend(this.sendButton.element, dom.$('span.codicon.codicon-twitter')); + dom.prepend(this.sendButton.element, dom.$('span' + Codicon.twitter.cssSelector)); this.sendButton.element.classList.add('send'); this.sendButton.element.title = nls.localize('tweetFeedback', "Tweet Feedback"); disposables.add(attachButtonStyler(this.sendButton, this.themeService)); @@ -288,7 +288,7 @@ export class FeedbackDropdown extends Dropdown { if (this.feedbackForm) { this.feedbackForm.style.backgroundColor = colors.editorWidgetBackground ? colors.editorWidgetBackground.toString() : ''; this.feedbackForm.style.color = colors.editorWidgetForeground ? colors.editorWidgetForeground.toString() : ''; - this.feedbackForm.style.boxShadow = colors.widgetShadow ? `0 0 8px ${colors.widgetShadow}` : ''; + this.feedbackForm.style.boxShadow = colors.widgetShadow ? `0 0 8px 2px ${colors.widgetShadow}` : ''; } if (this.feedbackDescriptionInput) { this.feedbackDescriptionInput.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : ''; diff --git a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts index 3bbf80a06..5f4f0b991 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts @@ -28,7 +28,7 @@ class TwitterFeedbackService implements IFeedbackDelegate { } submitFeedback(feedback: IFeedback, openerService: IOpenerService): void { - const queryString = `?${feedback.sentiment === 1 ? `hashtags=${this.combineHashTagsAsString()}&` : null}ref_src=twsrc%5Etfw&related=twitterapi%2Ctwitter&text=${encodeURIComponent(feedback.feedback)}&tw_p=tweetbutton&via=${TwitterFeedbackService.VIA_NAME}`; + const queryString = `?${feedback.sentiment === 1 ? `hashtags=${this.combineHashTagsAsString()}&` : ''}ref_src=twsrc%5Etfw&related=twitterapi%2Ctwitter&text=${encodeURIComponent(feedback.feedback)}&tw_p=tweetbutton&via=${TwitterFeedbackService.VIA_NAME}`; const url = TwitterFeedbackService.TWITTER_URL + queryString; openerService.open(URI.parse(url)); diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index ad397db18..ec837dc48 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -9,7 +9,7 @@ import { isFunction, assertIsDefined } from 'vs/base/common/types'; import { isValidBasename } from 'vs/base/common/extpath'; import { basename } from 'vs/base/common/resources'; import { Action } from 'vs/base/common/actions'; -import { VIEWLET_ID, TEXT_FILE_EDITOR_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; +import { VIEWLET_ID, TEXT_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; import { ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { EditorOptions, TextEditorOptions, IEditorInput, IEditorOpenContext } from 'vs/workbench/common/editor'; @@ -30,6 +30,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; /** * An implementation of editor for file system resources. @@ -60,12 +61,16 @@ export class TextFileEditor extends BaseTextEditor { // Move view state for moved files this._register(this.fileService.onDidRunOperation(e => this.onDidRunOperation(e))); + + // Listen to file system provider changes + this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onDidFileSystemProviderChange(e.scheme))); + this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onDidFileSystemProviderChange(e.scheme))); } private onDidFilesChange(e: FileChangesEvent): void { const deleted = e.getDeleted(); if (deleted?.length) { - this.clearTextEditorViewState(deleted.map(d => d.resource)); + this.clearTextEditorViewState(deleted.map(({ resource }) => resource)); } } @@ -75,6 +80,14 @@ export class TextFileEditor extends BaseTextEditor { } } + private onDidFileSystemProviderChange(scheme: string): void { + const control = this.getControl(); + const input = this.input; + if (control && input?.resource.scheme === scheme) { + control.updateOptions({ readOnly: input.isReadonly() }); + } + } + protected onWillCloseEditorInGroup(editor: IEditorInput): void { // React to editors closing to preserve or clear view state. This needs to happen diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index 023992aeb..6873c2f15 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -23,7 +23,7 @@ import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileE import { SAVE_FILE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands'; import { INotificationService, INotificationHandle, INotificationActions, Severity } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ExecuteCommandAction } from 'vs/platform/actions/common/actions'; import { IProductService } from 'vs/platform/product/common/productService'; import { Event } from 'vs/base/common/event'; @@ -32,7 +32,6 @@ import { isWindows } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { SaveReason } from 'vs/workbench/common/editor'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext'; export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution'; @@ -56,13 +55,9 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa @ITextModelService textModelService: ITextModelService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IStorageService private readonly storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, version: 1 }); - const provider = this._register(instantiationService.createInstance(TextFileContentProvider)); this._register(textModelService.registerTextModelContentProvider(CONFLICT_RESOLUTION_SCHEME, provider)); @@ -225,7 +220,7 @@ class DoNotShowResolveConflictLearnMoreAction extends Action { } async run(notification: IDisposable): Promise { - this.storageService.store(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true, StorageScope.GLOBAL); + this.storageService.store(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true, StorageScope.GLOBAL, StorageTarget.USER); // Hide notification notification.dispose(); @@ -239,13 +234,9 @@ class ResolveSaveConflictAction extends Action { @IEditorService private readonly editorService: IEditorService, @INotificationService private readonly notificationService: INotificationService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IProductService private readonly productService: IProductService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService + @IProductService private readonly productService: IProductService ) { super('workbench.files.action.resolveConflict', nls.localize('compareChanges', "Compare")); - - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, version: 1 }); } async run(): Promise { diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/browser/explorerService.ts similarity index 68% rename from src/vs/workbench/contrib/files/common/explorerService.ts rename to src/vs/workbench/contrib/files/browser/explorerService.ts index a5069cb03..a1a537578 100644 --- a/src/vs/workbench/contrib/files/common/explorerService.ts +++ b/src/vs/workbench/contrib/files/browser/explorerService.ts @@ -6,32 +6,29 @@ import { Event } from 'vs/base/common/event'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IExplorerService, IFilesConfiguration, SortOrder, IExplorerView } from 'vs/workbench/contrib/files/common/files'; +import { IFilesConfiguration, SortOrder } from 'vs/workbench/contrib/files/common/files'; import { ExplorerItem, ExplorerModel } from 'vs/workbench/contrib/files/common/explorerModel'; import { URI } from 'vs/base/common/uri'; -import { FileOperationEvent, FileOperation, IFileService, FileChangesEvent, FILES_EXCLUDE_CONFIG, FileChangeType, IResolveFileOptions } from 'vs/platform/files/common/files'; +import { FileOperationEvent, FileOperation, IFileService, FileChangesEvent, FileChangeType, IResolveFileOptions } from 'vs/platform/files/common/files'; import { dirname } from 'vs/base/common/resources'; -import { memoize } from 'vs/base/common/decorators'; -import { ResourceGlobMatcher } from 'vs/workbench/common/resources'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { IExpression } from 'vs/base/common/glob'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditableData } from 'vs/workbench/common/views'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IBulkEditService, ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; +import { UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; +import { IExplorerView, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { RunOnceScheduler } from 'vs/base/common/async'; -function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): IExpression { - const scope = root ? { resource: root } : undefined; - const configuration = scope ? configurationService.getValue(scope) : configurationService.getValue(); - - return configuration?.files?.exclude || Object.create(null); -} +export const UNDO_REDO_SOURCE = new UndoRedoSource(); export class ExplorerService implements IExplorerService { declare readonly _serviceBrand: undefined; - private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first + private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 1000; // delay in ms to react to file changes to give our internal events a chance to react first private readonly disposables = new DisposableStore(); private editable: { stat: ExplorerItem, data: IEditableData } | undefined; @@ -39,22 +36,54 @@ export class ExplorerService implements IExplorerService { private cutItems: ExplorerItem[] | undefined; private view: IExplorerView | undefined; private model: ExplorerModel; + private onFileChangesScheduler: RunOnceScheduler; + private fileChangeEvents: FileChangesEvent[] = []; constructor( @IFileService private fileService: IFileService, - @IInstantiationService private instantiationService: IInstantiationService, @IConfigurationService private configurationService: IConfigurationService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @IClipboardService private clipboardService: IClipboardService, @IEditorService private editorService: IEditorService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @IBulkEditService private readonly bulkEditService: IBulkEditService, + @IProgressService private readonly progressService: IProgressService ) { this._sortOrder = this.configurationService.getValue('explorer.sortOrder'); this.model = new ExplorerModel(this.contextService, this.uriIdentityService, this.fileService); this.disposables.add(this.model); this.disposables.add(this.fileService.onDidRunOperation(e => this.onDidRunOperation(e))); - this.disposables.add(this.fileService.onDidFilesChange(e => this.onDidFilesChange(e))); + + this.onFileChangesScheduler = new RunOnceScheduler(async () => { + const events = this.fileChangeEvents; + this.fileChangeEvents = []; + + // Filter to the ones we care + const types = [FileChangeType.ADDED, FileChangeType.DELETED]; + if (this._sortOrder === SortOrder.Modified) { + types.push(FileChangeType.UPDATED); + } + + let shouldRefresh = false; + this.roots.forEach(r => { + if (this.view && !shouldRefresh) { + shouldRefresh = doesFileEventAffect(r, this.view, events, types); + } + }); + + if (shouldRefresh) { + await this.refresh(false); + } + + }, ExplorerService.EXPLORER_FILE_CHANGES_REACT_DELAY); + + this.disposables.add(this.fileService.onDidFilesChange(e => { + this.fileChangeEvents.push(e); + if (!this.onFileChangesScheduler.isScheduled()) { + this.onFileChangesScheduler.schedule(); + } + })); this.disposables.add(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue()))); this.disposables.add(Event.any<{ scheme: string }>(this.fileService.onDidChangeFileSystemProviderRegistrations, this.fileService.onDidChangeFileSystemProviderCapabilities)(async e => { let affected = false; @@ -96,16 +125,27 @@ export class ExplorerService implements IExplorerService { return this.view.getContext(respectMultiSelection); } - // Memoized locals - @memoize private get fileEventsFilter(): ResourceGlobMatcher { - const fileEventsFilter = this.instantiationService.createInstance( - ResourceGlobMatcher, - (root?: URI) => getFileEventsExcludes(this.configurationService, root), - (event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) - ); - this.disposables.add(fileEventsFilter); + async applyBulkEdit(edit: ResourceFileEdit[], options: { undoLabel: string, progressLabel: string }): Promise { + const cancellationTokenSource = new CancellationTokenSource(); + const promise = this.progressService.withProgress({ + location: ProgressLocation.Window, + delay: 500, + title: options.progressLabel, + cancellable: edit.length > 1 // Only allow cancellation when there is more than one edit. Since cancelling will not actually stop the current edit that is in progress. + }, async progress => { + await this.bulkEditService.apply(edit, { + undoRedoSource: UNDO_REDO_SOURCE, + label: options.undoLabel, + progress, + token: cancellationTokenSource.token + }); + }, () => cancellationTokenSource.cancel()); + await this.progressService.withProgress({ location: ProgressLocation.Explorer, delay: 500 }, () => promise); + cancellationTokenSource.dispose(); + } - return fileEventsFilter; + hasViewFocus(): boolean { + return !!this.view && this.view.hasFocus(); } // IExplorerService methods @@ -288,100 +328,6 @@ export class ExplorerService implements IExplorerService { } } - private onDidFilesChange(e: FileChangesEvent): void { - // Check if an explorer refresh is necessary (delayed to give internal events a chance to react first) - // Note: there is no guarantee when the internal events are fired vs real ones. Code has to deal with the fact that one might - // be fired first over the other or not at all. - setTimeout(async () => { - // Filter to the ones we care - const shouldRefresh = () => { - e = this.filterToViewRelevantEvents(e); - // Handle added files/folders - const added = e.getAdded(); - if (added.length) { - - // Check added: Refresh if added file/folder is not part of resolved root and parent is part of it - const ignoredPaths: Set = new Set(); - for (let i = 0; i < added.length; i++) { - const change = added[i]; - - // Find parent - const parent = dirname(change.resource); - - // Continue if parent was already determined as to be ignored - if (ignoredPaths.has(parent.toString())) { - continue; - } - - // Compute if parent is visible and added file not yet part of it - const parentStat = this.model.findClosest(parent); - if (parentStat && parentStat.isDirectoryResolved && !this.model.findClosest(change.resource)) { - return true; - } - - // Keep track of path that can be ignored for faster lookup - if (!parentStat || !parentStat.isDirectoryResolved) { - ignoredPaths.add(parent.toString()); - } - } - } - - // Handle deleted files/folders - const deleted = e.getDeleted(); - if (deleted.length) { - - // Check deleted: Refresh if deleted file/folder part of resolved root - for (let j = 0; j < deleted.length; j++) { - const del = deleted[j]; - const item = this.model.findClosest(del.resource); - if (item && item.parent) { - return true; - } - } - } - - // Handle updated files/folders if we sort by modified - if (this._sortOrder === SortOrder.Modified) { - const updated = e.getUpdated(); - - // Check updated: Refresh if updated file/folder part of resolved root - for (let j = 0; j < updated.length; j++) { - const upd = updated[j]; - const item = this.model.findClosest(upd.resource); - - if (item && item.parent) { - return true; - } - } - } - - return false; - }; - - if (shouldRefresh()) { - await this.refresh(false); - } - }, ExplorerService.EXPLORER_FILE_CHANGES_REACT_DELAY); - } - - private filterToViewRelevantEvents(e: FileChangesEvent): FileChangesEvent { - return e.filter(change => { - if (change.type === FileChangeType.UPDATED && this._sortOrder !== SortOrder.Modified) { - return false; // we only are about updated if we sort by modified time - } - - if (!this.contextService.isInsideWorkspace(change.resource)) { - return false; // exclude changes for resources outside of workspace - } - - if (this.fileEventsFilter.matches(change.resource)) { - return false; // excluded via files.exclude setting - } - - return true; - }); - } - private async onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): Promise { const configSortOrder = configuration?.explorer?.sortOrder || 'default'; if (this._sortOrder !== configSortOrder) { @@ -397,3 +343,20 @@ export class ExplorerService implements IExplorerService { this.disposables.dispose(); } } + +function doesFileEventAffect(item: ExplorerItem, view: IExplorerView, events: FileChangesEvent[], types: FileChangeType[]): boolean { + if (events.some(e => e.affects(item.resource, ...types))) { + return true; + } + for (let [_name, child] of item.children) { + if (view.isItemVisible(child)) { + if (child.isDirectory && child.isDirectoryResolved) { + if (doesFileEventAffect(child, view, events, types)) { + return true; + } + } + } + } + + return false; +} diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 54912b106..9745ec52d 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -38,6 +38,9 @@ import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { AddRootFolderAction, OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { isMacintosh } from 'vs/base/common/platform'; import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +const explorerViewIcon = registerIcon('explorer-view-icon', Codicon.files, localize('explorerViewIcon', 'View icon of the explorer view.')); export class ExplorerViewletViewsContribution extends Disposable implements IWorkbenchContribution { @@ -108,7 +111,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor id: OpenEditorsView.ID, name: OpenEditorsView.NAME, ctorDescriptor: new SyncDescriptor(OpenEditorsView), - containerIcon: 'codicon-files', + containerIcon: explorerViewIcon, order: 0, when: OpenEditorsVisibleContext, canToggleVisibility: true, @@ -125,7 +128,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: EmptyView.ID, name: EmptyView.NAME, - containerIcon: Codicon.files.classNames, + containerIcon: explorerViewIcon, ctorDescriptor: new SyncDescriptor(EmptyView), order: 1, canToggleVisibility: true, @@ -139,7 +142,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: VIEW_ID, name: localize('folders', "Folders"), - containerIcon: Codicon.files.classNames, + containerIcon: explorerViewIcon, ctorDescriptor: new SyncDescriptor(ExplorerView), order: 1, canToggleVisibility: false, @@ -267,7 +270,7 @@ export const VIEW_CONTAINER: ViewContainer = viewContainerRegistry.registerViewC name: localize('explore', "Explorer"), ctorDescriptor: new SyncDescriptor(ExplorerViewPaneContainer), storageId: 'workbench.explorer.views.state', - icon: Codicon.files.classNames, + icon: explorerViewIcon, alwaysUseContainerInfo: true, order: 0 }, ViewContainerLocation.Sidebar, true); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index fd399160c..80deb8269 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -15,7 +15,7 @@ import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/c import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { isMacintosh } from 'vs/base/common/platform'; -import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files'; +import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files'; import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands'; import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; @@ -29,6 +29,8 @@ import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAc import { ActiveEditorContext } from 'vs/workbench/common/editor'; import { SidebarFocusContext } from 'vs/workbench/common/viewlet'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; +import { Codicon } from 'vs/base/common/codicons'; // Contribute Global Actions const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; @@ -182,8 +184,8 @@ export function appendEditorTitleContextMenuItem(id: string, title: string, when } // Editor Title Menu for Conflict Resolution -appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), { id: 'codicon/check' }, -10, acceptLocalChangesCommand); -appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), { id: 'codicon/discard' }, -9, revertLocalChangesCommand); +appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), Codicon.check, -10, acceptLocalChangesCommand); +appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), Codicon.discard, -9, revertLocalChangesCommand); function appendSaveConflictEditorTitleAction(id: string, title: string, icon: ThemeIcon, order: number, command: ICommandHandler): void { diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 4f042f28a..46aacd31f 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -12,9 +12,8 @@ import { URI } from 'vs/base/common/uri'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Action } from 'vs/base/common/actions'; import { DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { VIEWLET_ID, IExplorerService, IFilesConfiguration, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { BinarySize, IFileService, IFileStatWithMetadata, IFileStreamContent } from 'vs/platform/files/common/files'; +import { VIEWLET_ID, IFilesConfiguration, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; +import { ByteSize, IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IQuickInputService, ItemActivation } from 'vs/platform/quickinput/common/quickInput'; @@ -22,9 +21,9 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ITextModel } from 'vs/editor/common/model'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { REVEAL_IN_EXPLORER_COMMAND_ID, SAVE_ALL_COMMAND_ID, SAVE_ALL_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileCommands'; +import { REVEAL_IN_EXPLORER_COMMAND_ID, SAVE_ALL_COMMAND_ID, SAVE_ALL_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileCommands'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -53,26 +52,21 @@ import { IProgressService, IProgressStep, ProgressLocation } from 'vs/platform/p import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); - export const NEW_FOLDER_COMMAND_ID = 'explorer.newFolder'; export const NEW_FOLDER_LABEL = nls.localize('newFolder', "New Folder"); - export const TRIGGER_RENAME_LABEL = nls.localize('rename', "Rename"); - export const MOVE_FILE_TO_TRASH_LABEL = nls.localize('delete', "Delete"); - export const COPY_FILE_LABEL = nls.localize('copyFile', "Copy"); - export const PASTE_FILE_LABEL = nls.localize('pasteFile', "Paste"); - export const FileCopiedContext = new RawContextKey('fileCopied', false); - export const DOWNLOAD_LABEL = nls.localize('download', "Download..."); - const CONFIRM_DELETE_SETTING_KEY = 'explorer.confirmDelete'; +const MAX_UNDO_FILE_SIZE = 5000000; // 5mb function onError(notificationService: INotificationService, error: any): void { if (error.message === 'string') { @@ -123,7 +117,7 @@ export class NewFolderAction extends Action { } } -async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise { +async function deleteFiles(explorerService: IExplorerService, workingCopyFileService: IWorkingCopyFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise { let primaryButton: string; if (useTrash) { primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash"); @@ -216,7 +210,7 @@ async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dial // Check for confirmation checkbox if (confirmation.confirmed && confirmation.checkboxChecked === true) { - await configurationService.updateValue(CONFIRM_DELETE_SETTING_KEY, false, ConfigurationTarget.USER); + await configurationService.updateValue(CONFIRM_DELETE_SETTING_KEY, false); } // Check for confirmation @@ -226,7 +220,12 @@ async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dial // Call function try { - await workingCopyFileService.delete(distinctElements.map(e => e.resource), { useTrash, recursive: true }); + const resourceFileEdits = distinctElements.map(e => new ResourceFileEdit(e.resource, undefined, { recursive: true, folder: e.isDirectory, skipTrashBin: !useTrash, maxSize: MAX_UNDO_FILE_SIZE })); + const options = { + undoLabel: distinctElements.length > 1 ? nls.localize('deleteBulkEdit', "Delete {0} files", distinctElements.length) : nls.localize('deleteFileBulkEdit', "Delete {0}", distinctElements[0].name), + progressLabel: distinctElements.length > 1 ? nls.localize('deletingBulkEdit', "Deleting {0} files", distinctElements.length) : nls.localize('deletingFileBulkEdit', "Deleting {0}", distinctElements[0].name), + }; + await explorerService.applyBulkEdit(resourceFileEdits, options); } catch (error) { // Handle error to delete file(s) from a modal confirmation dialog @@ -256,7 +255,7 @@ async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dial skipConfirm = true; - return deleteFiles(workingCopyFileService, dialogService, configurationService, elements, useTrash, skipConfirm); + return deleteFiles(explorerService, workingCopyFileService, dialogService, configurationService, elements, useTrash, skipConfirm); } } } @@ -472,7 +471,7 @@ export class GlobalCompareResourcesAction extends Action { override: this.editorService.openEditor({ leftResource: activeResource, rightResource: resource, - options: { override: false } + options: { override: false, pinned: true } }) }; } @@ -482,7 +481,7 @@ export class GlobalCompareResourcesAction extends Action { return { override: this.editorService.openEditor({ resource: activeResource, - options: { override: false } + options: { override: false, pinned: true } }) }; } @@ -828,7 +827,11 @@ export class CompareWithClipboardAction extends Action { const name = resources.basename(resource); const editorLabel = nls.localize('clipboardComparisonLabel', "Clipboard ↔ {0}", name); - await this.editorService.openEditor({ leftResource: resource.with({ scheme }), rightResource: resource, label: editorLabel }).finally(() => { + await this.editorService.openEditor({ + leftResource: resource.with({ scheme }), + rightResource: resource, label: editorLabel, + options: { pinned: true } + }).finally(() => { dispose(this.registrationDisposal); this.registrationDisposal = undefined; }); @@ -870,13 +873,26 @@ function onErrorWithRetry(notificationService: INotificationService, error: unkn async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boolean): Promise { const explorerService = accessor.get(IExplorerService); const fileService = accessor.get(IFileService); - const textFileService = accessor.get(ITextFileService); const editorService = accessor.get(IEditorService); const viewsService = accessor.get(IViewsService); const notificationService = accessor.get(INotificationService); - const workingCopyFileService = accessor.get(IWorkingCopyFileService); + const commandService = accessor.get(ICommandService); - await viewsService.openView(VIEW_ID, true); + const wasHidden = !viewsService.isViewVisible(VIEW_ID); + const view = await viewsService.openView(VIEW_ID, true); + if (wasHidden) { + // Give explorer some time to resolve itself #111218 + await timeout(500); + } + if (!view) { + // Can happen in empty workspace case (https://github.com/microsoft/vscode/issues/100604) + + if (isFolder) { + throw new Error('Open a folder or workspace first.'); + } + + return commandService.executeCommand(NEW_UNTITLED_FILE_COMMAND_ID); + } const stats = explorerService.getContext(false); const stat = stats.length > 0 ? stats[0] : undefined; @@ -896,12 +912,18 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole const onSuccess = async (value: string): Promise => { try { - const created = isFolder ? await workingCopyFileService.createFolder(resources.joinPath(folder.resource, value)) : await textFileService.create(resources.joinPath(folder.resource, value)); + const resourceToCreate = resources.joinPath(folder.resource, value); + await explorerService.applyBulkEdit([new ResourceFileEdit(undefined, resourceToCreate, { folder: isFolder })], { + undoLabel: nls.localize('createBulkEdit', "Create {0}", value), + progressLabel: nls.localize('creatingBulkEdit', "Creating {0}", value) + }); await refreshIfSeparator(value, explorerService); - isFolder ? - await explorerService.select(created.resource, true) : - await editorService.openEditor({ resource: created.resource, options: { pinned: true } }); + if (isFolder) { + await explorerService.select(resourceToCreate, true); + } else { + await editorService.openEditor({ resource: resourceToCreate, options: { pinned: true } }); + } } catch (error) { onErrorWithRetry(notificationService, error, () => onSuccess(value)); } @@ -935,7 +957,6 @@ CommandsRegistry.registerCommand({ export const renameHandler = async (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); - const workingCopyFileService = accessor.get(IWorkingCopyFileService); const notificationService = accessor.get(INotificationService); const stats = explorerService.getContext(false); @@ -952,7 +973,10 @@ export const renameHandler = async (accessor: ServicesAccessor) => { const targetResource = resources.joinPath(parentResource, value); if (stat.resource.toString() !== targetResource.toString()) { try { - await workingCopyFileService.move([{ source: stat.resource, target: targetResource }]); + await explorerService.applyBulkEdit([new ResourceFileEdit(stat.resource, targetResource)], { + undoLabel: nls.localize('renameBulkEdit', "Rename {0} to {1}", stat.name, value), + progressLabel: nls.localize('renamingBulkEdit', "Renaming {0} to {1}", stat.name, value), + }); await refreshIfSeparator(value, explorerService); } catch (e) { notificationService.error(e); @@ -968,7 +992,7 @@ export const moveFileToTrashHandler = async (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); const stats = explorerService.getContext(true).filter(s => !s.isRoot); if (stats.length) { - await deleteFiles(accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true); + await deleteFiles(accessor.get(IExplorerService), accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true); } }; @@ -977,7 +1001,7 @@ export const deleteFileHandler = async (accessor: ServicesAccessor) => { const stats = explorerService.getContext(true).filter(s => !s.isRoot); if (stats.length) { - await deleteFiles(accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false); + await deleteFiles(accessor.get(IExplorerService), accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false); } }; @@ -1001,10 +1025,9 @@ export const cutFileHandler = async (accessor: ServicesAccessor) => { }; export const DOWNLOAD_COMMAND_ID = 'explorer.download'; -const downloadFileHandler = (accessor: ServicesAccessor) => { +const downloadFileHandler = async (accessor: ServicesAccessor) => { const logService = accessor.get(ILogService); const fileService = accessor.get(IFileService); - const workingCopyFileService = accessor.get(IWorkingCopyFileService); const fileDialogService = accessor.get(IFileDialogService); const explorerService = accessor.get(IExplorerService); const progressService = accessor.get(IProgressService); @@ -1014,7 +1037,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { const cts = new CancellationTokenSource(); - const downloadPromise = progressService.withProgress({ + await progressService.withProgress({ location: ProgressLocation.Window, delay: 800, cancellable: isWeb, @@ -1034,7 +1057,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { return; } - const maxBlobDownloadSize = 32 * BinarySize.MB; // avoid to download via blob-trick >32MB to avoid memory pressure + const maxBlobDownloadSize = 32 * ByteSize.MB; // avoid to download via blob-trick >32MB to avoid memory pressure const preferFileSystemAccessWebApis = stat.isDirectory || stat.size > maxBlobDownloadSize; // Folder: use FS APIs to download files and folders if available and preferred @@ -1051,9 +1074,15 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { fileBytesDownloaded: number; } - async function pipeContents(name: string, source: IFileStreamContent, target: WebFileSystemAccess.FileSystemWritableFileStream, operation: IDownloadOperation): Promise { + async function downloadFileBuffered(resource: URI, target: WebFileSystemAccess.FileSystemWritableFileStream, operation: IDownloadOperation): Promise { + const contents = await fileService.readFileStream(resource); + if (cts.token.isCancellationRequested) { + target.close(); + return; + } + return new Promise((resolve, reject) => { - const sourceStream = source.value; + const sourceStream = contents.value; const disposables = new DisposableStore(); disposables.add(toDisposable(() => target.close())); @@ -1069,7 +1098,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { sourceStream.on('data', data => { if (!disposed) { target.write(data.buffer); - reportProgress(name, source.size, data.byteLength, operation); + reportProgress(contents.name, contents.size, data.byteLength, operation); } }); @@ -1085,18 +1114,34 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { }); } - async function downloadFile(targetFolder: WebFileSystemAccess.FileSystemDirectoryHandle, name: string, resource: URI, operation: IDownloadOperation): Promise { + async function downloadFileUnbuffered(resource: URI, target: WebFileSystemAccess.FileSystemWritableFileStream, operation: IDownloadOperation): Promise { + const contents = await fileService.readFile(resource); + if (!cts.token.isCancellationRequested) { + target.write(contents.value.buffer); + reportProgress(contents.name, contents.size, contents.value.byteLength, operation); + } + + target.close(); + } + + async function downloadFile(targetFolder: WebFileSystemAccess.FileSystemDirectoryHandle, file: IFileStatWithMetadata, operation: IDownloadOperation): Promise { // Report progress operation.filesDownloaded++; operation.fileBytesDownloaded = 0; // reset for this file - reportProgress(name, 0, 0, operation); + reportProgress(file.name, 0, 0, operation); // Start to download - const targetFile = await targetFolder.getFileHandle(name, { create: true }); + const targetFile = await targetFolder.getFileHandle(file.name, { create: true }); const targetFileWriter = await targetFile.createWritable(); - return pipeContents(name, await fileService.readFileStream(resource), targetFileWriter, operation); + // For large files, write buffered using streams + if (file.size > ByteSize.MB) { + return downloadFileBuffered(file.resource, targetFileWriter, operation); + } + + // For small files prefer to write unbuffered to reduce overhead + return downloadFileUnbuffered(file.resource, targetFileWriter, operation); } async function downloadFolder(folder: IFileStatWithMetadata, targetFolder: WebFileSystemAccess.FileSystemDirectoryHandle, operation: IDownloadOperation): Promise { @@ -1109,7 +1154,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { } if (child.isFile) { - await downloadFile(targetFolder, child.name, child.resource, operation); + await downloadFile(targetFolder, child, operation); } else { const childFolder = await targetFolder.getDirectoryHandle(child.name, { create: true }); const resolvedChildFolder = await fileService.resolve(child.resource, { resolveMetadata: true }); @@ -1128,17 +1173,17 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { // Small file let message: string; - if (fileSize < BinarySize.MB) { + if (fileSize < ByteSize.MB) { if (operation.filesTotal === 1) { message = name; } else { - message = nls.localize('downloadProgressSmallMany', "{0} of {1} files ({2}/s)", operation.filesDownloaded, operation.filesTotal, BinarySize.formatSize(bytesDownloadedPerSecond)); + message = nls.localize('downloadProgressSmallMany', "{0} of {1} files ({2}/s)", operation.filesDownloaded, operation.filesTotal, ByteSize.formatSize(bytesDownloadedPerSecond)); } } // Large file else { - message = nls.localize('downloadProgressLarge', "{0} ({1} of {2}, {3}/s)", name, BinarySize.formatSize(operation.fileBytesDownloaded), BinarySize.formatSize(fileSize), BinarySize.formatSize(bytesDownloadedPerSecond)); + message = nls.localize('downloadProgressLarge', "{0} ({1} of {2}, {3}/s)", name, ByteSize.formatSize(operation.fileBytesDownloaded), ByteSize.formatSize(fileSize), ByteSize.formatSize(bytesDownloadedPerSecond)); } // Report progress but limit to update only once per second @@ -1162,7 +1207,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { const targetFolder = await parentFolder.getDirectoryHandle(stat.name, { create: true }); await downloadFolder(stat, targetFolder, operation); } else { - await downloadFile(parentFolder, stat.name, stat.resource, operation); + await downloadFile(parentFolder, stat, operation); } operation.progressScheduler.dispose(); @@ -1191,10 +1236,8 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { else { progress.report({ message: explorerItem.name }); - let defaultUri = explorerItem.isDirectory ? fileDialogService.defaultFolderPath(Schemas.file) : fileDialogService.defaultFilePath(Schemas.file); - if (defaultUri) { - defaultUri = resources.joinPath(defaultUri, explorerItem.name); - } + let defaultUri = explorerItem.isDirectory ? await fileDialogService.defaultFolderPath(Schemas.file) : await fileDialogService.defaultFilePath(Schemas.file); + defaultUri = resources.joinPath(defaultUri, explorerItem.name); const destination = await fileDialogService.showSaveDialog({ availableFileSystems: [Schemas.file], @@ -1204,16 +1247,16 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { }); if (destination) { - await workingCopyFileService.copy([{ source: explorerItem.resource, target: destination }], { overwrite: true }); + await explorerService.applyBulkEdit([new ResourceFileEdit(explorerItem.resource, destination, { overwrite: true, copy: true })], { + undoLabel: nls.localize('downloadBulkEdit', "Download {0}", explorerItem.name), + progressLabel: nls.localize('downloadingBulkEdit', "Downloading {0}", explorerItem.name), + }); } else { cts.cancel(); // User canceled a download. In case there were multiple files selected we should cancel the remainder of the prompts #86100 } } })); }, () => cts.dispose(true)); - - // Also indicate progress in the files view - progressService.withProgress({ location: VIEW_ID, delay: 800 }, () => downloadPromise); }; CommandsRegistry.registerCommand({ @@ -1225,7 +1268,6 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => { const clipboardService = accessor.get(IClipboardService); const explorerService = accessor.get(IExplorerService); const fileService = accessor.get(IFileService); - const workingCopyFileService = accessor.get(IWorkingCopyFileService); const notificationService = accessor.get(INotificationService); const editorService = accessor.get(IEditorService); const configurationService = accessor.get(IConfigurationService); @@ -1258,21 +1300,31 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => { return { source: fileToPaste, target: targetFile }; })); - // Move/Copy File - let stats: IFileStatWithMetadata[] = []; - if (pasteShouldMove) { - stats = await workingCopyFileService.move(sourceTargetPairs); - } else { - stats = await workingCopyFileService.copy(sourceTargetPairs); - } - - if (stats.length >= 1) { - const stat = stats[0]; - if (stat && !stat.isDirectory && stats.length === 1) { - await editorService.openEditor({ resource: stat.resource, options: { pinned: true, preserveFocus: true } }); + if (sourceTargetPairs.length >= 1) { + // Move/Copy File + if (pasteShouldMove) { + const resourceFileEdits = sourceTargetPairs.map(pair => new ResourceFileEdit(pair.source, pair.target)); + const options = { + progressLabel: sourceTargetPairs.length > 1 ? nls.localize('movingBulkEdit', "Moving {0} files", sourceTargetPairs.length) : nls.localize('movingFileBulkEdit', "Moving {0}", resources.basenameOrAuthority(sourceTargetPairs[0].target)), + undoLabel: sourceTargetPairs.length > 1 ? nls.localize('moveBulkEdit', "Move {0} files", sourceTargetPairs.length) : nls.localize('moveFileBulkEdit', "Move {0}", resources.basenameOrAuthority(sourceTargetPairs[0].target)) + }; + await explorerService.applyBulkEdit(resourceFileEdits, options); + } else { + const resourceFileEdits = sourceTargetPairs.map(pair => new ResourceFileEdit(pair.source, pair.target, { copy: true })); + const options = { + progressLabel: sourceTargetPairs.length > 1 ? nls.localize('copyingBulkEdit', "Copying {0} files", sourceTargetPairs.length) : nls.localize('copyingFileBulkEdit', "Copying {0}", resources.basenameOrAuthority(sourceTargetPairs[0].target)), + undoLabel: sourceTargetPairs.length > 1 ? nls.localize('copyBulkEdit', "Copy {0} files", sourceTargetPairs.length) : nls.localize('copyFileBulkEdit', "Copy {0}", resources.basenameOrAuthority(sourceTargetPairs[0].target)) + }; + await explorerService.applyBulkEdit(resourceFileEdits, options); } - if (stat) { - await explorerService.select(stat.resource); + + const pair = sourceTargetPairs[0]; + await explorerService.select(pair.target); + if (sourceTargetPairs.length === 1) { + const item = explorerService.findClosest(pair.target); + if (item && !item.isDirectory) { + await editorService.openEditor({ resource: item.resource, options: { pinned: true, preserveFocus: true } }); + } } } } catch (e) { diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index f8fc0ee1d..8e1badc5f 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -11,7 +11,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ExplorerFocusCondition, TextFileContentProvider, VIEWLET_ID, IExplorerService, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, FilesExplorerFocusCondition, ExplorerFolderContext } from 'vs/workbench/contrib/files/common/files'; +import { ExplorerFocusCondition, TextFileContentProvider, VIEWLET_ID, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, FilesExplorerFocusCondition, ExplorerFolderContext } from 'vs/workbench/contrib/files/common/files'; import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -23,7 +23,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { isWindows } from 'vs/base/common/platform'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { getResourceForCommand, getMultiSelectedResources, getOpenEditorsViewMultiSelection } from 'vs/workbench/contrib/files/browser/files'; +import { getResourceForCommand, getMultiSelectedResources, getOpenEditorsViewMultiSelection, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { getMultiSelectedEditorContexts } from 'vs/workbench/browser/parts/editor/editorCommands'; import { Schemas } from 'vs/base/common/network'; @@ -44,6 +44,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; // Commands @@ -134,10 +135,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const untitledResources = resources.filter(resource => resource.scheme === Schemas.untitled); const fileResources = resources.filter(resource => resource.scheme !== Schemas.untitled); - const resolved = await fileService.resolveAll(fileResources.map(resource => ({ resource }))); - const editors = resolved.filter(r => r.stat && r.success && !r.stat.isDirectory).map(r => ({ - resource: r.stat!.resource - })).concat(...untitledResources.map(untitledResource => ({ resource: untitledResource }))); + const items = await Promise.all(fileResources.map(async resource => { + const item = explorerService.findClosest(resource); + if (item) { + // Explorer already resolved the item, no need to go to the file service #109780 + return item; + } + + return await fileService.resolve(resource); + })); + const files = items.filter(i => !i.isDirectory); + const editors = files.map(f => ({ + resource: f.resource, + options: { pinned: true } + })).concat(...untitledResources.map(untitledResource => ({ resource: untitledResource, options: { pinned: true } }))); await editorService.openEditors(editors, SIDE_GROUP); } @@ -146,7 +157,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingWeight.WorkbenchContrib + 10, - when: ContextKeyExpr.and(ExplorerFocusCondition, ExplorerFolderContext.toNegated()), + when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerFolderContext.toNegated()), primary: KeyCode.Enter, mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow @@ -157,7 +168,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const resources = explorerService.getContext(true); if (resources.length) { - await editorService.openEditors(resources.map(r => ({ resource: r.resource, options: { preserveFocus: false } }))); + await editorService.openEditors(resources.map(r => ({ resource: r.resource, options: { preserveFocus: false, pinned: true } }))); } } }); @@ -192,7 +203,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const editorLabel = nls.localize('modifiedLabel', "{0} (in file) ↔ {1}", name, name); try { - await TextFileContentProvider.open(uri, COMPARE_WITH_SAVED_SCHEMA, editorLabel, editorService); + await TextFileContentProvider.open(uri, COMPARE_WITH_SAVED_SCHEMA, editorLabel, editorService, { pinned: true }); // Dispose once no more diff editor is opened with the scheme if (registerEditorListener) { providerDisposables.push(editorService.onDidVisibleEditorsChange(() => { @@ -233,7 +244,8 @@ CommandsRegistry.registerCommand({ if (resources.length === 2) { return editorService.openEditor({ leftResource: resources[0], - rightResource: resources[1] + rightResource: resources[1], + options: { pinned: true } }); } @@ -251,7 +263,8 @@ CommandsRegistry.registerCommand({ if (globalResourceToCompare && rightResource) { editorService.openEditor({ leftResource: globalResourceToCompare, - rightResource + rightResource, + options: { pinned: true } }); } } @@ -432,7 +445,9 @@ async function doSaveEditors(accessor: ServicesAccessor, editors: IEditorIdentif try { await editorService.save(editors, options); } catch (error) { - notificationService.error(nls.localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); + if (!isPromiseCanceledError(error)) { + notificationService.error(nls.localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); + } } } diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 160acf7b8..7ea77d336 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -14,12 +14,12 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES } import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IEditorInputFactory, EditorInput, IFileEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor'; import { AutoSaveConfiguration, HotExitConfiguration, FILES_EXCLUDE_CONFIG, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; -import { VIEWLET_ID, SortOrder, FILE_EDITOR_INPUT_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; +import { VIEWLET_ID, SortOrder, FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files'; import { TextFileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/textFileEditorTracker'; import { TextFileSaveErrorHandler } from 'vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { BinaryFileEditor } from 'vs/workbench/contrib/files/browser/editors/binaryFileEditor'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -33,13 +33,16 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ExplorerService } from 'vs/workbench/contrib/files/common/explorerService'; +import { ExplorerService, UNDO_REDO_SOURCE } from 'vs/workbench/contrib/files/browser/explorerService'; import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/encoding'; import { Schemas } from 'vs/base/common/network'; import { WorkspaceWatcher } from 'vs/workbench/contrib/files/common/workspaceWatcher'; import { editorConfigurationBaseNode } from 'vs/editor/common/config/commonEditorConfig'; import { DirtyFilesIndicator } from 'vs/workbench/contrib/files/common/dirtyFilesIndicator'; import { isEqual } from 'vs/base/common/resources'; +import { UndoCommand, RedoCommand } from 'vs/editor/browser/editorExtensions'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; // Viewlet Action export class OpenExplorerViewletAction extends ShowViewletAction { @@ -102,8 +105,9 @@ Registry.as(EditorExtensions.Editors).registerEditor( // Register default file input factory Registry.as(EditorInputExtensions.EditorInputFactories).registerFileEditorInputFactory({ - createFileEditorInput: (resource, preferredResource, encoding, mode, instantiationService): IFileEditorInput => { - return instantiationService.createInstance(FileEditorInput, resource, preferredResource, encoding, mode); + + createFileEditorInput: (resource, preferredResource, preferredName, preferredDescription, preferredEncoding, preferredMode, instantiationService): IFileEditorInput => { + return instantiationService.createInstance(FileEditorInput, resource, preferredResource, preferredName, preferredDescription, preferredEncoding, preferredMode); }, isFileEditorInput: (obj): obj is IFileEditorInput => { @@ -114,6 +118,8 @@ Registry.as(EditorInputExtensions.EditorInputFactor interface ISerializedFileEditorInput { resourceJSON: UriComponents; preferredResourceJSON?: UriComponents; + name?: string; + description?: string; encoding?: string; modeId?: string; } @@ -132,6 +138,8 @@ class FileEditorInputFactory implements IEditorInputFactory { const serializedFileEditorInput: ISerializedFileEditorInput = { resourceJSON: resource.toJSON(), preferredResourceJSON: isEqual(resource, preferredResource) ? undefined : preferredResource, // only storing preferredResource if it differs from the resource + name: fileEditorInput.getPreferredName(), + description: fileEditorInput.getPreferredDescription(), encoding: fileEditorInput.getEncoding(), modeId: fileEditorInput.getPreferredMode() // only using the preferred user associated mode here if available to not store redundant data }; @@ -144,10 +152,12 @@ class FileEditorInputFactory implements IEditorInputFactory { const serializedFileEditorInput: ISerializedFileEditorInput = JSON.parse(serializedEditorInput); const resource = URI.revive(serializedFileEditorInput.resourceJSON); const preferredResource = URI.revive(serializedFileEditorInput.preferredResourceJSON); + const name = serializedFileEditorInput.name; + const description = serializedFileEditorInput.description; const encoding = serializedFileEditorInput.encoding; const mode = serializedFileEditorInput.modeId; - const fileEditorInput = accessor.get(IEditorService).createEditorInput({ resource, encoding, mode, forceFile: true }) as FileEditorInput; + const fileEditorInput = accessor.get(IEditorService).createEditorInput({ resource, label: name, description, encoding, mode, forceFile: true }) as FileEditorInput; if (preferredResource) { fileEditorInput.setPreferredResource(preferredResource); } @@ -393,6 +403,16 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize({ key: 'openEditorsVisible', comment: ['Open is an adjective'] }, "Number of editors shown in the Open Editors pane. Setting this to 0 hides the Open Editors pane."), 'default': 9 }, + 'explorer.openEditors.sortOrder': { + 'type': 'string', + 'enum': ['editorOrder', 'alphabetical'], + 'description': nls.localize({ key: 'openEditorsSortOrder', comment: ['Open is an adjective'] }, "Controls the sorting order of editors in the Open Editors pane."), + 'enumDescriptions': [ + nls.localize('sortOrder.editorOrder', 'Editors are ordered in the same order editor tabs are shown.'), + nls.localize('sortOrder.alphabetical', 'Editors are ordered in alphabetical order inside each editor group.') + ], + 'default': 'editorOrder' + }, 'explorer.autoReveal': { 'type': ['boolean', 'string'], 'enum': [true, false, 'focusNoScroll'], @@ -406,7 +426,7 @@ configurationRegistry.registerConfiguration({ }, 'explorer.enableDragAndDrop': { 'type': 'boolean', - 'description': nls.localize('enableDragAndDrop', "Controls whether the explorer should allow to move files and folders via drag and drop."), + 'description': nls.localize('enableDragAndDrop', "Controls whether the explorer should allow to move files and folders via drag and drop. This setting only effects drag and drop from inside the explorer."), 'default': true }, 'explorer.confirmDragAndDrop': { @@ -468,3 +488,25 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { }, order: 1 }); + +UndoCommand.addImplementation(110, (accessor: ServicesAccessor) => { + const undoRedoService = accessor.get(IUndoRedoService); + const explorerService = accessor.get(IExplorerService); + if (explorerService.hasViewFocus()) { + undoRedoService.undo(UNDO_REDO_SOURCE); + return true; + } + + return false; +}); + +RedoCommand.addImplementation(110, (accessor: ServicesAccessor) => { + const undoRedoService = accessor.get(IUndoRedoService); + const explorerService = accessor.get(IExplorerService); + if (explorerService.hasViewFocus()) { + undoRedoService.redo(UNDO_REDO_SOURCE); + return true; + } + + return false; +}); diff --git a/src/vs/workbench/contrib/files/browser/files.ts b/src/vs/workbench/contrib/files/browser/files.ts index ff2019d02..95e137cb3 100644 --- a/src/vs/workbench/contrib/files/browser/files.ts +++ b/src/vs/workbench/contrib/files/browser/files.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { IListService } from 'vs/platform/list/browser/listService'; -import { OpenEditor, IExplorerService } from 'vs/workbench/contrib/files/common/files'; +import { OpenEditor, SortOrder } from 'vs/workbench/contrib/files/common/files'; import { EditorResourceAccessor, SideBySideEditor, IEditorIdentifier } from 'vs/workbench/common/editor'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -13,6 +13,51 @@ import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditableData } from 'vs/workbench/common/views'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; + + +export interface IExplorerService { + readonly _serviceBrand: undefined; + readonly roots: ExplorerItem[]; + readonly sortOrder: SortOrder; + + getContext(respectMultiSelection: boolean): ExplorerItem[]; + hasViewFocus(): boolean; + setEditable(stat: ExplorerItem, data: IEditableData | null): Promise; + getEditable(): { stat: ExplorerItem, data: IEditableData } | undefined; + getEditableData(stat: ExplorerItem): IEditableData | undefined; + // If undefined is passed checks if any element is currently being edited. + isEditable(stat: ExplorerItem | undefined): boolean; + findClosest(resource: URI): ExplorerItem | null; + refresh(): Promise; + setToCopy(stats: ExplorerItem[], cut: boolean): Promise; + isCut(stat: ExplorerItem): boolean; + applyBulkEdit(edit: ResourceFileEdit[], options: { undoLabel: string, progressLabel: string }): Promise; + + /** + * Selects and reveal the file element provided by the given resource if its found in the explorer. + * Will try to resolve the path in case the explorer is not yet expanded to the file yet. + */ + select(resource: URI, reveal?: boolean | string): Promise; + + registerView(contextAndRefreshProvider: IExplorerView): void; +} + +export const IExplorerService = createDecorator('explorerService'); + +export interface IExplorerView { + getContext(respectMultiSelection: boolean): ExplorerItem[]; + refresh(recursive: boolean, item?: ExplorerItem): Promise; + selectResource(resource: URI | undefined, reveal?: boolean | string): Promise; + setTreeInput(): Promise; + itemsCopied(tats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void; + setEditable(stat: ExplorerItem, isEditing: boolean): Promise; + focusNeighbourIfItemFocused(item: ExplorerItem): void; + isItemVisible(item: ExplorerItem): boolean; + hasFocus(): boolean; +} function getFocus(listService: IListService): unknown | undefined { let list = listService.lastFocusedList; @@ -36,7 +81,7 @@ function getFocus(listService: IListService): unknown | undefined { return undefined; } -// Commands can get exeucted from a command pallete, from a context menu or from some list using a keybinding +// Commands can get executed from a command palette, from a context menu or from some list using a keybinding // To cover all these cases we need to properly compute the resource on which the command is being executed export function getResourceForCommand(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): URI | undefined { if (URI.isUri(resource)) { diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index 7257c3e62..8d645ccaa 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -76,7 +76,6 @@ } .explorer-viewlet .explorer-item .monaco-icon-name-container.multiple > .label-name > .monaco-highlighted-label { - padding: 1px; border-radius: 3px; } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts b/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts index d3a6797c9..8816edd6e 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts @@ -10,9 +10,9 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; import { listInvalidItemForeground, listDeemphasizedForeground } from 'vs/platform/theme/common/colorRegistry'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { explorerRootErrorEmitter } from 'vs/workbench/contrib/files/browser/views/explorerViewer'; import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; export function provideDecorations(fileStat: ExplorerItem): IDecorationData | undefined { if (fileStat.isRoot && fileStat.isError) { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 3db50a8a9..a9309b467 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -8,9 +8,8 @@ import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; -import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files'; +import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files'; import { NewFolderAction, NewFileAction, FileCopiedContext, RefreshExplorerView, CollapseExplorerView } from 'vs/workbench/contrib/files/browser/fileActions'; -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import * as DOM from 'vs/base/browser/dom'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ExplorerDecorationsProvider } from 'vs/workbench/contrib/files/browser/views/explorerDecorationsProvider'; @@ -31,13 +30,13 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { ExplorerDelegate, ExplorerDataSource, FilesRenderer, ICompressedNavigationController, FilesFilter, FileSorter, FileDragAndDrop, ExplorerCompressionDelegate, isCompressedFolderName } from 'vs/workbench/contrib/files/browser/views/explorerViewer'; import { IThemeService, IFileIconTheme } from 'vs/platform/theme/common/themeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; +import { ITreeContextMenuEvent, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { ResourceLabels } from 'vs/workbench/browser/labels'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -51,6 +50,8 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; interface IExplorerViewColors extends IColorMapping { listDropBackground?: ColorValue | undefined; @@ -134,6 +135,7 @@ export class ExplorerView extends ViewPane { private styleElement!: HTMLStyleElement; private treeContainer!: HTMLElement; + private container!: HTMLElement; private compressedFocusContext: IContextKey; private compressedFocusFirstContext: IContextKey; private compressedFocusLastContext: IContextKey; @@ -245,6 +247,7 @@ export class ExplorerView extends ViewPane { renderBody(container: HTMLElement): void { super.renderBody(container); + this.container = container; this.treeContainer = DOM.append(container, DOM.$('.explorer-folders-view')); this.styleElement = DOM.createStyleSheet(this.treeContainer); @@ -303,10 +306,18 @@ export class ExplorerView extends ViewPane { } } + hasFocus(): boolean { + return DOM.isAncestor(document.activeElement, this.container); + } + getContext(respectMultiSelection: boolean): ExplorerItem[] { return getContext(this.tree.getFocus(), this.tree.getSelection(), respectMultiSelection, this.renderer); } + isItemVisible(item: ExplorerItem): boolean { + return this.filter.filter(item, TreeVisibility.Visible); + } + async setEditable(stat: ExplorerItem, isEditing: boolean): Promise { if (isEditing) { this.horizontalScrolling = this.tree.options.horizontalScrolling; @@ -337,7 +348,8 @@ export class ExplorerView extends ViewPane { private selectActiveFile(reveal = this.autoReveal): void { if (this.autoReveal) { - const activeFile = this.getActiveFile(); + const activeFile = EditorResourceAccessor.getCanonicalUri(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); + if (activeFile) { const focus = this.tree.getFocus(); const selection = this.tree.getSelection(); @@ -458,7 +470,7 @@ export class ExplorerView extends ViewPane { // save view state this._register(this.storageService.onWillSaveState(() => { - this.storageService.store(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE); + this.storageService.store(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE, StorageTarget.MACHINE); })); } @@ -679,18 +691,6 @@ export class ExplorerView extends ViewPane { } } - private getActiveFile(): URI | undefined { - const input = this.editorService.activeEditor; - - // ignore diff editor inputs (helps to get out of diffing when returning to explorer) - if (input instanceof DiffEditorInput) { - return undefined; - } - - // check for files - return input?.resource; - } - public async selectResource(resource: URI | undefined, reveal = this.autoReveal, retry = 0): Promise { // do no retry more than once to prevent inifinite loops in cases of inconsistent model if (retry === 2) { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 9377aea41..9e8330b3d 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -9,17 +9,17 @@ import * as glob from 'vs/base/common/glob'; import { IListVirtualDelegate, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; import { IProgressService, ProgressLocation, IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IFileService, FileKind, FileOperationError, FileOperationResult, FileSystemProviderCapabilities, BinarySize } from 'vs/platform/files/common/files'; +import { IFileService, FileKind, FileOperationError, FileOperationResult, FileSystemProviderCapabilities, ByteSize } from 'vs/platform/files/common/files'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDisposable, Disposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IFileLabelOptions, IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; -import { ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource, ITreeSorter, ITreeDragAndDrop, ITreeDragOverReaction, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; +import { ITreeNode, ITreeFilter, TreeVisibility, IAsyncDataSource, ITreeSorter, ITreeDragAndDrop, ITreeDragOverReaction, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IFilesConfiguration, IExplorerService, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IFilesConfiguration, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; import { dirname, joinPath, basename, distinctParents } from 'vs/base/common/resources'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { localize } from 'vs/nls'; @@ -37,11 +37,10 @@ import { Schemas } from 'vs/base/common/network'; import { NativeDragAndDropData, ExternalElementsDragAndDropData, ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { IDialogService, IConfirmation, getFileNamesMessage } from 'vs/platform/dialogs/common/dialogs'; -import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { URI } from 'vs/base/common/uri'; -import { ITask, RunOnceWorker, sequence } from 'vs/base/common/async'; +import { RunOnceWorker } from 'vs/base/common/async'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { findValidPasteFileTarget } from 'vs/workbench/contrib/files/browser/fileActions'; @@ -58,6 +57,8 @@ import { IEditableData } from 'vs/workbench/common/views'; import { IEditorInput } from 'vs/workbench/common/editor'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -532,7 +533,7 @@ interface CachedParsedExpression { */ export class FilesFilter implements ITreeFilter { private hiddenExpressionPerRoot: Map; - private hiddenUris = new Set(); + private uriVisibilityMap = new Map(); private editorsAffectingFilter = new Set(); private _onDidChange = new Emitter(); private toDispose: IDisposable[] = []; @@ -554,13 +555,15 @@ export class FilesFilter implements ITreeFilter { this.toDispose.push(this.editorService.onDidVisibleEditorsChange(() => { const editors = this.editorService.visibleEditors; let shouldFire = false; - this.hiddenUris.forEach(u => { - editors.forEach(e => { - if (e.resource && this.uriIdentityService.extUri.isEqualOrParent(e.resource, u)) { - // A filtered resource suddenly became visible since user opened an editor - shouldFire = true; - } - }); + this.uriVisibilityMap.forEach((visible, uri) => { + if (!visible) { + editors.forEach(e => { + if (e.resource && this.uriIdentityService.extUri.isEqualOrParent(e.resource, uri)) { + // A filtered resource suddenly became visible since user opened an editor + shouldFire = true; + } + }); + } }); this.editorsAffectingFilter.forEach(e => { @@ -571,7 +574,7 @@ export class FilesFilter implements ITreeFilter { }); if (shouldFire) { this.editorsAffectingFilter.clear(); - this.hiddenUris.clear(); + this.uriVisibilityMap.clear(); this._onDidChange.fire(); } })); @@ -600,19 +603,20 @@ export class FilesFilter implements ITreeFilter { if (shouldFire) { this.editorsAffectingFilter.clear(); - this.hiddenUris.clear(); + this.uriVisibilityMap.clear(); this._onDidChange.fire(); } } - filter(stat: ExplorerItem, parentVisibility: TreeVisibility): TreeFilterResult { - const isVisible = this.isVisible(stat, parentVisibility); - if (isVisible) { - this.hiddenUris.delete(stat.resource); - } else { - this.hiddenUris.add(stat.resource); + filter(stat: ExplorerItem, parentVisibility: TreeVisibility): boolean { + const cachedVisibility = this.uriVisibilityMap.get(stat.resource); + if (typeof cachedVisibility === 'boolean') { + return cachedVisibility; } + const isVisible = this.isVisible(stat, parentVisibility); + this.uriVisibilityMap.set(stat.resource, isVisible); + return isVisible; } @@ -806,7 +810,6 @@ export class FileDragAndDrop implements ITreeDragAndDrop { @IFileService private fileService: IFileService, @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService private instantiationService: IInstantiationService, - @IWorkingCopyFileService private workingCopyFileService: IWorkingCopyFileService, @IHostService private hostService: IHostService, @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, @IProgressService private readonly progressService: IProgressService, @@ -868,7 +871,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Native DND if (isNative) { - if (!containsDragType(originalEvent, DataTransfers.FILES, CodeDataTransfers.FILES)) { + if (!containsDragType(originalEvent, DataTransfers.FILES, CodeDataTransfers.FILES, DataTransfers.RESOURCES)) { return false; } } @@ -972,14 +975,14 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // The only custom data transfer we set from the explorer is a file transfer // to be able to DND between multiple code file explorers across windows - const fileResources = items.filter(s => !s.isDirectory && s.resource.scheme === Schemas.file).map(r => r.resource.fsPath); + const fileResources = items.filter(s => s.resource.scheme === Schemas.file).map(r => r.resource.fsPath); if (fileResources.length) { originalEvent.dataTransfer.setData(CodeDataTransfers.FILES, JSON.stringify(fileResources)); } } } - drop(data: IDragAndDropData, target: ExplorerItem | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { + async drop(data: IDragAndDropData, target: ExplorerItem | undefined, targetIndex: number | undefined, originalEvent: DragEvent): Promise { this.compressedDropTargetDisposable.dispose(); // Find compressed target @@ -1010,26 +1013,29 @@ export class FileDragAndDrop implements ITreeDragAndDrop { if (data instanceof NativeDragAndDropData) { const cts = new CancellationTokenSource(); - // Indicate progress globally - const dropPromise = this.progressService.withProgress({ - location: ProgressLocation.Window, - delay: 800, - cancellable: true, - title: isWeb ? localize('uploadingFiles', "Uploading") : localize('copyingFiles', "Copying") - }, async progress => { - try { - if (isWeb) { - await this.handleWebExternalDrop(data, resolvedTarget, originalEvent, progress, cts.token); - } else { - await this.handleExternalDrop(data, resolvedTarget, originalEvent, progress, cts.token); + if (isWeb) { + // Indicate progress globally + const dropPromise = this.progressService.withProgress({ + location: ProgressLocation.Window, + delay: 800, + cancellable: true, + title: localize('uploadingFiles', "Uploading") + }, async progress => { + try { + await this.handleWebExternalDrop(resolvedTarget, originalEvent, progress, cts.token); + } catch (error) { + this.notificationService.warn(error); } + }, () => cts.dispose(true)); + // Also indicate progress in the files view + this.progressService.withProgress({ location: VIEW_ID, delay: 500 }, () => dropPromise); + } else { + try { + await this.handleExternalDrop(resolvedTarget, originalEvent, cts.token); } catch (error) { this.notificationService.warn(error); } - }, () => cts.dispose(true)); - - // Also indicate progress in the files view - this.progressService.withProgress({ location: VIEW_ID, delay: 800 }, () => dropPromise); + } } // In-Explorer DND (Move/Copy file) else { @@ -1037,7 +1043,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } } - private async handleWebExternalDrop(data: NativeDragAndDropData, target: ExplorerItem, originalEvent: DragEvent, progress: IProgress, token: CancellationToken): Promise { + private async handleWebExternalDrop(target: ExplorerItem, originalEvent: DragEvent, progress: IProgress, token: CancellationToken): Promise { const items = (originalEvent.dataTransfer as unknown as IWebkitDataTransfer).items; // Somehow the items thing is being modified at random, maybe as a security @@ -1071,7 +1077,10 @@ export class FileDragAndDrop implements ITreeDragAndDrop { continue; } - await this.workingCopyFileService.delete([joinPath(target.resource, entry.name)], { recursive: true }); + await this.explorerService.applyBulkEdit([new ResourceFileEdit(joinPath(target.resource, entry.name), undefined, { recursive: true })], { + undoLabel: localize('overwrite', "Overwrite {0}", entry.name), + progressLabel: localize('overwriting', "Overwriting {0}", entry.name), + }); if (token.isCancellationRequested) { break; @@ -1109,17 +1118,17 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Small file let message: string; - if (fileSize < BinarySize.MB) { + if (fileSize < ByteSize.MB) { if (operation.filesTotal === 1) { message = `${entry.name}`; } else { - message = localize('uploadProgressSmallMany', "{0} of {1} files ({2}/s)", operation.filesUploaded, operation.filesTotal, BinarySize.formatSize(bytesUploadedPerSecond)); + message = localize('uploadProgressSmallMany', "{0} of {1} files ({2}/s)", operation.filesUploaded, operation.filesTotal, ByteSize.formatSize(bytesUploadedPerSecond)); } } // Large file else { - message = localize('uploadProgressLarge', "{0} ({1} of {2}, {3}/s)", entry.name, BinarySize.formatSize(fileBytesUploaded), BinarySize.formatSize(fileSize), BinarySize.formatSize(bytesUploadedPerSecond)); + message = localize('uploadProgressLarge', "{0} ({1} of {2}, {3}/s)", entry.name, ByteSize.formatSize(fileBytesUploaded), ByteSize.formatSize(fileSize), ByteSize.formatSize(bytesUploadedPerSecond)); } // Report progress but limit to update only once per second @@ -1137,12 +1146,13 @@ export class FileDragAndDrop implements ITreeDragAndDrop { return undefined; } - // Chrome/Edge/Firefox support stream method - if (typeof file.stream === 'function') { + // Chrome/Edge/Firefox support stream method, but only use it for + // larger files to reduce the overhead of the streaming approach + if (typeof file.stream === 'function' && file.size > ByteSize.MB) { await this.doUploadWebFileEntryBuffered(resource, file, reportProgress, token); } - // Fallback to unbuffered upload for other browsers + // Fallback to unbuffered upload for other browsers or small files else { await this.doUploadWebFileEntryUnbuffered(resource, file, reportProgress); } @@ -1258,7 +1268,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { }); } - private async handleExternalDrop(data: NativeDragAndDropData, target: ExplorerItem, originalEvent: DragEvent, progress: IProgress, token: CancellationToken): Promise { + private async handleExternalDrop(target: ExplorerItem, originalEvent: DragEvent, token: CancellationToken): Promise { // Check for dropped external files to be folders const droppedResources = extractResources(originalEvent, true); @@ -1271,9 +1281,9 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Pass focus to window this.hostService.focus(); - // Handle folders by adding to workspace if we are in workspace context + // Handle folders by adding to workspace if we are in workspace context and if dropped on top const folders = result.filter(r => r.success && r.stat && r.stat.isDirectory).map(result => ({ uri: result.stat!.resource })); - if (folders.length > 0) { + if (folders.length > 0 && target.isRoot) { const buttons = [ folders.length > 1 ? localize('copyFolders', "&&Copy Folders") : localize('copyFolder', "&&Copy Folder"), localize('cancel', "Cancel") @@ -1292,7 +1302,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { return this.workspaceEditingService.addFolders(folders); } if (choice === buttons.length - 2) { - return this.addResources(target, droppedResources.map(res => res.resource), progress, token); + return this.addResources(target, droppedResources.map(res => res.resource), token); } return undefined; @@ -1300,11 +1310,11 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Handle dropped files (only support FileStat as target) else if (target instanceof ExplorerItem) { - return this.addResources(target, droppedResources.map(res => res.resource), progress, token); + return this.addResources(target, droppedResources.map(res => res.resource), token); } } - private async addResources(target: ExplorerItem, resources: URI[], progress: IProgress, token: CancellationToken): Promise { + private async addResources(target: ExplorerItem, resources: URI[], token: CancellationToken): Promise { if (resources && resources.length > 0) { // Resolve target to check for name collisions and ask user @@ -1323,36 +1333,33 @@ export class FileDragAndDrop implements ITreeDragAndDrop { }); } - // Run add in sequence - const addPromisesFactory: ITask>[] = []; - await Promise.all(resources.map(async resource => { + const resourcesFiltered = (await Promise.all(resources.map(async resource => { if (targetNames.has(caseSensitive ? basename(resource) : basename(resource).toLowerCase())) { const confirmationResult = await this.dialogService.confirm(getFileOverwriteConfirm(basename(resource))); if (!confirmationResult.confirmed) { - return; + return undefined; } } + return resource; + }))).filter(r => r instanceof URI) as URI[]; + const resourceFileEdits = resourcesFiltered.map(resource => { + const sourceFileName = basename(resource); + const targetFile = joinPath(target.resource, sourceFileName); + return new ResourceFileEdit(resource, targetFile, { overwrite: true, copy: true }); + }); - addPromisesFactory.push(async () => { - if (token.isCancellationRequested) { - return; - } + await this.explorerService.applyBulkEdit(resourceFileEdits, { + undoLabel: resourcesFiltered.length === 1 ? localize('copyFile', "Copy {0}", basename(resourcesFiltered[0])) : localize('copynFile', "Copy {0} resources", resourcesFiltered.length), + progressLabel: resourcesFiltered.length === 1 ? localize('copyingFile', "Copying {0}", basename(resourcesFiltered[0])) : localize('copyingnFile', "Copying {0} resources", resourcesFiltered.length) + }); - const sourceFile = resource; - const sourceFileName = basename(sourceFile); - const targetFile = joinPath(target.resource, sourceFileName); - - progress.report({ message: sourceFileName }); - - const stat = (await this.workingCopyFileService.copy([{ source: sourceFile, target: targetFile }], { overwrite: true }))[0]; - // if we only add one file, just open it directly - if (resources.length === 1 && !stat.isDirectory) { - this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); - } - }); - })); - - await sequence(addPromisesFactory); + // if we only add one file, just open it directly + if (resourceFileEdits.length === 1) { + const item = this.explorerService.findClosest(resourceFileEdits[0].newResource!); + if (item && !item.isDirectory) { + this.editorService.openEditor({ resource: item.resource, options: { pinned: true } }); + } + } } } @@ -1386,7 +1393,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Check for confirmation checkbox if (confirmation.checkboxChecked === true) { - await this.configurationService.updateValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY, false, ConfigurationTarget.USER); + await this.configurationService.updateValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY, false); } } @@ -1436,9 +1443,17 @@ export class FileDragAndDrop implements ITreeDragAndDrop { private async doHandleExplorerDropOnCopy(sources: ExplorerItem[], target: ExplorerItem): Promise { // Reuse duplicate action when user copies const incrementalNaming = this.configurationService.getValue().explorer.incrementalNaming; - const sourceTargetPairs = sources.map(({ resource, isDirectory }) => ({ source: resource, target: findValidPasteFileTarget(this.explorerService, target, { resource, isDirectory, allowOverwrite: false }, incrementalNaming) })); - const stats = await this.workingCopyFileService.copy(sourceTargetPairs); - const editors = stats.filter(stat => !stat.isDirectory).map(({ resource }) => ({ resource, options: { pinned: true } })); + const resourceFileEdits = sources.map(({ resource, isDirectory }) => (new ResourceFileEdit(resource, findValidPasteFileTarget(this.explorerService, target, { resource, isDirectory, allowOverwrite: false }, incrementalNaming), { copy: true }))); + const labelSufix = getFileOrFolderLabelSufix(sources); + await this.explorerService.applyBulkEdit(resourceFileEdits, { + undoLabel: localize('copy', "Copy {0}", labelSufix), + progressLabel: localize('copying', "Copying {0}", labelSufix), + }); + + const editors = resourceFileEdits.filter(edit => { + const item = edit.newResource ? this.explorerService.findClosest(edit.newResource) : undefined; + return item && !item.isDirectory; + }).map(edit => ({ resource: edit.newResource, options: { pinned: true } })); await this.editorService.openEditors(editors); } @@ -1446,18 +1461,23 @@ export class FileDragAndDrop implements ITreeDragAndDrop { private async doHandleExplorerDropOnMove(sources: ExplorerItem[], target: ExplorerItem): Promise { // Do not allow moving readonly items - const sourceTargetPairs = sources.filter(source => !source.isReadonly).map(source => ({ source: source.resource, target: joinPath(target.resource, source.name) })); + const resourceFileEdits = sources.filter(source => !source.isReadonly).map(source => new ResourceFileEdit(source.resource, joinPath(target.resource, source.name))); + const labelSufix = getFileOrFolderLabelSufix(sources); + const options = { + undoLabel: localize('move', "Move {0}", labelSufix), + progressLabel: localize('moving', "Moving {0}", labelSufix) + }; try { - await this.workingCopyFileService.move(sourceTargetPairs); + await this.explorerService.applyBulkEdit(resourceFileEdits, options); } catch (error) { // Conflict if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) { const overwrites: URI[] = []; - for (const { target } of sourceTargetPairs) { - if (await this.fileService.exists(target)) { - overwrites.push(target); + for (const edit of resourceFileEdits) { + if (edit.newResource && await this.fileService.exists(edit.newResource)) { + overwrites.push(edit.newResource); } } @@ -1466,7 +1486,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const { confirmed } = await this.dialogService.confirm(confirm); if (confirmed) { try { - await this.workingCopyFileService.move(sourceTargetPairs, { overwrite: true }); + await this.explorerService.applyBulkEdit(resourceFileEdits.map(re => new ResourceFileEdit(re.oldResource, re.newResource, { overwrite: true })), options); } catch (error) { this.notificationService.error(error); } @@ -1550,3 +1570,18 @@ export class ExplorerCompressionDelegate implements ITreeCompressionDelegate i.isDirectory)) { + return `${items.length} folders`; + } + if (items.every(i => !i.isDirectory)) { + return `${items.length} files`; + } + + return `${items.length} files and folders`; +} diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 5c1e9e59b..3535108c3 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -48,6 +48,7 @@ import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { compareFileNamesDefault } from 'vs/base/common/comparers'; const $ = dom.$; @@ -64,6 +65,8 @@ export class OpenEditorsView extends ViewPane { private listLabels: ResourceLabels | undefined; private contributedContextMenu!: IMenu; private needsRefresh = false; + private elements: (OpenEditor | IEditorGroup)[] = []; + private sortOrder: 'editorOrder' | 'alphabetical'; private resourceContext!: ResourceContextKey; private groupFocusedContext!: IContextKey; private dirtyEditorFocusedContext!: IContextKey; @@ -91,13 +94,14 @@ export class OpenEditorsView extends ViewPane { this.structuralRefreshDelay = 0; this.listRefreshScheduler = new RunOnceScheduler(() => { const previousLength = this.list.length; - this.list.splice(0, this.list.length, this.elements); + this.list.splice(0, this.list.length, this.getElements()); this.focusActiveEditor(); if (previousLength !== this.list.length) { this.updateSize(); } this.needsRefresh = false; }, this.structuralRefreshDelay); + this.sortOrder = configurationService.getValue('explorer.openEditors.sortOrder'); this.registerUpdateEvents(); @@ -132,7 +136,7 @@ export class OpenEditorsView extends ViewPane { const index = this.getIndex(group, e.editor); switch (e.kind) { case GroupChangeKind.GROUP_INDEX: { - if (this.showGroups) { + if (index >= 0) { this.list.splice(index, 1, [group]); } break; @@ -149,19 +153,10 @@ export class OpenEditorsView extends ViewPane { this.list.splice(index, 1, [new OpenEditor(e.editor!, group)]); break; } - case GroupChangeKind.EDITOR_OPEN: { - this.list.splice(index, 0, [new OpenEditor(e.editor!, group)]); - setTimeout(() => this.updateSize(), this.structuralRefreshDelay); - break; - } - case GroupChangeKind.EDITOR_CLOSE: { - const previousIndex = this.getIndex(group, undefined) + (e.editorIndex || 0) + (this.showGroups ? 1 : 0); - this.list.splice(previousIndex, 1); - this.updateSize(); - break; - } + case GroupChangeKind.EDITOR_OPEN: + case GroupChangeKind.EDITOR_CLOSE: case GroupChangeKind.EDITOR_MOVE: { - this.listRefreshScheduler.schedule(); + updateWholeList(); break; } } @@ -272,20 +267,16 @@ export class OpenEditorsView extends ViewPane { })); const resourceNavigator = this._register(new ListResourceNavigator(this.list, { configurationService: this.configurationService })); this._register(resourceNavigator.onDidOpen(e => { - if (typeof e.element !== 'number') { + if (!e.element) { return; - } - - const element = this.list.element(e.element); - - if (element instanceof OpenEditor) { + } else if (e.element instanceof OpenEditor) { if (e.browserEvent instanceof MouseEvent && e.browserEvent.button === 1) { return; // middle click already handled above: closes the editor } - this.openEditor(element, { preserveFocus: e.editorOptions.preserveFocus, pinned: e.editorOptions.pinned, sideBySide: e.sideBySide }); + this.openEditor(e.element, { preserveFocus: e.editorOptions.preserveFocus, pinned: e.editorOptions.pinned, sideBySide: e.sideBySide }); } else { - this.editorGroupService.activateGroup(element); + this.editorGroupService.activateGroup(e.element); } })); @@ -331,33 +322,28 @@ export class OpenEditorsView extends ViewPane { return this.editorGroupService.groups.length > 1; } - private get elements(): Array { - const result: Array = []; + private getElements(): Array { + this.elements = []; this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).forEach(g => { if (this.showGroups) { - result.push(g); + this.elements.push(g); } - result.push(...g.editors.map(ei => new OpenEditor(ei, g))); + let editors = g.editors.map(ei => new OpenEditor(ei, g)); + if (this.sortOrder === 'alphabetical') { + editors = editors.sort((first, second) => compareFileNamesDefault(first.editor.getName(), second.editor.getName())); + } + this.elements.push(...editors); }); - return result; + return this.elements; } private getIndex(group: IEditorGroup, editor: IEditorInput | undefined | null): number { - let index = editor ? group.getIndexOfEditor(editor) : 0; - if (!this.showGroups) { - return index; + if (!editor) { + return this.elements.findIndex(e => !(e instanceof OpenEditor) && e.id === group.id); } - for (let g of this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)) { - if (g.id === group.id) { - return index + (!!editor ? 1 : 0); - } else { - index += g.count + 1; - } - } - - return -1; + return this.elements.findIndex(e => e instanceof OpenEditor && e.editor === editor && e.group.id === group.id); } private openEditor(element: OpenEditor, options: { preserveFocus?: boolean; pinned?: boolean; sideBySide?: boolean; }): void { @@ -384,7 +370,7 @@ export class OpenEditorsView extends ViewPane { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => actions, - getActionsContext: () => element instanceof OpenEditor ? { groupId: element.groupId, editorIndex: element.editorIndex } : { groupId: element.id }, + getActionsContext: () => element instanceof OpenEditor ? { groupId: element.groupId, editorIndex: element.group.getIndexOfEditor(element.editor) } : { groupId: element.id }, onHide: () => dispose(actionsDisposable) }); } @@ -393,9 +379,13 @@ export class OpenEditorsView extends ViewPane { if (this.list.length && this.editorGroupService.activeGroup) { const index = this.getIndex(this.editorGroupService.activeGroup, this.editorGroupService.activeGroup.activeEditor); if (index >= 0) { - this.list.setFocus([index]); - this.list.setSelection([index]); - this.list.reveal(index); + try { + this.list.setFocus([index]); + this.list.setSelection([index]); + this.list.reveal(index); + } catch (e) { + // noop list updated in the meantime + } return; } } @@ -408,9 +398,9 @@ export class OpenEditorsView extends ViewPane { if (event.affectsConfiguration('explorer.openEditors')) { this.updateSize(); } - - // Trigger a 'repaint' when decoration settings change - if (event.affectsConfiguration('explorer.decorations')) { + // Trigger a 'repaint' when decoration settings change or the sort order changed + if (event.affectsConfiguration('explorer.decorations') || event.affectsConfiguration('explorer.openEditors.sortOrder')) { + this.sortOrder = this.configurationService.getValue('explorer.openEditors.sortOrder'); this.listRefreshScheduler.schedule(); } } @@ -500,7 +490,7 @@ class OpenEditorActionRunner extends ActionRunner { return; } - return super.run(action, { groupId: this.editor.groupId, editorIndex: this.editor.editorIndex }); + return super.run(action, { groupId: this.editor.groupId, editorIndex: this.editor.group.getIndexOfEditor(this.editor.editor) }); } } @@ -687,7 +677,7 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop; - getEditable(): { stat: ExplorerItem, data: IEditableData } | undefined; - getEditableData(stat: ExplorerItem): IEditableData | undefined; - // If undefined is passed checks if any element is currently being edited. - isEditable(stat: ExplorerItem | undefined): boolean; - findClosest(resource: URI): ExplorerItem | null; - refresh(): Promise; - setToCopy(stats: ExplorerItem[], cut: boolean): Promise; - isCut(stat: ExplorerItem): boolean; - - /** - * Selects and reveal the file element provided by the given resource if its found in the explorer. - * Will try to resolve the path in case the explorer is not yet expanded to the file yet. - */ - select(resource: URI, reveal?: boolean | string): Promise; - - registerView(contextAndRefreshProvider: IExplorerView): void; -} - -export interface IExplorerView { - getContext(respectMultiSelection: boolean): ExplorerItem[]; - refresh(recursive: boolean, item?: ExplorerItem): Promise; - selectResource(resource: URI | undefined, reveal?: boolean | string): Promise; - setTreeInput(): Promise; - itemsCopied(tats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void; - setEditable(stat: ExplorerItem, isEditing: boolean): Promise; - focusNeighbourIfItemFocused(item: ExplorerItem): void; -} - -export const IExplorerService = createDecorator('explorerService'); - /** * Context Keys to use with keybindings for the Explorer and Open Editors view */ @@ -116,6 +76,7 @@ export interface IFilesConfiguration extends PlatformIFilesConfiguration, IWorkb explorer: { openEditors: { visible: number; + sortOrder: 'editorOrder' | 'alphabetical'; }; autoReveal: boolean | 'focusNoScroll'; enableDragAndDrop: boolean; @@ -230,18 +191,17 @@ export class TextFileContentProvider extends Disposable implements ITextModelCon export class OpenEditor implements IEditorIdentifier { + private id: number; + private static COUNTER = 0; + constructor(private _editor: IEditorInput, private _group: IEditorGroup) { - // noop + this.id = OpenEditor.COUNTER++; } get editor() { return this._editor; } - get editorIndex() { - return this._group.getIndexOfEditor(this.editor); - } - get group() { return this._group; } @@ -251,7 +211,7 @@ export class OpenEditor implements IEditorIdentifier { } getId(): string { - return `openeditor:${this.groupId}:${this.editorIndex}:${this.editor.getName()}:${this.editor.getDescription()}`; + return `openeditor:${this.groupId}:${this.id}`; } isPreview(): boolean { diff --git a/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts b/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts index 086d9c9dd..72211dd04 100644 --- a/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts @@ -14,18 +14,19 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; +import { getMultiSelectedResources, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { IListService } from 'vs/platform/list/browser/listService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { revealResourcesInOS } from 'vs/workbench/contrib/files/electron-sandbox/fileCommands'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { appendToCommandPalette, appendEditorTitleContextMenuItem } from 'vs/workbench/contrib/files/browser/fileActions.contribution'; -import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { SideBySideEditor, EditorResourceAccessor } from 'vs/workbench/common/editor'; +import { ContextKeyOrExpr } from 'vs/platform/contextkey/common/contextkey'; const REVEAL_IN_OS_COMMAND_ID = 'revealFileInOS'; const REVEAL_IN_OS_LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in File Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder"); +const REVEAL_IN_OS_WHEN_CONTEXT = ContextKeyOrExpr.create([ResourceContextKey.Scheme.isEqualTo(Schemas.file), ResourceContextKey.Scheme.isEqualTo(Schemas.userData)]); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: REVEAL_IN_OS_COMMAND_ID, @@ -57,19 +58,19 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ResourceContextKey.Scheme.isEqualTo(Schemas.file)); +appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, REVEAL_IN_OS_WHEN_CONTEXT); // Menu registration - open editors const revealInOsCommand = { id: REVEAL_IN_OS_COMMAND_ID, - title: isWindows ? nls.localize('revealInWindows', "Reveal in File Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder") + title: REVEAL_IN_OS_LABEL }; MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: 'navigation', order: 20, command: revealInOsCommand, - when: ResourceContextKey.IsFileSystemResource + when: REVEAL_IN_OS_WHEN_CONTEXT }); // Menu registration - explorer @@ -78,10 +79,10 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { group: 'navigation', order: 20, command: revealInOsCommand, - when: ResourceContextKey.Scheme.isEqualTo(Schemas.file) + when: REVEAL_IN_OS_WHEN_CONTEXT }); // Command Palette const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; -appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in File Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category, ResourceContextKey.Scheme.isEqualTo(Schemas.file)); +appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in File Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category, REVEAL_IN_OS_WHEN_CONTEXT); diff --git a/src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts b/src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts index c6655f263..d310e9872 100644 --- a/src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts @@ -21,9 +21,9 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; /** * An implementation of editor for file system resources. diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts index 8e0e190bf..0df2ed5d5 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts @@ -24,12 +24,16 @@ suite('Files - FileEditorInput', () => { let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; + function createFileInput(resource: URI, preferredResource?: URI, preferredMode?: string, preferredName?: string, preferredDescription?: string): FileEditorInput { + return instantiationService.createInstance(FileEditorInput, resource, preferredResource, preferredName, preferredDescription, undefined, preferredMode); + } + setup(() => { instantiationService = workbenchInstantiationService({ editorService: () => { return new class extends TestEditorService { createEditorInput(input: IResourceEditorInput) { - return instantiationService.createInstance(FileEditorInput, input.resource, undefined, undefined, undefined); + return createFileInput(input.resource); } }; } @@ -39,9 +43,9 @@ suite('Files - FileEditorInput', () => { }); test('Basics', async function () { - let input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); - const otherInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/otherfile.js'), undefined, undefined, undefined); - const otherInputSame = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/file.js'), undefined, undefined, undefined); + let input = createFileInput(toResource.call(this, '/foo/bar/file.js')); + const otherInput = createFileInput(toResource.call(this, 'foo/bar/otherfile.js')); + const otherInputSame = createFileInput(toResource.call(this, 'foo/bar/file.js')); assert(input.matches(input)); assert(input.matches(otherInputSame)); @@ -56,10 +60,10 @@ suite('Files - FileEditorInput', () => { assert.strictEqual(toResource.call(this, '/foo/bar/file.js').fsPath, input.resource.fsPath); assert(input.resource instanceof URI); - input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar.html'), undefined, undefined, undefined); + input = createFileInput(toResource.call(this, '/foo/bar.html')); - const inputToResolve: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); - const sameOtherInput: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); + const inputToResolve: FileEditorInput = createFileInput(toResource.call(this, '/foo/bar/file.js')); + const sameOtherInput: FileEditorInput = createFileInput(toResource.call(this, '/foo/bar/file.js')); let resolved = await inputToResolve.resolve(); assert.ok(inputToResolve.isResolved()); @@ -97,11 +101,11 @@ suite('Files - FileEditorInput', () => { const resource = toResource.call(this, '/foo/bar/updatefile.js'); const preferredResource = toResource.call(this, '/foo/bar/UPDATEFILE.js'); - const inputWithoutPreferredResource = instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined); + const inputWithoutPreferredResource = createFileInput(resource); assert.equal(inputWithoutPreferredResource.resource.toString(), resource.toString()); assert.equal(inputWithoutPreferredResource.preferredResource.toString(), resource.toString()); - const inputWithPreferredResource = instantiationService.createInstance(FileEditorInput, resource, preferredResource, undefined, undefined); + const inputWithPreferredResource = createFileInput(resource, preferredResource); assert.equal(inputWithPreferredResource.resource.toString(), resource.toString()); assert.equal(inputWithPreferredResource.preferredResource.toString(), preferredResource.toString()); @@ -130,7 +134,7 @@ suite('Files - FileEditorInput', () => { id: mode, }); - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, mode); + const input = createFileInput(toResource.call(this, '/foo/bar/file.js'), undefined, mode); assert.equal(input.getPreferredMode(), mode); const model = await input.resolve() as TextFileEditorModel; @@ -140,7 +144,7 @@ suite('Files - FileEditorInput', () => { assert.equal(input.getPreferredMode(), 'text'); assert.equal(model.textEditorModel!.getModeId(), PLAINTEXT_MODE_ID); - const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); + const input2 = createFileInput(toResource.call(this, '/foo/bar/file.js')); input2.setPreferredMode(mode); const model2 = await input2.resolve() as TextFileEditorModel; @@ -148,10 +152,10 @@ suite('Files - FileEditorInput', () => { }); test('matches', function () { - const input1 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); - const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); - const input3 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/other.js'), undefined, undefined, undefined); - const input2Upper = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/UPDATEFILE.js'), undefined, undefined, undefined); + const input1 = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); + const input2 = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); + const input3 = createFileInput(toResource.call(this, '/foo/bar/other.js')); + const input2Upper = createFileInput(toResource.call(this, '/foo/bar/UPDATEFILE.js')); assert.strictEqual(input1.matches(null), false); assert.strictEqual(input1.matches(input1), true); @@ -162,7 +166,7 @@ suite('Files - FileEditorInput', () => { }); test('getEncoding/setEncoding', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); input.setEncoding('utf16', EncodingMode.Encode); assert.equal(input.getEncoding(), 'utf16'); @@ -173,7 +177,7 @@ suite('Files - FileEditorInput', () => { }); test('save', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); const resolved = await input.resolve() as TextFileEditorModel; resolved.textEditorModel!.setValue('changed'); @@ -185,7 +189,7 @@ suite('Files - FileEditorInput', () => { }); test('revert', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); const resolved = await input.resolve() as TextFileEditorModel; resolved.textEditorModel!.setValue('changed'); @@ -201,7 +205,7 @@ suite('Files - FileEditorInput', () => { }); test('resolve handles binary files', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); accessor.textFileService.setResolveTextContentErrorOnce(new TextFileOperationError('error', TextFileOperationResult.FILE_IS_BINARY)); @@ -211,7 +215,7 @@ suite('Files - FileEditorInput', () => { }); test('resolve handles too large files', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_TOO_LARGE)); @@ -221,7 +225,7 @@ suite('Files - FileEditorInput', () => { }); test('attaches to model when created and reports dirty', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); let listenerCount = 0; const listener = input.onDidChangeDirty(() => { @@ -241,7 +245,7 @@ suite('Files - FileEditorInput', () => { }); test('force open text/binary', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); input.setForceOpenAsBinary(); let resolved = await input.resolve(); @@ -258,7 +262,7 @@ suite('Files - FileEditorInput', () => { test('file editor input factory', async function () { instantiationService.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); const factory = Registry.as(EditorExtensions.EditorInputFactories).getEditorInputFactory(input.getTypeId()); if (!factory) { @@ -276,7 +280,7 @@ suite('Files - FileEditorInput', () => { assert.equal(input.matches(inputDeserialized), true); const preferredResource = toResource.call(this, '/foo/bar/UPDATEfile.js'); - const inputWithPreferredResource = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), preferredResource, undefined, undefined); + const inputWithPreferredResource = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'), preferredResource); const inputWithPreferredResourceSerialized = factory.serialize(inputWithPreferredResource); if (!inputWithPreferredResourceSerialized) { @@ -287,4 +291,49 @@ suite('Files - FileEditorInput', () => { assert.equal(inputWithPreferredResource.resource.toString(), inputWithPreferredResourceDeserialized.resource.toString()); assert.equal(inputWithPreferredResource.preferredResource.toString(), inputWithPreferredResourceDeserialized.preferredResource.toString()); }); + + test('preferred name/description', async function () { + + // Works with custom file input + const customFileInput = createFileInput(toResource.call(this, '/foo/bar/updatefile.js').with({ scheme: 'test-custom' }), undefined, undefined, 'My Name', 'My Description'); + + let didChangeLabelCounter = 0; + customFileInput.onDidChangeLabel(() => { + didChangeLabelCounter++; + }); + + assert.equal(customFileInput.getName(), 'My Name'); + assert.equal(customFileInput.getDescription(), 'My Description'); + + customFileInput.setPreferredName('My Name 2'); + customFileInput.setPreferredDescription('My Description 2'); + + assert.equal(customFileInput.getName(), 'My Name 2'); + assert.equal(customFileInput.getDescription(), 'My Description 2'); + + assert.equal(didChangeLabelCounter, 2); + + customFileInput.dispose(); + + // Disallowed with local file input + const fileInput = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, 'My Name', 'My Description'); + + didChangeLabelCounter = 0; + fileInput.onDidChangeLabel(() => { + didChangeLabelCounter++; + }); + + assert.notEqual(fileInput.getName(), 'My Name'); + assert.notEqual(fileInput.getDescription(), 'My Description'); + + fileInput.setPreferredName('My Name 2'); + fileInput.setPreferredDescription('My Description 2'); + + assert.notEqual(fileInput.getName(), 'My Name 2'); + assert.notEqual(fileInput.getDescription(), 'My Description 2'); + + assert.equal(didChangeLabelCounter, 0); + + fileInput.dispose(); + }); }); diff --git a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts index 9355b4e6d..1efff633b 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts @@ -32,7 +32,7 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { alias: 'Format Document', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider.toNegated()), kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider.toNegated()), + kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, weight: KeybindingWeight.EditorContrib, @@ -55,6 +55,8 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { return commandService.executeCommand('editor.action.formatDocument.multiple'); } else if (formatterCount === 1) { return commandService.executeCommand('editor.action.formatDocument'); + } else if (model.isTooLargeForSyncing()) { + notificationService.prompt(Severity.Info, nls.localize('too.large', "This file cannot be formatted because it is too large"), []); } else { const langName = model.getLanguageIdentifier().language; const message = nls.localize('no.provider', "There is no formatter for '{0}' files installed.", langName); diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts index 88b20371e..acd99472a 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts @@ -10,7 +10,7 @@ import { SyncActionDescriptor, ICommandAction, MenuRegistry, MenuId } from 'vs/p import { IWorkbenchActionRegistry, Extensions, CATEGORIES } from 'vs/workbench/common/actions'; import { ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer } from 'vs/workbench/contrib/issue/electron-sandbox/issueActions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-sandbox/issue'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { WorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-sandbox/issueService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IssueReporterData } from 'vs/platform/issue/common/issue'; diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts index 5285dbdba..44a396575 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts @@ -6,7 +6,7 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import { IssueType } from 'vs/platform/issue/common/issue'; -import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-sandbox/issue'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; export class OpenProcessExplorer extends Action { static readonly ID = 'workbench.action.openProcessExplorer'; diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts index ba04db21e..f7beaf0f5 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts @@ -11,11 +11,12 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getZoomLevel } from 'vs/base/browser/browser'; -import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-sandbox/issue'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IProductService } from 'vs/platform/product/common/productService'; +import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; export class WorkbenchIssueService implements IWorkbenchIssueService { declare readonly _serviceBrand: undefined; @@ -26,12 +27,13 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, - @IProductService private readonly productService: IProductService + @IProductService private readonly productService: IProductService, + @ITASExperimentService private readonly experimentService: ITASExperimentService ) { } async openReporter(dataOverrides: Partial = {}): Promise { const extensions = await this.extensionManagementService.getInstalled(); - const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension)); + const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension) || (dataOverrides.extensionId && extension.identifier.id === dataOverrides.extensionId)); const extensionData = enabledExtensions.map((extension): IssueReporterExtensionData => { const { manifest } = extension; const manifestKeys = manifest.contributes ? Object.keys(manifest.contributes) : []; @@ -49,11 +51,13 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { isBuiltin, }; }); + const experiments = await this.experimentService.getCurrentExperiments(); const theme = this.themeService.getColorTheme(); const issueReporterData: IssueReporterData = Object.assign({ styles: getIssueReporterStyles(theme), zoomLevel: getZoomLevel(), enabledExtensions: extensionData, + experiments: experiments?.join('\n') }, dataOverrides); return this.issueService.openReporter(issueReporterData); } diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index 446ad446f..f8b4c1582 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -19,13 +19,12 @@ import Severity from 'vs/base/common/severity'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { VIEWLET_ID as EXTENSIONS_VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; import { minimumTranslatedStrings } from 'vs/workbench/contrib/localizations/browser/minimalTranslations'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; // Register action to configure locale and related settings const registry = Registry.as(Extensions.WorkbenchActions); @@ -44,12 +43,9 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @IViewletService private readonly viewletService: IViewletService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); - storageKeysSyncRegistryService.registerStorageKey({ key: LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: 'langugage.update.donotask', version: 1 }); this.checkAndInstall(); this._register(this.extensionManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e))); } @@ -175,7 +171,8 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo this.storageService.store( LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY, JSON.stringify(languagePackSuggestionIgnoreList), - StorageScope.GLOBAL + StorageScope.GLOBAL, + StorageTarget.USER ); logUserReaction('neverShowAgain'); } diff --git a/src/vs/workbench/contrib/logs/common/logConstants.ts b/src/vs/workbench/contrib/logs/common/logConstants.ts index ca22b9947..9ba3e7aa0 100644 --- a/src/vs/workbench/contrib/logs/common/logConstants.ts +++ b/src/vs/workbench/contrib/logs/common/logConstants.ts @@ -9,3 +9,5 @@ export const rendererLogChannelId = 'rendererLog'; export const extHostLogChannelId = 'extHostLog'; export const telemetryLogChannelId = 'telemetryLog'; export const userDataSyncLogChannelId = 'userDataSyncLog'; + +export const showWindowLogActionId = 'workbench.action.showWindowLog'; diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 5d04d1b97..713e81985 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { join } from 'vs/base/common/path'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { SetLogLevelAction, OpenWindowSessionLogFileAction } from 'vs/workbench/contrib/logs/common/logsActions'; import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; @@ -20,8 +20,9 @@ import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { dirname } from 'vs/base/common/resources'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner'; +import { IOutputService } from 'vs/workbench/contrib/output/common/output'; const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SetLogLevelAction), 'Developer: Set Log Level...', CATEGORIES.Developer.value); @@ -54,6 +55,21 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { }; registerTelemetryChannel(this.logService.getLevel()); this.logService.onDidChangeLogLevel(registerTelemetryChannel); + + registerAction2(class ShowWindowLogAction extends Action2 { + constructor() { + super({ + id: Constants.showWindowLogActionId, + title: { value: nls.localize('show window log', "Show Window Log"), original: 'Show Window Log' }, + category: CATEGORIES.Developer, + f1: true + }); + } + async run(servicesAccessor: ServicesAccessor): Promise { + const outputService = servicesAccessor.get(IOutputService); + outputService.showChannel(Constants.rendererLogChannelId); + } + }); } private registerWebContributions(): void { diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts index 625a8d783..f19eb8a8f 100644 --- a/src/vs/workbench/contrib/logs/common/logsActions.ts +++ b/src/vs/workbench/contrib/logs/common/logsActions.ts @@ -94,7 +94,7 @@ export class OpenWindowSessionLogFileAction extends Action { placeHolder: nls.localize('log placeholder', "Select Log file") }); if (logFileResult) { - return this.editorService.openEditor({ resource: URI.parse(logFileResult.id!) }).then(() => undefined); + return this.editorService.openEditor({ resource: URI.parse(logFileResult.id!), options: { pinned: true } }).then(() => undefined); } } } diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index e04673226..208fea76b 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -18,8 +18,7 @@ import { ShowProblemsPanelAction } from 'vs/workbench/contrib/markers/browser/ma import Constants from 'vs/workbench/contrib/markers/browser/constants'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IMarkersWorkbenchService, MarkersWorkbenchService, ActivityUpdater } from 'vs/workbench/contrib/markers/browser/markers'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ActivityUpdater } from 'vs/workbench/contrib/markers/browser/markers'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -33,8 +32,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions'; import { Codicon } from 'vs/base/common/codicons'; - -registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService, false); +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.MARKER_OPEN_ACTION_ID, @@ -124,11 +122,13 @@ class ToggleMarkersPanelAction extends ToggleViewAction { } } +const markersViewIcon = registerIcon('markers-view-icon', Codicon.warning, localize('markersViewIcon', 'View icon of the markers view.')); + // markers view container const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: Constants.MARKERS_CONTAINER_ID, name: Messages.MARKERS_PANEL_TITLE_PROBLEMS, - icon: Codicon.warning.classNames, + icon: markersViewIcon, hideIfEmpty: true, order: 0, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [Constants.MARKERS_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), @@ -142,7 +142,7 @@ const VIEW_CONTAINER: ViewContainer = Registry.as(ViewC Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: Constants.MARKERS_VIEW_ID, - containerIcon: Codicon.warning.classNames, + containerIcon: markersViewIcon, name: Messages.MARKERS_PANEL_TITLE_PROBLEMS, canToggleVisibility: false, canMoveView: true, diff --git a/src/vs/workbench/contrib/markers/browser/markers.ts b/src/vs/workbench/contrib/markers/browser/markers.ts index 383d52fca..196235465 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.ts @@ -3,55 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { MarkersModel, compareMarkersByUri } from './markersModel'; import { Disposable, MutableDisposable, IDisposable } from 'vs/base/common/lifecycle'; -import { IMarkerService, MarkerSeverity, IMarker } from 'vs/platform/markers/common/markers'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { localize } from 'vs/nls'; import Constants from './constants'; -import { URI } from 'vs/base/common/uri'; -import { groupBy } from 'vs/base/common/arrays'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { Event } from 'vs/base/common/event'; -import { ResourceMap } from 'vs/base/common/map'; - -export const IMarkersWorkbenchService = createDecorator('markersWorkbenchService'); - -export interface IMarkersWorkbenchService { - readonly _serviceBrand: undefined; - readonly markersModel: MarkersModel; -} - -export class MarkersWorkbenchService extends Disposable implements IMarkersWorkbenchService { - declare readonly _serviceBrand: undefined; - - readonly markersModel: MarkersModel; - - constructor( - @IMarkerService private readonly markerService: IMarkerService, - @IInstantiationService instantiationService: IInstantiationService, - ) { - super(); - this.markersModel = this._register(instantiationService.createInstance(MarkersModel)); - - this.markersModel.setResourceMarkers(groupBy(this.readMarkers(), compareMarkersByUri).map(group => [group[0].resource, group])); - this._register(Event.debounce>(markerService.onMarkerChanged, (resourcesMap, resources) => { - resourcesMap = resourcesMap ? resourcesMap : new ResourceMap(); - resources.forEach(resource => resourcesMap!.set(resource, resource)); - return resourcesMap; - }, 0)(resourcesMap => this.onMarkerChanged([...resourcesMap.values()]))); - } - - private onMarkerChanged(resources: URI[]): void { - this.markersModel.setResourceMarkers(resources.map(resource => [resource, this.readMarkers(resource)])); - } - - private readMarkers(resource?: URI): IMarker[] { - return this.markerService.read({ resource, severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }); - } - -} export class ActivityUpdater extends Disposable implements IWorkbenchContribution { diff --git a/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts b/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts index 393094a99..d6c633ebe 100644 --- a/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts @@ -4,10 +4,41 @@ *--------------------------------------------------------------------------------------------*/ import { IFilter, matchesFuzzy, matchesFuzzy2 } from 'vs/base/common/filters'; -import { IExpression, splitGlobAware, getEmptyExpression } from 'vs/base/common/glob'; +import { IExpression, splitGlobAware, getEmptyExpression, ParsedExpression, parse } from 'vs/base/common/glob'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { ResourceGlobMatcher } from 'vs/base/common/resources'; +import { relativePath } from 'vs/base/common/resources'; +import { TernarySearchTree } from 'vs/base/common/map'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; + +export class ResourceGlobMatcher { + + private readonly globalExpression: ParsedExpression; + private readonly expressionsByRoot: TernarySearchTree; + + constructor( + globalExpression: IExpression, + rootExpressions: { root: URI, expression: IExpression }[], + uriIdentityService: IUriIdentityService + ) { + this.globalExpression = parse(globalExpression); + this.expressionsByRoot = TernarySearchTree.forUris<{ root: URI, expression: ParsedExpression }>(uri => uriIdentityService.extUri.ignorePathCasing(uri)); + for (const expression of rootExpressions) { + this.expressionsByRoot.set(expression.root, { root: expression.root, expression: parse(expression.expression) }); + } + } + + matches(resource: URI): boolean { + const rootExpression = this.expressionsByRoot.findSubstr(resource); + if (rootExpression) { + const path = relativePath(rootExpression.root, resource); + if (path && !!rootExpression.expression(path)) { + return true; + } + } + return !!this.globalExpression(resource.path); + } +} export class FilterOptions { @@ -21,7 +52,16 @@ export class FilterOptions { readonly excludesMatcher: ResourceGlobMatcher; readonly includesMatcher: ResourceGlobMatcher; - constructor(readonly filter: string = '', filesExclude: { root: URI, expression: IExpression }[] | IExpression = [], showWarnings: boolean = false, showErrors: boolean = false, showInfos: boolean = false) { + static EMPTY(uriIdentityService: IUriIdentityService) { return new FilterOptions('', [], false, false, false, uriIdentityService); } + + constructor( + readonly filter: string, + filesExclude: { root: URI, expression: IExpression }[] | IExpression, + showWarnings: boolean, + showErrors: boolean, + showInfos: boolean, + uriIdentityService: IUriIdentityService + ) { filter = filter.trim(); this.showWarnings = showWarnings; this.showErrors = showErrors; @@ -43,8 +83,8 @@ export class FilterOptions { } } - this.excludesMatcher = new ResourceGlobMatcher(excludesExpression, filesExcludeByRoot); - this.includesMatcher = new ResourceGlobMatcher(includeExpression, []); + this.excludesMatcher = new ResourceGlobMatcher(excludesExpression, filesExcludeByRoot, uriIdentityService); + this.includesMatcher = new ResourceGlobMatcher(includeExpression, [], uriIdentityService); this.textFilter = this.textFilter.trim(); } diff --git a/src/vs/workbench/contrib/markers/browser/markersModel.ts b/src/vs/workbench/contrib/markers/browser/markersModel.ts index f8016d3d6..a12e1658e 100644 --- a/src/vs/workbench/contrib/markers/browser/markersModel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersModel.ts @@ -12,6 +12,7 @@ import { ResourceMap } from 'vs/base/common/map'; import { Emitter, Event } from 'vs/base/common/event'; import { Hasher } from 'vs/base/common/hash'; import { withUndefinedAsNull } from 'vs/base/common/types'; +import { splitLines } from 'vs/base/common/strings'; export function compareMarkersByUri(a: IMarker, b: IMarker) { @@ -95,7 +96,7 @@ export class Marker { private _lines: string[] | undefined; get lines(): string[] { if (!this._lines) { - this._lines = this.marker.message.split(/\r\n|\r|\n/g); + this._lines = splitLines(this.marker.message); } return this._lines; } @@ -150,6 +151,16 @@ export class MarkersModel { this.resourcesByUri = new Map(); } + reset(): void { + const removed = new Set(); + for (const resourceMarker of this.resourcesByUri.values()) { + removed.add(resourceMarker); + } + this.resourcesByUri.clear(); + this._total = 0; + this._onDidChange.fire({ removed, added: new Set(), updated: new Set() }); + } + private _total: number = 0; get total(): number { return this._total; diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 67f945f0b..6a344c8a0 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -14,7 +14,7 @@ import { ResourceMarkers, Marker, RelatedInformation } from 'vs/workbench/contri import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; -import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { QuickFixAction, QuickFixActionViewItem } from 'vs/workbench/contrib/markers/browser/markersViewActions'; @@ -53,6 +53,8 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Progress } from 'vs/platform/progress/common/progress'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export type TreeElement = ResourceMarkers | Marker | RelatedInformation; @@ -255,9 +257,10 @@ export class MarkerRenderer implements ITreeRenderer 1; action.tooltip = multiline ? localize('single line', "Show message in single line") : localize('multi line', "Show message in multiple lines"); - action.class = multiline ? expandedClass : collapsedClass; + action.class = ThemeIcon.asClassName(multiline ? expandedIcon : collapsedIcon); action.run = () => { if (viewModel) { viewModel.multiline = !viewModel.multiline; } return Promise.resolve(); }; this.multilineActionbar.push([action], { icon: true, label: false }); } diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 2163578c6..8836c8371 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -11,7 +11,7 @@ import { IAction, IActionViewItem, Action, Separator } from 'vs/base/common/acti import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; -import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent } from 'vs/workbench/contrib/markers/browser/markersModel'; +import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent, MarkersModel, compareMarkersByUri } from 'vs/workbench/contrib/markers/browser/markersModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MarkersFilterActionViewItem, MarkersFilters, IMarkersFiltersChangeEvent, IMarkerFilterController } from 'vs/workbench/contrib/markers/browser/markersViewActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -19,8 +19,7 @@ import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations'; import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IMarkersWorkbenchService } from 'vs/workbench/contrib/markers/browser/markers'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService, ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Iterable } from 'vs/base/common/iterator'; @@ -39,7 +38,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { domEvent } from 'vs/base/browser/event'; import { ResourceLabels } from 'vs/workbench/browser/labels'; -import { IMarker } from 'vs/platform/markers/common/markers'; +import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { MementoObject, Memento } from 'vs/workbench/common/memento'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -51,6 +50,11 @@ import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { Codicon } from 'vs/base/common/codicons'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { groupBy } from 'vs/base/common/arrays'; +import { ResourceMap } from 'vs/base/common/map'; +import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterable> { return Iterable.map(resourceMarkers.markers, m => { @@ -67,7 +71,9 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { private currentActiveResource: URI | null = null; private readonly rangeHighlightDecorations: RangeHighlightDecorations; + private readonly markersModel: MarkersModel; private readonly filter: Filter; + private readonly onVisibleDisposables = this._register(new DisposableStore()); private tree: MarkersTree | undefined; private filterActionBar: ActionBar | undefined; @@ -102,11 +108,12 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { @IEditorService private readonly editorService: IEditorService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService telemetryService: ITelemetryService, - @IMarkersWorkbenchService private readonly markersWorkbenchService: IMarkersWorkbenchService, + @IMarkerService private readonly markerService: IMarkerService, @IContextKeyService contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IContextMenuService contextMenuService: IContextMenuService, @IMenuService private readonly menuService: IMenuService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IKeybindingService keybindingService: IKeybindingService, @IStorageService storageService: IStorageService, @IOpenerService openerService: IOpenerService, @@ -114,17 +121,15 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.smallLayoutContextKey = Constants.MarkersViewSmallLayoutContextKey.bindTo(this.contextKeyService); - this.panelState = new Memento(Constants.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE); + this.panelState = new Memento(Constants.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE, StorageTarget.USER); + this.markersModel = this._register(instantiationService.createInstance(MarkersModel)); this.markersViewModel = this._register(instantiationService.createInstance(MarkersViewModel, this.panelState['multiline'])); - for (const resourceMarker of this.markersWorkbenchService.markersModel.resourceMarkers) { - resourceMarker.markers.forEach(marker => this.markersViewModel.add(marker)); - } - this._register(this.markersViewModel.onDidChange(marker => this.onDidChangeViewState(marker))); + this._register(this.onDidChangeVisibility(visible => this.onDidChangeMarkersViewVisibility(visible))); this.setCurrentActiveEditor(); - this.filter = new Filter(new FilterOptions()); + this.filter = new Filter(FilterOptions.EMPTY(uriIdentityService)); this.rangeHighlightDecorations = this._register(this.instantiationService.createInstance(RangeHighlightDecorations)); // actions @@ -152,18 +157,9 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { this.createArialLabelElement(container); this.createMessageBox(container); this.createTree(container); - this.createListeners(); this.updateFilter(); - this._register(this.onDidChangeVisibility(visible => { - if (visible) { - this.refreshPanel(); - } else { - this.rangeHighlightDecorations.removeHighlightRange(); - } - })); - this.filterActionBar!.push(new Action(`workbench.actions.treeView.${this.id}.filter`)); this.renderContent(); } @@ -225,7 +221,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { group: 'navigation', order: Number.MAX_SAFE_INTEGER, }, - icon: { id: 'codicon/collapse-all' } + icon: Codicon.collapseAll }); } async run(): Promise { @@ -332,11 +328,15 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } private setTreeSelection(): void { - if (this.tree && this.tree.getSelection().length === 0) { - const firstMarker = this.markersWorkbenchService.markersModel.resourceMarkers[0]?.markers[0]; - if (firstMarker) { - this.tree.setFocus([firstMarker]); - this.tree.setSelection([firstMarker]); + if (this.tree && this.tree.isVisible() && this.tree.getSelection().length === 0) { + const firstVisibleElement = this.tree.firstVisibleElement; + const marker = firstVisibleElement ? + firstVisibleElement instanceof ResourceMarkers ? firstVisibleElement.markers[0] : + firstVisibleElement instanceof Marker ? firstVisibleElement : undefined + : undefined; + if (marker) { + this.tree.setFocus([marker]); + this.tree.setSelection([marker]); } } } @@ -352,20 +352,20 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { let resourceMarkers: ResourceMarkers[] = []; if (this.filters.activeFile) { if (this.currentActiveResource) { - const activeResourceMarkers = this.markersWorkbenchService.markersModel.getResourceMarkers(this.currentActiveResource); + const activeResourceMarkers = this.markersModel.getResourceMarkers(this.currentActiveResource); if (activeResourceMarkers) { resourceMarkers = [activeResourceMarkers]; } } } else { - resourceMarkers = this.markersWorkbenchService.markersModel.resourceMarkers; + resourceMarkers = this.markersModel.resourceMarkers; } this.tree.setChildren(null, Iterable.map(resourceMarkers, m => ({ element: m, children: createResourceMarkersIterator(m) }))); } private updateFilter() { this.cachedFilterStats = undefined; - this.filter.options = new FilterOptions(this.filters.filterText, this.getFilesExcludeExpressions(), this.filters.showWarnings, this.filters.showErrors, this.filters.showInfos); + this.filter.options = new FilterOptions(this.filters.filterText, this.getFilesExcludeExpressions(), this.filters.showWarnings, this.filters.showErrors, this.filters.showInfos, this.uriIdentityService); if (this.tree) { this.tree.refilter(); } @@ -497,6 +497,8 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } } })); + + this._register(this.tree.onDidChangeSelection(() => this.onSelected())); } private collapseAll(): void { @@ -509,18 +511,45 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } } - private createListeners(): void { - this._register(Event.any(this.markersWorkbenchService.markersModel.onDidChange, this.editorService.onDidActiveEditorChange)(changes => { + private onDidChangeMarkersViewVisibility(visible: boolean): void { + this.onVisibleDisposables.clear(); + if (visible) { + for (const disposable of this.reInitialize()) { + this.onVisibleDisposables.add(disposable); + } + this.refreshPanel(); + } + } + + private reInitialize(): IDisposable[] { + const disposables = []; + + // Markers Model + const readMarkers = (resource?: URI) => this.markerService.read({ resource, severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }); + this.markersModel.setResourceMarkers(groupBy(readMarkers(), compareMarkersByUri).map(group => [group[0].resource, group])); + disposables.push(Event.debounce>(this.markerService.onMarkerChanged, (resourcesMap, resources) => { + resourcesMap = resourcesMap || new ResourceMap(); + resources.forEach(resource => resourcesMap!.set(resource, resource)); + return resourcesMap; + }, 64)(resourcesMap => { + this.markersModel.setResourceMarkers([...resourcesMap.values()].map(resource => [resource, readMarkers(resource)])); + })); + disposables.push(Event.any(this.markersModel.onDidChange, this.editorService.onDidActiveEditorChange)(changes => { if (changes) { this.onDidChangeModel(changes); } else { this.onActiveEditorChanged(); } })); - if (this.tree) { - this._register(this.tree.onDidChangeSelection(() => this.onSelected())); - } - this._register(this.filters.onDidChange((event: IMarkersFiltersChangeEvent) => { + disposables.push(toDisposable(() => this.markersModel.reset())); + + // Markers View Model + this.markersModel.resourceMarkers.forEach(resourceMarker => resourceMarker.markers.forEach(marker => this.markersViewModel.add(marker))); + disposables.push(this.markersViewModel.onDidChange(marker => this.onDidChangeViewState(marker))); + disposables.push(toDisposable(() => this.markersModel.resourceMarkers.forEach(resourceMarker => this.markersViewModel.remove(resourceMarker.resource)))); + + // Markers Filters + disposables.push(this.filters.onDidChange((event: IMarkersFiltersChangeEvent) => { this.reportFilteringUsed(); if (event.activeFile) { this.refreshPanel(); @@ -528,14 +557,19 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { this.updateFilter(); } })); + disposables.push(toDisposable(() => { this.cachedFilterStats = undefined; })); + + disposables.push(toDisposable(() => this.rangeHighlightDecorations.removeHighlightRange())); + + return disposables; } - private onDidChangeModel(change: MarkerChangesEvent) { + private onDidChangeModel(change: MarkerChangesEvent): void { const resourceMarkers = [...change.added, ...change.removed, ...change.updated]; const resources: URI[] = []; for (const { resource } of resourceMarkers) { this.markersViewModel.remove(resource); - const resourceMarkers = this.markersWorkbenchService.markersModel.getResourceMarkers(resource); + const resourceMarkers = this.markersModel.getResourceMarkers(resource); if (resourceMarkers) { for (const marker of resourceMarkers.markers) { this.markersViewModel.add(marker); @@ -574,7 +608,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { private setCurrentActiveEditor(): void { const activeEditor = this.editorService.activeEditor; - this.currentActiveResource = activeEditor ? withUndefinedAsNull(activeEditor.resource) : null; + this.currentActiveResource = activeEditor ? withUndefinedAsNull(EditorResourceAccessor.getOriginalUri(activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY })) : null; } private onSelected(): void { @@ -631,7 +665,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } private renderFilterMessageForActiveFile(container: HTMLElement): void { - if (this.currentActiveResource && this.markersWorkbenchService.markersModel.getResourceMarkers(this.currentActiveResource)) { + if (this.currentActiveResource && this.markersModel.getResourceMarkers(this.currentActiveResource)) { this.renderFilteredByFilterMessage(container); } else { this.renderNoProblemsMessageForActiveFile(container); @@ -716,7 +750,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } private getResourceForCurrentActiveResource(): ResourceMarkers | null { - return this.currentActiveResource ? this.markersWorkbenchService.markersModel.getResourceMarkers(this.currentActiveResource) : null; + return this.currentActiveResource ? this.markersModel.getResourceMarkers(this.currentActiveResource) : null; } private hasSelectedMarkerFor(resource: ResourceMarkers): boolean { @@ -844,7 +878,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } } - return { total: this.markersWorkbenchService.markersModel.total, filtered }; + return { total: this.markersModel.total, filtered }; } private getTelemetryData({ source, code }: IMarker): any { @@ -917,6 +951,10 @@ class MarkersTree extends WorkbenchObjectTree { this.container.classList.toggle('hidden', hide); } + isVisible(): boolean { + return !this.container.classList.contains('hidden'); + } + } registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index 55fbbbb14..973e9e697 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -12,7 +12,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; -import { IThemeService, registerThemingParticipant, ICssStyleCollector, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, ICssStyleCollector, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -28,6 +28,7 @@ import { IViewsService } from 'vs/workbench/common/views'; import { Codicon } from 'vs/base/common/codicons'; import { BaseActionViewItem, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export class ShowProblemsPanelAction extends Action { @@ -256,6 +257,9 @@ class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { } + +const filterIcon = registerIcon('markers-view-filter', Codicon.filter, localize('filterIcon', 'Icon for the filter configuration in the markers view.')); + export class MarkersFilterActionViewItem extends BaseActionViewItem { private delayedFilterUpdate: Delayer; @@ -279,7 +283,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); this._register(filterController.onDidFocusFilter(() => this.focus())); this._register(filterController.onDidClearFilterText(() => this.clearFilterText())); - this.filtersAction = new Action('markersFiltersAction', Messages.MARKERS_PANEL_ACTION_TOOLTIP_MORE_FILTERS, 'markers-filters codicon-filter'); + this.filtersAction = new Action('markersFiltersAction', Messages.MARKERS_PANEL_ACTION_TOOLTIP_MORE_FILTERS, 'markers-filters ' + ThemeIcon.asClassName(filterIcon)); this.filtersAction.checked = this.hasFiltersChanged(); this._register(filterController.filters.onDidChange(e => this.onDidFiltersChange(e))); } diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts index 245f72a87..3232b0474 100644 --- a/src/vs/workbench/contrib/notebook/browser/constants.ts +++ b/src/vs/workbench/contrib/notebook/browser/constants.ts @@ -22,7 +22,7 @@ export const CELL_TOP_MARGIN = 6; export const CELL_BOTTOM_MARGIN = 6; // Top and bottom padding inside the monaco editor in a cell, which are included in `cell.editorHeight` -export const EDITOR_TOP_PADDING = 12; +// export const EDITOR_TOP_PADDING = 12; export const EDITOR_BOTTOM_PADDING = 4; export const EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR = 12; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 337cc9eb5..acf32d1a9 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -6,7 +6,7 @@ import * as glob from 'vs/base/common/glob'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -21,12 +21,13 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { CATEGORIES } from 'vs/workbench/common/actions'; -import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, IActiveNotebookEditor, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellEditType, CellKind, ICellEditOperation, ICellRange, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, ICellEditOperation, ICellRange, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BEGIN_END, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; // Notebook Commands const EXECUTE_NOTEBOOK_COMMAND_ID = 'notebook.execute'; @@ -64,7 +65,6 @@ const SPLIT_CELL_COMMAND_ID = 'notebook.cell.split'; const JOIN_CELL_ABOVE_COMMAND_ID = 'notebook.cell.joinAbove'; const JOIN_CELL_BELOW_COMMAND_ID = 'notebook.cell.joinBelow'; -const EXECUTE_CELL_COMMAND_ID = 'notebook.cell.execute'; const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution'; const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow'; const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow'; @@ -103,7 +103,7 @@ const enum CellOverflowToolbarGroups { export interface INotebookActionContext { readonly cellTemplate?: BaseCellRenderTemplate; readonly cell?: ICellViewModel; - readonly notebookEditor: INotebookEditor; + readonly notebookEditor: IActiveNotebookEditor; readonly ui?: boolean; } @@ -162,6 +162,10 @@ abstract class NotebookAction extends Action2 { return; } + if (!editor.hasModel()) { + return; + } + const activeCell = editor.getActiveCell(); return { cell: activeCell, @@ -233,7 +237,7 @@ registerAction2(class extends NotebookCellAction { } ] }, - icon: { id: 'codicon/play' }, + icon: icons.executeIcon }); } @@ -267,7 +271,7 @@ registerAction2(class extends NotebookCellAction { super({ id: CANCEL_CELL_COMMAND_ID, title: localize('notebookActions.cancel', "Stop Cell Execution"), - icon: { id: 'codicon/primitive-square' }, + icon: icons.stopIcon, description: { description: localize('notebookActions.execute', "Execute Cell"), args: [ @@ -326,7 +330,7 @@ export class ExecuteCellAction extends MenuItemAction { { id: EXECUTE_CELL_COMMAND_ID, title: localize('notebookActions.executeCell', "Execute Cell"), - icon: { id: 'codicon/play' } + icon: icons.executeIcon }, undefined, { shouldForwardArgs: true }, @@ -344,7 +348,7 @@ export class CancelCellAction extends MenuItemAction { { id: CANCEL_CELL_COMMAND_ID, title: localize('notebookActions.CancelCell', "Cancel Execution"), - icon: { id: 'codicon/primitive-square' } + icon: icons.stopIcon }, undefined, { shouldForwardArgs: true }, @@ -362,7 +366,7 @@ export class DeleteCellAction extends MenuItemAction { { id: DELETE_CELL_COMMAND_ID, title: localize('notebookActions.deleteCell', "Delete Cell"), - icon: { id: 'codicon/trash' } + icon: icons.deleteCellIcon, }, undefined, { shouldForwardArgs: true }, @@ -385,7 +389,7 @@ registerAction2(class extends NotebookCellAction { } async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - const idx = context.notebookEditor.viewModel?.getCellIndex(context.cell); + const idx = context.notebookEditor.viewModel.getCellIndex(context.cell); if (typeof idx !== 'number') { return; } @@ -393,7 +397,7 @@ registerAction2(class extends NotebookCellAction { const executionP = runCell(accessor, context); // Try to select below, fall back on inserting - const nextCell = context.notebookEditor.viewModel?.viewCells[idx + 1]; + const nextCell = context.notebookEditor.viewModel.viewCells[idx + 1]; if (nextCell) { context.notebookEditor.focusNotebookCell(nextCell, 'container'); } else { @@ -471,7 +475,7 @@ registerAction2(class extends NotebookAction { }); function renderAllMarkdownCells(context: INotebookActionContext): void { - context.notebookEditor.viewModel!.viewCells.forEach(cell => { + context.notebookEditor.viewModel.viewCells.forEach(cell => { if (cell.cellKind === CellKind.Markdown) { cell.editState = CellEditState.Preview; } @@ -508,7 +512,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: EXECUTE_NOTEBOOK_COMMAND_ID, title: localize('notebookActions.menu.executeNotebook', "Execute Notebook (Run all cells)"), - icon: { id: 'codicon/run-all' } + icon: icons.executeAllIcon, }, order: -1, group: 'navigation', @@ -519,7 +523,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CANCEL_NOTEBOOK_COMMAND_ID, title: localize('notebookActions.menu.cancelNotebook', "Stop Notebook Execution"), - icon: { id: 'codicon/primitive-square' } + icon: icons.stopIcon, }, order: -1, group: 'navigation', @@ -836,7 +840,7 @@ registerAction2(class extends NotebookCellAction { order: CellToolbarOrder.EditCell, group: CELL_TITLE_CELL_GROUP_ID }, - icon: { id: 'codicon/pencil' } + icon: icons.editIcon, }); } @@ -860,13 +864,15 @@ registerAction2(class extends NotebookCellAction { order: CellToolbarOrder.SaveCell, group: CELL_TITLE_CELL_GROUP_ID }, - icon: { id: 'codicon/check' }, + icon: icons.stopEditIcon, keybinding: { when: ContextKeyExpr.and( NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext, EditorContextKeys.hoverVisible.toNegated(), - EditorContextKeys.hasNonEmptySelection.toNegated()), + EditorContextKeys.hasNonEmptySelection.toNegated(), + EditorContextKeys.hasMultipleSelections.toNegated() + ), primary: KeyCode.Escape, weight: EDITOR_WIDGET_ACTION_WEIGHT - 5 }, @@ -900,19 +906,19 @@ registerAction2(class extends NotebookCellAction { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), weight: KeybindingWeight.WorkbenchContrib }, - icon: { id: 'codicon/trash' }, + icon: icons.deleteCellIcon }); } async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - const index = context.notebookEditor.viewModel!.getCellIndex(context.cell); + const index = context.notebookEditor.viewModel.getCellIndex(context.cell); const result = await context.notebookEditor.deleteNotebookCell(context.cell); if (result) { // deletion succeeds, move focus to the next cell - const nextCellIdx = index < context.notebookEditor.viewModel!.length ? index : context.notebookEditor.viewModel!.length - 1; + const nextCellIdx = index < context.notebookEditor.viewModel.length ? index : context.notebookEditor.viewModel.length - 1; if (nextCellIdx >= 0) { - context.notebookEditor.focusNotebookCell(context.notebookEditor.viewModel!.viewCells[nextCellIdx], 'container'); + context.notebookEditor.focusNotebookCell(context.notebookEditor.viewModel.viewCells[nextCellIdx], 'container'); } } } @@ -944,7 +950,7 @@ registerAction2(class extends NotebookCellAction { { id: MOVE_CELL_UP_COMMAND_ID, title: localize('notebookActions.moveCellUp', "Move Cell Up"), - icon: { id: 'codicon/arrow-up' }, + icon: icons.moveUpIcon, keybinding: { primary: KeyMod.Alt | KeyCode.UpArrow, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()), @@ -964,7 +970,7 @@ registerAction2(class extends NotebookCellAction { { id: MOVE_CELL_DOWN_COMMAND_ID, title: localize('notebookActions.moveCellDown', "Move Cell Down"), - icon: { id: 'codicon/arrow-down' }, + icon: icons.moveDownIcon, keybinding: { primary: KeyMod.Alt | KeyCode.DownArrow, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()), @@ -1139,7 +1145,7 @@ registerAction2(class extends NotebookCellAction { return; } - const currCellIndex = viewModel.getCellIndex(context!.cell); + const currCellIndex = viewModel.getCellIndex(context.cell); let topPastedCell: CellViewModel | undefined = undefined; pasteCells.items.reverse().map(cell => { @@ -1237,12 +1243,12 @@ registerAction2(class extends NotebookCellAction { const editor = context.notebookEditor; const activeCell = context.cell; - const idx = editor.viewModel?.getCellIndex(activeCell); + const idx = editor.viewModel.getCellIndex(activeCell); if (typeof idx !== 'number') { return; } - const newCell = editor.viewModel?.viewCells[idx + 1]; + const newCell = editor.viewModel.viewCells[idx + 1]; if (!newCell) { return; @@ -1276,7 +1282,7 @@ registerAction2(class extends NotebookCellAction { const editor = context.notebookEditor; const activeCell = context.cell; - const idx = editor.viewModel?.getCellIndex(activeCell); + const idx = editor.viewModel.getCellIndex(activeCell); if (typeof idx !== 'number') { return; } @@ -1286,7 +1292,7 @@ registerAction2(class extends NotebookCellAction { return; } - const newCell = editor.viewModel?.viewCells[idx - 1]; + const newCell = editor.viewModel.viewCells[idx - 1]; if (!newCell) { return; @@ -1407,7 +1413,7 @@ registerAction2(class extends NotebookCellAction { primary: KeyMod.Alt | KeyCode.Delete, weight: KeybindingWeight.WorkbenchContrib }, - icon: { id: 'codicon/clear-all' }, + icon: icons.clearIcon }); } @@ -1427,7 +1433,7 @@ registerAction2(class extends NotebookCellAction { editor.viewModel.notebookDocument.applyEdits(editor.viewModel.notebookDocument.versionId, [{ editType: CellEditType.Output, index, outputs: [] }], true, undefined, () => undefined, undefined); if (context.cell.metadata && context.cell.metadata?.runState !== NotebookCellRunState.Running) { - context.notebookEditor.viewModel!.notebookDocument.applyEdits(context.notebookEditor.viewModel!.notebookDocument.versionId, [{ + context.notebookEditor.viewModel.notebookDocument.applyEdits(context.notebookEditor.viewModel.notebookDocument.versionId, [{ editType: CellEditType.Metadata, index, metadata: { ...context.cell.metadata, runState: NotebookCellRunState.Idle, @@ -1466,7 +1472,7 @@ export class ChangeCellLanguageAction extends NotebookCellAction { const modelService = accessor.get(IModelService); const quickInputService = accessor.get(IQuickInputService); - const providerLanguages = [...context.notebookEditor.viewModel!.notebookDocument.resolvedLanguages, 'markdown']; + const providerLanguages = [...context.notebookEditor.viewModel.notebookDocument.resolvedLanguages, 'markdown']; providerLanguages.forEach(languageId => { let description: string; if (context.cell.cellKind === CellKind.Markdown ? (languageId === 'markdown') : (languageId === context.cell.language)) { @@ -1515,9 +1521,9 @@ export class ChangeCellLanguageAction extends NotebookCellAction { } else if (selection.languageId !== 'markdown' && context.cell?.cellKind === CellKind.Markdown) { await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, selection.languageId); } else { - const index = context.notebookEditor.viewModel!.notebookDocument.cells.indexOf(context.cell.model); - context.notebookEditor.viewModel!.notebookDocument.applyEdits( - context.notebookEditor.viewModel!.notebookDocument.versionId, + const index = context.notebookEditor.viewModel.notebookDocument.cells.indexOf(context.cell.model); + context.notebookEditor.viewModel.notebookDocument.applyEdits( + context.notebookEditor.viewModel.notebookDocument.versionId, [{ editType: CellEditType.CellLanguage, index, language: selection.languageId }], true, undefined, () => undefined, undefined ); @@ -1557,7 +1563,7 @@ registerAction2(class extends NotebookAction { group: 'navigation', order: 0 }, - icon: { id: 'codicon/clear-all' }, + icon: icons.clearIcon }); } @@ -1589,7 +1595,7 @@ registerAction2(class extends NotebookAction { } }).filter(edit => !!edit) as ICellEditOperation[]; if (clearExecutionMetadataEdits.length) { - context.notebookEditor.viewModel!.notebookDocument.applyEdits(context.notebookEditor.viewModel!.notebookDocument.versionId, clearExecutionMetadataEdits, true, undefined, () => undefined, undefined); + context.notebookEditor.viewModel.notebookDocument.applyEdits(context.notebookEditor.viewModel.notebookDocument.versionId, clearExecutionMetadataEdits, true, undefined, () => undefined, undefined); } } }); @@ -1609,7 +1615,7 @@ registerAction2(class extends NotebookCellAction { title: localize('notebookActions.splitCell', "Split Cell"), menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED), + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_EDITOR_CURSOR_BEGIN_END.toNegated()), order: CellToolbarOrder.SplitCell, group: CELL_TITLE_CELL_GROUP_ID, // alt: { @@ -1617,9 +1623,9 @@ registerAction2(class extends NotebookCellAction { // title: localize('notebookActions.joinCellBelow', "Join with Next Cell") // } }, - icon: { id: 'codicon/split-vertical' }, + icon: icons.splitCellIcon, keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED), + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_EDITOR_CURSOR_BEGIN_END.toNegated()), primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH), weight: KeybindingWeight.WorkbenchContrib }, @@ -1713,7 +1719,7 @@ registerAction2(class extends NotebookCellAction { abstract class ChangeNotebookCellMetadataAction extends NotebookCellAction { async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { const cell = context.cell; - const textModel = context.notebookEditor.viewModel?.notebookDocument; + const textModel = context.notebookEditor.viewModel.notebookDocument; if (!textModel) { return; } @@ -1840,6 +1846,10 @@ registerAction2(class extends Action2 { return; } + if (!editor.hasModel()) { + return; + } + const activeCell = editor.getActiveCell(); return { cell: activeCell, @@ -1851,7 +1861,7 @@ registerAction2(class extends Action2 { const activeEditorContext = this.getActiveEditorContext(accessor); if (activeEditorContext) { - const viewModel = activeEditorContext.notebookEditor.viewModel!; + const viewModel = activeEditorContext.notebookEditor.viewModel; console.log('--- notebook ---'); console.log(viewModel.layoutInfo); console.log('--- cells ---'); @@ -1865,6 +1875,19 @@ registerAction2(class extends Action2 { } }); +// Revisit once we have a story for trusted workspace +CommandsRegistry.registerCommand('notebook.trust', (accessor, args) => { + const uri = URI.revive(args as UriComponents); + const notebookService = accessor.get(INotebookService); + + + const document = notebookService.listNotebookDocuments().find(document => document.uri.toString() === uri.toString()); + + if (document) { + document.applyEdits(document.versionId, [{ editType: CellEditType.DocumentMetadata, metadata: { ...document.metadata, ...{ trusted: true } } }], true, undefined, () => undefined, undefined, false); + } +}); + CommandsRegistry.registerCommand('_resolveNotebookContentProvider', (accessor, args): { viewType: string; displayName: string; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts index 71f749486..2c2c46b89 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts @@ -43,6 +43,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote private _currentMatchDecorations: ICellModelDecorations[] = []; private _showTimeout: number | null = null; private _hideTimeout: number | null = null; + private _previousFocusElement?: HTMLElement; constructor( private readonly _notebookEditor: INotebookEditor, @@ -65,6 +66,10 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote this._register(this._state.onFindReplaceStateChange(() => { this.onInputChanged(); })); + + this._register(DOM.addDisposableListener(this.getDomNode(), DOM.EventType.FOCUS, e => { + this._previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined; + }, true)); } private _onFindInputKeyDown(e: IKeyboardEvent): void { @@ -114,7 +119,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote if (!this._findMatchesStarts) { this.set(this._findMatches, true); } else { - const totalVal = this._findMatchesStarts!.getTotalValue(); + const totalVal = this._findMatchesStarts.getTotalValue(); const nextVal = (this._currentMatch + (previous ? -1 : 1) + totalVal) % totalVal; this._currentMatch = nextVal; } @@ -167,6 +172,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote } protected onFocusTrackerBlur() { + this._previousFocusElement = undefined; this._findWidgetFocused.reset(); } @@ -324,6 +330,11 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote } else { // no op } + + if (this._previousFocusElement && this._previousFocusElement.offsetParent) { + this._previousFocusElement.focus(); + this._previousFocusElement = undefined; + } } clear() { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts index 0e4f30a00..0235b4431 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts @@ -116,7 +116,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont const target = e.event.target as HTMLElement; - if (target.classList.contains('codicon-chevron-down') || target.classList.contains('codicon-chevron-right')) { + if (target.classList.contains('codicon-notebook-collapsed') || target.classList.contains('codicon-notebook-expanded')) { const parent = target.parentElement as HTMLElement; if (!parent.classList.contains('notebook-folding-indicator')) { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts index 3675729c2..777b97b7d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts @@ -48,7 +48,11 @@ export class FoldingModel extends Disposable { })); this._viewModelStore.add(this._viewModel.onDidChangeSelection(() => { - const selectionHandles = this._viewModel!.selectionHandles; + if (!this._viewModel) { + return; + } + + const selectionHandles = this._viewModel.selectionHandles; const indexes = selectionHandles.map(handle => this._viewModel!.getCellIndex(this._viewModel!.getCellByHandle(handle)!) ); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts deleted file mode 100644 index 670a805cf..000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts +++ /dev/null @@ -1,163 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { INotebookEditorContribution, INotebookEditor } from '../../notebookBrowser'; -import { registerNotebookContribution } from '../../notebookEditorExtensions'; -import { ISCMService } from 'vs/workbench/contrib/scm/common/scm'; -import { createProviderComparer } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; -import { first, ThrottledDelayer } from 'vs/base/common/async'; -import { INotebookService } from '../../../common/notebookService'; -import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { FileService } from 'vs/platform/files/common/fileService'; -import { IFileService } from 'vs/platform/files/common/files'; -import { URI } from 'vs/base/common/uri'; - -export class SCMController extends Disposable implements INotebookEditorContribution { - static id: string = 'workbench.notebook.findController'; - private _lastDecorationId: string[] = []; - private _localDisposable = new DisposableStore(); - private _originalDocument: NotebookTextModel | undefined = undefined; - private _originalResourceDisposableStore = new DisposableStore(); - private _diffDelayer = new ThrottledDelayer(200); - - private _lastVersion = -1; - - - constructor( - private readonly _notebookEditor: INotebookEditor, - @IFileService private readonly _fileService: FileService, - @ISCMService private readonly _scmService: ISCMService, - @INotebookService private readonly _notebookService: INotebookService - - ) { - super(); - - if (!this._notebookEditor.isEmbedded) { - this._register(this._notebookEditor.onDidChangeModel(() => { - this._localDisposable.clear(); - this._originalResourceDisposableStore.clear(); - this._diffDelayer.cancel(); - this.update(); - - if (this._notebookEditor.textModel) { - this._localDisposable.add(this._notebookEditor.textModel.onDidChangeContent((e) => { - this.update(); - })); - } - })); - - this._register(this._notebookEditor.onWillDispose(() => { - this._localDisposable.clear(); - this._originalResourceDisposableStore.clear(); - })); - - this.update(); - } - } - - private async _resolveNotebookDocument(uri: URI, viewType: string) { - const providers = this._scmService.repositories.map(r => r.provider); - const rootedProviders = providers.filter(p => !!p.rootUri); - - rootedProviders.sort(createProviderComparer(uri)); - - const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri))); - - if (!result) { - this._originalDocument = undefined; - this._originalResourceDisposableStore.clear(); - return; - } - - if (result.toString() === this._originalDocument?.uri.toString()) { - // original document not changed - return; - } - - this._originalResourceDisposableStore.add(this._fileService.watch(result)); - this._originalResourceDisposableStore.add(this._fileService.onDidFilesChange(e => { - if (e.contains(result)) { - this._originalDocument = undefined; - this._originalResourceDisposableStore.clear(); - this.update(); - } - })); - - const originalDocument = await this._notebookService.resolveNotebook(viewType, result, false); - this._originalResourceDisposableStore.add({ - dispose: () => { - this._originalDocument?.dispose(); - this._originalDocument = undefined; - } - }); - - this._originalDocument = originalDocument; - } - - async update() { - if (!this._diffDelayer) { - return; - } - - await this._diffDelayer - .trigger(async () => { - const modifiedDocument = this._notebookEditor.textModel; - if (!modifiedDocument) { - return; - } - - if (this._lastVersion >= modifiedDocument.versionId) { - return; - } - - this._lastVersion = modifiedDocument.versionId; - await this._resolveNotebookDocument(modifiedDocument.uri, modifiedDocument.viewType); - - if (!this._originalDocument) { - this._clear(); - return; - } - - // const diff = new LcsDiff(new CellSequence(this._originalDocument), new CellSequence(modifiedDocument)); - // const diffResult = diff.ComputeDiff(false); - - // const decorations: INotebookDeltaDecoration[] = []; - // diffResult.changes.forEach(change => { - // if (change.originalLength === 0) { - // // doesn't exist in original - // for (let i = 0; i < change.modifiedLength; i++) { - // decorations.push({ - // handle: modifiedDocument.cells[change.modifiedStart + i].handle, - // options: { gutterClassName: 'nb-gutter-cell-inserted' } - // }); - // } - // } else { - // if (change.modifiedLength === 0) { - // // diff.deleteCount - // // removed from original - // } else { - // // modification - // for (let i = 0; i < change.modifiedLength; i++) { - // decorations.push({ - // handle: modifiedDocument.cells[change.modifiedStart + i].handle, - // options: { gutterClassName: 'nb-gutter-cell-changed' } - // }); - // } - // } - // } - // }); - - - // this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, decorations); - }); - } - - private _clear() { - this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, []); - } -} - -registerNotebookContribution(SCMController.id, SCMController); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts index 7d7abed7f..2f32225cb 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts @@ -21,6 +21,8 @@ import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/l import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar'; import { NotebookKernelProviderAssociation, NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { configureKernelIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; registerAction2(class extends Action2 { @@ -30,14 +32,13 @@ registerAction2(class extends Action2 { category: NOTEBOOK_ACTIONS_CATEGORY, title: { value: nls.localize('notebookActions.selectKernel', "Select Notebook Kernel"), original: 'Select Notebook Kernel' }, precondition: NOTEBOOK_IS_ACTIVE_EDITOR, - icon: { id: 'codicon/server-environment' }, + icon: selectKernelIcon, f1: true }); } async run(accessor: ServicesAccessor, context?: INotebookActionContext): Promise { const editorService = accessor.get(IEditorService); - const notebookService = accessor.get(INotebookService); const quickInputService = accessor.get(IQuickInputService); const configurationService = accessor.get(IConfigurationService); @@ -48,8 +49,14 @@ registerAction2(class extends Action2 { const editor = editorService.activeEditorPane?.getControl() as INotebookEditor; const activeKernel = editor.activeKernel; + const picker = quickInputService.createQuickPick<(IQuickPickItem & { run(): void; kernelProviderId?: string })>(); + picker.placeholder = nls.localize('notebook.runCell.selectKernel', "Select a notebook kernel to run this notebook"); + picker.matchOnDetail = true; + picker.show(); + picker.busy = true; + const tokenSource = new CancellationTokenSource(); - const availableKernels2 = await notebookService.getContributedNotebookKernels2(editor.viewModel!.viewType, editor.viewModel!.uri, tokenSource.token); + const availableKernels2 = await editor.beginComputeContributedKernels(); const picks: QuickPickInput[] = [...availableKernels2].map((a) => { return { id: a.id, @@ -68,17 +75,15 @@ registerAction2(class extends Action2 { a.resolve(editor.uri!, editor.getId(), tokenSource.token); }, buttons: [{ - iconClass: 'codicon-settings-gear', + iconClass: ThemeIcon.asClassName(configureKernelIcon), tooltip: nls.localize('notebook.promptKernel.setDefaultTooltip', "Set as default kernel provider for '{0}'", editor.viewModel!.viewType) }] }; }); - const picker = quickInputService.createQuickPick<(IQuickPickItem & { run(): void; kernelProviderId?: string })>(); picker.items = picks; + picker.busy = false; picker.activeItems = picks.filter(pick => (pick as IQuickPickItem).picked) as (IQuickPickItem & { run(): void; kernelProviderId?: string; })[]; - picker.placeholder = nls.localize('pickAction', "Select Action"); - picker.matchOnDetail = true; const pickedItem = await new Promise<(IQuickPickItem & { run(): void; kernelProviderId?: string; }) | undefined>(resolve => { picker.onDidAccept(() => { @@ -113,7 +118,6 @@ registerAction2(class extends Action2 { } }); - picker.show(); }); tokenSource.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index a53d35365..f526080b2 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -9,10 +9,9 @@ import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/edit import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CellDiffViewModel, PropertyFoldingState } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, DIFF_CELL_MARGIN, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; -import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; -import { renderCodicons } from 'vs/base/browser/codicons'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { format } from 'vs/base/common/jsonFormatter'; @@ -22,12 +21,16 @@ import { hash } from 'vs/base/common/hash'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; +import { getEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { renderCodicons } from 'vs/base/browser/codicons'; const fixedEditorOptions: IEditorOptions = { padding: { @@ -62,7 +65,8 @@ const fixedDiffEditorOptions: IDiffEditorOptions = { glyphMargin: true, enableSplitViewResizing: false, renderIndicators: false, - readOnly: false + readOnly: false, + isInEmbeddedEditor: true }; @@ -123,11 +127,13 @@ class PropertyHeader extends Disposable { return undefined; } }); + this._register(this._toolbar); this._toolbar.context = { cell: this.cell }; this._menu = this.menuService.createMenu(this.accessor.menuId, this.contextKeyService); + this._register(this._menu); if (metadataChanged) { const actions: IAction[] = []; @@ -142,7 +148,7 @@ class PropertyHeader extends Disposable { const target = e.event.target as HTMLElement; - if (target.classList.contains('codicon-chevron-down') || target.classList.contains('codicon-chevron-right')) { + if (target.classList.contains('codicon-notebook-collapsed') || target.classList.contains('codicon-notebook-expanded')) { const parent = target.parentElement as HTMLElement; if (!parent) { @@ -194,9 +200,9 @@ class PropertyHeader extends Disposable { private _updateFoldingIcon() { if (this.accessor.getFoldingState(this.cell) === PropertyFoldingState.Collapsed) { - DOM.reset(this._foldingIndicator, ...renderCodicons('$(chevron-right)')); + DOM.reset(this._foldingIndicator, ...renderCodicons(ThemeIcon.asCodiconLabel(collapsedIcon))); } else { - DOM.reset(this._foldingIndicator, ...renderCodicons('$(chevron-down)')); + DOM.reset(this._foldingIndicator, ...renderCodicons(ThemeIcon.asCodiconLabel(expandedIcon))); } } } @@ -228,6 +234,7 @@ abstract class AbstractCellRenderer extends Disposable { outputHeight: number; bodyMargin: number; }; + protected _isDisposed: boolean; constructor( readonly notebookEditor: INotebookTextDiffEditor, @@ -247,6 +254,7 @@ abstract class AbstractCellRenderer extends Disposable { ) { super(); // init + this._isDisposed = false; this._layoutInfo = { editorHeight: 0, editorMargin: 0, @@ -528,6 +536,7 @@ abstract class AbstractCellRenderer extends Disposable { originalEditable: false, ignoreTrimWhitespace: false }); + this._register(this._metadataEditor); this._metadataEditorContainer?.classList.add('diff'); @@ -583,6 +592,7 @@ abstract class AbstractCellRenderer extends Disposable { overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), readOnly: false }, {}); + this._register(this._metadataEditor); const mode = this.modeService.create('jsonc'); const originalMetadataSource = this._getFormatedMetadataJSON( @@ -632,6 +642,7 @@ abstract class AbstractCellRenderer extends Disposable { readOnly: true, ignoreTrimWhitespace: false }); + this._register(this._outputEditor); this._outputEditorContainer?.classList.add('diff'); @@ -671,10 +682,11 @@ abstract class AbstractCellRenderer extends Disposable { }, overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); + this._register(this._outputEditor); const mode = this.modeService.create('json'); const originaloutputSource = this._getFormatedOutputJSON( - this.notebookEditor.textModel!.transientOptions + this.notebookEditor.textModel!.transientOptions.transientOutputs ? [] : this.cell.type === 'insert' ? this.cell.modified!.outputs || [] @@ -706,6 +718,11 @@ abstract class AbstractCellRenderer extends Disposable { ); } + dispose() { + this._isDisposed = true; + super.dispose(); + } + abstract initData(): void; abstract styleContainer(container: HTMLElement): void; abstract buildSourceEditor(sourceContainer: HTMLElement): void; @@ -744,7 +761,7 @@ export class DeletedCell extends AbstractCellRenderer { const originalCell = this.cell.original!; const lineCount = originalCell.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const editorHeight = lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); @@ -756,6 +773,8 @@ export class DeletedCell extends AbstractCellRenderer { }, overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); + this._register(this._editor); + this._layoutInfo.editorHeight = editorHeight; this._register(this._editor.onDidContentSizeChange((e) => { @@ -766,6 +785,10 @@ export class DeletedCell extends AbstractCellRenderer { })); originalCell.resolveTextModelRef().then(ref => { + if (this._isDisposed) { + return; + } + this._register(ref); const textModel = ref.object.textEditorModel; @@ -838,7 +861,7 @@ export class InsertCell extends AbstractCellRenderer { const modifiedCell = this.cell.modified!; const lineCount = modifiedCell.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const editorHeight = lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { @@ -850,6 +873,7 @@ export class InsertCell extends AbstractCellRenderer { overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), readOnly: false }, {}); + this._register(this._editor); this._layoutInfo.editorHeight = editorHeight; @@ -861,6 +885,10 @@ export class InsertCell extends AbstractCellRenderer { })); modifiedCell.resolveTextModelRef().then(ref => { + if (this._isDisposed) { + return; + } + this._register(ref); const textModel = ref.object.textEditorModel; @@ -937,7 +965,7 @@ export class ModifiedCell extends AbstractCellRenderer { const modifiedCell = this.cell.modified!; const lineCount = modifiedCell.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const editorHeight = lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; this._editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); this._editor = this.instantiationService.createInstance(DiffEditorWidget, this._editorContainer, { @@ -946,6 +974,7 @@ export class ModifiedCell extends AbstractCellRenderer { originalEditable: false, ignoreTrimWhitespace: false }); + this._register(this._editor); this._editorContainer.classList.add('diff'); this._editor.layout({ @@ -982,6 +1011,7 @@ export class ModifiedCell extends AbstractCellRenderer { }; this._menu = this.menuService.createMenu(MenuId.NotebookDiffCellInputTitle, this.contextKeyService); + this._register(this._menu); const actions: IAction[] = []; createAndFillInActionBarActions(this._menu, { shouldForwardArgs: true }, actions); this._toolbar.setActions(actions); @@ -1007,6 +1037,11 @@ export class ModifiedCell extends AbstractCellRenderer { const originalRef = await originalCell.resolveTextModelRef(); const modifiedRef = await modifiedCell.resolveTextModelRef(); + + if (this._isDisposed) { + return; + } + const textModel = originalRef.object.textEditorModel; const modifiedTextModel = modifiedRef.object.textEditorModel; this._register(originalRef); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts index f91e70ba6..cb8709d16 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts @@ -7,11 +7,11 @@ import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/b import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; -import { ActiveEditorContext } from 'vs/workbench/common/editor'; +import { ActiveEditorContext, viewColumnToEditorGroup } from 'vs/workbench/common/editor'; import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { NotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor'; import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput'; +import { openAsTextIcon, revertIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -21,7 +21,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'notebook.diff.switchToText', - icon: { id: 'codicon/file-code' }, + icon: openAsTextIcon, title: { value: localize('notebook.diff.switchToText', "Open Text Diff Editor"), original: 'Open Text Diff Editor' }, precondition: ActiveEditorContext.isEqualTo(NotebookTextDiffEditor.ID), menu: [{ @@ -57,7 +57,7 @@ registerAction2(class extends Action2 { { id: 'notebook.diff.cell.revertMetadata', title: localize('notebook.diff.cell.revertMetadata', "Revert Metadata"), - icon: { id: 'codicon/discard' }, + icon: revertIcon, f1: false, menu: { id: MenuId.NotebookDiffCellMetadataTitle @@ -87,7 +87,7 @@ registerAction2(class extends Action2 { { id: 'notebook.diff.cell.revertOutputs', title: localize('notebook.diff.cell.revertOutputs', "Revert Outputs"), - icon: { id: 'codicon/discard' }, + icon: revertIcon, f1: false, menu: { id: MenuId.NotebookDiffCellOutputsTitle @@ -117,7 +117,7 @@ registerAction2(class extends Action2 { { id: 'notebook.diff.cell.revertInput', title: localize('notebook.diff.cell.revertInput', "Revert Input"), - icon: { id: 'codicon/discard' }, + icon: revertIcon, f1: false, menu: { id: MenuId.NotebookDiffCellInputTitle diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 6c6c52dc9..13f6973c0 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -60,6 +60,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD return this._model?.modified.notebook; } + private _revealFirst: boolean; + constructor( @IInstantiationService readonly instantiationService: IInstantiationService, @IThemeService readonly themeService: IThemeService, @@ -74,6 +76,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD super(NotebookTextDiffEditor.ID, telemetryService, themeService, storageService); const editorOptions = this.configurationService.getValue('editor'); this._fontInfo = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()); + this._revealFirst = true; this._register(this._modifiedResourceDisposableStore); } @@ -152,13 +155,15 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD return; } + this._revealFirst = true; + this._modifiedResourceDisposableStore.add(this._fileService.watch(this._model.modified.resource)); this._modifiedResourceDisposableStore.add(this._fileService.onDidFilesChange(async e => { if (this._model === null) { return; } - if (e.contains(this._model!.modified.resource)) { + if (e.contains(this._model.modified.resource)) { if (this._model.modified.isDirty()) { return; } @@ -174,7 +179,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } } - if (e.contains(this._model!.original.resource)) { + if (e.contains(this._model.original.resource)) { if (this._model.original.isDirty()) { return; } @@ -223,6 +228,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD let originalCellIndex = 0; let modifiedCellIndex = 0; + let firstChangeIndex = -1; + for (let i = 0; i < cellChanges.length; i++) { const change = cellChanges[i]; // common cells @@ -238,6 +245,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._eventDispatcher! )); } else { + if (firstChangeIndex === -1) { + firstChangeIndex = cellDiffViewModels.length; + } + cellDiffViewModels.push(new CellDiffViewModel( originalCell, modifiedCell, @@ -247,7 +258,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } } - cellDiffViewModels.push(...this._computeModifiedLCS(change, originalModel, modifiedModel)); + const modifiedLCS = this._computeModifiedLCS(change, originalModel, modifiedModel); + if (modifiedLCS.length && firstChangeIndex === -1) { + firstChangeIndex = cellDiffViewModels.length; + } + + cellDiffViewModels.push(...modifiedLCS); originalCellIndex = change.originalStart + change.originalLength; modifiedCellIndex = change.modifiedStart + change.modifiedLength; } @@ -262,6 +278,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } this._list.splice(0, this._list.length, cellDiffViewModels); + + if (this._revealFirst && firstChangeIndex !== -1) { + this._revealFirst = false; + this._list.setFocus([firstChangeIndex]); + this._list.reveal(firstChangeIndex, 0.3); + } } private _computeModifiedLCS(change: IDiffChange, originalModel: NotebookTextModel, modifiedModel: NotebookTextModel) { @@ -306,7 +328,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD layoutNotebookCell(cell: CellDiffViewModel, height: number) { const relayout = (cell: CellDiffViewModel, height: number) => { - const viewIndex = this._list!.indexOf(cell); + const viewIndex = this._list.indexOf(cell); this._list?.updateElementHeight(viewIndex, height); }; diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 526e80d5b..c2b2830c4 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -211,6 +211,15 @@ max-width: 100%; } +.monaco-workbench .notebookOverlay .output-show-more-container { + position: absolute; +} + +.monaco-workbench .notebookOverlay .output-show-more-container p { + padding: 8px 8px 0 8px; + margin: 0px; +} + .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu { position: absolute; left: 0; @@ -258,8 +267,8 @@ /* top and bottom borders on cells */ .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:before, +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:after { content: ""; position: absolute; width: 100%; @@ -268,35 +277,29 @@ /* top border */ .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before { +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:before { border-top: 1px solid transparent; } -.monaco-workbench.hc-black .notebookOverlay .monaco-list .monaco-list-row.focused.cell-editor-focus .cell-focus-indicator-top:before, -.monaco-workbench.hc-black .notebookOverlay .monaco-list .markdown-cell-row.focused.cell-editor-focus:before { - border-top-style: dashed; -} - /* bottom border */ .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:after { border-bottom: 1px solid transparent; } -.monaco-workbench.hc-black .notebookOverlay .monaco-list .monaco-list-row.focused.cell-editor-focus .cell-focus-indicator-bottom:before, -.monaco-workbench.hc-black .notebookOverlay .monaco-list .markdown-cell-row.focused.cell-editor-focus:after { - border-bottom-style: dashed; -} - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before { +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:before { top: 0; } .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:after { bottom: 0px; } +.monaco-workbench.hc-black .notebookOverlay .monaco-list-row .cell-editor-focus .cell-editor-part:before { + outline-style: dashed; +} + .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu.mouseover, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu:hover { cursor: pointer; @@ -433,9 +436,7 @@ } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container { position: relative; - height: 16px; flex-shrink: 0; - top: 9px; z-index: 27; /* Above the drag handle */ } @@ -445,8 +446,8 @@ } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar .codicon { - margin: 0; - padding-right: 4px; + margin: 0 4px 0 0; + padding: 6px; } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar .actions-container { @@ -479,7 +480,7 @@ } .monaco-workbench .notebookOverlay .cell .monaco-progress-container { - top: -5px; + top: -3px; position: absolute; left: 0; @@ -491,7 +492,8 @@ height: 2px; } -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell-has-toolbar-actions .cell-title-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list:focus-within > .monaco-scrollable-element > .monaco-list-rows:not(:hover) > .monaco-list-row.focused .cell-has-toolbar-actions .cell-title-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell-has-toolbar-actions .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions.cell-output-hover .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions:hover .cell-title-toolbar { visibility: visible; @@ -533,6 +535,11 @@ cursor: grab; } +.monaco-workbench .notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar.visible { + z-index: 25; + cursor: default; +} + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator .codicon:hover { cursor: pointer; } @@ -795,6 +802,7 @@ .monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator .codicon { visibility: visible; height: 16px; + padding: 4px; } /** Theming */ @@ -847,3 +855,11 @@ position: absolute; display: flex; } + +.output-show-more { + padding: 8px 0; +} + +.cell-contributed-items.cell-contributed-items-left { + margin-left: 4px; +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 7cd908830..b3e9c30f7 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -241,7 +241,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri }); }, open: (editor, options, group) => { - return this.onEditorOpening2(editor, options, group); + return this.onEditorOpening(editor, options, group); } })); @@ -285,7 +285,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri return this.notebookService.getContributedNotebookProviders(resource); } - private onEditorOpening2(originalInput: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined { + private onEditorOpening(originalInput: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined { let id = typeof options?.override === 'string' ? options.override : undefined; if (id === undefined && originalInput.resource?.scheme === Schemas.untitled) { @@ -304,6 +304,10 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri return undefined; } + if (originalInput instanceof NotebookDiffEditorInput) { + return undefined; + } + let notebookUri: URI = originalInput.resource; let cellOptions: IResourceEditorInput | undefined; @@ -319,7 +323,12 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri } if (id === undefined) { - const existingEditors = group.editors.filter(editor => editor.resource && isEqual(editor.resource, notebookUri) && !(editor instanceof NotebookEditorInput)); + const existingEditors = group.editors.filter(editor => + editor.resource + && isEqual(editor.resource, notebookUri) + && !(editor instanceof NotebookEditorInput) + && !(editor instanceof NotebookDiffEditorInput) + ); if (existingEditors.length) { return undefined; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 0306ba95e..1633a9f7b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -8,7 +8,7 @@ import { IListContextMenuEvent, IListEvent, IListMouseEvent } from 'vs/base/brow import { IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { Event } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { URI } from 'vs/base/common/uri'; @@ -60,6 +60,7 @@ export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey('notebo // Shared commands export const EXPAND_CELL_CONTENT_COMMAND_ID = 'notebook.cell.expandCellContent'; +export const EXECUTE_CELL_COMMAND_ID = 'notebook.cell.execute'; // Kernels @@ -91,6 +92,8 @@ export interface CodeCellLayoutInfo { readonly totalHeight: number; readonly outputContainerOffset: number; readonly outputTotalHeight: number; + readonly outputShowMoreContainerHeight: number; + readonly outputShowMoreContainerOffset: number; readonly indicatorHeight: number; readonly bottomToolbarOffset: number; readonly layoutState: CodeCellLayoutState; @@ -99,6 +102,7 @@ export interface CodeCellLayoutInfo { export interface CodeCellLayoutChangeEvent { editorHeight?: boolean; outputHeight?: boolean; + outputShowMoreContainerHeight?: number; totalHeight?: boolean; outerWidth?: number; font?: BareFontInfo; @@ -203,6 +207,11 @@ export interface INotebookEditorCreationOptions { readonly contributions?: INotebookEditorContributionDescription[]; } +export interface IActiveNotebookEditor extends INotebookEditor { + viewModel: NotebookViewModel; + uri: URI; +} + export interface INotebookEditor extends IEditor { isEmbedded: boolean; @@ -212,6 +221,7 @@ export interface INotebookEditor extends IEditor { * Notebook view model attached to the current editor */ viewModel: NotebookViewModel | undefined; + hasModel(): this is IActiveNotebookEditor; /** * An event emitted when the model of this editor has changed. @@ -256,11 +266,17 @@ export interface INotebookEditor extends IEditor { * Layout info for the notebook editor */ getLayoutInfo(): NotebookLayoutInfo; + /** * Fetch the output renderers for notebook outputs. */ getOutputRenderer(): OutputRenderer; + /** + * Fetch the contributed kernels for this notebook + */ + beginComputeContributedKernels(): Promise; + /** * Insert a new cell around `cell` */ @@ -493,6 +509,7 @@ export interface INotebookCellList { revealElementInView(element: ICellViewModel): void; revealElementInCenterIfOutsideViewport(element: ICellViewModel): void; revealElementInCenter(element: ICellViewModel): void; + revealElementInCenterIfOutsideViewportAsync(element: ICellViewModel): Promise; revealElementLineInViewAsync(element: ICellViewModel, line: number): Promise; revealElementLineInCenterAsync(element: ICellViewModel, line: number): Promise; revealElementLineInCenterIfOutsideViewportAsync(element: ICellViewModel, line: number): Promise; @@ -552,6 +569,7 @@ export interface CodeCellRenderTemplate extends BaseCellRenderTemplate { runButtonContainer: HTMLElement; executionOrderLabel: HTMLElement; outputContainer: HTMLElement; + outputShowMoreContainer: HTMLElement; focusSinkElement: HTMLElement; editor: ICodeEditor; progressBar: ProgressBar; @@ -708,3 +726,17 @@ export function getActiveNotebookEditor(editorService: IEditorService): INoteboo const activeEditorPane = editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined; return activeEditorPane?.isNotebookEditor ? (editorService.activeEditorPane?.getControl() as INotebookEditor) : undefined; } + +let EDITOR_TOP_PADDING = 12; +const editorTopPaddingChangeEmitter = new Emitter(); + +export const EditorTopPaddingChangeEvent = editorTopPaddingChangeEmitter.event; + +export function updateEditorTopPadding(top: number) { + EDITOR_TOP_PADDING = top; + editorTopPaddingChangeEmitter.fire(); +} + +export function getEditorTopPadding() { + return EDITOR_TOP_PADDING; +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts index 55391ea4f..3e8cd0308 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts @@ -14,7 +14,6 @@ import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IReference } from 'vs/base/common/lifecycle'; import { INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { IPathService } from 'vs/workbench/services/path/common/pathService'; interface NotebookEditorInputOptions { startDirty?: boolean; @@ -39,7 +38,6 @@ export class NotebookEditorInput extends EditorInput { @INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService, @IFilesConfigurationService private readonly _filesConfigurationService: IFilesConfigurationService, @IFileDialogService private readonly _fileDialogService: IFileDialogService, - @IPathService private readonly _pathService: IPathService, @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); @@ -145,7 +143,7 @@ ${patterns} } async suggestName(suggestedFilename: string) { - return joinPath(this._fileDialogService.defaultFilePath() || (await this._pathService.userHome()), suggestedFilename); + return joinPath(await this._fileDialogService.defaultFilePath(), suggestedFilename); } // called when users rename a notebook document diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 0aa5ef97e..b58b545fe 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -9,7 +9,7 @@ import * as strings from 'vs/base/common/strings'; import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { IAction, Separator } from 'vs/base/common/actions'; -import { SequencerByKey } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, SequencerByKey } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Color, RGBA } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -33,16 +33,16 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { contrastBorder, diffInserted, diffRemoved, editorBackground, errorForeground, focusBorder, foreground, listFocusBackground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; -import { IColorTheme, IThemeService, registerThemingParticipant, ThemeColor } from 'vs/platform/theme/common/themeService'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { contrastBorder, diffInserted, diffRemoved, editorBackground, errorForeground, focusBorder, foreground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, IThemeService, registerThemingParticipant, ThemeColor, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorMemento } from 'vs/workbench/common/editor'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { PANEL_BORDER } from 'vs/workbench/common/theme'; import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, ICellViewModel, INotebookCellList, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, IActiveNotebookEditor, ICellViewModel, INotebookCellList, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookKernelProviderAssociation, NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; @@ -50,7 +50,7 @@ import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/outpu import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CodeCellRenderer, ListTopCellToolbar, MarkdownCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; -import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; +import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; @@ -61,6 +61,7 @@ import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookS import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { configureKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; const $ = DOM.$; @@ -76,7 +77,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _webviewResolved: boolean = false; private _webviewResolvePromise: Promise | null = null; private _webviewTransparentCover: HTMLElement | null = null; - private _list: INotebookCellList | undefined; + private _list!: INotebookCellList; private _dndController: CellDragAndDropController | null = null; private _listTopCellToolbar: ListTopCellToolbar | null = null; private _renderedEditors: Map = new Map(); @@ -108,9 +109,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private readonly _insetModifyQueueByOutputId = new SequencerByKey(); set scrollTop(top: number) { - if (this._list) { - this._list.scrollTop = top; - } + this._list.scrollTop = top; } private _cellContextKeyManager: CellContextKeyManager | null = null; @@ -153,6 +152,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private readonly _onDidChangeAvailableKernels = this._register(new Emitter()); readonly onDidChangeAvailableKernels: Event = this._onDidChangeAvailableKernels.event; + private _contributedKernelsComputePromise: CancelablePromise | null = null; + get activeKernel() { return this._activeKernel; } @@ -162,6 +163,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } + if (!this.viewModel) { + return; + } + if (this._activeKernel === kernel) { return; } @@ -169,8 +174,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._activeKernel = kernel; this._activeKernelResolvePromise = undefined; - const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL); - memento[this.viewModel!.viewType] = this._activeKernel?.id; + const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + memento[this.viewModel.viewType] = this._activeKernel?.id; this._activeKernelMemento.saveMemento(); this._onDidChangeKernel.fire(); } @@ -197,7 +202,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - const [focused] = this._list!.getFocusedElements(); + const [focused] = this._list.getFocusedElements(); return this._renderedEditors.get(focused); } @@ -224,7 +229,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor onDidChangeVisibleRanges: Event = this._onDidChangeVisibleRanges.event; get visibleRanges() { - return this._list?.visibleRanges || []; + return this._list.visibleRanges || []; } readonly isEmbedded: boolean; @@ -273,6 +278,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor }); this.notebookService.addNotebookEditor(this); + this._createEditor(); } /** @@ -286,7 +292,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return this.viewModel?.selectionHandles || []; } - hasModel() { + hasModel(): this is IActiveNotebookEditor { return !!this._notebookViewModel; } @@ -305,7 +311,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } protected getMemento(scope: StorageScope): MementoObject { - return this._memento.getMemento(scope); + return this._memento.getMemento(scope, StorageTarget.MACHINE); } public get isNotebookEditor() { @@ -335,7 +341,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // a descendent of the notebook editor root. const focused = DOM.isAncestor(document.activeElement, this._overlayContainer); this._editorFocus?.set(focused); - this._notebookViewModel?.setFocus(focused); + this.viewModel?.setFocus(focused); } hasFocus() { @@ -380,7 +386,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return false; } - createEditor(): void { + private _createEditor(): void { const id = generateUuid(); this._overlayContainer.id = `notebook-${id}`; this._overlayContainer.className = 'notebookOverlay'; @@ -440,7 +446,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._body.classList.add('cell-list-container'); this._dndController = this._register(new CellDragAndDropController(this, this._body)); - const getScopedContextKeyService = (container?: HTMLElement) => this._list!.contextKeyService.createScoped(container); + const getScopedContextKeyService = (container?: HTMLElement) => this._list.contextKeyService.createScoped(container); const renderers = [ this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._dndController, getScopedContextKeyService), this.instantiationService.createInstance(MarkdownCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService), @@ -465,7 +471,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor enableKeyboardNavigation: true, additionalScrollHeight: 0, transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', - styleController: (_suffix: string) => { return this._list!; }, + styleController: (_suffix: string) => { return this._list; }, overrideStyles: { listBackground: editorBackground, listActiveSelectionBackground: editorBackground, @@ -583,7 +589,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // Will fire onDidChangeFocus, resetting the state to Container applyFocusChange(); - const newFocusedCell = this._list!.getFocusedElements()[0]; + const newFocusedCell = this._list.getFocusedElements()[0]; if (newFocusedCell.cellKind === CellKind.Code || newFocusedCell.editState === CellEditState.Editing) { this.focusNotebookCell(newFocusedCell, 'editor'); } else { @@ -621,9 +627,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor if (this._webiewFocused) { this._webview?.focusWebview(); } else { - const focus = this._list?.getFocus()[0]; - if (typeof focus === 'number') { - const element = this._notebookViewModel!.viewCells[focus]; + const focus = this._list.getFocus()[0]; + if (typeof focus === 'number' && this.viewModel) { + const element = this.viewModel.viewCells[focus]; if (element.focusMode === CellFocusMode.Editor) { element.editState = CellEditState.Editing; @@ -633,7 +639,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } - this._list?.domFocus(); + this._list.domFocus(); } this._onDidFocusEditorWidget.fire(); @@ -644,7 +650,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async setModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined): Promise { - if (this._notebookViewModel === undefined || !this._notebookViewModel.equal(textModel)) { + if (this.viewModel === undefined || !this.viewModel.equal(textModel)) { this._detachModel(); await this._attachModel(textModel, viewState); } else { @@ -657,7 +663,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._currentKernelTokenSource = new CancellationTokenSource(); this._localStore.add(this._currentKernelTokenSource); // we don't await for it, otherwise it will slow down the file opening - this._setKernels(textModel, this._currentKernelTokenSource); + this._setKernels(this._currentKernelTokenSource); this._localStore.add(this.notebookService.onDidChangeKernels(async (e) => { if (e && e.toString() !== this.textModel?.uri.toString()) { @@ -666,11 +672,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } this._currentKernelTokenSource?.cancel(); this._currentKernelTokenSource = new CancellationTokenSource(); - await this._setKernels(textModel, this._currentKernelTokenSource); + await this._setKernels(this._currentKernelTokenSource); })); - this._localStore.add(this._list!.onDidChangeFocus(() => { - const focused = this._list!.getFocusedElements()[0]; + this._localStore.add(this._list.onDidChangeFocus(() => { + const focused = this._list.getFocusedElements()[0]; if (focused) { if (!this._cellContextKeyManager) { this._cellContextKeyManager = this._localStore.add(new CellContextKeyManager(this.scopedContextKeyService, this, textModel, focused as CellViewModel)); @@ -683,12 +689,12 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor async setOptions(options: NotebookEditorOptions | undefined) { // reveal cell if editor options tell to do so - if (options?.cellOptions) { + if (options?.cellOptions && this.viewModel) { const cellOptions = options.cellOptions; - const cell = this._notebookViewModel!.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString()); + const cell = this.viewModel.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString()); if (cell) { this.selectElement(cell); - this.revealInCenterIfOutsideViewport(cell); + await this.revealInCenterIfOutsideViewportAsync(cell); const editor = this._renderedEditors.get(cell)!; if (editor) { if (cellOptions.options?.selection) { @@ -714,21 +720,40 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _detachModel() { this._localStore.clear(); - this._list?.detachViewModel(); + this._list.detachViewModel(); this.viewModel?.dispose(); // avoid event - this._notebookViewModel = undefined; + this.viewModel = undefined; // this.webview?.clearInsets(); // this.webview?.clearPreloadsCache(); this._webview?.dispose(); this._webview?.element.remove(); this._webview = null; - this._list?.clear(); + this._list.clear(); } - private async _setKernels(textModel: NotebookTextModel, tokenSource: CancellationTokenSource) { - const provider = this.notebookService.getContributedNotebookProvider(textModel.viewType) || this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; - const availableKernels2 = await this.notebookService.getContributedNotebookKernels2(textModel.viewType, textModel.uri, tokenSource.token); + async beginComputeContributedKernels() { + if (this._contributedKernelsComputePromise) { + return this._contributedKernelsComputePromise; + } + + this._contributedKernelsComputePromise = createCancelablePromise(token => { + return this.notebookService.getContributedNotebookKernels(this.viewModel!.viewType, this.viewModel!.uri, token); + }); + + const result = await this._contributedKernelsComputePromise; + this._contributedKernelsComputePromise = null; + + return result; + } + + private async _setKernels(tokenSource: CancellationTokenSource) { + if (!this.viewModel) { + return; + } + + const provider = this.notebookService.getContributedNotebookProvider(this.viewModel.viewType) || this.notebookService.getContributedNotebookProviders(this.viewModel.uri)[0]; + const availableKernels = await this.beginComputeContributedKernels(); if (tokenSource.token.isCancellationRequested) { return; @@ -738,7 +763,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - if ((availableKernels2.length) > 1) { + if ((availableKernels.length) > 1) { this._notebookHasMultipleKernels!.set(true); this.multipleKernelsAvailable = true; } else { @@ -746,30 +771,24 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.multipleKernelsAvailable = false; } - const activeKernelStillExist = [...availableKernels2].find(kernel => kernel.id === this.activeKernel?.id && this.activeKernel?.id !== undefined); + const activeKernelStillExist = [...availableKernels].find(kernel => kernel.id === this.activeKernel?.id && this.activeKernel?.id !== undefined); if (activeKernelStillExist) { // the kernel still exist, we don't want to modify the selection otherwise user's temporary preference is lost return; } - if (availableKernels2.length) { - return this._setKernelsFromProviders(provider, availableKernels2, tokenSource); + if (availableKernels.length) { + return this._setKernelsFromProviders(provider, availableKernels, tokenSource); } - // the provider doesn't have a builtin kernel, choose a kernel - // this.activeKernel = availableKernels[0]; - // if (this.activeKernel) { - // await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); - // } - tokenSource.dispose(); } private async _setKernelsFromProviders(provider: NotebookProviderInfo, kernels: INotebookKernelInfo2[], tokenSource: CancellationTokenSource) { const rawAssociations = this.configurationService.getValue(notebookKernelProviderAssociationsSettingId) || []; const userSetKernelProvider = rawAssociations.filter(e => e.viewType === this.viewModel?.viewType)[0]?.kernelProvider; - const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL); + const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); if (userSetKernelProvider) { const filteredKernels = kernels.filter(kernel => kernel.extension.value === userSetKernelProvider); @@ -860,9 +879,13 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } private _updateForMetadata(): void { - const notebookMetadata = this.viewModel!.metadata; + if (!this.viewModel) { + return; + } + + const notebookMetadata = this.viewModel.metadata; this._editorEditable?.set(!!notebookMetadata?.editable); - this._editorRunnable?.set(!!notebookMetadata?.runnable); + this._editorRunnable?.set(this.viewModel.runnable); this._overflowContainer.classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable); this.getDomNode().classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable); @@ -881,12 +904,20 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor if (!this._webview) { this._webview = this.instantiationService.createInstance(BackLayerWebView, this, this.getId(), this.textModel!.uri); // attach the webview container to the DOM tree first - this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); + this._list.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); } this._webviewResolvePromise = new Promise(async resolve => { - await this._webview!.createWebview(); - this._webview!.webview!.onDidBlur(() => { + if (!this._webview) { + throw new Error('Notebook output webview object is not created successfully.'); + } + + await this._webview.createWebview(); + if (!this._webview.webview) { + throw new Error('Notebook output webview elemented is not created successfully.'); + } + + this._webview.webview.onDidBlur(() => { this._outputFocus?.set(false); this.updateEditorFocus(); @@ -894,7 +925,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._webiewFocused = false; } }); - this._webview!.webview!.onDidFocus(() => { + this._webview.webview.onDidFocus(() => { this._outputFocus?.set(true); this.updateEditorFocus(); this._onDidFocusEmitter.fire(); @@ -904,7 +935,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } }); - this._localStore.add(this._webview!.onMessage(({ message, forRenderer }) => { + this._localStore.add(this._webview.onMessage(({ message, forRenderer }) => { if (this.viewModel) { this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message); } @@ -912,7 +943,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._webviewResolved = true; - resolve(this._webview!); + resolve(this._webview); }); return this._webviewResolvePromise; @@ -921,7 +952,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private async _createWebview(id: string, resource: URI): Promise { this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource); // attach the webview container to the DOM tree first - this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); + this._list.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); } private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) { @@ -959,7 +990,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._onDidChangeSelection.fire(); })); - this._localStore.add(this._list!.onWillScroll(e => { + this._localStore.add(this._list.onWillScroll(e => { this._onWillScroll.fire(e); if (!this._webviewResolved) { return; @@ -969,14 +1000,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._webviewTransparentCover!.style.top = `${e.scrollTop}px`; })); - this._localStore.add(this._list!.onDidChangeContentHeight(() => { + this._localStore.add(this._list.onDidChangeContentHeight(() => { DOM.scheduleAtNextAnimationFrame(() => { if (this._isDisposed) { return; } - const scrollTop = this._list?.scrollTop || 0; - const scrollHeight = this._list?.scrollHeight || 0; + const scrollTop = this._list.scrollTop; + const scrollHeight = this._list.scrollHeight; if (!this._webviewResolved) { return; @@ -989,7 +1020,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const removedItems: IProcessedOutput[] = []; this._webview?.insetMapping.forEach((value, key) => { const cell = value.cell; - const viewIndex = this._list?.getViewIndex(cell); + const viewIndex = this._list.getViewIndex(cell); if (viewIndex === undefined) { return; @@ -1000,7 +1031,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor removedItems.push(key); } - const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; + const cellTop = this._list.getAbsoluteTopOfElement(cell); if (this._webview!.shouldUpdateInset(cell, key, cellTop)) { updateItems.push({ cell: cell, @@ -1019,18 +1050,18 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor }); })); - this._list!.attachViewModel(this.viewModel); - this._localStore.add(this._list!.onDidRemoveOutput(output => { + this._list.attachViewModel(this.viewModel); + this._localStore.add(this._list.onDidRemoveOutput(output => { this.removeInset(output); })); - this._localStore.add(this._list!.onDidHideOutput(output => { + this._localStore.add(this._list.onDidHideOutput(output => { this.hideInset(output); })); if (this._dimension) { - this._list?.layout(this._dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, this._dimension.width); + this._list.layout(this._dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, this._dimension.width); } else { - this._list!.layout(); + this._list.layout(); } this._dndController?.clearGlobalDragState(); @@ -1041,23 +1072,23 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor restoreListViewState(viewState: INotebookEditorViewState | undefined): void { if (viewState?.scrollPosition !== undefined) { - this._list!.scrollTop = viewState!.scrollPosition.top; - this._list!.scrollLeft = viewState!.scrollPosition.left; + this._list.scrollTop = viewState!.scrollPosition.top; + this._list.scrollLeft = viewState!.scrollPosition.left; } else { - this._list!.scrollTop = 0; - this._list!.scrollLeft = 0; + this._list.scrollTop = 0; + this._list.scrollLeft = 0; } const focusIdx = typeof viewState?.focus === 'number' ? viewState.focus : 0; - if (focusIdx < this._list!.length) { - this._list!.setFocus([focusIdx]); - this._list!.setSelection([focusIdx]); - } else if (this._list!.length > 0) { - this._list!.setFocus([0]); + if (focusIdx < this._list.length) { + this._list.setFocus([focusIdx]); + this._list.setSelection([focusIdx]); + } else if (this._list.length > 0) { + this._list.setFocus([0]); } if (viewState?.editorFocused) { - const cell = this._notebookViewModel?.viewCells[focusIdx]; + const cell = this.viewModel?.viewCells[focusIdx]; if (cell) { cell.focusMode = CellFocusMode.Editor; } @@ -1065,7 +1096,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } getEditorViewState(): INotebookEditorViewState { - const state = this._notebookViewModel?.getEditorViewState(); + const state = this.viewModel?.getEditorViewState(); if (!state) { return { editingCells: {}, @@ -1088,10 +1119,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor state.cellTotalHeights = cellHeights; const focus = this._list.getFocus()[0]; - if (typeof focus === 'number') { - const element = this._notebookViewModel!.viewCells[focus]; + if (typeof focus === 'number' && this.viewModel) { + const element = this.viewModel.viewCells[focus]; if (element) { - const itemDOM = this._list?.domElementOfElement(element); + const itemDOM = this._list.domElementOfElement(element); const editorFocused = !!(document.activeElement && itemDOM && itemDOM.contains(document.activeElement)); state.editorFocused = editorFocused; @@ -1143,8 +1174,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._dimension = new DOM.Dimension(dimension.width, dimension.height); DOM.size(this._body, dimension.width, dimension.height); - this._list?.updateOptions({ additionalScrollHeight: this._scrollBeyondLastLine ? dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP : 0 }); - this._list?.layout(dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, dimension.width); + this._list.updateOptions({ additionalScrollHeight: this._scrollBeyondLastLine ? dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP : 0 }); + this._list.layout(dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, dimension.width); this._overlayContainer.style.visibility = 'visible'; this._overlayContainer.style.display = 'block'; @@ -1177,56 +1208,60 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#region Editor Features selectElement(cell: ICellViewModel) { - this._list?.selectElement(cell); + this._list.selectElement(cell); // this.viewModel!.selectionHandles = [cell.handle]; } revealInView(cell: ICellViewModel) { - this._list?.revealElementInView(cell); + this._list.revealElementInView(cell); } revealInCenterIfOutsideViewport(cell: ICellViewModel) { - this._list?.revealElementInCenterIfOutsideViewport(cell); + this._list.revealElementInCenterIfOutsideViewport(cell); + } + + async revealInCenterIfOutsideViewportAsync(cell: ICellViewModel) { + return this._list.revealElementInCenterIfOutsideViewportAsync(cell); } revealInCenter(cell: ICellViewModel) { - this._list?.revealElementInCenter(cell); + this._list.revealElementInCenter(cell); } async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise { - return this._list?.revealElementLineInViewAsync(cell, line); + return this._list.revealElementLineInViewAsync(cell, line); } async revealLineInCenterAsync(cell: ICellViewModel, line: number): Promise { - return this._list?.revealElementLineInCenterAsync(cell, line); + return this._list.revealElementLineInCenterAsync(cell, line); } async revealLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise { - return this._list?.revealElementLineInCenterIfOutsideViewportAsync(cell, line); + return this._list.revealElementLineInCenterIfOutsideViewportAsync(cell, line); } async revealRangeInViewAsync(cell: ICellViewModel, range: Range): Promise { - return this._list?.revealElementRangeInViewAsync(cell, range); + return this._list.revealElementRangeInViewAsync(cell, range); } async revealRangeInCenterAsync(cell: ICellViewModel, range: Range): Promise { - return this._list?.revealElementRangeInCenterAsync(cell, range); + return this._list.revealElementRangeInCenterAsync(cell, range); } async revealRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Range): Promise { - return this._list?.revealElementRangeInCenterIfOutsideViewportAsync(cell, range); + return this._list.revealElementRangeInCenterIfOutsideViewportAsync(cell, range); } setCellSelection(cell: ICellViewModel, range: Range): void { - this._list?.setCellSelection(cell, range); + this._list.setCellSelection(cell, range); } changeModelDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null { - return this._notebookViewModel?.changeModelDecorations(callback) || null; + return this.viewModel?.changeModelDecorations(callback) || null; } setHiddenAreas(_ranges: ICellRange[]): boolean { - return this._list!.setHiddenAreas(_ranges, true); + return this._list.setHiddenAreas(_ranges, true); } private _editorStyleSheets = new Map(); @@ -1301,7 +1336,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#region Cell operations async layoutNotebookCell(cell: ICellViewModel, height: number): Promise { - const viewIndex = this._list!.getViewIndex(cell); + const viewIndex = this._list.getViewIndex(cell); if (viewIndex === undefined) { // the cell is hidden return; @@ -1312,7 +1347,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - this._list?.updateElementHeight2(cell, height); + this._list.updateElementHeight2(cell, height); }; if (this.pendingLayouts.has(cell)) { @@ -1339,42 +1374,90 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return new Promise(resolve => { r = resolve; }); } + private _nearestCodeCellIndex(index: number /* exclusive */, direction: 'above' | 'below') { + if (!this.viewModel) { + return -1; + } + + const nearest = this.viewModel.viewCells.slice(0, index).reverse().findIndex(cell => cell.cellKind === CellKind.Code); + if (nearest > -1) { + return index - nearest - 1; + } else { + const nearestCellTheOtherDirection = this.viewModel.viewCells.slice(index + 1).findIndex(cell => cell.cellKind === CellKind.Code); + if (nearestCellTheOtherDirection > -1) { + return index + nearestCellTheOtherDirection; + } + return -1; + } + } + insertNotebookCell(cell: ICellViewModel | undefined, type: CellKind, direction: 'above' | 'below' = 'above', initialText: string = '', ui: boolean = false): CellViewModel | null { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { return null; } - const index = cell ? this._notebookViewModel!.getCellIndex(cell) : 0; - const nextIndex = ui ? this._notebookViewModel!.getNextVisibleCellIndex(index) : index + 1; - const newLanguages = this._notebookViewModel!.resolvedLanguages; - const language = (cell?.cellKind === CellKind.Code && type === CellKind.Code) - ? cell.language - : ((type === CellKind.Code && newLanguages && newLanguages.length) ? newLanguages[0] : 'markdown'); + if (!this.viewModel.metadata.editable) { + return null; + } + + const index = cell ? this.viewModel.getCellIndex(cell) : 0; + const nextIndex = ui ? this.viewModel.getNextVisibleCellIndex(index) : index + 1; + let language; + if (type === CellKind.Code) { + if (cell?.cellKind === CellKind.Code) { + language = cell.language; + } else if (cell?.cellKind === CellKind.Markdown) { + const nearestCodeCellIndex = this._nearestCodeCellIndex(index, direction); + if (nearestCodeCellIndex > -1) { + language = this.viewModel.viewCells[nearestCodeCellIndex].language; + } else { + language = this.viewModel.resolvedLanguages[0] || 'plaintext'; + } + } else { + if (cell === undefined && direction === 'above') { + // insert cell at the very top + language = this.viewModel.viewCells.find(cell => cell.cellKind === CellKind.Code)?.language || this.viewModel.resolvedLanguages[0] || 'plaintext'; + } else { + language = this.viewModel.resolvedLanguages[0] || 'plaintext'; + } + } + } else { + language = 'markdown'; + } + const insertIndex = cell ? (direction === 'above' ? index : nextIndex) : index; - const focused = this._list?.getFocusedElements(); - const newCell = this._notebookViewModel!.createCell(insertIndex, initialText, language, type, undefined, [], true, undefined, focused); + const focused = this._list.getFocusedElements(); + const newCell = this.viewModel.createCell(insertIndex, initialText, language, type, undefined, [], true, undefined, focused); return newCell as CellViewModel; } async splitNotebookCell(cell: ICellViewModel): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { return null; } - const index = this._notebookViewModel!.getCellIndex(cell); + if (!this.viewModel.metadata.editable) { + return null; + } - return this._notebookViewModel!.splitNotebookCell(index); + const index = this.viewModel.getCellIndex(cell); + + return this.viewModel.splitNotebookCell(index); } async joinNotebookCells(cell: ICellViewModel, direction: 'above' | 'below', constraint?: CellKind): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { return null; } - const index = this._notebookViewModel!.getCellIndex(cell); - const ret = await this._notebookViewModel!.joinNotebookCells(index, direction, constraint); + if (!this.viewModel.metadata.editable) { + return null; + } + + const index = this.viewModel.getCellIndex(cell); + const ret = await this.viewModel.joinNotebookCells(index, direction, constraint); if (ret) { ret.deletedCells.forEach(cell => { @@ -1390,7 +1473,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async deleteNotebookCell(cell: ICellViewModel): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { + return false; + } + + if (!this.viewModel.metadata.editable) { return false; } @@ -1398,18 +1485,22 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.pendingLayouts.get(cell)!.dispose(); } - const index = this._notebookViewModel!.getCellIndex(cell); - this._notebookViewModel!.deleteCell(index, true); + const index = this.viewModel.getCellIndex(cell); + this.viewModel.deleteCell(index, true); return true; } async moveCellDown(cell: ICellViewModel): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { return null; } - const index = this._notebookViewModel!.getCellIndex(cell); - if (index === this._notebookViewModel!.length - 1) { + if (!this.viewModel.metadata.editable) { + return null; + } + + const index = this.viewModel.getCellIndex(cell); + if (index === this.viewModel.length - 1) { return null; } @@ -1418,11 +1509,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async moveCellUp(cell: ICellViewModel): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { return null; } - const index = this._notebookViewModel!.getCellIndex(cell); + if (!this.viewModel.metadata.editable) { + return null; + } + + const index = this.viewModel.getCellIndex(cell); if (index === 0) { return null; } @@ -1432,7 +1527,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { + return null; + } + + if (!this.viewModel.metadata.editable) { return null; } @@ -1440,15 +1539,19 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return null; } - const originalIdx = this._notebookViewModel!.getCellIndex(cell); - const relativeToIndex = this._notebookViewModel!.getCellIndex(relativeToCell); + const originalIdx = this.viewModel.getCellIndex(cell); + const relativeToIndex = this.viewModel.getCellIndex(relativeToCell); const newIdx = direction === 'above' ? relativeToIndex : relativeToIndex + 1; return this._moveCellToIndex(originalIdx, 1, newIdx); } async moveCellsToIdx(index: number, length: number, toIdx: number): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { + return null; + } + + if (!this.viewModel.metadata.editable) { return null; } @@ -1461,6 +1564,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor * @example to move the cell from index 0 down one spot, call with (0, 2) */ private async _moveCellToIndex(index: number, length: number, desiredIndex: number): Promise { + if (!this.viewModel) { + return null; + } + if (index < desiredIndex) { // The cell is moving "down", it will free up one index spot and consume a new one desiredIndex -= length; @@ -1470,7 +1577,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return null; } - if (!this._notebookViewModel!.moveCellToIdx(index, length, desiredIndex, true)) { + if (!this.viewModel.moveCellToIdx(index, length, desiredIndex, true)) { throw new Error('Notebook Editor move cell, index out of range'); } @@ -1478,10 +1585,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor DOM.scheduleAtNextAnimationFrame(() => { if (this._isDisposed) { r(null); + return; } - const viewCell = this._notebookViewModel!.viewCells[desiredIndex]; - this._list?.revealElementInView(viewCell); + if (!this.viewModel) { + r(null); + return; + } + + const viewCell = this.viewModel.viewCells[desiredIndex]; + this._list.revealElementInView(viewCell); r(viewCell); }); @@ -1489,7 +1602,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } editNotebookCell(cell: CellViewModel): void { - if (!cell.getEvaluatedMetadata(this._notebookViewModel!.metadata).editable) { + if (!this.viewModel) { + return; + } + + if (!cell.getEvaluatedMetadata(this.viewModel.metadata).editable) { return; } @@ -1499,7 +1616,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } getActiveCell() { - const elements = this._list?.getFocusedElements(); + const elements = this._list.getFocusedElements(); if (elements && elements.length) { return elements[0]; @@ -1519,9 +1636,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // pick active kernel + const picker = this.quickInputService.createQuickPick<(IQuickPickItem & { run(): void; kernelProviderId?: string })>(); + picker.placeholder = nls.localize('notebook.runCell.selectKernel', "Select a notebook kernel to run this notebook"); + picker.matchOnDetail = true; + picker.busy = true; + picker.show(); + const tokenSource = new CancellationTokenSource(); - const availableKernels2 = await this.notebookService.getContributedNotebookKernels2(this.viewModel!.viewType, this.viewModel!.uri, tokenSource.token); - const picks: QuickPickInput[] = availableKernels2.map((a) => { + const availableKernels = await this.beginComputeContributedKernels(); + const picks: QuickPickInput[] = availableKernels.map((a) => { return { id: a.id, label: a.label, @@ -1537,16 +1660,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._activeKernelResolvePromise = this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token); }, buttons: [{ - iconClass: 'codicon-settings-gear', + iconClass: ThemeIcon.asClassName(configureKernelIcon), tooltip: nls.localize('notebook.promptKernel.setDefaultTooltip', "Set as default kernel provider for '{0}'", this.viewModel!.viewType) }] }; }); - const picker = this.quickInputService.createQuickPick<(IQuickPickItem & { run(): void; kernelProviderId?: string })>(); picker.items = picks; - picker.placeholder = nls.localize('notebook.runCell.selectKernel', "Select a notebook kernel to run this notebook"); - picker.matchOnDetail = true; + picker.busy = false; const pickedItem = await new Promise<(IQuickPickItem & { run(): void; kernelProviderId?: string; }) | undefined>(resolve => { picker.onDidAccept(() => { @@ -1581,7 +1702,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } }); - picker.show(); }); tokenSource.dispose(); @@ -1594,29 +1714,41 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async cancelNotebookExecution(): Promise { - if (this._notebookViewModel?.metadata.runState !== NotebookRunState.Running) { + if (!this.viewModel) { + return; + } + + if (this.viewModel.metadata.runState !== NotebookRunState.Running) { return; } await this._ensureActiveKernel(); - await this._activeKernel?.cancelNotebookCell!(this._notebookViewModel!.uri, undefined); + await this._activeKernel?.cancelNotebookCell!(this.viewModel.uri, undefined); } async executeNotebook(): Promise { - if (!this._notebookViewModel!.metadata.runnable) { + if (!this.viewModel) { + return; + } + + if (!this.viewModel.runnable) { return; } await this._ensureActiveKernel(); - await this._activeKernel?.executeNotebookCell!(this._notebookViewModel!.uri, undefined); + await this._activeKernel?.executeNotebookCell!(this.viewModel.uri, undefined); } async cancelNotebookCellExecution(cell: ICellViewModel): Promise { + if (!this.viewModel) { + return; + } + if (cell.cellKind !== CellKind.Code) { return; } - const metadata = cell.getEvaluatedMetadata(this._notebookViewModel!.metadata); + const metadata = cell.getEvaluatedMetadata(this.viewModel.metadata); if (!metadata.runnable) { return; } @@ -1626,21 +1758,25 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } await this._ensureActiveKernel(); - await this._activeKernel?.cancelNotebookCell!(this._notebookViewModel!.uri, cell.handle); + await this._activeKernel?.cancelNotebookCell!(this.viewModel.uri, cell.handle); } async executeNotebookCell(cell: ICellViewModel): Promise { + if (!this.viewModel) { + return; + } + if (cell.cellKind === CellKind.Markdown) { this.focusNotebookCell(cell, 'container'); return; } - if (!cell.getEvaluatedMetadata(this._notebookViewModel!.metadata).runnable) { + if (!cell.getEvaluatedMetadata(this.viewModel.metadata).runnable) { return; } await this._ensureActiveKernel(); - await this._activeKernel?.executeNotebookCell!(this._notebookViewModel!.uri, cell.handle); + await this._activeKernel?.executeNotebookCell!(this.viewModel.uri, cell.handle); } focusNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output') { @@ -1650,14 +1786,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor if (focusItem === 'editor') { this.selectElement(cell); - this._list?.focusView(); + this._list.focusView(); cell.editState = CellEditState.Editing; cell.focusMode = CellFocusMode.Editor; this.revealInCenterIfOutsideViewport(cell); } else if (focusItem === 'output') { this.selectElement(cell); - this._list?.focusView(); + this._list.focusView(); if (!this._webview) { return; @@ -1668,7 +1804,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor cell.focusMode = CellFocusMode.Container; this.revealInCenterIfOutsideViewport(cell); } else { - const itemDOM = this._list?.domElementOfElement(cell); + const itemDOM = this._list.domElementOfElement(cell); if (document.activeElement && itemDOM && itemDOM.contains(document.activeElement)) { (document.activeElement as HTMLElement).blur(); } @@ -1678,7 +1814,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.selectElement(cell); this.revealInCenterIfOutsideViewport(cell); - this._list?.focusView(); + this._list.focusView(); } } @@ -1687,7 +1823,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#region MISC deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] { - return this._notebookViewModel?.deltaCellDecorations(oldDecorations, newDecorations) || []; + return this.viewModel?.deltaCellDecorations(oldDecorations, newDecorations) || []; } deltaCellOutputContainerClassNames(cellId: string, added: string[], removed: string[]) { @@ -1707,7 +1843,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } triggerScroll(event: IMouseWheelEvent) { - this._list?.triggerScrollFromMouseWheelEvent(event); + this._list.triggerScrollFromMouseWheelEvent(event); } async createInset(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number): Promise { @@ -1719,11 +1855,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor await this._resolveWebview(); if (!this._webview!.insetMapping.has(output.source)) { - const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; + const cellTop = this._list.getAbsoluteTopOfElement(cell); await this._webview!.createInset(cell, output, cellTop, offset); } else { - const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; - const scrollTop = this._list?.scrollTop || 0; + const cellTop = this._list.getAbsoluteTopOfElement(cell); + const scrollTop = this._list.scrollTop; this._webview!.updateViewScrollTop(-scrollTop, true, [{ cell, output: output.source, cellTop }]); } @@ -1809,7 +1945,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } this._localStore.clear(); - this._list?.dispose(); + this._list.dispose(); this._listTopCellToolbar?.dispose(); this._overlayContainer.remove(); @@ -1828,7 +1964,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } export const notebookCellBorder = registerColor('notebook.cellBorderColor', { - dark: transparent(PANEL_BORDER, .4), + dark: transparent(listInactiveSelectionBackground, 1), light: transparent(listInactiveSelectionBackground, 1), hc: PANEL_BORDER }, nls.localize('notebook.cellBorderColor', "The border color for notebook cells.")); @@ -1859,7 +1995,7 @@ export const cellStatusIconRunning = registerColor('notebookStatusRunningIcon.fo export const notebookOutputContainerColor = registerColor('notebook.outputContainerBackgroundColor', { dark: notebookCellBorder, - light: transparent(listFocusBackground, .4), + light: notebookCellBorder, hc: null }, nls.localize('notebook.outputContainerBackgroundColor', "The Color of the notebook output container background.")); @@ -1871,8 +2007,8 @@ export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeparat }, nls.localize('notebook.cellToolbarSeparator', "The color of the separator in the cell bottom toolbar")); export const focusedCellBackground = registerColor('notebook.focusedCellBackground', { - dark: transparent(PANEL_BORDER, .4), - light: transparent(listFocusBackground, .4), + dark: null, + light: null, hc: null }, nls.localize('focusedCellBackground', "The background color of a cell when the cell is focused.")); @@ -1882,9 +2018,15 @@ export const cellHoverBackground = registerColor('notebook.cellHoverBackground', hc: null }, nls.localize('notebook.cellHoverBackground', "The background color of a cell when the cell is hovered.")); +export const selectedCellBorder = registerColor('notebook.selectedCellBorder', { + dark: notebookCellBorder, + light: notebookCellBorder, + hc: contrastBorder +}, nls.localize('notebook.selectedCellBorder', "The color of the cell's top and bottom border when the cell is selected but not focusd.")); + export const focusedCellBorder = registerColor('notebook.focusedCellBorder', { - dark: Color.white.transparent(0.12), - light: Color.black.transparent(0.12), + dark: focusBorder, + light: focusBorder, hc: focusBorder }, nls.localize('notebook.focusedCellBorder', "The color of the cell's top and bottom border when the cell is focused.")); @@ -1935,12 +2077,17 @@ registerThemingParticipant((theme, collector) => { const link = theme.getColor(textLinkForeground); if (link) { collector.addRule(`.notebookOverlay .output a, - .notebookOverlay .cell.markdown a { color: ${link};} `); + .notebookOverlay .cell.markdown a, + .notebookOverlay .output-show-more-container a + { color: ${link};} `); + } const activeLink = theme.getColor(textLinkActiveForeground); if (activeLink) { collector.addRule(`.notebookOverlay .output a:hover, - .notebookOverlay .cell .output a:active { color: ${activeLink}; }`); + .notebookOverlay .cell .output a:active, + .notebookOverlay .output-show-more-container a:active + { color: ${activeLink}; }`); } const shortcut = theme.getColor(textPreformatForeground); if (shortcut) { @@ -1964,6 +2111,7 @@ registerThemingParticipant((theme, collector) => { if (containerBackground) { collector.addRule(`.notebookOverlay .output { background-color: ${containerBackground}; }`); collector.addRule(`.notebookOverlay .output-element { background-color: ${containerBackground}; }`); + collector.addRule(`.notebookOverlay .output-show-more-container { background-color: ${containerBackground}; }`); } const editorBackgroundColor = theme.getColor(editorBackground); @@ -2005,13 +2153,23 @@ registerThemingParticipant((theme, collector) => { } const focusedCellBorderColor = theme.getColor(focusedCellBorder); - collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-top:before, + collector.addRule(` + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-top:before, .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-bottom:before, - .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused:before, - .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused:after { + .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):after { border-color: ${focusedCellBorderColor} !important; }`); + const selectedCellBorderColor = theme.getColor(selectedCellBorder); + collector.addRule(` + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-editor-focus .cell-focus-indicator-top:before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-editor-focus .cell-focus-indicator-bottom:before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container.cell-editor-focus:before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container.cell-editor-focus:after { + border-color: ${selectedCellBorderColor} !important; + }`); + const cellSymbolHighlightColor = theme.getColor(cellSymbolHighlight); if (cellSymbolHighlightColor) { collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-symbolHighlight .cell-focus-indicator, @@ -2033,12 +2191,12 @@ registerThemingParticipant((theme, collector) => { const cellStatusSuccessIcon = theme.getColor(cellStatusIconSuccess); if (cellStatusSuccessIcon) { - collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-check { color: ${cellStatusSuccessIcon} }`); + collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-notebook-state-success { color: ${cellStatusSuccessIcon} }`); } const cellStatusErrorIcon = theme.getColor(cellStatusIconError); if (cellStatusErrorIcon) { - collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-error { color: ${cellStatusErrorIcon} }`); + collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-notebook-state-error { color: ${cellStatusErrorIcon} }`); } const cellStatusRunningIcon = theme.getColor(cellStatusIconRunning); @@ -2123,6 +2281,9 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`); + collector.addRule(`.notebookOverlay .output-show-more-container { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); + collector.addRule(`.notebookOverlay .output-show-more-container { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .cell .run-button-container { width: 20px; margin: 0px ${Math.floor(CELL_RUN_GUTTER - 20) / 2}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top { height: ${CELL_TOP_MARGIN}px; }`); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts index 783b82070..f9650ad09 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts @@ -127,7 +127,6 @@ class NotebookEditorWidgetService implements INotebookEditorWidgetService { // NEW widget const instantiationService = accessor.get(IInstantiationService); const widget = instantiationService.createInstance(NotebookEditorWidget, { isEmbedded: false }); - widget.createEditor(); const token = this._tokenPool++; value = { widget, token }; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts new file mode 100644 index 000000000..6780f1fa9 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const configureKernelIcon = registerIcon('notebook-kernel-configure', Codicon.settingsGear, localize('configureKernel', 'Configure icon in kernel configuation widget in notebook editors.')); +export const selectKernelIcon = registerIcon('notebook-kernel-select', Codicon.serverEnvironment, localize('selectKernelIcon', 'Configure icon to select a kernel in notebook editors.')); + +export const executeIcon = registerIcon('notebook-execute', Codicon.play, localize('executeIcon', 'Icon to execute in notebook editors.')); +export const stopIcon = registerIcon('notebook-stop', Codicon.primitiveSquare, localize('stopIcon', 'Icon to stop an execution in notebook editors.')); +export const deleteCellIcon = registerIcon('notebook-delete-cell', Codicon.trash, localize('deleteCellIcon', 'Icon to delete a cell in notebook editors.')); +export const executeAllIcon = registerIcon('notebook-execute-all', Codicon.runAll, localize('executeAllIcon', 'Icon to execute all cells in notebook editors.')); +export const editIcon = registerIcon('notebook-edit', Codicon.pencil, localize('editIcon', 'Icon to edit a cell in notebook editors.')); +export const stopEditIcon = registerIcon('notebook-stop-edit', Codicon.check, localize('stopEditIcon', 'Icon to stop editing a cell in notebook editors.')); +export const moveUpIcon = registerIcon('notebook-move-up', Codicon.arrowUp, localize('moveUpIcon', 'Icon to move a cell up in notebook editors.')); +export const moveDownIcon = registerIcon('notebook-move-down', Codicon.arrowDown, localize('moveDownIcon', 'Icon to move a cell down in notebook editors.')); +export const clearIcon = registerIcon('notebook-clear', Codicon.clearAll, localize('clearIcon', 'Icon to clear cell outputs in notebook editors.')); +export const splitCellIcon = registerIcon('notebook-split-cell', Codicon.splitVertical, localize('splitCellIcon', 'Icon to split a cell in notebook editors.')); +export const unfoldIcon = registerIcon('notebook-unfold', Codicon.unfold, localize('unfoldIcon', 'Icon to unfold a cell in notebook editors.')); + +export const successStateIcon = registerIcon('notebook-state-success', Codicon.check, localize('successStateIcon', 'Icon to indicate a success state in notebook editors.')); +export const errorStateIcon = registerIcon('notebook-state-error', Codicon.error, localize('errorStateIcon', 'Icon to indicate a error state in notebook editors.')); + +export const collapsedIcon = registerIcon('notebook-collapsed', Codicon.chevronRight, localize('collapsedIcon', 'Icon to annotated a collapsed section in notebook editors.')); +export const expandedIcon = registerIcon('notebook-expanded', Codicon.chevronDown, localize('expandedIcon', 'Icon to annotated a expanded section in notebook editors.')); +export const openAsTextIcon = registerIcon('notebook-open-as-text', Codicon.fileCode, localize('openAsTextIcon', 'Icon to open the notebook in a text editor.')); +export const revertIcon = registerIcon('notebook-revert', Codicon.discard, localize('revertIcon', 'Icon to revert in notebook editors.')); +export const mimetypeIcon = registerIcon('notebook-mimetype', Codicon.code, localize('mimetypeIcon', 'Icon for a mime type in notebook editors.')); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 36869d4c8..ec00de561 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getZoomLevel } from 'vs/base/browser/browser'; import { flatten } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; @@ -13,22 +14,25 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; import * as nls from 'vs/nls'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; import { Memento } from 'vs/workbench/common/memento'; import { INotebookEditorContribution, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; -import { getActiveNotebookEditor, INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, getActiveNotebookEditor, INotebookEditor, NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRegistry, updateNotebookKernelProvideAssociationSchema } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellKind, CellOutputKind, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellOutputsSplice, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellKind, CellOutputKind, DisplayOrderKey, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -88,7 +92,7 @@ export class NotebookProviderInfoStore extends Disposable { super(); this._memento = new Memento(NotebookProviderInfoStore.CUSTOM_EDITORS_STORAGE_ID, storageService); - const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); for (const info of (mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] || []) as NotebookEditorDescriptor[]) { this.add(new NotebookProviderInfo(info)); } @@ -129,7 +133,7 @@ export class NotebookProviderInfoStore extends Disposable { } } - const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values()); this._memento.saveMemento(); @@ -179,7 +183,7 @@ export class NotebookProviderInfoStore extends Disposable { } this._contributedEditors.set(info.id, info); - const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values()); this._memento.saveMemento(); } @@ -276,7 +280,9 @@ export class NotebookService extends Disposable implements INotebookService, ICu @IConfigurationService private readonly _configurationService: IConfigurationService, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @IStorageService private readonly _storageService: IStorageService, - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); @@ -336,6 +342,41 @@ export class NotebookService extends Disposable implements INotebookService, ICu updateOrder(); })); + let decorationTriggeredAdjustment = false; + let decorationCheckSet = new Set(); + this._register(this._codeEditorService.onDecorationTypeRegistered(e => { + if (decorationTriggeredAdjustment) { + return; + } + + if (decorationCheckSet.has(e)) { + return; + } + + const options = this._codeEditorService.resolveDecorationOptions(e, true); + if (options.afterContentClassName || options.beforeContentClassName) { + const cssRules = this._codeEditorService.resolveDecorationCSSRules(e); + if (cssRules !== null) { + for (let i = 0; i < cssRules.length; i++) { + // The following ways to index into the list are equivalent + if ( + ((cssRules[i] as CSSStyleRule).selectorText.endsWith('::after') || (cssRules[i] as CSSStyleRule).selectorText.endsWith('::after')) + && (cssRules[i] as CSSStyleRule).cssText.indexOf('top:') > -1 + ) { + // there is a `::before` or `::after` text decoration whose position is above or below current line + // we at least make sure that the editor top padding is at least one line + const editorOptions = this.configurationService.getValue('editor'); + updateEditorTopPadding(BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()).lineHeight + 2); + decorationTriggeredAdjustment = true; + break; + } + } + } + } + + decorationCheckSet.add(e); + })); + const getContext = () => { const editor = getActiveNotebookEditor(this._editorService); const activeCell = editor?.getActiveCell(); @@ -352,7 +393,13 @@ export class NotebookService extends Disposable implements INotebookService, ICu if (editor?.viewModel) { return editor.viewModel.undo().then(cellResources => { if (cellResources?.length) { - editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] } })); + editor?.viewModel?.viewCells.forEach(cell => { + if (cell.cellKind === CellKind.Markdown && cellResources.find(resource => resource.fragment === cell.model.uri.fragment)) { + cell.editState = CellEditState.Editing; + } + }); + + editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] }, preserveFocus: true })); } }); } @@ -365,7 +412,13 @@ export class NotebookService extends Disposable implements INotebookService, ICu if (editor?.viewModel) { return editor.viewModel.redo().then(cellResources => { if (cellResources?.length) { - editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] } })); + editor?.viewModel?.viewCells.forEach(cell => { + if (cell.cellKind === CellKind.Markdown && cellResources.find(resource => resource.fragment === cell.model.uri.fragment)) { + cell.editState = CellEditState.Editing; + } + }); + + editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] }, preserveFocus: true })); } }); } @@ -630,7 +683,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu }); } - async getContributedNotebookKernels2(viewType: string, resource: URI, token: CancellationToken): Promise { + async getContributedNotebookKernels(viewType: string, resource: URI, token: CancellationToken): Promise { const filteredProvider = this.notebookKernelProviderInfoStore.get(viewType, resource); const result = new Array(filteredProvider.length); @@ -706,8 +759,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu this._models.set(uri, modelData); this._onDidAddNotebookDocument.fire(notebookModel); - // after the document is added to the store and sent to ext host, we transform the ouputs - await this.transformTextModelOutputs(notebookModel); return modelData.model; } @@ -720,68 +771,12 @@ export class NotebookService extends Disposable implements INotebookService, ICu return Iterable.map(this._models.values(), data => data.model); } - private async transformTextModelOutputs(textModel: NotebookTextModel) { - for (let i = 0; i < textModel.cells.length; i++) { - const cell = textModel.cells[i]; - - cell.outputs.forEach((output) => { - if (output.outputKind === CellOutputKind.Rich) { - // TODO@rebornix no string[] casting - const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); - const orderedMimeTypes = ret.orderedMimeTypes!; - const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; - output.pickedMimeTypeIndex = pickedMimeTypeIndex; - output.orderedMimeTypes = orderedMimeTypes; - } - }); - } + getMimeTypeInfo(textModel: NotebookTextModel, output: ITransformedDisplayOutputDto): readonly IOrderedMimeType[] { + // TODO@rebornix no string[] casting + return this._getOrderedMimeTypes(textModel, output, textModel.metadata.displayOrder as string[] ?? []); } - transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]) { - edits.forEach((edit) => { - if (edit.editType === CellEditType.Replace) { - edit.cells.forEach((cell) => { - const outputs = cell.outputs; - outputs.map((output) => { - if (output.outputKind === CellOutputKind.Rich) { - const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); - const orderedMimeTypes = ret.orderedMimeTypes!; - const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; - output.pickedMimeTypeIndex = pickedMimeTypeIndex; - output.orderedMimeTypes = orderedMimeTypes; - } - }); - }); - } else if (edit.editType === CellEditType.Output) { - edit.outputs.map((output) => { - if (output.outputKind === CellOutputKind.Rich) { - const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); - const orderedMimeTypes = ret.orderedMimeTypes!; - const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; - output.pickedMimeTypeIndex = pickedMimeTypeIndex; - output.orderedMimeTypes = orderedMimeTypes; - } - }); - } - }); - } - - transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]) { - splices.forEach((splice) => { - const outputs = splice[2]; - outputs.map((output) => { - if (output.outputKind === CellOutputKind.Rich) { - const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); - const orderedMimeTypes = ret.orderedMimeTypes!; - const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; - output.pickedMimeTypeIndex = pickedMimeTypeIndex; - output.orderedMimeTypes = orderedMimeTypes; - } - }); - }); - } - - private _transformMimeTypes(output: IDisplayOutput, outputId: string, documentDisplayOrder: string[]): ITransformedDisplayOutputDto { + private _getOrderedMimeTypes(textModel: NotebookTextModel, output: IDisplayOutput, documentDisplayOrder: string[]): IOrderedMimeType[] { const mimeTypes = Object.keys(output.data); const coreDisplayOrder = this._displayOrder; const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], documentDisplayOrder, coreDisplayOrder?.defaultOrder || []); @@ -797,36 +792,42 @@ export class NotebookService extends Disposable implements INotebookService, ICu orderMimeTypes.push({ mimeType: mimeType, rendererId: handler.id, + isTrusted: textModel.metadata.trusted }); for (let i = 1; i < handlers.length; i++) { orderMimeTypes.push({ mimeType: mimeType, - rendererId: handlers[i].id + rendererId: handlers[i].id, + isTrusted: textModel.metadata.trusted }); } if (mimeTypeSupportedByCore(mimeType)) { orderMimeTypes.push({ mimeType: mimeType, - rendererId: BUILTIN_RENDERER_ID + rendererId: BUILTIN_RENDERER_ID, + isTrusted: mimeTypeIsAlwaysSecure(mimeType) || textModel.metadata.trusted }); } } else { - orderMimeTypes.push({ - mimeType: mimeType, - rendererId: BUILTIN_RENDERER_ID - }); + if (mimeTypeSupportedByCore(mimeType)) { + orderMimeTypes.push({ + mimeType: mimeType, + rendererId: BUILTIN_RENDERER_ID, + isTrusted: mimeTypeIsAlwaysSecure(mimeType) || textModel.metadata.trusted + }); + } else { + orderMimeTypes.push({ + mimeType: mimeType, + rendererId: RENDERER_NOT_AVAILABLE, + isTrusted: textModel.metadata.trusted + }); + } } }); - return { - outputKind: output.outputKind, - outputId, - data: output.data, - orderedMimeTypes: orderMimeTypes, - pickedMimeTypeIndex: 0 - }; + return orderMimeTypes; } private _findBestMatchedRenderer(mimeType: string): readonly NotebookOutputRendererInfo[] { @@ -992,11 +993,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu if (modelData) { // delete editors and documents const willRemovedEditors: INotebookEditor[] = []; - this._notebookEditors.forEach(editor => { - if (editor.textModel === modelData!.model) { + for (const editor of this._notebookEditors.values()) { + if (editor.textModel === modelData.model) { willRemovedEditors.push(editor); } - }); + } modelData.model.dispose(); modelData.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index e51a8c00d..d6d8ed7c0 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -21,7 +21,7 @@ import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CellRevealPosition, CellRevealType, CursorAtBoundary, getVisibleCells, ICellViewModel, INotebookCellList, reduceCellRanges, CellEditState, CellFocusMode, BaseCellRenderTemplate, NOTEBOOK_CELL_LIST_FOCUSED, cellRangesEqual } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { diff, IProcessedOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { diff, IProcessedOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, ICellRange, NOTEBOOK_EDITOR_CURSOR_BEGIN_END } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { clamp } from 'vs/base/common/numbers'; import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; @@ -115,6 +115,9 @@ export class NotebookCellList extends WorkbenchList implements ID const notebookEditorCursorAtBoundaryContext = NOTEBOOK_EDITOR_CURSOR_BOUNDARY.bindTo(contextKeyService); notebookEditorCursorAtBoundaryContext.set('none'); + const notebookEditorCursorAtBeginEndContext = NOTEBOOK_EDITOR_CURSOR_BEGIN_END.bindTo(contextKeyService); + notebookEditorCursorAtBeginEndContext.set(false); + let cursorSelectionListener: IDisposable | null = null; let textEditorAttachListener: IDisposable | null = null; @@ -133,6 +136,13 @@ export class NotebookCellList extends WorkbenchList implements ID notebookEditorCursorAtBoundaryContext.set('none'); break; } + + if (element.cursorAtBeginEnd()) { + notebookEditorCursorAtBeginEndContext.set(true); + } else { + notebookEditorCursorAtBeginEndContext.set(false); + } + return; }; @@ -595,6 +605,14 @@ export class NotebookCellList extends WorkbenchList implements ID } } + async revealElementInCenterIfOutsideViewportAsync(cell: ICellViewModel): Promise { + const index = this._getViewIndexUpperBound(cell); + + if (index >= 0) { + return this._revealInCenterIfOutsideViewportAsync(index); + } + } + async revealElementLineInViewAsync(cell: ICellViewModel, line: number): Promise { const index = this._getViewIndexUpperBound(cell); @@ -855,6 +873,17 @@ export class NotebookCellList extends WorkbenchList implements ID } } + private async _revealInCenterIfOutsideViewportAsync(viewIndex: number): Promise { + this._revealInternal(viewIndex, true, CellRevealPosition.Center); + const element = this.view.element(viewIndex); + + if (!element.editorAttached) { + return getEditorAttachedPromise(element); + } + + return; + } + private async _revealLineInCenterIfOutsideViewportAsync(viewIndex: number, line: number): Promise { return this._revealRangeInCenterIfOutsideViewportInternalAsync(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index 7840f0231..b2cec3995 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -16,8 +16,10 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { URI } from 'vs/base/common/uri'; import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { handleANSIOutput } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform'; import { dirname } from 'vs/base/common/resources'; +import { truncatedArrayOfString } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; class RichRenderer implements IOutputTransformContribution { private _richMimeTypeRenderers = new Map IRenderOutput>(); @@ -27,7 +29,9 @@ class RichRenderer implements IOutputTransformContribution { @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, @IModeService private readonly modeService: IModeService, - @IThemeService private readonly themeService: IThemeService + @IThemeService private readonly themeService: IThemeService, + @IOpenerService readonly openerService: IOpenerService, + @ITextFileService readonly textFileService: ITextFileService, ) { this._richMimeTypeRenderers.set('application/json', this.renderJSON.bind(this)); this._richMimeTypeRenderers.set('application/javascript', this.renderJavaScript.bind(this)); @@ -204,9 +208,8 @@ class RichRenderer implements IOutputTransformContribution { renderPlainText(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const data = output.data['text/plain']; - const str = (isArray(data) ? data.join('') : data) as string; const contentNode = DOM.$('.output-plaintext'); - contentNode.appendChild(handleANSIOutput(str, this.themeService)); + truncatedArrayOfString(contentNode, isArray(data) ? data : [data], this.openerService, this.textFileService, this.themeService, true); container.appendChild(contentNode); return { type: RenderOutputType.None, hasDynamicHeight: false }; diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts index 65f1826f7..b05a25d3b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts @@ -7,16 +7,23 @@ import * as DOM from 'vs/base/browser/dom'; import { IRenderOutput, CellOutputKind, IStreamOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { truncatedArrayOfString } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; class StreamRenderer implements IOutputTransformContribution { constructor( - editor: INotebookEditor + editor: INotebookEditor, + @IOpenerService readonly openerService: IOpenerService, + @ITextFileService readonly textFileService: ITextFileService, + @IThemeService readonly themeService: IThemeService ) { } render(output: IStreamOutput, container: HTMLElement): IRenderOutput { const contentNode = DOM.$('.output-stream'); - contentNode.innerText = output.text; + truncatedArrayOfString(contentNode, [output.text], this.openerService, this.textFileService, this.themeService, false); container.appendChild(contentNode); return { type: RenderOutputType.None, hasDynamicHeight: false }; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts new file mode 100644 index 000000000..8ef36f850 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DefaultEndOfLine, EndOfLinePreference, ITextBuffer } from 'vs/editor/common/model'; +import { Range } from 'vs/editor/common/core/range'; +import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { handleANSIOutput } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; + +const SIZE_LIMIT = 65535; +const LINES_LIMIT = 500; + +function generateViewMoreElement(outputs: string[], openerService: IOpenerService, textFileService: ITextFileService) { + const md: IMarkdownString = { + value: '[show more (open the raw output data in a text editor) ...](command:workbench.action.openLargeOutput)', + isTrusted: true, + supportThemeIcons: true + }; + + const element = renderMarkdown(md, { + actionHandler: { + callback: (content) => { + if (content === 'command:workbench.action.openLargeOutput') { + return textFileService.untitled.resolve({ + associatedResource: undefined, + mode: 'plaintext', + initialValue: outputs.join('') + }).then(model => { + const resource = model.resource; + openerService.open(resource); + }); + } + + return; + }, + disposeables: new DisposableStore() + } + }); + + element.classList.add('output-show-more'); + return element; +} + +export function truncatedArrayOfString(container: HTMLElement, outputs: string[], openerService: IOpenerService, textFileService: ITextFileService, themeService: IThemeService, renderANSI: boolean) { + const fullLen = outputs.reduce((p, c) => { + return p + c.length; + }, 0); + + let buffer: ITextBuffer | undefined = undefined; + + if (fullLen > SIZE_LIMIT) { + // it's too large and we should find min(maxSizeLimit, maxLineLimit) + const bufferBuilder = new PieceTreeTextBufferBuilder(); + outputs.forEach(output => bufferBuilder.acceptChunk(output)); + const factory = bufferBuilder.finish(); + buffer = factory.create(DefaultEndOfLine.LF); + const sizeBufferLimitPosition = buffer.getPositionAt(SIZE_LIMIT); + if (sizeBufferLimitPosition.lineNumber < LINES_LIMIT) { + const truncatedText = buffer.getValueInRange(new Range(1, 1, sizeBufferLimitPosition.lineNumber, sizeBufferLimitPosition.column), EndOfLinePreference.TextDefined); + if (renderANSI) { + container.appendChild(handleANSIOutput(truncatedText, themeService)); + } else { + const pre = DOM.$('div'); + pre.innerText = truncatedText; + container.appendChild(pre); + } + + // view more ... + container.appendChild(generateViewMoreElement(outputs, openerService, textFileService)); + return; + } + } + + if (!buffer) { + const bufferBuilder = new PieceTreeTextBufferBuilder(); + outputs.forEach(output => bufferBuilder.acceptChunk(output)); + const factory = bufferBuilder.finish(); + buffer = factory.create(DefaultEndOfLine.LF); + } + + if (buffer.getLineCount() < LINES_LIMIT) { + const lineCount = buffer.getLineCount(); + const fullRange = new Range(1, 1, lineCount, buffer.getLineLastNonWhitespaceColumn(lineCount)); + + container.innerText = buffer.getValueInRange(fullRange, EndOfLinePreference.TextDefined); + return; + } + + if (renderANSI) { + container.appendChild(handleANSIOutput(buffer.getValueInRange(new Range(1, 1, LINES_LIMIT - 5, buffer.getLineLastNonWhitespaceColumn(LINES_LIMIT - 5)), EndOfLinePreference.TextDefined), themeService)); + } else { + const pre = DOM.$('div'); + pre.innerText = buffer.getValueInRange(new Range(1, 1, LINES_LIMIT - 5, buffer.getLineLastNonWhitespaceColumn(LINES_LIMIT - 5)), EndOfLinePreference.TextDefined); + container.appendChild(pre); + } + + + // view more ... + container.appendChild(generateViewMoreElement(outputs, openerService, textFileService)); + + const lineCount = buffer.getLineCount(); + if (renderANSI) { + container.appendChild(handleANSIOutput(buffer.getValueInRange(new Range(lineCount - 5, 1, lineCount, buffer.getLineLastNonWhitespaceColumn(lineCount)), EndOfLinePreference.TextDefined), themeService)); + } else { + const post = DOM.$('div'); + post.innerText = buffer.getValueInRange(new Range(lineCount - 5, 1, lineCount, buffer.getLineLastNonWhitespaceColumn(lineCount)), EndOfLinePreference.TextDefined); + container.appendChild(post); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 7e99796e6..31fc6590e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -6,7 +6,6 @@ import * as DOM from 'vs/base/browser/dom'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import * as path from 'vs/base/common/path'; import { isWeb } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; @@ -373,7 +372,7 @@ export class BackLayerWebView extends Disposable { }()); `; const htmlContent = this.generateContent(CELL_OUTPUT_PADDING, coreDependencies, baseUrl.toString()); - this.initialize(htmlContent); + this._initialize(htmlContent); resolveFunc!(); } else { const loaderUri = FileAccess.asBrowserUri('vs/loader.js', require); @@ -397,7 +396,7 @@ var requirejs = (function() { `; const htmlContent = this.generateContent(CELL_OUTPUT_PADDING, coreDependencies, baseUrl.toString()); - this.initialize(htmlContent); + this._initialize(htmlContent); resolveFunc!(); }); } @@ -405,7 +404,7 @@ var requirejs = (function() { await this._initalized; } - async initialize(content: string) { + private async _initialize(content: string) { if (!document.body.contains(this.element)) { throw new Error('Element is already detached from the DOM tree'); } @@ -555,9 +554,8 @@ var requirejs = (function() { } private _createInset(webviewService: IWebviewService, content: string) { - const rootPathStr = path.dirname(FileAccess.asFileUri('', require).fsPath); + const rootPath = isWeb ? FileAccess.asBrowserUri('', require) : FileAccess.asFileUri('', require); - const rootPath = isWeb ? FileAccess.asBrowserUri(rootPathStr, require) : FileAccess.asFileUri(rootPathStr, require); const workspaceFolders = this.contextService.getWorkspace().folders.map(x => x.uri); this.localResourceRootsCache = [...this.notebookService.getNotebookProviderResourceRoots(), ...workspaceFolders, rootPath]; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts index 2e698f1ed..3ded6bb42 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts @@ -3,10 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action, IAction, Separator } from 'vs/base/common/actions'; -import { IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { renderCodicons } from 'vs/base/browser/codicons'; +import * as DOM from 'vs/base/browser/dom'; import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export class VerticalSeparator extends Action { static readonly ID = 'vs.actions.verticalSeparator'; @@ -74,3 +79,19 @@ function asDisposable(groups: ReadonlyArray<[string, ReadonlyArray(); + + readonly resizeListener = new DisposableStore(); + domNode!: HTMLElement; + renderResult?: IRenderOutput; + + constructor( + private notebookEditor: INotebookEditor, + private notebookService: INotebookService, + private quickInputService: IQuickInputService, + private viewCell: CodeCellViewModel, + private outputContainer: HTMLElement, + readonly output: IProcessedOutput + ) { + super(); + } + + render(index: number, beforeElement?: HTMLElement) { + if (this.viewCell.metadata.outputCollapsed) { + return; + } + + const outputItemDiv = document.createElement('div'); + let result: IRenderOutput | undefined = undefined; + + if (this.output.outputKind === CellOutputKind.Rich) { + const transformedDisplayOutput = this.output as ITransformedDisplayOutputDto; + + const mimeTypes = this.notebookService.getMimeTypeInfo(this.notebookEditor.textModel!, this.output); + + // there is at least one mimetype which is safe and can be rendered by the core + const pick = this.pickedMimeTypes.get(this.output) ?? Math.max(mimeTypes.findIndex(mimeType => mimeType.rendererId !== RENDERER_NOT_AVAILABLE && mimeType.isTrusted), 0); + + if (mimeTypes.length > 1) { + outputItemDiv.style.position = 'relative'; + const mimeTypePicker = DOM.$('.multi-mimetype-output'); + mimeTypePicker.classList.add(...ThemeIcon.asClassNameArray(mimetypeIcon)); + mimeTypePicker.tabIndex = 0; + mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", mimeTypes.map(mimeType => mimeType.mimeType).join(', ')); + outputItemDiv.appendChild(mimeTypePicker); + this.resizeListener.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => { + if (e.leftButton) { + e.preventDefault(); + e.stopPropagation(); + await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); + } + })); + + this.resizeListener.add((DOM.addDisposableListener(mimeTypePicker, DOM.EventType.KEY_DOWN, async e => { + const event = new StandardKeyboardEvent(e); + if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) { + e.preventDefault(); + e.stopPropagation(); + await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); + } + }))); + + } + const pickedMimeTypeRenderer = mimeTypes[pick]; + + const innerContainer = DOM.$('.output-inner-container'); + DOM.append(outputItemDiv, innerContainer); + + + if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) { + const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId); + result = renderer + ? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType } + : this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); + } else { + result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); + } + + this.pickedMimeTypes.set(this.output, pick); + } else { + // for text and error, there is no mimetype + const innerContainer = DOM.$('.output-inner-container'); + DOM.append(outputItemDiv, innerContainer); + + result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),); + } + + this.domNode = outputItemDiv; + this.renderResult = result; + + if (!result) { + this.viewCell.updateOutputHeight(index, 0); + return; + } + + if (beforeElement) { + this.outputContainer.insertBefore(outputItemDiv, beforeElement); + } else { + this.outputContainer.appendChild(outputItemDiv); + } + + if (result.type !== RenderOutputType.None) { + this.viewCell.selfSizeMonitoring = true; + this.notebookEditor.createInset(this.viewCell, result as any, this.viewCell.getOutputOffset(index)); + } else { + outputItemDiv.classList.add('foreground', 'output-element'); + outputItemDiv.style.position = 'absolute'; + } + + if (outputHasDynamicHeight(result)) { + this.viewCell.selfSizeMonitoring = true; + + const clientHeight = outputItemDiv.clientHeight; + const dimension = { + width: this.viewCell.layoutInfo.editorWidth, + height: clientHeight + }; + const elementSizeObserver = getResizesObserver(outputItemDiv, dimension, () => { + if (this.outputContainer && document.body.contains(this.outputContainer!)) { + const height = Math.ceil(elementSizeObserver.getHeight()); + + if (clientHeight === height) { + return; + } + + const currIndex = this.viewCell.outputs.indexOf(this.output); + if (currIndex < 0) { + return; + } + + this.viewCell.updateOutputHeight(currIndex, height); + this.relayoutCell(); + } + }); + elementSizeObserver.startObserving(); + this.resizeListener.add(elementSizeObserver); + this.viewCell.updateOutputHeight(index, clientHeight); + } else if (result.type === RenderOutputType.None) { // no-op if it's a webview + const clientHeight = Math.ceil(outputItemDiv.clientHeight); + this.viewCell.updateOutputHeight(index, clientHeight); + + const top = this.viewCell.getOutputOffsetInContainer(index); + outputItemDiv.style.top = `${top}px`; + } + } + + async pickActiveMimeTypeRenderer(output: ITransformedDisplayOutputDto) { + + const mimeTypes = this.notebookService.getMimeTypeInfo(this.notebookEditor.textModel!, output); + const currIndex = this.pickedMimeTypes.get(output); + + // const currIndex = output.pickedMimeTypeIndex; + const items = mimeTypes.filter(mimeType => mimeType.isTrusted).map((mimeType, index): IMimeTypeRenderer => ({ + label: mimeType.mimeType, + id: mimeType.mimeType, + index: index, + picked: index === currIndex, + detail: this.generateRendererInfo(mimeType.rendererId), + description: index === currIndex ? nls.localize('curruentActiveMimeType', "Currently Active") : undefined + })); + + const picker = this.quickInputService.createQuickPick(); + picker.items = items; + picker.activeItems = items.filter(item => !!item.picked); + picker.placeholder = items.length !== mimeTypes.length + ? nls.localize('promptChooseMimeTypeInSecure.placeHolder', "Select mimetype to render for current output. Rich mimetypes are available only when the notebook is trusted") + : nls.localize('promptChooseMimeType.placeHolder', "Select mimetype to render for current output"); + + const pick = await new Promise(resolve => { + picker.onDidAccept(() => { + resolve(picker.selectedItems.length === 1 ? (picker.selectedItems[0] as IMimeTypeRenderer).index : undefined); + picker.dispose(); + }); + picker.show(); + }); + + if (pick === undefined) { + return; + } + + if (pick !== currIndex) { + // user chooses another mimetype + const index = this.viewCell.outputs.indexOf(output); + const nextElement = this.domNode.nextElementSibling; + this.resizeListener.clear(); + const element = this.domNode; + if (element) { + element.parentElement?.removeChild(element); + this.notebookEditor.removeInset(output); + } + + this.pickedMimeTypes.set(output, pick); + this.render(index, nextElement as HTMLElement); + this.relayoutCell(); + } + } + + private getNotebookUri(): URI | undefined { + return CellUri.parse(this.viewCell.uri)?.notebook; + } + + generateRendererInfo(renderId: string | undefined): string { + if (renderId === undefined || renderId === BUILTIN_RENDERER_ID) { + return nls.localize('builtinRenderInfo', "built-in"); + } + + const renderInfo = this.notebookService.getRendererInfo(renderId); + + if (renderInfo) { + const displayName = renderInfo.displayName !== '' ? renderInfo.displayName : renderInfo.id; + return `${displayName} (${renderInfo.extensionId.value})`; + } + + return nls.localize('builtinRenderInfo', "built-in"); + } + + relayoutCell() { + this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); + } +} + +export class OutputContainer extends Disposable { + private outputEntries = new Map(); + + constructor( + private notebookEditor: INotebookEditor, + private viewCell: CodeCellViewModel, + private templateData: CodeCellRenderTemplate, + @INotebookService private notebookService: INotebookService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IOpenerService readonly openerService: IOpenerService, + @ITextFileService readonly textFileService: ITextFileService, + ) { + super(); + + this._register(viewCell.onDidChangeOutputs(splices => { + this._updateOutputs(splices); + })); + + this._register(viewCell.onDidChangeLayout(() => { + this.outputEntries.forEach((value, key) => { + const index = viewCell.outputs.indexOf(key); + if (index >= 0) { + const top = this.viewCell.getOutputOffsetInContainer(index); + value.domNode.style.top = `${top}px`; + } + }); + })); + } + + render(editorHeight: number) { + if (this.viewCell.outputs.length > 0) { + let layoutCache = false; + if (this.viewCell.layoutInfo.totalHeight !== 0 && this.viewCell.layoutInfo.editorHeight > editorHeight) { + layoutCache = true; + this._relayoutCell(); + } + + this.templateData.outputContainer.style.display = 'block'; + // there are outputs, we need to calcualte their sizes and trigger relayout + // @TODO@rebornix, if there is no resizable output, we should not check their height individually, which hurts the performance + const outputsToRender = this._calcuateOutputsToRender(); + for (let index = 0; index < outputsToRender.length; index++) { + const currOutput = this.viewCell.outputs[index]; + + // always add to the end + this._renderOutput(currOutput, index, undefined); + } + + this.viewCell.editorHeight = editorHeight; + if (this.viewCell.outputs.length > OUTPUT_COUNT_LIMIT) { + this.templateData.outputShowMoreContainer.style.display = 'block'; + this.viewCell.updateOutputShowMoreContainerHeight(46); + } + + if (layoutCache) { + this._relayoutCellDebounced(); + } else { + this._relayoutCell(); + } + } else { + // noop + this.viewCell.editorHeight = editorHeight; + this._relayoutCell(); + this.templateData.outputContainer.style.display = 'none'; + } + + this.templateData.outputShowMoreContainer.innerText = ''; + this.templateData.outputShowMoreContainer.appendChild(this._generateShowMoreElement()); + // this.templateData.outputShowMoreContainer.style.top = `${this.viewCell.layoutInfo.outputShowMoreContainerOffset}px`; + + if (this.viewCell.outputs.length < OUTPUT_COUNT_LIMIT) { + this.templateData.outputShowMoreContainer.style.display = 'none'; + this.viewCell.updateOutputShowMoreContainerHeight(0); + } + } + + viewUpdateShowOutputs(): void { + for (let index = 0; index < this.viewCell.outputs.length; index++) { + const currOutput = this.viewCell.outputs[index]; + + const renderedOutput = this.outputEntries.get(currOutput); + if (renderedOutput && renderedOutput.renderResult) { + if (renderedOutput.renderResult.type !== RenderOutputType.None) { + this.notebookEditor.createInset(this.viewCell, renderedOutput.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index)); + } else { + // Anything else, just update the height + this.viewCell.updateOutputHeight(index, renderedOutput.domNode.clientHeight); + } + } else { + // Wasn't previously rendered, render it now + this._renderOutput(currOutput, index); + } + } + + this._relayoutCell(); + } + + viewUpdateHideOuputs(): void { + for (const e of this.outputEntries.keys()) { + this.notebookEditor.hideInset(e); + } + } + + onCellWidthChange(): void { + this.viewCell.outputs.forEach((o, i) => { + const renderedOutput = this.outputEntries.get(o); + if (renderedOutput && renderedOutput.renderResult && renderedOutput.renderResult.type === RenderOutputType.None && !renderedOutput.renderResult.hasDynamicHeight) { + this.viewCell.updateOutputHeight(i, renderedOutput.domNode.clientHeight); + } + }); + } + + private _calcuateOutputsToRender() { + const outputs = this.viewCell.outputs.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputs.length)); + if (!this.notebookEditor.viewModel!.metadata.trusted) { + // not trusted + const secureOutput = outputs.filter(output => { + switch (output.outputKind) { + case CellOutputKind.Text: + return true; + case CellOutputKind.Error: + return true; + case CellOutputKind.Rich: + { + const mimeTypes = []; + for (const property in output.data) { + mimeTypes.push(property); + } + + if (mimeTypes.indexOf('text/plain') >= 0 + || mimeTypes.indexOf('text/markdown') >= 0 + || mimeTypes.indexOf('application/json') >= 0 + || mimeTypes.includes('image/png')) { + return true; + } + + return false; + } + default: + return false; + } + }); + + return secureOutput; + } + + return outputs; + } + + private _updateOutputs(splices: NotebookCellOutputsSplice[]) { + if (!splices.length) { + return; + } + + const previousOutputHeight = this.viewCell.layoutInfo.outputTotalHeight; + + if (this.viewCell.outputs.length) { + this.templateData.outputContainer.style.display = 'block'; + } else { + this.templateData.outputContainer.style.display = 'none'; + } + + const reversedSplices = splices.reverse(); + + reversedSplices.forEach(splice => { + this.viewCell.spliceOutputHeights(splice[0], splice[1], splice[2].map(_ => 0)); + }); + + const removedKeys: IProcessedOutput[] = []; + + this.outputEntries.forEach((value, key) => { + if (this.viewCell.outputs.indexOf(key) < 0) { + // already removed + removedKeys.push(key); + // remove element from DOM + this.templateData?.outputContainer?.removeChild(value.domNode); + this.notebookEditor.removeInset(key); + } + }); + + removedKeys.forEach(key => { + this.outputEntries.get(key)?.dispose(); + this.outputEntries.delete(key); + }); + + let prevElement: HTMLElement | undefined = undefined; + const outputsToRender = this._calcuateOutputsToRender(); + + outputsToRender.reverse().forEach(output => { + if (this.outputEntries.has(output)) { + // already exist + prevElement = this.outputEntries.get(output)!.domNode; + return; + } + + // newly added element + const currIndex = this.viewCell.outputs.indexOf(output); + this._renderOutput(output, currIndex, prevElement); + prevElement = this.outputEntries.get(output)?.domNode; + }); + + if (this.viewCell.outputs.length > OUTPUT_COUNT_LIMIT) { + this.templateData.outputShowMoreContainer.style.display = 'block'; + this.viewCell.updateOutputShowMoreContainerHeight(46); + } else { + this.templateData.outputShowMoreContainer.style.display = 'none'; + } + + const editorHeight = this.templateData.editor.getContentHeight(); + this.viewCell.editorHeight = editorHeight; + + if (previousOutputHeight === 0 || this.viewCell.outputs.length === 0) { + // first execution or removing all outputs + this._relayoutCell(); + } else { + this._relayoutCellDebounced(); + } + } + + private _renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement) { + if (!this.outputEntries.has(currOutput)) { + this.outputEntries.set(currOutput, new OutputElement(this.notebookEditor, this.notebookService, this.quickInputService, this.viewCell, this.templateData.outputContainer, currOutput)); + } + + this.outputEntries.get(currOutput)!.render(index, beforeElement); + } + + private _generateShowMoreElement(): any { + const md: IMarkdownString = { + value: `There are more than ${OUTPUT_COUNT_LIMIT} outputs, [show more (open the raw output data in a text editor) ...](command:workbench.action.openLargeOutput)`, + isTrusted: true, + supportThemeIcons: true + }; + + const element = renderMarkdown(md, { + actionHandler: { + callback: (content) => { + if (content === 'command:workbench.action.openLargeOutput') { + const content = JSON.stringify(this.viewCell.outputs.map(output => { + switch (output.outputKind) { + case CellOutputKind.Text: + return { + outputKind: 'text', + text: output.text + }; + case CellOutputKind.Error: + return { + outputKind: 'error', + ename: output.ename, + evalue: output.evalue, + traceback: output.traceback + }; + case CellOutputKind.Rich: + return { + data: output.data, + metadata: output.metadata + }; + } + })); + const edits = format(content, undefined, {}); + const metadataSource = applyEdits(content, edits); + + return this.textFileService.untitled.resolve({ + associatedResource: undefined, + mode: 'json', + initialValue: metadataSource + }).then(model => { + const resource = model.resource; + this.openerService.open(resource); + }); + } + + return; + }, + disposeables: new DisposableStore() + } + }); + + element.classList.add('output-show-more'); + return element; + } + + private _relayoutCell() { + if (this._timer !== null) { + clearTimeout(this._timer); + } + + this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); + } + + private _timer: number | null = null; + + private _relayoutCellDebounced() { + if (this._timer !== null) { + clearTimeout(this._timer); + } + + this._timer = setTimeout(() => { + this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); + this._timer = null; + }, 200) as unknown as number | null; + } + + dispose() { + this.outputEntries.forEach((value) => { + value.dispose(); + }); + + super.dispose(); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 25156780e..2fccd56c7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -35,21 +35,22 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR, EDITOR_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; import { CancelCellAction, DeleteCellAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; -import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, EditorTopPaddingChangeEvent, EXPAND_CELL_CONTENT_COMMAND_ID, getEditorTopPadding, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; -import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; +import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd'; import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellEditType, CellKind, NotebookCellMetadata, NotebookCellRunState, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparator, VerticalSeparatorViewItem } from './cellActionView'; +import { CellEditType, CellKind, NotebookCellMetadata, NotebookCellRunState, NotebookCellsChangeType, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CodiconActionViewItem, createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparator, VerticalSeparatorViewItem } from './cellActionView'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { errorStateIcon, successStateIcon, unfoldIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; const $ = DOM.$; @@ -118,10 +119,16 @@ export class CellEditorOptions { } }); + EditorTopPaddingChangeEvent(() => { + this._value = computeEditorOptions(); + this._onDidChange.fire(this.value); + + }); + const computeEditorOptions = () => { const showCellStatusBar = configurationService.getValue(ShowCellStatusBarKey); const editorPadding = { - top: EDITOR_TOP_PADDING, + top: getEditorTopPadding(), bottom: showCellStatusBar ? EDITOR_BOTTOM_PADDING : EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR }; @@ -352,8 +359,8 @@ abstract class AbstractCellRenderer { } protected setupCollapsedPart(container: HTMLElement): { collapsedPart: HTMLElement, expandButton: HTMLElement } { - const collapsedPart = DOM.append(container, $('.cell.cell-collapsed-part', undefined, ...renderCodicons('$(unfold)'))); - const expandButton = collapsedPart.querySelector('.codicon') as HTMLElement; + const collapsedPart = DOM.append(container, $('.cell.cell-collapsed-part', undefined, $('span.expandButton' + ThemeIcon.asCSSSelector(unfoldIcon)))); + const expandButton = collapsedPart.querySelector('.expandButton') as HTMLElement; const keybinding = this.keybindingService.lookupKeybinding(EXPAND_CELL_CONTENT_COMMAND_ID); let title = localize('cellExpandButtonLabel', "Expand"); if (keybinding) { @@ -415,6 +422,9 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService)); const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); + DOM.hide(statusBar.durationContainer); + DOM.hide(statusBar.cellRunStatusContainer); + const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService)); const templateData: MarkdownCellRenderTemplate = { @@ -446,7 +456,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR } private getDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { - if (templateData.currentRenderedCell!.editState === CellEditState.Editing) { + if (templateData.currentRenderedCell?.editState === CellEditState.Editing) { return this.getEditDragImage(templateData); } else { return this.getMarkdownDragImage(templateData); @@ -472,6 +482,10 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR } renderElement(element: MarkdownCellViewModel, index: number, templateData: MarkdownCellRenderTemplate, height: number | undefined): void { + if (!this.notebookEditor.hasModel()) { + throw new Error('The notebook editor is not attached with view model yet.'); + } + const removedClassNames: string[] = []; templateData.rootContainer.classList.forEach(className => { if (/^nb\-.*$/.test(className)) { @@ -489,7 +503,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR templateData.currentRenderedCell = element; templateData.currentEditor = undefined; - templateData.editorPart!.style.display = 'none'; + templateData.editorPart.style.display = 'none'; templateData.cellContainer.innerText = ''; if (height === undefined) { @@ -514,7 +528,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR } })); - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel?.notebookDocument!, element)); + elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel.notebookDocument!, element)); // render toolbar first this.setupCellToolbarActions(templateData, elementDisposables); @@ -715,7 +729,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende }, {}); disposables.add(this.editorOptions.onDidChange(newValue => editor.updateOptions(newValue))); - const { collapsedPart, expandButton } = this.setupCollapsedPart(container); const progressBar = new ProgressBar(editorPart); @@ -727,6 +740,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const cellRunState = new RunStateRenderer(statusBar.cellRunStatusContainer, runToolbar, this.instantiationService); const outputContainer = DOM.append(container, $('.output')); + const outputShowMoreContainer = DOM.append(container, $('.output-show-more-container')); const focusIndicatorRight = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-right')); @@ -761,6 +775,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende runButtonContainer, executionOrderLabel, outputContainer, + outputShowMoreContainer, editor, disposables, elementDisposables: new DisposableStore(), @@ -793,7 +808,11 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } private updateForMetadata(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { - const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel!.notebookDocument.metadata); + if (!this.notebookEditor.hasModel()) { + return; + } + + const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel.notebookDocument.metadata); templateData.container.classList.toggle('runnable', !!metadata.runnable); this.updateExecutionOrder(metadata, templateData); templateData.statusBar.cellStatusMessageContainer.textContent = metadata?.statusMessage || ''; @@ -843,10 +862,15 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateData.focusIndicatorRight.style.height = `${element.layoutInfo.indicatorHeight}px`; templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`; templateData.outputContainer.style.top = `${element.layoutInfo.outputContainerOffset}px`; + templateData.outputShowMoreContainer.style.top = `${element.layoutInfo.outputShowMoreContainerOffset}px`; templateData.dragHandle.style.height = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP}px`; } renderElement(element: CodeCellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { + if (!this.notebookEditor.hasModel()) { + throw new Error('The notebook editor is not attached with view model yet.'); + } + const removedClassNames: string[] = []; templateData.rootContainer.classList.forEach(className => { if (/^nb\-.*$/.test(className)) { @@ -893,7 +917,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende elementDisposables.add(this.instantiationService.createInstance(CodeCell, this.notebookEditor, element, templateData)); this.renderedEditors.set(element, templateData.editor); - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel?.notebookDocument!, element)); + elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel.notebookDocument!, element)); this.updateForLayout(element, templateData); elementDisposables.add(element.onDidChangeLayout(() => { @@ -912,6 +936,11 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.updateForHover(element, templateData); } })); + elementDisposables.add(this.notebookEditor.viewModel.notebookDocument.onDidChangeContent(e => { + if (e.rawEvents.find(event => event.kind === NotebookCellsChangeType.ChangeDocumentMetadata)) { + this.updateForMetadata(element, templateData); + } + })); this.updateForOutputs(element, templateData); elementDisposables.add(element.onDidChangeOutputs(_e => this.updateForOutputs(element, templateData))); @@ -999,11 +1028,13 @@ export class RunStateRenderer { private pendingNewState: NotebookCellRunState | undefined; constructor(private readonly element: HTMLElement, private readonly runToolbar: ToolBar, private readonly instantiationService: IInstantiationService) { + DOM.hide(element); } clear() { if (this.spinnerTimer) { clearTimeout(this.spinnerTimer); + this.spinnerTimer = undefined; } } @@ -1020,9 +1051,9 @@ export class RunStateRenderer { } if (runState === NotebookCellRunState.Success) { - DOM.reset(this.element, ...renderCodicons('$(check)')); + DOM.reset(this.element, ...renderCodicons(ThemeIcon.asCodiconLabel(successStateIcon))); } else if (runState === NotebookCellRunState.Error) { - DOM.reset(this.element, ...renderCodicons('$(error)')); + DOM.reset(this.element, ...renderCodicons(ThemeIcon.asCodiconLabel(errorStateIcon))); } else if (runState === NotebookCellRunState.Running) { DOM.reset(this.element, ...renderCodicons('$(sync~spin)')); @@ -1036,6 +1067,12 @@ export class RunStateRenderer { } else { this.element.innerText = ''; } + + if (runState === NotebookCellRunState.Idle) { + DOM.hide(this.element); + } else { + this.element.style.display = 'flex'; + } } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts index 41bb82bb6..6be1604ee 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts @@ -9,9 +9,13 @@ import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { stripCodicons } from 'vs/base/common/codicons'; import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { isWindows } from 'vs/base/common/platform'; import { extUri } from 'vs/base/common/resources'; +import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; +import { IDimension } from 'vs/editor/common/editorCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; import { localize } from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -20,11 +24,25 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ChangeCellLanguageAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + const $ = DOM.$; +export interface IClickTarget { + type: ClickTargetType; + event: MouseEvent; +} + +export const enum ClickTargetType { + Container = 0, + CellStatus = 1, + ContributedTextItem = 2, + ContributedCommandItem = 3 +} + export class CellEditorStatusBar extends Disposable { readonly cellStatusMessageContainer: HTMLElement; readonly cellRunStatusContainer: HTMLElement; @@ -37,6 +55,8 @@ export class CellEditorStatusBar extends Disposable { private readonly itemsDisposable: DisposableStore; private currentContext: INotebookCellActionContext | undefined; + protected readonly _onDidClick: Emitter = this._register(new Emitter()); + readonly onDidClick: Event = this._onDidClick.event; constructor( container: HTMLElement, @@ -60,6 +80,38 @@ export class CellEditorStatusBar extends Disposable { this.updateStatusBarItems(); } })); + + this._register(DOM.addDisposableListener(this.statusBarContainer, DOM.EventType.CLICK, e => { + if (e.target === leftItemsContainer || e.target === rightItemsContainer || e.target === this.statusBarContainer) { + // hit on empty space + this._onDidClick.fire({ + type: ClickTargetType.Container, + event: e + }); + } else if (e.target && ( + this.cellStatusMessageContainer.contains(e.target as Node) + || this.cellRunStatusContainer.contains(e.target as Node) + || this.durationContainer.contains(e.target as Node) + )) { + this._onDidClick.fire({ + type: ClickTargetType.CellStatus, + event: e + }); + } else { + if ((e.target as HTMLElement).classList.contains('cell-status-item-has-command')) { + this._onDidClick.fire({ + type: ClickTargetType.ContributedCommandItem, + event: e + }); + } else { + // text + this._onDidClick.fire({ + type: ClickTargetType.ContributedTextItem, + event: e + }); + } + } + })); } update(context: INotebookCellActionContext) { @@ -110,6 +162,10 @@ class CellStatusBarItem extends Disposable { super(); new CodiconLabel(this.container).text = this._itemModel.text; + if (this._itemModel.opacity) { + this.container.style.opacity = this._itemModel.opacity; + } + let ariaLabel: string; let role: string | undefined; if (this._itemModel.accessibilityInformation) { @@ -181,11 +237,12 @@ export class CellLanguageStatusBarItem extends Disposable { super(); this.labelElement = DOM.append(container, $('.cell-language-picker.cell-status-item')); this.labelElement.tabIndex = 0; + this.labelElement.classList.add('cell-status-item-has-command'); this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.CLICK, () => { this.run(); })); - this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.KEY_UP, e => { + this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { this.run(); @@ -196,7 +253,10 @@ export class CellLanguageStatusBarItem extends Disposable { private run() { this.instantiationService.invokeFunction(accessor => { - new ChangeCellLanguageAction().run(accessor, { notebookEditor: this.editor!, cell: this.cell! }); + if (!this.editor || !this.editor.hasModel() || !this.cell) { + return; + } + new ChangeCellLanguageAction().run(accessor, { notebookEditor: this.editor, cell: this.cell }); }); } @@ -215,3 +275,87 @@ export class CellLanguageStatusBarItem extends Disposable { this.labelElement.title = localize('notebook.cell.status.language', "Select Cell Language Mode"); } } + +declare const ResizeObserver: any; + +export interface IResizeObserver { + startObserving: () => void; + stopObserving: () => void; + getWidth(): number; + getHeight(): number; + dispose(): void; +} + +export class BrowserResizeObserver extends Disposable implements IResizeObserver { + private readonly referenceDomElement: HTMLElement | null; + + private readonly observer: any; + private width: number; + private height: number; + + constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void) { + super(); + + this.referenceDomElement = referenceDomElement; + this.width = -1; + this.height = -1; + + this.observer = new ResizeObserver((entries: any) => { + for (const entry of entries) { + if (entry.target === referenceDomElement && entry.contentRect) { + if (this.width !== entry.contentRect.width || this.height !== entry.contentRect.height) { + this.width = entry.contentRect.width; + this.height = entry.contentRect.height; + DOM.scheduleAtNextAnimationFrame(() => { + changeCallback(); + }); + } + } + } + }); + } + + getWidth(): number { + return this.width; + } + + getHeight(): number { + return this.height; + } + + startObserving(): void { + this.observer.observe(this.referenceDomElement!); + } + + stopObserving(): void { + this.observer.unobserve(this.referenceDomElement!); + } + + dispose(): void { + this.observer.disconnect(); + super.dispose(); + } +} + +export function getResizesObserver(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void): IResizeObserver { + if (ResizeObserver) { + return new BrowserResizeObserver(referenceDomElement, dimension, changeCallback); + } else { + return new ElementSizeObserver(referenceDomElement, dimension, changeCallback); + } +} + +export function getExecuteCellPlaceholder(viewCell: BaseCellViewModel) { + return { + alignment: CellStatusbarAlignment.LEFT, + priority: -1, + cellResource: viewCell.uri, + command: undefined, + // text: `${keybinding?.getLabel() || 'Ctrl + Enter'} to run`, + // tooltip: `${keybinding?.getLabel() || 'Ctrl + Enter'} to run`, + text: isWindows ? 'Ctrl + Alt + Enter' : 'Ctrl + Enter to run', + tooltip: isWindows ? 'Ctrl + Alt + Enter' : 'Ctrl + Enter to run', + visible: true, + opacity: '0.7' + }; +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 98638963f..56869eb83 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -4,239 +4,41 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { raceCancellation } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IDimension } from 'vs/editor/common/editorCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; -import * as nls from 'vs/nls'; -import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellFocusMode, CodeCellRenderTemplate, getEditorTopPadding, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { BUILTIN_RENDERER_ID, CellOutputKind, CellUri, IInsetRenderOutput, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ClickTargetType, getExecuteCellPlaceholder } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; +import { OutputContainer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellOutput'; +import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; +import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -interface IMimeTypeRenderer extends IQuickPickItem { - index: number; -} - -class OutputElement extends Disposable { - readonly resizeListener = new DisposableStore(); - domNode!: HTMLElement; - renderResult?: IRenderOutput; - - constructor( - private notebookEditor: INotebookEditor, - private notebookService: INotebookService, - private quickInputService: IQuickInputService, - private viewCell: CodeCellViewModel, - private outputContainer: HTMLElement, - readonly output: IProcessedOutput - ) { - super(); - } - - render(index: number, beforeElement?: HTMLElement) { - if (this.viewCell.metadata.outputCollapsed) { - return; - } - - const outputItemDiv = document.createElement('div'); - let result: IRenderOutput | undefined = undefined; - - if (this.output.outputKind === CellOutputKind.Rich) { - const transformedDisplayOutput = this.output as ITransformedDisplayOutputDto; - - if (transformedDisplayOutput.orderedMimeTypes!.length > 1) { - outputItemDiv.style.position = 'relative'; - const mimeTypePicker = DOM.$('.multi-mimetype-output'); - mimeTypePicker.classList.add('codicon', 'codicon-code'); - mimeTypePicker.tabIndex = 0; - mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", transformedDisplayOutput.orderedMimeTypes!.map(mimeType => mimeType.mimeType).join(', ')); - outputItemDiv.appendChild(mimeTypePicker); - this.resizeListener.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => { - if (e.leftButton) { - e.preventDefault(); - e.stopPropagation(); - await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); - } - })); - - this.resizeListener.add((DOM.addDisposableListener(mimeTypePicker, DOM.EventType.KEY_DOWN, async e => { - const event = new StandardKeyboardEvent(e); - if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) { - e.preventDefault(); - e.stopPropagation(); - await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); - } - }))); - - } - const pickedMimeTypeRenderer = this.output.orderedMimeTypes![this.output.pickedMimeTypeIndex!]; - - const innerContainer = DOM.$('.output-inner-container'); - DOM.append(outputItemDiv, innerContainer); - - - if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) { - const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId); - result = renderer - ? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType } - : this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); - } else { - result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); - } - } else { - // for text and error, there is no mimetype - const innerContainer = DOM.$('.output-inner-container'); - DOM.append(outputItemDiv, innerContainer); - - result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),); - } - - this.domNode = outputItemDiv; - this.renderResult = result; - - if (!result) { - this.viewCell.updateOutputHeight(index, 0); - return; - } - - if (beforeElement) { - this.outputContainer.insertBefore(outputItemDiv, beforeElement); - } else { - this.outputContainer.appendChild(outputItemDiv); - } - - if (result.type !== RenderOutputType.None) { - this.viewCell.selfSizeMonitoring = true; - this.notebookEditor.createInset(this.viewCell, result as any, this.viewCell.getOutputOffset(index)); - } else { - outputItemDiv.classList.add('foreground', 'output-element'); - outputItemDiv.style.position = 'absolute'; - } - - if (outputHasDynamicHeight(result)) { - this.viewCell.selfSizeMonitoring = true; - - const clientHeight = outputItemDiv.clientHeight; - const dimension = { - width: this.viewCell.layoutInfo.editorWidth, - height: clientHeight - }; - const elementSizeObserver = getResizesObserver(outputItemDiv, dimension, () => { - if (this.outputContainer && document.body.contains(this.outputContainer!)) { - const height = Math.ceil(elementSizeObserver.getHeight()); - - if (clientHeight === height) { - return; - } - - const currIndex = this.viewCell.outputs.indexOf(this.output); - if (currIndex < 0) { - return; - } - - this.viewCell.updateOutputHeight(currIndex, height); - this.relayoutCell(); - } - }); - elementSizeObserver.startObserving(); - this.resizeListener.add(elementSizeObserver); - this.viewCell.updateOutputHeight(index, clientHeight); - } else if (result.type === RenderOutputType.None) { // no-op if it's a webview - const clientHeight = Math.ceil(outputItemDiv.clientHeight); - this.viewCell.updateOutputHeight(index, clientHeight); - - const top = this.viewCell.getOutputOffsetInContainer(index); - outputItemDiv.style.top = `${top}px`; - } - } - - async pickActiveMimeTypeRenderer(output: ITransformedDisplayOutputDto) { - const currIndex = output.pickedMimeTypeIndex; - const items = output.orderedMimeTypes!.map((mimeType, index): IMimeTypeRenderer => ({ - label: mimeType.mimeType, - id: mimeType.mimeType, - index: index, - picked: index === currIndex, - detail: this.generateRendererInfo(mimeType.rendererId), - description: index === currIndex ? nls.localize('curruentActiveMimeType', "Currently Active") : undefined - })); - - const picker = this.quickInputService.createQuickPick(); - picker.items = items; - picker.activeItems = items.filter(item => !!item.picked); - picker.placeholder = nls.localize('promptChooseMimeType.placeHolder', "Select output mimetype to render for current output"); - - const pick = await new Promise(resolve => { - picker.onDidAccept(() => { - resolve(picker.selectedItems.length === 1 ? (picker.selectedItems[0] as IMimeTypeRenderer).index : undefined); - picker.dispose(); - }); - picker.show(); - }); - - if (pick === undefined) { - return; - } - - if (pick !== currIndex) { - // user chooses another mimetype - const index = this.viewCell.outputs.indexOf(output); - const nextElement = this.domNode.nextElementSibling; - this.resizeListener.clear(); - const element = this.domNode; - if (element) { - element.parentElement?.removeChild(element); - this.notebookEditor.removeInset(output); - } - - output.pickedMimeTypeIndex = pick; - this.render(index, nextElement as HTMLElement); - this.relayoutCell(); - } - } - - private getNotebookUri(): URI | undefined { - return CellUri.parse(this.viewCell.uri)?.notebook; - } - - generateRendererInfo(renderId: string | undefined): string { - if (renderId === undefined || renderId === BUILTIN_RENDERER_ID) { - return nls.localize('builtinRenderInfo', "built-in"); - } - - const renderInfo = this.notebookService.getRendererInfo(renderId); - - if (renderInfo) { - const displayName = renderInfo.displayName !== '' ? renderInfo.displayName : renderInfo.id; - return `${displayName} (${renderInfo.extensionId.value})`; - } - - return nls.localize('builtinRenderInfo', "built-in"); - } - - relayoutCell() { - this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); - } -} export class CodeCell extends Disposable { - private outputEntries = new Map(); + private _outputContainerRenderer: OutputContainer; + private _activeCellRunPlaceholder: IDisposable | null = null; + private _untrustedStatusItem: IDisposable | null = null; constructor( - private notebookEditor: INotebookEditor, + private notebookEditor: IActiveNotebookEditor, private viewCell: CodeCellViewModel, private templateData: CodeCellRenderTemplate, - @INotebookService private notebookService: INotebookService, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IModeService private readonly _modeService: IModeService + @INotebookService notebookService: INotebookService, + @IQuickInputService quickInputService: IQuickInputService, + @INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService, + @IOpenerService readonly openerService: IOpenerService, + @ITextFileService readonly textFileService: ITextFileService, + @IModeService private readonly _modeService: IModeService, + // @IKeybindingService private readonly _keybindingService: IKeybindingService, + ) { super(); @@ -244,7 +46,7 @@ export class CodeCell extends Disposable { const lineNum = this.viewCell.lineCount; const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; const editorHeight = this.viewCell.layoutInfo.editorHeight === 0 - ? lineNum * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING + ? lineNum * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING : this.viewCell.layoutInfo.editorHeight; this.layoutEditor( @@ -292,10 +94,10 @@ export class CodeCell extends Disposable { })); updateForFocusMode(); - templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel!.metadata).editable) }); + templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel.metadata).editable) }); this._register(viewCell.onDidChangeState((e) => { if (e.metadataChanged) { - templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel!.metadata).editable) }); + templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel.metadata).editable) }); // TODO@rob this isn't nice this.viewCell.layoutChange({}); @@ -313,14 +115,14 @@ export class CodeCell extends Disposable { this._register(viewCell.onDidChangeLayout((e) => { if (e.outerWidth !== undefined) { - const layoutInfo = templateData.editor!.getLayoutInfo(); + const layoutInfo = templateData.editor.getLayoutInfo(); if (layoutInfo.width !== viewCell.layoutInfo.editorWidth) { this.onCellWidthChange(); } } })); - this._register(templateData.editor!.onDidContentSizeChange((e) => { + this._register(templateData.editor.onDidContentSizeChange((e) => { if (e.contentHeightChanged) { if (this.viewCell.layoutInfo.editorHeight !== e.contentHeight) { this.onCellHeightChange(e.contentHeight); @@ -328,92 +130,20 @@ export class CodeCell extends Disposable { } })); - this._register(templateData.editor!.onDidChangeCursorSelection((e) => { + this._register(templateData.editor.onDidChangeCursorSelection((e) => { if (e.source === 'restoreState') { // do not reveal the cell into view if this selection change was caused by restoring editors... return; } - const primarySelection = templateData.editor!.getSelection(); + const primarySelection = templateData.editor.getSelection(); if (primarySelection) { - this.notebookEditor.revealLineInViewAsync(viewCell, primarySelection!.positionLineNumber); + this.notebookEditor.revealLineInViewAsync(viewCell, primarySelection.positionLineNumber); } })); - this._register(viewCell.onDidChangeOutputs((splices) => { - if (!splices.length) { - return; - } - - const previousOutputHeight = this.viewCell.layoutInfo.outputTotalHeight; - - if (this.viewCell.outputs.length) { - this.templateData.outputContainer!.style.display = 'block'; - } else { - this.templateData.outputContainer!.style.display = 'none'; - } - - const reversedSplices = splices.reverse(); - - reversedSplices.forEach(splice => { - viewCell.spliceOutputHeights(splice[0], splice[1], splice[2].map(_ => 0)); - }); - - const removedKeys: IProcessedOutput[] = []; - - this.outputEntries.forEach((value, key) => { - if (viewCell.outputs.indexOf(key) < 0) { - // already removed - removedKeys.push(key); - // remove element from DOM - this.templateData?.outputContainer?.removeChild(value.domNode); - this.notebookEditor.removeInset(key); - } - }); - - removedKeys.forEach(key => { - this.outputEntries.get(key)?.dispose(); - this.outputEntries.delete(key); - }); - - let prevElement: HTMLElement | undefined = undefined; - - [...this.viewCell.outputs].reverse().forEach(output => { - if (this.outputEntries.has(output)) { - // already exist - prevElement = this.outputEntries.get(output)!.domNode; - return; - } - - // newly added element - const currIndex = this.viewCell.outputs.indexOf(output); - this.renderOutput(output, currIndex, prevElement); - prevElement = this.outputEntries.get(output)?.domNode; - }); - - const editorHeight = templateData.editor!.getContentHeight(); - viewCell.editorHeight = editorHeight; - - if (previousOutputHeight === 0 || this.viewCell.outputs.length === 0) { - // first execution or removing all outputs - this.relayoutCell(); - } else { - this.relayoutCellDebounced(); - } - })); - - this._register(viewCell.onDidChangeLayout(() => { - this.outputEntries.forEach((value, key) => { - const index = viewCell.outputs.indexOf(key); - if (index >= 0) { - const top = this.viewCell.getOutputOffsetInContainer(index); - value.domNode.style.top = `${top}px`; - } - }); - - })); - + // Apply decorations this._register(viewCell.onCellDecorationsChanged((e) => { e.added.forEach(options => { if (options.className) { @@ -435,7 +165,6 @@ export class CodeCell extends Disposable { } }); })); - // apply decorations viewCell.getCellDecorations().forEach(options => { if (options.className) { @@ -447,7 +176,18 @@ export class CodeCell extends Disposable { } }); - this._register(templateData.editor!.onMouseDown(e => { + // Mouse click handlers + this._register(templateData.statusBar.onDidClick(e => { + if (e.type !== ClickTargetType.ContributedCommandItem) { + const target = templateData.editor.getTargetAtClientPoint(e.event.clientX, e.event.clientY - viewCell.getEditorStatusbarHeight()); + if (target?.position) { + templateData.editor.setPosition(target.position); + templateData.editor.focus(); + } + } + })); + + this._register(templateData.editor.onMouseDown(e => { // prevent default on right mouse click, otherwise it will trigger unexpected focus changes // the catch is, it means we don't allow customization of right button mouse down handlers other than the built in ones. if (e.event.rightButton) { @@ -455,49 +195,88 @@ export class CodeCell extends Disposable { } })); - const updateFocusMode = () => viewCell.focusMode = templateData.editor!.hasWidgetFocus() ? CellFocusMode.Editor : CellFocusMode.Container; - this._register(templateData.editor!.onDidFocusEditorWidget(() => { + // Focus Mode + const updateFocusMode = () => viewCell.focusMode = templateData.editor.hasWidgetFocus() ? CellFocusMode.Editor : CellFocusMode.Container; + this._register(templateData.editor.onDidFocusEditorWidget(() => { updateFocusMode(); })); - - this._register(templateData.editor!.onDidBlurEditorWidget(() => { - updateFocusMode(); + this._register(templateData.editor.onDidBlurEditorWidget(() => { + // this is for a special case: + // users click the status bar empty space, which we will then focus the editor + // so we don't want to update the focus state too eagerly + if (document.activeElement?.contains(this.templateData.container)) { + setTimeout(() => { + updateFocusMode(); + }, 300); + } else { + updateFocusMode(); + } })); - updateFocusMode(); - if (viewCell.outputs.length > 0) { - let layoutCache = false; - if (this.viewCell.layoutInfo.totalHeight !== 0 && this.viewCell.layoutInfo.editorHeight > editorHeight) { - layoutCache = true; - this.relayoutCell(); - } - - this.templateData.outputContainer!.style.display = 'block'; - // there are outputs, we need to calcualte their sizes and trigger relayout - // @TODO@rebornix, if there is no resizable output, we should not check their height individually, which hurts the performance - for (let index = 0; index < this.viewCell.outputs.length; index++) { - const currOutput = this.viewCell.outputs[index]; - - // always add to the end - this.renderOutput(currOutput, index, undefined); - } - - viewCell.editorHeight = editorHeight; - if (layoutCache) { - this.relayoutCellDebounced(); - } else { - this.relayoutCell(); - } - } else { - // noop - viewCell.editorHeight = editorHeight; - this.relayoutCell(); - this.templateData.outputContainer!.style.display = 'none'; - } - + // Render Outputs + this._outputContainerRenderer = new OutputContainer(notebookEditor, viewCell, templateData, notebookService, quickInputService, openerService, textFileService); + this._outputContainerRenderer.render(editorHeight); // Need to do this after the intial renderOutput updateForCollapseState(); + + const updatePlaceholder = () => { + if (this.notebookEditor.viewModel + && this.notebookEditor.getActiveCell() === this.viewCell + && viewCell.getEvaluatedMetadata(this.notebookEditor.viewModel.metadata).runnable + && viewCell.metadata.runState === undefined + && viewCell.metadata.lastRunDuration === undefined + ) { + // active cell and no run status + if (this._activeCellRunPlaceholder === null) { + // const keybinding = this._keybindingService.lookupKeybinding(EXECUTE_CELL_COMMAND_ID); + this._activeCellRunPlaceholder = this.notebookCellStatusBarService.addEntry(getExecuteCellPlaceholder(this.viewCell)); + } + + return; + } + + this._activeCellRunPlaceholder?.dispose(); + this._activeCellRunPlaceholder = null; + }; + + this._register(this.notebookEditor.onDidChangeActiveCell(() => { + updatePlaceholder(); + })); + + this._register(this.viewCell.model.onDidChangeMetadata(() => { + updatePlaceholder(); + })); + + // const updateUntrustedStatus = () => { + // if (this.notebookEditor.viewModel + // && this.notebookEditor.viewModel.metadata.trusted) { + // this._untrustedStatusItem?.dispose(); + // this._untrustedStatusItem = null; + // } else { + // if (this._untrustedStatusItem === null) { + // this._untrustedStatusItem = this.notebookCellStatusBarService.addEntry({ + // alignment: CellStatusbarAlignment.LEFT, + // priority: -1, + // cellResource: viewCell.uri, + // command: TRUST_NOTEBOOK_COMMAND_ID, + // text: 'Untrusted', + // tooltip: 'Untrusted notebook', + // visible: true, + // }); + // } + // } + // }; + + this._register(this.notebookEditor.viewModel.notebookDocument.onDidChangeContent(e => { + if (e.rawEvents.find(event => event.kind === NotebookCellsChangeType.ChangeDocumentMetadata)) { + updatePlaceholder(); + // updateUntrustedStatus(); + } + })); + + updatePlaceholder(); + // updateUntrustedStatus(); } private viewUpdate(): void { @@ -512,52 +291,24 @@ export class CodeCell extends Disposable { } } - private viewUpdateShowOutputs(): void { - for (let index = 0; index < this.viewCell.outputs.length; index++) { - const currOutput = this.viewCell.outputs[index]; - - const renderedOutput = this.outputEntries.get(currOutput); - if (renderedOutput && renderedOutput.renderResult) { - if (renderedOutput.renderResult.type !== RenderOutputType.None) { - this.notebookEditor.createInset(this.viewCell, renderedOutput.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index)); - } else { - // Anything else, just update the height - this.viewCell.updateOutputHeight(index, renderedOutput.domNode.clientHeight); - } - } else { - // Wasn't previously rendered, render it now - this.renderOutput(currOutput, index); - } - } - - this.relayoutCell(); - } - private viewUpdateInputCollapsed(): void { DOM.hide(this.templateData.cellContainer); DOM.hide(this.templateData.runButtonContainer); DOM.show(this.templateData.collapsedPart); DOM.show(this.templateData.outputContainer); this.templateData.container.classList.toggle('collapsed', true); - - this.viewUpdateShowOutputs(); + this._outputContainerRenderer.viewUpdateShowOutputs(); this.relayoutCell(); } - private viewUpdateHideOuputs(): void { - for (const e of this.outputEntries.keys()) { - this.notebookEditor.hideInset(e); - } - } - private viewUpdateOutputCollapsed(): void { DOM.show(this.templateData.cellContainer); DOM.show(this.templateData.runButtonContainer); DOM.show(this.templateData.collapsedPart); DOM.hide(this.templateData.outputContainer); - this.viewUpdateHideOuputs(); + this._outputContainerRenderer.viewUpdateHideOuputs(); this.templateData.container.classList.toggle('collapsed', false); this.templateData.container.classList.toggle('output-collapsed', true); @@ -572,11 +323,7 @@ export class CodeCell extends Disposable { DOM.hide(this.templateData.outputContainer); this.templateData.container.classList.toggle('collapsed', true); this.templateData.container.classList.toggle('output-collapsed', true); - - for (const e of this.outputEntries.keys()) { - this.notebookEditor.hideInset(e); - } - + this._outputContainerRenderer.viewUpdateHideOuputs(); this.relayoutCell(); } @@ -587,9 +334,7 @@ export class CodeCell extends Disposable { DOM.show(this.templateData.outputContainer); this.templateData.container.classList.toggle('collapsed', false); this.templateData.container.classList.toggle('output-collapsed', false); - - this.viewUpdateShowOutputs(); - + this._outputContainerRenderer.viewUpdateShowOutputs(); this.relayoutCell(); } @@ -599,7 +344,7 @@ export class CodeCell extends Disposable { } private onCellWidthChange(): void { - const realContentHeight = this.templateData.editor!.getContentHeight(); + const realContentHeight = this.templateData.editor.getContentHeight(); this.viewCell.editorHeight = realContentHeight; this.relayoutCell(); @@ -611,16 +356,11 @@ export class CodeCell extends Disposable { ); // for contents for which we don't observe for dynamic height, update them manually - this.viewCell.outputs.forEach((o, i) => { - const renderedOutput = this.outputEntries.get(o); - if (renderedOutput && renderedOutput.renderResult && renderedOutput.renderResult.type === RenderOutputType.None && !renderedOutput.renderResult.hasDynamicHeight) { - this.viewCell.updateOutputHeight(i, renderedOutput.domNode.clientHeight); - } - }); + this._outputContainerRenderer.onCellWidthChange(); } private onCellHeightChange(newHeight: number): void { - const viewLayout = this.templateData.editor!.getLayoutInfo(); + const viewLayout = this.templateData.editor.getLayoutInfo(); this.viewCell.editorHeight = newHeight; this.relayoutCell(); this.layoutEditor( @@ -631,14 +371,6 @@ export class CodeCell extends Disposable { ); } - private renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement) { - if (!this.outputEntries.has(currOutput)) { - this.outputEntries.set(currOutput, new OutputElement(this.notebookEditor, this.notebookService, this.quickInputService, this.viewCell, this.templateData.outputContainer, currOutput)); - } - - this.outputEntries.get(currOutput)!.render(index, beforeElement); - } - relayoutCell() { if (this._timer !== null) { clearTimeout(this._timer); @@ -662,13 +394,11 @@ export class CodeCell extends Disposable { dispose() { this.viewCell.detachTextEditor(); - this.outputEntries.forEach((value) => { - value.dispose(); - }); - - this.templateData.focusIndicatorLeft!.style.height = 'initial'; + this._outputContainerRenderer.dispose(); + this._activeCellRunPlaceholder?.dispose(); + this._untrustedStatusItem?.dispose(); + this.templateData.focusIndicatorLeft.style.height = 'initial'; super.dispose(); } } - diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts deleted file mode 100644 index 4b5002ebc..000000000 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as DOM from 'vs/base/browser/dom'; -import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { MenuItemAction } from 'vs/platform/actions/common/actions'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { renderCodicons } from 'vs/base/browser/codicons'; - -export class CodiconActionViewItem extends MenuEntryActionViewItem { - constructor( - readonly _action: MenuItemAction, - keybindingService: IKeybindingService, - notificationService: INotificationService, - ) { - super(_action, keybindingService, notificationService); - } - updateLabel(): void { - if (this.options.label && this.label) { - DOM.reset(this.label, ...renderCodicons(this._commandAction.label ?? '')); - } - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index f75ca70ee..da2e04bc6 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -6,20 +6,24 @@ import * as DOM from 'vs/base/browser/dom'; import { raceCancellation } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { renderCodicons } from 'vs/base/browser/codicons'; -import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, INotebookEditor, MarkdownCellRenderTemplate, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFocusMode, MarkdownCellRenderTemplate, ICellViewModel, getEditorTopPadding, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellFoldingState } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; +import { getExecuteCellPlaceholder, getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; +import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; +import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { renderCodicons } from 'vs/base/browser/codicons'; export class StatefulMarkdownCell extends Disposable { @@ -29,14 +33,16 @@ export class StatefulMarkdownCell extends Disposable { private localDisposables = new DisposableStore(); private foldingState: CellFoldingState; + private _activeCellRunPlaceholder: IDisposable | null = null; constructor( - private readonly notebookEditor: INotebookEditor, + private readonly notebookEditor: IActiveNotebookEditor, private readonly viewCell: MarkdownCellViewModel, private readonly templateData: MarkdownCellRenderTemplate, private editorOptions: IEditorOptions, private readonly renderedEditors: Map, @IContextKeyService private readonly contextKeyService: IContextKeyService, + @INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService, @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); @@ -129,6 +135,40 @@ export class StatefulMarkdownCell extends Disposable { }); this.viewUpdate(); + + const updatePlaceholder = () => { + if ( + this.notebookEditor.getActiveCell() === this.viewCell + && !!this.notebookEditor.viewModel.metadata.trusted + ) { + // active cell and no run status + if (this._activeCellRunPlaceholder === null) { + // const keybinding = this._keybindingService.lookupKeybinding(EXECUTE_CELL_COMMAND_ID); + this._activeCellRunPlaceholder = this.notebookCellStatusBarService.addEntry(getExecuteCellPlaceholder(this.viewCell)); + } + + return; + } + + this._activeCellRunPlaceholder?.dispose(); + this._activeCellRunPlaceholder = null; + }; + + this._register(this.notebookEditor.onDidChangeActiveCell(() => { + updatePlaceholder(); + })); + + this._register(this.viewCell.model.onDidChangeMetadata(() => { + updatePlaceholder(); + })); + + this._register(this.notebookEditor.viewModel.notebookDocument.onDidChangeContent(e => { + if (e.rawEvents.find(event => event.kind === NotebookCellsChangeType.ChangeDocumentMetadata)) { + updatePlaceholder(); + } + })); + + updatePlaceholder(); } private viewUpdate(): void { @@ -159,13 +199,13 @@ export class StatefulMarkdownCell extends Disposable { this.templateData.container.classList.toggle('collapsed', false); if (this.editor) { - editorHeight = this.editor!.getContentHeight(); + editorHeight = this.editor.getContentHeight(); // not first time, we don't need to create editor or bind listeners this.viewCell.attachTextEditor(this.editor); this.focusEditorIfNeeded(); - this.bindEditorListeners(); + this.bindEditorListeners(this.editor); this.editor.layout({ width: this.viewCell.layoutInfo.editorWidth, @@ -175,7 +215,7 @@ export class StatefulMarkdownCell extends Disposable { const width = this.viewCell.layoutInfo.editorWidth; const lineNum = this.viewCell.lineCount; const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; - editorHeight = Math.max(lineNum, 1) * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + editorHeight = Math.max(lineNum, 1) * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; this.templateData.editorContainer.innerText = ''; @@ -221,7 +261,7 @@ export class StatefulMarkdownCell extends Disposable { this.focusEditorIfNeeded(); } - this.bindEditorListeners(); + this.bindEditorListeners(this.editor!); this.viewCell.editorHeight = editorHeight; }); @@ -255,7 +295,7 @@ export class StatefulMarkdownCell extends Disposable { this.relayoutCell(); } else { // first time, readonly mode - this.localDisposables.add(markdownRenderer.onDidRenderCodeBlock(() => { + this.localDisposables.add(markdownRenderer.onDidRenderAsync(() => { this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight; this.relayoutCell(); })); @@ -315,10 +355,10 @@ export class StatefulMarkdownCell extends Disposable { this.templateData.foldingIndicator.innerText = ''; break; case CellFoldingState.Collapsed: - DOM.reset(this.templateData.foldingIndicator, ...renderCodicons('$(chevron-right)')); + DOM.reset(this.templateData.foldingIndicator, ...renderCodicons(ThemeIcon.asCodiconLabel(collapsedIcon))); break; case CellFoldingState.Expanded: - DOM.reset(this.templateData.foldingIndicator, ...renderCodicons('$(chevron-down)')); + DOM.reset(this.templateData.foldingIndicator, ...renderCodicons(ThemeIcon.asCodiconLabel(expandedIcon))); break; default: @@ -326,13 +366,13 @@ export class StatefulMarkdownCell extends Disposable { } } - private bindEditorListeners() { - this.localDisposables.add(this.editor!.onDidContentSizeChange(e => { - const viewLayout = this.editor!.getLayoutInfo(); + private bindEditorListeners(editor: CodeEditorWidget) { + this.localDisposables.add(editor.onDidContentSizeChange(e => { + const viewLayout = editor.getLayoutInfo(); if (e.contentHeightChanged) { this.viewCell.editorHeight = e.contentHeight; - this.editor!.layout( + editor.layout( { width: viewLayout.width, height: e.contentHeight @@ -341,26 +381,35 @@ export class StatefulMarkdownCell extends Disposable { } })); - this.localDisposables.add(this.editor!.onDidChangeCursorSelection((e) => { + this.localDisposables.add(editor.onDidChangeCursorSelection((e) => { if (e.source === 'restoreState') { // do not reveal the cell into view if this selection change was caused by restoring editors... return; } - const primarySelection = this.editor!.getSelection(); + const primarySelection = editor.getSelection(); if (primarySelection) { - this.notebookEditor.revealLineInViewAsync(this.viewCell, primarySelection!.positionLineNumber); + this.notebookEditor.revealLineInViewAsync(this.viewCell, primarySelection.positionLineNumber); } })); - const updateFocusMode = () => this.viewCell.focusMode = this.editor!.hasWidgetFocus() ? CellFocusMode.Editor : CellFocusMode.Container; - this.localDisposables.add(this.editor!.onDidFocusEditorWidget(() => { + const updateFocusMode = () => this.viewCell.focusMode = editor.hasWidgetFocus() ? CellFocusMode.Editor : CellFocusMode.Container; + this.localDisposables.add(editor.onDidFocusEditorWidget(() => { updateFocusMode(); })); - this.localDisposables.add(this.editor!.onDidBlurEditorWidget(() => { - updateFocusMode(); + this.localDisposables.add(editor.onDidBlurEditorWidget(() => { + // this is for a special case: + // users click the status bar empty space, which we will then focus the editor + // so we don't want to update the focus state too eagerly + if (document.activeElement?.contains(this.templateData.container)) { + setTimeout(() => { + updateFocusMode(); + }, 300); + } else { + updateFocusMode(); + } })); updateFocusMode(); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts deleted file mode 100644 index d9be462c0..000000000 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts +++ /dev/null @@ -1,78 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as DOM from 'vs/base/browser/dom'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IDimension } from 'vs/editor/common/editorCommon'; -import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; - -declare const ResizeObserver: any; - -export interface IResizeObserver { - startObserving: () => void; - stopObserving: () => void; - getWidth(): number; - getHeight(): number; - dispose(): void; -} - -export class BrowserResizeObserver extends Disposable implements IResizeObserver { - private readonly referenceDomElement: HTMLElement | null; - - private readonly observer: any; - private width: number; - private height: number; - - constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void) { - super(); - - this.referenceDomElement = referenceDomElement; - this.width = -1; - this.height = -1; - - this.observer = new ResizeObserver((entries: any) => { - for (const entry of entries) { - if (entry.target === referenceDomElement && entry.contentRect) { - if (this.width !== entry.contentRect.width || this.height !== entry.contentRect.height) { - this.width = entry.contentRect.width; - this.height = entry.contentRect.height; - DOM.scheduleAtNextAnimationFrame(() => { - changeCallback(); - }); - } - } - } - }); - } - - getWidth(): number { - return this.width; - } - - getHeight(): number { - return this.height; - } - - startObserving(): void { - this.observer.observe(this.referenceDomElement!); - } - - stopObserving(): void { - this.observer.unobserve(this.referenceDomElement!); - } - - dispose(): void { - this.observer.disconnect(); - super.dispose(); - } -} - -export function getResizesObserver(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void): IResizeObserver { - if (ResizeObserver) { - return new BrowserResizeObserver(referenceDomElement, dimension, changeCallback); - } else { - return new ElementSizeObserver(referenceDomElement, dimension, changeCallback); - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 4036c5128..936792e8a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -101,16 +101,30 @@ function webviewPreloads() { } }; - const runScript = async (url: string, originalUri: string, globals: { [name: string]: unknown } = {}): Promise => { - const res = await fetch(url); - const text = await res.text(); - if (!res.ok) { - throw new Error(`Unexpected ${res.status} requesting ${originalUri}: ${text || res.statusText}`); + const runScript = async (url: string, originalUri: string, globals: { [name: string]: unknown } = {}): Promise<() => (PreloadResult)> => { + let text: string; + try { + const res = await fetch(url); + text = await res.text(); + if (!res.ok) { + throw new Error(`Unexpected ${res.status} requesting ${originalUri}: ${text || res.statusText}`); + } + + globals.scriptUrl = url; + } catch (e) { + return () => ({ state: PreloadState.Error, error: e.message }); } const args = Object.entries(globals); - new Function(...args.map(([k]) => k), text)(...args.map(([, v]) => v)); - return undefined; + return () => { + try { + new Function(...args.map(([k]) => k), text)(...args.map(([, v]) => v)); + return { state: PreloadState.Ok }; + } catch (e) { + console.error(e); + return { state: PreloadState.Error, error: e.message }; + } + }; }; const outputObservers = new Map(); @@ -123,14 +137,27 @@ function webviewPreloads() { } if (entry.target.id === id && entry.contentRect) { - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'dimension', - id: id, - data: { - height: entry.contentRect.height + __outputNodePadding__ * 2 - } - }); + if (entry.contentRect.height !== 0) { + entry.target.style.padding = `${__outputNodePadding__}px`; + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'dimension', + id: id, + data: { + height: entry.contentRect.height + __outputNodePadding__ * 2 + } + }); + } else { + entry.target.style.padding = `0px`; + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'dimension', + id: id, + data: { + height: entry.contentRect.height + } + }); + } } } }); @@ -324,11 +351,18 @@ function webviewPreloads() { }; }; + const enum PreloadState { + Ok, + Error + } + + type PreloadResult = { state: PreloadState.Ok } | { state: PreloadState.Error, error: string }; + /** * Map of preload resource URIs to promises that resolve one the resource * loads or errors. */ - const preloadPromises = new Map>(); + const preloadPromises = new Map>(); const queuedOuputActions = new Map>(); /** @@ -361,7 +395,7 @@ function webviewPreloads() { switch (event.data.type) { case 'html': enqueueOutputAction(event.data, async data => { - const preloadErrs = await Promise.all(data.requiredPreloads.map(p => preloadPromises.get(p.uri))); + const preloadResults = await Promise.all(data.requiredPreloads.map(p => preloadPromises.get(p.uri))); if (!queuedOuputActions.has(data.outputId)) { // output was cleared while loading return; } @@ -389,7 +423,8 @@ function webviewPreloads() { outputNode.style.top = data.top + 'px'; outputNode.style.left = data.left + 'px'; outputNode.style.width = 'calc(100% - ' + data.left + 'px)'; - outputNode.style.minHeight = '32px'; + // outputNode.style.minHeight = '32px'; + outputNode.style.padding = '0px'; outputNode.id = outputId; addMouseoverListeners(outputNode, outputId); @@ -398,13 +433,13 @@ function webviewPreloads() { outputNode.innerHTML = content.htmlContent; cellOutputContainer.appendChild(outputNode); domEval(outputNode); - } else if (preloadErrs.some(e => !!e)) { + } else if (preloadResults.some(e => e?.state === PreloadState.Error)) { outputNode.innerText = `Error loading preloads:`; const errList = document.createElement('ul'); - for (const err of preloadErrs) { - if (err) { + for (const result of preloadResults) { + if (result?.state === PreloadState.Error) { const item = document.createElement('li'); - item.innerText = err; + item.innerText = result.error; errList.appendChild(item); } } @@ -498,11 +533,20 @@ function webviewPreloads() { case 'preload': const resources = event.data.resources; const globals = event.data.type === 'preload' ? { acquireVsCodeApi } : {}; + let queue: Promise = Promise.resolve({ state: PreloadState.Ok }); for (const { uri, originalUri } of resources) { - preloadPromises.set(uri, runScript(uri, originalUri, globals).catch(err => { - console.error(err); - return err.message || String(err); + // create the promise so that the scripts download in parallel, but + // only invoke them in series within the queue + const promise = runScript(uri, originalUri, globals); + queue = queue.then(() => promise.then(fn => { + const result = fn(); + if (result.state === PreloadState.Error) { + console.error(result.error); + } + + return result; })); + preloadPromises.set(uri, queue); } break; case 'focus-output': diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index 6d42d6707..3d4b3b4e5 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -12,8 +12,8 @@ import { IPosition } from 'vs/editor/common/core/position'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; -import { CELL_STATUSBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CELL_STATUSBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions, getEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, INotebookSearchOptions, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -131,7 +131,7 @@ export abstract class BaseCellViewModel extends Disposable { })); } - protected getEditorStatusbarHeight() { + getEditorStatusbarHeight() { const showCellStatusBar = this._configurationService.getValue(ShowCellStatusBarKey); return showCellStatusBar ? CELL_STATUSBAR_HEIGHT : 0; } @@ -341,7 +341,7 @@ export abstract class BaseCellViewModel extends Disposable { return 0; } - return this._textEditor.getTopForLineNumber(line) + EDITOR_TOP_PADDING; + return this._textEditor.getTopForLineNumber(line) + getEditorTopPadding(); } getPositionScrollTopOffset(line: number, column: number): number { @@ -349,7 +349,35 @@ export abstract class BaseCellViewModel extends Disposable { return 0; } - return this._textEditor.getTopForPosition(line, column) + EDITOR_TOP_PADDING; + return this._textEditor.getTopForPosition(line, column) + getEditorTopPadding(); + } + + cursorAtBeginEnd(): boolean { + if (!this._textEditor) { + return false; + } + + if (!this.textModel) { + return false; + } + + // only validate primary cursor + const selection = this._textEditor.getSelection(); + + // only validate empty cursor + if (!selection || !selection.isEmpty()) { + return false; + } + + if (selection.startLineNumber === 1 && selection.startColumn === 1) { + return true; + } + + if (selection.startLineNumber === this._textModel?.getLineCount() && selection.startColumn === this._textModel?.getLineMaxColumn(selection.startLineNumber)) { + return true; + } + + return false; } cursorAtBoundary(): CursorAtBoundary { @@ -425,8 +453,8 @@ export abstract class BaseCellViewModel extends Disposable { const editable = this.metadata?.editable ?? documentMetadata.cellEditable; - const runnable = this.metadata?.runnable ?? - documentMetadata.cellRunnable; + const runnable = (this.metadata?.runnable ?? + documentMetadata.cellRunnable) && !!documentMetadata.trusted; const hasExecutionOrder = this.metadata?.hasExecutionOrder ?? documentMetadata.cellHasExecutionOrder; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 9e57b51a6..bbc8ee237 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -9,8 +9,8 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CodeCellLayoutState, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CodeCellLayoutState, getEditorTopPadding, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { CellKind, INotebookSearchOptions, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -86,9 +86,11 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod this._layoutInfo = { fontInfo: initialNotebookLayoutInfo?.fontInfo || null, editorHeight: 0, - editorWidth: initialNotebookLayoutInfo ? this.computeEditorWidth(initialNotebookLayoutInfo!.width) : 0, + editorWidth: initialNotebookLayoutInfo ? this.computeEditorWidth(initialNotebookLayoutInfo.width) : 0, outputContainerOffset: 0, outputTotalHeight: 0, + outputShowMoreContainerHeight: 0, + outputShowMoreContainerOffset: 0, totalHeight: 0, indicatorHeight: 0, bottomToolbarOffset: 0, @@ -103,6 +105,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod layoutChange(state: CodeCellLayoutChangeEvent) { // recompute this._ensureOutputsTop(); + const outputShowMoreContainerHeight = state.outputShowMoreContainerHeight ? state.outputShowMoreContainerHeight : this._layoutInfo.outputShowMoreContainerHeight; let outputTotalHeight = this.metadata?.outputCollapsed ? COLLAPSED_INDICATOR_HEIGHT : this._outputsTop!.getTotalValue(); if (!this.metadata?.inputCollapsed) { @@ -117,17 +120,18 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } else if (state.editorHeight || this._layoutInfo.layoutState === CodeCellLayoutState.Measured) { // Editor has been measured editorHeight = this._editorHeight; - totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight); + totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight, outputShowMoreContainerHeight); newState = CodeCellLayoutState.Measured; } else { editorHeight = this.estimateEditorHeight(state.font?.lineHeight); - totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight); + totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight, outputShowMoreContainerHeight); newState = CodeCellLayoutState.Estimated; } const statusbarHeight = this.getEditorStatusbarHeight(); - const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight; + const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight + outputShowMoreContainerHeight; const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + statusbarHeight; + const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight; const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; @@ -137,6 +141,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod editorWidth, outputContainerOffset, outputTotalHeight, + outputShowMoreContainerHeight, + outputShowMoreContainerOffset, totalHeight, indicatorHeight, bottomToolbarOffset, @@ -144,9 +150,10 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod }; } else { outputTotalHeight = this.metadata?.inputCollapsed && this.metadata.outputCollapsed ? 0 : outputTotalHeight; - const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight; + const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight + outputShowMoreContainerHeight; const outputContainerOffset = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT; - const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight; + const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight + outputShowMoreContainerHeight; + const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight; const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; @@ -156,6 +163,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod editorWidth, outputContainerOffset, outputTotalHeight, + outputShowMoreContainerHeight, + outputShowMoreContainerOffset, totalHeight, indicatorHeight, bottomToolbarOffset, @@ -183,6 +192,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod editorWidth: this._layoutInfo.editorWidth, outputContainerOffset: this._layoutInfo.outputContainerOffset, outputTotalHeight: this._layoutInfo.outputTotalHeight, + outputShowMoreContainerHeight: this._layoutInfo.outputShowMoreContainerHeight, + outputShowMoreContainerOffset: this._layoutInfo.outputShowMoreContainerOffset, totalHeight: totalHeight, indicatorHeight: this._layoutInfo.indicatorHeight, bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset, @@ -203,18 +214,18 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod getHeight(lineHeight: number) { if (this._layoutInfo.layoutState === CodeCellLayoutState.Uninitialized) { const editorHeight = this.estimateEditorHeight(lineHeight); - return this.computeTotalHeight(editorHeight, 0); + return this.computeTotalHeight(editorHeight, 0, 0); } else { return this._layoutInfo.totalHeight; } } private estimateEditorHeight(lineHeight: number | undefined = 20): number { - return this.lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + return this.lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; } - private computeTotalHeight(editorHeight: number, outputsTotalHeight: number): number { - return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + this.getEditorStatusbarHeight() + outputsTotalHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN; + private computeTotalHeight(editorHeight: number, outputsTotalHeight: number, outputShowMoreContainerHeight: number): number { + return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + this.getEditorStatusbarHeight() + outputsTotalHeight + outputShowMoreContainerHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN; } /** @@ -240,6 +251,10 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod this.editState = CellEditState.Preview; } + updateOutputShowMoreContainerHeight(height: number) { + this.layoutChange({ outputShowMoreContainerHeight: height }); + } + updateOutputHeight(index: number, height: number) { if (index >= this._outputCollection.length) { throw new Error('Output index out of range!'); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 4ea7ba52c..d1843f21d 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -180,6 +180,10 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return this._notebook.metadata; } + get runnable() { + return !!this._notebook.metadata?.runnable && !!this._notebook.metadata?.trusted; + } + private readonly _onDidChangeViewCells = this._register(new Emitter()); get onDidChangeViewCells(): Event { return this._onDidChangeViewCells.event; } @@ -348,7 +352,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD }); })); - this._viewCells = this._notebook!.cells.map(cell => { + this._viewCells = this._notebook.cells.map(cell => { return createCellViewModel(this._instantiationService, this, cell); }); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index d46143ecb..709f0a4f7 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -12,6 +12,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { hash } from 'vs/base/common/hash'; +import { createTextBuffer } from 'vs/editor/common/model/textModel'; export class NotebookCellTextModel extends Disposable implements ICell { private _onDidChangeOutputs = new Emitter(); @@ -171,6 +172,9 @@ export class NotebookCellTextModel extends Disposable implements ICell { } dispose() { + // Manually release reference to previous text buffer to avoid large leaks + // in case someone leaks a CellTextModel reference + this._textBuffer = createTextBuffer('', model.DefaultEndOfLine.LF); super.dispose(); } } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 36c08e95a..f932f185e 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -337,14 +337,14 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._replaceCells(edit.index, edit.count, edit.cells, synchronous, computeUndoRedo); break; case CellEditType.Output: - //TODO@joh,@rebornix no event, no undo stop (?) + //TODO@jrieken,@rebornix no event, no undo stop (?) this._assertIndex(edit.index); const cell = this._cells[edit.index]; this._spliceNotebookCellOutputs2(cell.handle, edit.outputs, computeUndoRedo); break; case CellEditType.OutputsSplice: { - //TODO@joh,@rebornix no event, no undo stop (?) + //TODO@jrieken,@rebornix no event, no undo stop (?) this._assertIndex(edit.index); const cell = this._cells[edit.index]; this._spliceNotebookCellOutputs(cell.handle, edit.splices, computeUndoRedo); @@ -483,6 +483,17 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } + private _isDocumentMetadataChangeTransient(a: NotebookDocumentMetadata, b: NotebookDocumentMetadata) { + const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); + for (let key of keys) { + if (key !== 'trusted') { + return true; + } + } + + return false; + } + private _updateNotebookMetadata(metadata: NotebookDocumentMetadata, computeUndoRedo: boolean) { const oldMetadata = this.metadata; this.metadata = metadata; @@ -504,7 +515,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel }(), undefined, undefined); } - this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata, transient: false }, true); + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata, transient: this._isDocumentMetadataChangeTransient(oldMetadata, metadata) }, true); } private _insertNewCell(index: number, cells: NotebookCellTextModel[], synchronous: boolean, endSelections?: number[]): void { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 4b3bbab23..941f4a479 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -58,6 +58,7 @@ export const ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER = [ ]; export const BUILTIN_RENDERER_ID = '_builtin'; +export const RENDERER_NOT_AVAILABLE = '_notAvailable'; export enum NotebookRunState { Running = 1, @@ -72,7 +73,8 @@ export const notebookDocumentMetadataDefaults: Required output.outputKind === CellOutputKind.Rich ? ({ ...output, outputId: id }) : output; @@ -548,6 +539,19 @@ export namespace CellUri { } } +export function mimeTypeIsAlwaysSecure(mimeType: string) { + if ([ + 'application/json', + 'text/markdown', + 'image/png', + 'text/plain' + ].indexOf(mimeType) > -1) { + return true; + } + + return false; +} + export function mimeTypeSupportedByCore(mimeType: string) { if ([ 'application/json', @@ -681,6 +685,7 @@ export interface ICellEditorViewState { } export const NOTEBOOK_EDITOR_CURSOR_BOUNDARY = new RawContextKey<'none' | 'top' | 'bottom' | 'both'>('notebookEditorCursorAtBoundary', 'none'); +export const NOTEBOOK_EDITOR_CURSOR_BEGIN_END = new RawContextKey('notebookEditorCursorAtEditorBeginEnd', false); export interface INotebookEditorModel extends IEditorModel { @@ -743,7 +748,6 @@ export interface IEditor extends editorCommon.ICompositeCodeEditor { textModel?: NotebookTextModel; getId(): string; hasFocus(): boolean; - hasModel(): boolean; } export enum NotebookEditorPriority { @@ -865,6 +869,7 @@ export interface INotebookCellStatusBarEntry { readonly command: string | Command | undefined; readonly accessibilityInformation?: IAccessibilityInformation; readonly visible: boolean; + readonly opacity?: string; } export const DisplayOrderKey = 'notebook.displayOrder'; @@ -882,3 +887,4 @@ export interface INotebookDecorationRenderOptions { borderColor?: string | ThemeColor; top?: editorCommon.IContentDecorationRenderOptions; } + diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index fd79380d7..1ad9b60a8 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -17,6 +17,8 @@ import { Schemas } from 'vs/base/common/network'; import { IFileStatWithMetadata, IFileService } from 'vs/platform/files/common/files'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ILabelService } from 'vs/platform/label/common/label'; +import { ILogService } from 'vs/platform/log/common/log'; +import { TaskSequentializer } from 'vs/base/common/async'; export interface INotebookLoadOptions { @@ -40,6 +42,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM private readonly _name: string; private readonly _workingCopyResource: URI; + private readonly saveSequentializer = new TaskSequentializer(); private _dirty = false; @@ -51,6 +54,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM @IBackupFileService private readonly _backupFileService: IBackupFileService, @IFileService private readonly _fileService: IFileService, @INotificationService private readonly _notificationService: INotificationService, + @ILogService private readonly _logService: ILogService, @ILabelService labelService: ILabelService, ) { super(); @@ -197,8 +201,14 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM } private async _assertStat() { + this._logService.debug('[notebook editor model] start assert stat'); const stats = await this._resolveStats(this.resource); if (this._lastResolvedFileStat && stats && stats.mtime > this._lastResolvedFileStat.mtime) { + this._logService.debug(`[notebook editor model] noteboook file on disk is newer: +LastResolvedStat: ${this._lastResolvedFileStat ? JSON.stringify(this._lastResolvedFileStat) : undefined}. +Current stat: ${JSON.stringify(stats)} +`); + this._lastResolvedFileStat = stats; return new Promise<'overwrite' | 'revert' | 'none'>(resolve => { const handle = this._notificationService.prompt( Severity.Info, @@ -221,31 +231,57 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM resolve('none'); }); }); + } else if (!this._lastResolvedFileStat && stats) { + // finally get a stats + this._lastResolvedFileStat = stats; } return 'overwrite'; } async save(): Promise { - const result = await this._assertStat(); - if (result === 'none') { - return false; + let versionId = this._notebook.versionId; + this._logService.debug(`[notebook editor model] save(${versionId}) - enter with versionId ${versionId}`, this.resource.toString(true)); + + if (this.saveSequentializer.hasPending(versionId)) { + this._logService.debug(`[notebook editor model] save(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource.toString(true)); + return this.saveSequentializer.pending.then(() => { + return true; + }); } - if (result === 'revert') { - await this.revert(); + if (this.saveSequentializer.hasPending()) { + return this.saveSequentializer.setNext(async () => { + await this.save(); + }).then(() => { + return true; + }); + } + + return this.saveSequentializer.setPending(versionId, (async () => { + const result = await this._assertStat(); + if (result === 'none') { + return; + } + + if (result === 'revert') { + await this.revert(); + return; + } + + const tokenSource = new CancellationTokenSource(); + await this._notebookService.save(this.notebook.viewType, this.notebook.uri, tokenSource.token); + this._logService.debug(`[notebook editor model] save(${versionId}) - document saved saved, start updating file stats`, this.resource.toString(true)); + const newStats = await this._resolveStats(this.resource); + this._lastResolvedFileStat = newStats; + this.setDirty(false); + })()).then(() => { return true; - } - - const tokenSource = new CancellationTokenSource(); - await this._notebookService.save(this.notebook.viewType, this.notebook.uri, tokenSource.token); - const newStats = await this._resolveStats(this.resource); - this._lastResolvedFileStat = newStats; - this.setDirty(false); - return true; + }); } async saveAs(targetResource: URI): Promise { + this._logService.debug(`[notebook editor model] saveAs - enter`, this.resource.toString(true)); const result = await this._assertStat(); if (result === 'none') { @@ -259,6 +295,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM const tokenSource = new CancellationTokenSource(); await this._notebookService.saveAs(this.notebook.viewType, this.notebook.uri, targetResource, tokenSource.token); + this._logService.debug(`[notebook editor model] saveAs - document saved, start updating file stats`, this.resource.toString(true)); const newStats = await this._resolveStats(this.resource); this._lastResolvedFileStat = newStats; this.setDirty(false); @@ -271,7 +308,9 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM } try { + this._logService.debug(`[notebook editor model] _resolveStats`, this.resource.toString(true)); const newStats = await this._fileService.resolve(this.resource, { resolveMetadata: true }); + this._logService.debug(`[notebook editor model] _resolveStats - latest file stats: ${JSON.stringify(newStats)}`, this.resource.toString(true)); return newStats; } catch (e) { return undefined; diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 51a80bc3c..06f770817 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -10,7 +10,7 @@ import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.pr import { Event } from 'vs/base/common/event'; import { INotebookTextModel, INotebookRendererInfo, - IEditor, ICellEditOperation, NotebookCellOutputsSplice, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata, NotebookDataDto, TransientOptions, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter + IEditor, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata, NotebookDataDto, TransientOptions, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOrderedMimeType, ITransformedDisplayOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -49,10 +49,10 @@ export interface INotebookService { onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }>; registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable; - transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]): void; - transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]): void; + getMimeTypeInfo(textModel: NotebookTextModel, output: ITransformedDisplayOutputDto): readonly IOrderedMimeType[]; + registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable; - getContributedNotebookKernels2(viewType: string, resource: URI, token: CancellationToken): Promise; + getContributedNotebookKernels(viewType: string, resource: URI, token: CancellationToken): Promise; getContributedNotebookOutputRenderers(id: string): NotebookOutputRendererInfo | undefined; getRendererInfo(id: string): INotebookRendererInfo | undefined; diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index b8d970e6e..1ec883fe1 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -156,7 +156,7 @@ suite('NotebookViewModel', () => { ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false, runnable: false }], ], (editor, viewModel) => { - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, cellHasExecutionOrder: true }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, cellHasExecutionOrder: true, trusted: true }; const defaults = { hasExecutionOrder: true }; @@ -190,7 +190,7 @@ suite('NotebookViewModel', () => { ...defaults }); - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: true, cellHasExecutionOrder: true }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: true, cellHasExecutionOrder: true, trusted: true }; assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), { editable: true, @@ -222,7 +222,7 @@ suite('NotebookViewModel', () => { ...defaults }); - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: false, cellHasExecutionOrder: true }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: false, cellHasExecutionOrder: true, trusted: true }; assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), { editable: false, diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 1679604a8..855ffe9fc 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -66,6 +66,9 @@ export class TestNotebookEditor implements INotebookEditor { constructor( ) { } + async beginComputeContributedKernels(): Promise { + return []; + } setEditorDecorations(key: string, range: ICellRange): void { // throw new Error('Method not implemented.'); } diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index 3dc1cf7fb..01b5f2a9e 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -11,13 +11,17 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { OutlineConfigKeys, OutlineViewId } from 'vs/editor/contrib/documentSymbols/outline'; import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export const PANEL_ID = 'panel.view.outline'; +const outlineViewIcon = registerIcon('outline-view-icon', Codicon.symbolClass, localize('outlineViewIcon', 'View icon of the outline view.')); + const _outlineDesc = { id: OutlineViewId, name: localize('name', "Outline"), - containerIcon: 'codicon-symbol-class', + containerIcon: outlineViewIcon, ctorDescriptor: new SyncDescriptor(OutlinePane), canToggleVisibility: true, canMoveView: true, diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 3f981c8e4..ab48b16b7 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -29,7 +29,7 @@ import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor' import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchDataTree } from 'vs/platform/list/browser/listService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -48,6 +48,7 @@ import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Codicon } from 'vs/base/common/codicons'; class RequestState { @@ -211,7 +212,7 @@ class OutlineViewState { followCursor: this.followCursor, sortBy: this.sortBy, filterOnType: this.filterOnType, - }), StorageScope.WORKSPACE); + }), StorageScope.WORKSPACE, StorageTarget.USER); } restore(storageService: IStorageService): void { @@ -399,7 +400,7 @@ export class OutlinePane extends ViewPane { getActions(): IAction[] { return [ - new CollapseAction(() => this._tree, true, 'explorer-action codicon-collapse-all') + new CollapseAction(() => this._tree, true, 'explorer-action ' + Codicon.collapseAll.classNames) ]; } @@ -425,7 +426,7 @@ export class OutlinePane extends ViewPane { private _onDidChangeUserState(e: { followCursor?: boolean, sortBy?: boolean, filterOnType?: boolean }) { this._outlineViewState.persist(this._storageService); if (e.followCursor) { - // todo@joh update immediately + // todo@jrieken update immediately } if (e.sortBy) { this._treeComparator.type = this._outlineViewState.sortBy; diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index 7b0283691..0b6157459 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -33,6 +33,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { ContextKeyEqualsExpr, ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions'; import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { CATEGORIES } from 'vs/workbench/common/actions'; // Register Service @@ -60,10 +61,13 @@ const toggleOutputActionKeybindings = { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_H) // On Ubuntu Ctrl+Shift+U is taken by some global OS command } }; + +const outputViewIcon = registerIcon('output-view-icon', Codicon.output, nls.localize('outputViewIcon', 'View icon of the output view.')); + const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: OUTPUT_VIEW_ID, name: nls.localize('output', "Output"), - icon: Codicon.output.classNames, + icon: outputViewIcon, order: 1, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [OUTPUT_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: OUTPUT_VIEW_ID, @@ -74,7 +78,7 @@ const VIEW_CONTAINER: ViewContainer = Registry.as(ViewC Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: OUTPUT_VIEW_ID, name: nls.localize('output', "Output"), - containerIcon: Codicon.output.classNames, + containerIcon: outputViewIcon, canMoveView: true, canToggleVisibility: false, ctorDescriptor: new SyncDescriptor(OutputViewPane), @@ -139,7 +143,7 @@ registerAction2(class extends Action2 { id: MenuId.EditorContext, when: CONTEXT_IN_OUTPUT }], - icon: { id: 'codicon/clear-all' } + icon: Codicon.clearAll }); } async run(accessor: ServicesAccessor): Promise { @@ -163,10 +167,10 @@ registerAction2(class extends Action2 { group: 'navigation', order: 3, }, - icon: { id: 'codicon/unlock' }, + icon: Codicon.unlock, toggled: { condition: CONTEXT_OUTPUT_SCROLL_LOCK, - icon: { id: 'codicon/lock' }, + icon: Codicon.lock, tooltip: { value: nls.localize('outputScrollOn', "Turn Auto Scrolling On"), original: 'Turn Auto Scrolling On' } } }); @@ -190,7 +194,7 @@ registerAction2(class extends Action2 { id: MenuId.CommandPalette, when: CONTEXT_ACTIVE_LOG_OUTPUT, }], - icon: { id: 'codicon/go-to-file' }, + icon: Codicon.goToFile, precondition: CONTEXT_ACTIVE_LOG_OUTPUT }); } @@ -200,7 +204,7 @@ registerAction2(class extends Action2 { const instantiationService = accessor.get(IInstantiationService); const logFileOutputChannelDescriptor = this.getLogFileOutputChannelDescriptor(outputService); if (logFileOutputChannelDescriptor) { - await editorService.openEditor(instantiationService.createInstance(LogViewerInput, logFileOutputChannelDescriptor)); + await editorService.openEditor(instantiationService.createInstance(LogViewerInput, logFileOutputChannelDescriptor), { pinned: true }); } } private getLogFileOutputChannelDescriptor(outputService: IOutputService): IFileOutputChannelDescriptor | null { @@ -298,7 +302,7 @@ registerAction2(class extends Action2 { const entry = await quickInputService.pick(entries, { placeHolder: nls.localize('selectlogFile', "Select Log file") }); if (entry) { assertIsDefined(entry.channel.file); - await editorService.openEditor(instantiationService.createInstance(LogViewerInput, (entry.channel as IFileOutputChannelDescriptor))); + await editorService.openEditor(instantiationService.createInstance(LogViewerInput, (entry.channel as IFileOutputChannelDescriptor)), { pinned: true }); } } }); diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index f1f111052..062b00c08 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -7,7 +7,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { IOutputChannel, IOutputService, OUTPUT_VIEW_ID, OUTPUT_SCHEME, LOG_SCHEME, LOG_MIME, OUTPUT_MIME } from 'vs/workbench/contrib/output/common/output'; import { IOutputChannelDescriptor, Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output'; @@ -16,7 +16,7 @@ import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/s import { ITextModel } from 'vs/editor/common/model'; import { ILogService } from 'vs/platform/log/common/log'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { IOutputChannelModel, IOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; +import { IOutputChannelModel, IOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModel'; import { IViewsService } from 'vs/workbench/common/views'; import { OutputViewPane } from 'vs/workbench/contrib/output/browser/outputView'; @@ -177,7 +177,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo this.activeChannel = channel; if (this.activeChannel) { - this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE); + this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE); } diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index 7e86eb96f..a01c54ae7 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -11,7 +11,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { AbstractTextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; @@ -164,11 +163,6 @@ export class OutputViewPane extends ViewPane { export class OutputEditor extends AbstractTextResourceEditor { - // Override the instantiation service to use to be the scoped one - private scopedInstantiationService: IInstantiationService; - protected get instantiationService(): IInstantiationService { return this.scopedInstantiationService; } - protected set instantiationService(instantiationService: IInstantiationService) { } - constructor( @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService instantiationService: IInstantiationService, @@ -177,15 +171,10 @@ export class OutputEditor extends AbstractTextResourceEditor { @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, @IOutputService private readonly outputService: IOutputService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @IEditorService editorService: IEditorService ) { super(OUTPUT_VIEW_ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService); - - // Initially, the scoped instantiation service is the global - // one until the editor is created later on - this.scopedInstantiationService = instantiationService; } getId(): string { @@ -258,13 +247,12 @@ export class OutputEditor extends AbstractTextResourceEditor { parent.setAttribute('role', 'document'); - // First create the scoped instantiation service and only then construct the editor using the scoped service - const scopedContextKeyService = this._register(this.contextKeyService.createScoped(parent)); - this.scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); - super.createEditor(parent); - CONTEXT_IN_OUTPUT.bindTo(scopedContextKeyService).set(true); + const scopedContextKeyService = this.scopedContextKeyService; + if (scopedContextKeyService) { + CONTEXT_IN_OUTPUT.bindTo(scopedContextKeyService).set(true); + } } } diff --git a/src/vs/workbench/services/output/common/outputChannelModel.ts b/src/vs/workbench/contrib/output/common/outputChannelModel.ts similarity index 99% rename from src/vs/workbench/services/output/common/outputChannelModel.ts rename to src/vs/workbench/contrib/output/common/outputChannelModel.ts index 3fa844819..364c4a8b1 100644 --- a/src/vs/workbench/services/output/common/outputChannelModel.ts +++ b/src/vs/workbench/contrib/output/common/outputChannelModel.ts @@ -37,7 +37,7 @@ export interface IOutputChannelModelService { } -export abstract class AsbtractOutputChannelModelService { +export abstract class AbstractOutputChannelModelService { constructor( @IInstantiationService protected readonly instantiationService: IInstantiationService diff --git a/src/vs/workbench/services/output/common/outputChannelModelService.ts b/src/vs/workbench/contrib/output/common/outputChannelModelService.ts similarity index 75% rename from src/vs/workbench/services/output/common/outputChannelModelService.ts rename to src/vs/workbench/contrib/output/common/outputChannelModelService.ts index 4d9c36ad0..794183199 100644 --- a/src/vs/workbench/services/output/common/outputChannelModelService.ts +++ b/src/vs/workbench/contrib/output/common/outputChannelModelService.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IOutputChannelModelService, AsbtractOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; +import { IOutputChannelModelService, AbstractOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModel'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -export class OutputChannelModelService extends AsbtractOutputChannelModelService implements IOutputChannelModelService { +export class OutputChannelModelService extends AbstractOutputChannelModelService implements IOutputChannelModelService { declare readonly _serviceBrand: undefined; } diff --git a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts index e532dbc50..d9297827b 100644 --- a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts +++ b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts @@ -56,7 +56,7 @@ export class OutputLinkComputer { } const links: ILink[] = []; - const lines = model.getValue().split(/\r\n|\r|\n/); + const lines = strings.splitLines(model.getValue()); // For each workspace root patterns for (const [folderUri, folderPatterns] of this.patterns) { diff --git a/src/vs/workbench/services/output/electron-browser/outputChannelModelService.ts b/src/vs/workbench/contrib/output/electron-browser/outputChannelModelService.ts similarity index 97% rename from src/vs/workbench/services/output/electron-browser/outputChannelModelService.ts rename to src/vs/workbench/contrib/output/electron-browser/outputChannelModelService.ts index fdc63aae9..6613887f7 100644 --- a/src/vs/workbench/services/output/electron-browser/outputChannelModelService.ts +++ b/src/vs/workbench/contrib/output/electron-browser/outputChannelModelService.ts @@ -14,7 +14,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import { IOutputChannelModel, AbstractFileOutputChannelModel, IOutputChannelModelService, AsbtractOutputChannelModelService, BufferredOutputChannel } from 'vs/workbench/services/output/common/outputChannelModel'; +import { IOutputChannelModel, AbstractFileOutputChannelModel, IOutputChannelModelService, AbstractOutputChannelModelService, BufferredOutputChannel } from 'vs/workbench/contrib/output/common/outputChannelModel'; import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { toLocalISOString } from 'vs/base/common/date'; @@ -197,7 +197,7 @@ class DelegatedOutputChannelModel extends Disposable implements IOutputChannelMo } -export class OutputChannelModelService extends AsbtractOutputChannelModelService implements IOutputChannelModelService { +export class OutputChannelModelService extends AbstractOutputChannelModelService implements IOutputChannelModelService { declare readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts index a1f68bc0f..2daa52259 100644 --- a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts +++ b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts @@ -51,6 +51,6 @@ registerAction2(class extends Action2 { run(accessor: ServicesAccessor) { const editorService = accessor.get(IEditorService); const instaService = accessor.get(IInstantiationService); - return editorService.openEditor(instaService.createInstance(PerfviewInput)); + return editorService.openEditor(instaService.createInstance(PerfviewInput), { pinned: true }); } }); diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 4d0b7970c..60f7caa47 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -18,14 +18,15 @@ import * as perf from 'vs/base/common/performance'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { writeTransientState } from 'vs/workbench/contrib/codeEditor/browser/toggleWordWrap'; -import { mergeSort } from 'vs/base/common/arrays'; +import { LoaderStats } from 'vs/base/common/amd'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IFileService } from 'vs/platform/files/common/files'; +import { ByteSize, IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { isWeb } from 'vs/base/common/platform'; export class PerfviewContrib { @@ -107,7 +108,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { this._modelDisposables.push(langId); this._modelDisposables.push(this._extensionService.onDidChangeExtensionsStatus(this._updateModel, this)); - writeTransientState(this._model, { forceWordWrap: 'off', forceWordWrapMinified: false }, this._editorService); + writeTransientState(this._model, { wordWrapOverride: 'off' }, this._editorService); } this._updateModel(); return Promise.resolve(this._model); @@ -150,10 +151,10 @@ class PerfModelContentProvider implements ITextModelContentProvider { md.li(`CPUs: ${metrics.cpus.model}(${metrics.cpus.count} x ${metrics.cpus.speed})`); } if (typeof metrics.totalmem === 'number' && typeof metrics.freemem === 'number') { - md.li(`Memory(System): ${(metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)} GB(${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free)`); + md.li(`Memory(System): ${(metrics.totalmem / (ByteSize.GB)).toFixed(2)} GB(${(metrics.freemem / (ByteSize.GB)).toFixed(2)}GB free)`); } if (metrics.meminfo) { - md.li(`Memory(Process): ${(metrics.meminfo.workingSetSize / 1024).toFixed(2)} MB working set(${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared)`); + md.li(`Memory(Process): ${(metrics.meminfo.workingSetSize / ByteSize.KB).toFixed(2)} MB working set(${(metrics.meminfo.privateBytes / ByteSize.KB).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / ByteSize.KB).toFixed(2)}MB shared)`); } md.li(`VM(likelyhood): ${metrics.isVMLikelyhood}%`); md.li(`Initial Startup: ${metrics.initialStartup}`); @@ -171,8 +172,13 @@ class PerfModelContentProvider implements ITextModelContentProvider { table.push(['app.isReady => window.loadUrl()', metrics.timers.ellapsedWindowLoad, '[main]', `initial startup: ${metrics.initialStartup}`]); table.push(['window.loadUrl() => begin to require(workbench.desktop.main.js)', metrics.timers.ellapsedWindowLoadToRequire, '[main->renderer]', StartupKindToString(metrics.windowKind)]); table.push(['require(workbench.desktop.main.js)', metrics.timers.ellapsedRequire, '[renderer]', `cached data: ${(metrics.didUseCachedData ? 'YES' : 'NO')}${stats ? `, node_modules took ${stats.nodeRequireTotal}ms` : ''}`]); + table.push(['wait for shell environment', metrics.timers.ellapsedWaitForShellEnv, '[renderer]', undefined]); table.push(['require & init workspace storage', metrics.timers.ellapsedWorkspaceStorageInit, '[renderer]', undefined]); table.push(['init workspace service', metrics.timers.ellapsedWorkspaceServiceInit, '[renderer]', undefined]); + if (isWeb) { + table.push(['init settings and global state from settings sync service', metrics.timers.ellapsedRequiredUserDataInit, '[renderer]', undefined]); + table.push(['init keybindings, snippets & extensions from settings sync service', metrics.timers.ellapsedOtherUserDataInit, '[renderer]', undefined]); + } table.push(['register extensions & spawn extension host', metrics.timers.ellapsedExtensions, '[renderer]', undefined]); table.push(['restore viewlet', metrics.timers.ellapsedViewletRestore, '[renderer]', metrics.viewletId]); table.push(['restore panel', metrics.timers.ellapsedPanelRestore, '[renderer]', metrics.panelId]); @@ -279,100 +285,6 @@ class PerfModelContentProvider implements ITextModelContentProvider { } } -abstract class LoaderStats { - abstract get amdLoad(): (string | number)[][]; - abstract get amdInvoke(): (string | number)[][]; - abstract get nodeRequire(): (string | number)[][]; - abstract get nodeEval(): (string | number)[][]; - abstract get nodeRequireTotal(): number; - - - static get(): LoaderStats { - - - const amdLoadScript = new Map(); - const amdInvokeFactory = new Map(); - const nodeRequire = new Map(); - const nodeEval = new Map(); - - function mark(map: Map, stat: LoaderEvent) { - if (map.has(stat.detail)) { - // console.warn('BAD events, DOUBLE start', stat); - // map.delete(stat.detail); - return; - } - map.set(stat.detail, -stat.timestamp); - } - - function diff(map: Map, stat: LoaderEvent) { - let duration = map.get(stat.detail); - if (!duration) { - // console.warn('BAD events, end WITHOUT start', stat); - // map.delete(stat.detail); - return; - } - if (duration >= 0) { - // console.warn('BAD events, DOUBLE end', stat); - // map.delete(stat.detail); - return; - } - map.set(stat.detail, duration + stat.timestamp); - } - - const stats = mergeSort(require.getStats().slice(0), (a, b) => a.timestamp - b.timestamp); - - for (const stat of stats) { - switch (stat.type) { - case LoaderEventType.BeginLoadingScript: - mark(amdLoadScript, stat); - break; - case LoaderEventType.EndLoadingScriptOK: - case LoaderEventType.EndLoadingScriptError: - diff(amdLoadScript, stat); - break; - - case LoaderEventType.BeginInvokeFactory: - mark(amdInvokeFactory, stat); - break; - case LoaderEventType.EndInvokeFactory: - diff(amdInvokeFactory, stat); - break; - - case LoaderEventType.NodeBeginNativeRequire: - mark(nodeRequire, stat); - break; - case LoaderEventType.NodeEndNativeRequire: - diff(nodeRequire, stat); - break; - - case LoaderEventType.NodeBeginEvaluatingScript: - mark(nodeEval, stat); - break; - case LoaderEventType.NodeEndEvaluatingScript: - diff(nodeEval, stat); - break; - } - } - - let nodeRequireTotal = 0; - nodeRequire.forEach(value => nodeRequireTotal += value); - - function to2dArray(map: Map): (string | number)[][] { - let res: (string | number)[][] = []; - map.forEach((value, index) => res.push([index, value])); - return res; - } - - return { - amdLoad: to2dArray(amdLoadScript), - amdInvoke: to2dArray(amdInvokeFactory), - nodeRequire: to2dArray(nodeRequire), - nodeEval: to2dArray(nodeEval), - nodeRequireTotal - }; - } -} - class MarkdownBuilder { value: string = ''; @@ -393,34 +305,6 @@ class MarkdownBuilder { } table(header: string[], rows: Array>) { - let lengths: number[] = []; - header.forEach((cell, ci) => { - lengths[ci] = cell.length; - }); - rows.forEach(row => { - row.forEach((cell, ci) => { - if (typeof cell === 'undefined') { - cell = row[ci] = '-'; - } - const len = cell.toString().length; - lengths[ci] = Math.max(len, lengths[ci]); - }); - }); - - // header - header.forEach((cell, ci) => { this.value += `| ${cell + ' '.repeat(lengths[ci] - cell.toString().length)} `; }); - this.value += '|\n'; - header.forEach((_cell, ci) => { this.value += `| ${'-'.repeat(lengths[ci])} `; }); - this.value += '|\n'; - - // cells - rows.forEach(row => { - row.forEach((cell, ci) => { - if (typeof cell !== 'undefined') { - this.value += `| ${cell + ' '.repeat(lengths[ci] - cell.toString().length)} `; - } - }); - this.value += '|\n'; - }); + this.value += LoaderStats.toMarkdownTable(header, rows); } } diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts b/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts index cfb822450..0e640c752 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts @@ -24,6 +24,8 @@ import { editorWidgetBackground, editorWidgetForeground, widgetShadow } from 'vs import { ScrollType } from 'vs/editor/common/editorCommon'; import { SearchWidget, SearchOptions } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { timeout } from 'vs/base/common/async'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; export interface KeybindingsSearchOptions extends SearchOptions { recordEnter?: boolean; @@ -50,13 +52,14 @@ export class KeybindingsSearchWidget extends SearchWidget { private _onBlur = this._register(new Emitter()); readonly onBlur: Event = this._onBlur.event; - constructor(parent: HTMLElement, options: SearchOptions, + constructor(parent: HTMLElement, options: KeybindingsSearchOptions, @IContextViewService contextViewService: IContextViewService, @IKeybindingService private readonly keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService + @IThemeService themeService: IThemeService, + @IContextKeyService contextKeyService: IContextKeyService ) { - super(parent, options, contextViewService, instantiationService, themeService); + super(parent, options, contextViewService, instantiationService, themeService, contextKeyService); this._register(attachInputBoxStyler(this.inputBox, themeService)); this._register(toDisposable(() => this.stopRecordingKeys())); this._firstPart = null; @@ -198,7 +201,7 @@ export class DefineKeybindingWidget extends Widget { } })); - this._keybindingInputWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, this._domNode.domNode, { ariaLabel: message })); + this._keybindingInputWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, this._domNode.domNode, { ariaLabel: message, history: [] })); this._keybindingInputWidget.startRecordingKeys(); this._register(this._keybindingInputWidget.onKeybinding(keybinding => this.onKeybinding(keybinding))); this._register(this._keybindingInputWidget.onEnter(() => this.hide())); @@ -219,7 +222,7 @@ export class DefineKeybindingWidget extends Widget { define(): Promise { this._keybindingInputWidget.clear(); - return new Promise((c) => { + return new Promise(async (c) => { if (!this._isVisible) { this._isVisible = true; this._domNode.setDisplay('block'); @@ -229,6 +232,11 @@ export class DefineKeybindingWidget extends Widget { this._keybindingInputWidget.setInputValue(''); dom.clearNode(this._outputNode); dom.clearNode(this._showExistingKeybindingsNode); + + // Input is not getting focus without timeout in safari + // https://github.com/microsoft/vscode/issues/108817 + await timeout(0); + this._keybindingInputWidget.focus(); } const disposable = this._onHide.event(() => { diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index 08273a277..7b7c3dd59 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -21,12 +21,12 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { KeybindingsEditorModel, IKeybindingItemEntry, IListEntry, KEYBINDING_ENTRY_TEMPLATE_ID } from 'vs/workbench/services/preferences/common/keybindingsEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService, IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; -import { DefineKeybindingWidget, KeybindingsSearchWidget, KeybindingsSearchOptions } from 'vs/workbench/contrib/preferences/browser/keybindingWidgets'; -import { IKeybindingsEditorPane, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR } from 'vs/workbench/contrib/preferences/common/preferences'; +import { DefineKeybindingWidget, KeybindingsSearchWidget } from 'vs/workbench/contrib/preferences/browser/keybindingWidgets'; +import { CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_ADD } from 'vs/workbench/contrib/preferences/common/preferences'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; -import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes'; @@ -37,16 +37,17 @@ import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { attachStylerCallback, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; -import { IStorageService } from 'vs/platform/storage/common/storage'; +import { attachStylerCallback, attachInputBoxStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { Emitter, Event } from 'vs/base/common/event'; import { MenuRegistry, MenuId, isIMenuItem } from 'vs/platform/actions/common/actions'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { Color, RGBA } from 'vs/base/common/color'; import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; -import { ThemableCheckboxActionViewItem } from 'vs/platform/theme/browser/checkbox'; +import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { IKeybindingsEditorPane } from 'vs/workbench/services/preferences/common/preferences'; +import { keybindingsRecordKeysIcon, keybindingsSortIcon, keybindingsAddIcon, preferencesClearInputIcon, keybindingsEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; const $ = DOM.$; @@ -58,6 +59,20 @@ interface ColumnItem { const oddRowBackgroundColor = new Color(new RGBA(130, 130, 130, 0.04)); +class ThemableCheckboxActionViewItem extends CheckboxActionViewItem { + + constructor(context: any, action: IAction, options: IBaseActionViewItemOptions | undefined, private readonly themeService: IThemeService) { + super(context, action, options); + } + + render(container: HTMLElement): void { + super.render(container); + if (this.checkbox) { + this.disposables.add(attachCheckboxStyler(this.checkbox, this.themeService)); + } + } +} + export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorPane { static readonly ID: string = 'workbench.editor.keybindings'; @@ -73,6 +88,7 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP private headerContainer!: HTMLElement; private actionsContainer!: HTMLElement; private searchWidget!: KeybindingsSearchWidget; + private searchHistoryDelayer: Delayer; private overlayContainer!: HTMLElement; private defineKeybindingWidget!: DefineKeybindingWidget; @@ -86,7 +102,6 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP private dimension: DOM.Dimension | null = null; private delayedFiltering: Delayer; private latestEmptyFilters: string[] = []; - private delayedFilterLogging: Delayer; private keybindingsEditorContextKey: IContextKey; private keybindingFocusContextKey: IContextKey; private searchFocusContextKey: IContextKey; @@ -116,16 +131,16 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService); this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService); this.keybindingFocusContextKey = CONTEXT_KEYBINDING_FOCUS.bindTo(this.contextKeyService); - this.delayedFilterLogging = new Delayer(1000); + this.searchHistoryDelayer = new Delayer(500); const recordKeysActionKeybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS); const recordKeysActionLabel = localize('recordKeysLabel', "Record Keys"); - this.recordKeysAction = new Action(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, recordKeysActionKeybinding ? localize('recordKeysLabelWithKeybinding', "{0} ({1})", recordKeysActionLabel, recordKeysActionKeybinding.getLabel()) : recordKeysActionLabel, 'codicon-record-keys'); + this.recordKeysAction = new Action(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, recordKeysActionKeybinding ? localize('recordKeysLabelWithKeybinding', "{0} ({1})", recordKeysActionLabel, recordKeysActionKeybinding.getLabel()) : recordKeysActionLabel, ThemeIcon.asClassName(keybindingsRecordKeysIcon)); this.recordKeysAction.checked = false; const sortByPrecedenceActionKeybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE); const sortByPrecedenceActionLabel = localize('sortByPrecedeneLabel', "Sort by Precedence"); - this.sortByPrecedenceAction = new Action('keybindings.editor.sortByPrecedence', sortByPrecedenceActionKeybinding ? localize('sortByPrecedeneLabelWithKeybinding', "{0} ({1})", sortByPrecedenceActionLabel, sortByPrecedenceActionKeybinding.getLabel()) : sortByPrecedenceActionLabel, 'codicon-sort-precedence'); + this.sortByPrecedenceAction = new Action('keybindings.editor.sortByPrecedence', sortByPrecedenceActionKeybinding ? localize('sortByPrecedeneLabelWithKeybinding', "{0} ({1})", sortByPrecedenceActionLabel, sortByPrecedenceActionKeybinding.getLabel()) : sortByPrecedenceActionLabel, ThemeIcon.asClassName(keybindingsSortIcon)); this.sortByPrecedenceAction.checked = false; } @@ -190,13 +205,13 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP return focusedElement && focusedElement.templateId === KEYBINDING_ENTRY_TEMPLATE_ID ? focusedElement : null; } - defineKeybinding(keybindingEntry: IKeybindingItemEntry): Promise { + defineKeybinding(keybindingEntry: IKeybindingItemEntry, add: boolean): Promise { this.selectEntry(keybindingEntry); this.showOverlayContainer(); return this.defineKeybindingWidget.define().then(key => { if (key) { this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_DEFINE, keybindingEntry.keybindingItem.command, key); - return this.updateKeybinding(keybindingEntry, key, keybindingEntry.keybindingItem.when); + return this.updateKeybinding(keybindingEntry, key, keybindingEntry.keybindingItem.when, add); } return null; }).then(() => { @@ -217,17 +232,18 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP } } - updateKeybinding(keybindingEntry: IKeybindingItemEntry, key: string, when: string | undefined): Promise { + async updateKeybinding(keybindingEntry: IKeybindingItemEntry, key: string, when: string | undefined, add?: boolean): Promise { const currentKey = keybindingEntry.keybindingItem.keybinding ? keybindingEntry.keybindingItem.keybinding.getUserSettingsLabel() : ''; if (currentKey !== key || keybindingEntry.keybindingItem.when !== when) { - return this.keybindingEditingService.editKeybinding(keybindingEntry.keybindingItem.keybindingItem, key, when || undefined) - .then(() => { - if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering - this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry; - } - }); + if (add) { + await this.keybindingEditingService.addKeybinding(keybindingEntry.keybindingItem.keybindingItem, key, when || undefined); + } else { + await this.keybindingEditingService.editKeybinding(keybindingEntry.keybindingItem.keybindingItem, key, when || undefined); + } + if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering + this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry; + } } - return Promise.resolve(); } removeKeybinding(keybindingEntry: IKeybindingItemEntry): Promise { @@ -286,6 +302,7 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP search(filter: string): void { this.focusSearch(); this.searchWidget.setValue(filter); + this.selectEntry(0); } clearSearchResults(): void { @@ -328,16 +345,17 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP const fullTextSearchPlaceholder = localize('SearchKeybindings.FullTextSearchPlaceholder', "Type to search in keybindings"); const keybindingsSearchPlaceholder = localize('SearchKeybindings.KeybindingsSearchPlaceholder', "Recording Keys. Press Escape to exit"); - const clearInputAction = new Action(KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Keybindings Search Input"), 'codicon-clear-all', false, () => { this.search(''); return Promise.resolve(null); }); + const clearInputAction = new Action(KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Keybindings Search Input"), ThemeIcon.asClassName(preferencesClearInputIcon), false, () => { this.clearSearchResults(); return Promise.resolve(null); }); const searchContainer = DOM.append(this.headerContainer, $('.search-container')); - this.searchWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, searchContainer, { + this.searchWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, searchContainer, { ariaLabel: fullTextSearchPlaceholder, placeholder: fullTextSearchPlaceholder, focusKey: this.searchFocusContextKey, ariaLabelledBy: 'keybindings-editor-aria-label-element', recordEnter: true, - quoteRecordedKeys: true + quoteRecordedKeys: true, + history: this.getMemento(StorageScope.GLOBAL, StorageTarget.USER)['searchHistory'] || [], })); this._register(this.searchWidget.onDidChange(searchValue => { clearInputAction.enabled = !!searchValue; @@ -483,7 +501,7 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP this._register(this.keybindingsList.onMouseDblClick(() => { const activeKeybindingEntry = this.activeKeybindingEntry; if (activeKeybindingEntry) { - this.defineKeybinding(activeKeybindingEntry); + this.defineKeybinding(activeKeybindingEntry, false); } })); this._register(this.keybindingsList.onKeyDown(e => { @@ -491,7 +509,7 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP if (event.keyCode === KeyCode.Enter) { const keybindingEntry = this.activeKeybindingEntry; if (keybindingEntry) { - this.defineKeybinding(keybindingEntry); + this.defineKeybinding(keybindingEntry, false); } e.stopPropagation(); } @@ -529,7 +547,12 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP private filterKeybindings(): void { this.renderKeybindingsEntries(this.searchWidget.hasFocus()); - this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(this.searchWidget.getValue())); + this.searchHistoryDelayer.trigger(() => { + this.searchWidget.inputBox.addToHistory(); + this.getMemento(StorageScope.GLOBAL, StorageTarget.USER)['searchHistory'] = this.searchWidget.inputBox.getHistory(); + this.saveState(); + this.reportFilteringUsed(this.searchWidget.getValue()); + }); } private renderKeybindingsEntries(reset: boolean, preserveFocus?: boolean): void { @@ -657,19 +680,24 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP } if (e.element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) { - this.selectEntry(e.element); + const keybindingItemEntry = e.element; + this.selectEntry(keybindingItemEntry); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => [ - this.createCopyAction(e.element), - this.createCopyCommandAction(e.element), + this.createCopyAction(keybindingItemEntry), + this.createCopyCommandAction(keybindingItemEntry), new Separator(), - this.createDefineAction(e.element), - this.createRemoveAction(e.element), - this.createResetAction(e.element), - this.createDefineWhenExpressionAction(e.element), + ...(keybindingItemEntry.keybindingItem.keybinding + ? [this.createDefineKeybindingAction(keybindingItemEntry), this.createAddKeybindingAction(keybindingItemEntry)] + : [this.createDefineKeybindingAction(keybindingItemEntry)]), new Separator(), - this.createShowConflictsAction(e.element)] + this.createRemoveAction(keybindingItemEntry), + this.createResetAction(keybindingItemEntry), + new Separator(), + this.createDefineWhenExpressionAction(keybindingItemEntry), + new Separator(), + this.createShowConflictsAction(keybindingItemEntry)] }); } } @@ -685,12 +713,21 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP } } - private createDefineAction(keybindingItemEntry: IKeybindingItemEntry): IAction { + private createDefineKeybindingAction(keybindingItemEntry: IKeybindingItemEntry): IAction { return { - label: keybindingItemEntry.keybindingItem.keybinding ? localize('changeLabel', "Change Keybinding") : localize('addLabel', "Add Keybinding"), + label: keybindingItemEntry.keybindingItem.keybinding ? localize('changeLabel', "Change Keybinding...") : localize('addLabel', "Add Keybinding..."), enabled: true, id: KEYBINDINGS_EDITOR_COMMAND_DEFINE, - run: () => this.defineKeybinding(keybindingItemEntry) + run: () => this.defineKeybinding(keybindingItemEntry, false) + }; + } + + private createAddKeybindingAction(keybindingItemEntry: IKeybindingItemEntry): IAction { + return { + label: localize('addLabel', "Add Keybinding..."), + enabled: true, + id: KEYBINDINGS_EDITOR_COMMAND_ADD, + run: () => this.defineKeybinding(keybindingItemEntry, true) }; } @@ -897,22 +934,22 @@ class ActionsColumn extends Column { private createEditAction(keybindingItemEntry: IKeybindingItemEntry): IAction { const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE); return { - class: preferencesEditIcon.classNames, + class: ThemeIcon.asClassName(keybindingsEditIcon), enabled: true, id: 'editKeybinding', tooltip: keybinding ? localize('editKeybindingLabelWithKey', "Change Keybinding {0}", `(${keybinding.getLabel()})`) : localize('editKeybindingLabel', "Change Keybinding"), - run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry) + run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry, false) }; } private createAddAction(keybindingItemEntry: IKeybindingItemEntry): IAction { const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE); return { - class: 'codicon-add', + class: ThemeIcon.asClassName(keybindingsAddIcon), enabled: true, id: 'addKeybinding', tooltip: keybinding ? localize('addKeybindingLabelWithKey', "Add Keybinding {0}", `(${keybinding.getLabel()})`) : localize('addKeybindingLabel', "Add Keybinding"), - run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry) + run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry, false) }; } diff --git a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts index cdf16c245..a7e4bad5e 100644 --- a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts +++ b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor } from 'vs/workbench/services/statusbar/common/statusbar'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; -import { IKeymapService, areKeyboardLayoutsEqual, parseKeyboardLayoutDescription, getKeyboardLayoutId, IKeyboardLayoutInfo } from 'vs/workbench/services/keybinding/common/keymapInfo'; +import { parseKeyboardLayoutDescription, areKeyboardLayoutsEqual, getKeyboardLayoutId, IKeyboardLayoutService, IKeyboardLayoutInfo } from 'vs/platform/keyboardLayout/common/keyboardLayout'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; @@ -27,12 +27,12 @@ export class KeyboardLayoutPickerContribution extends Disposable implements IWor private readonly pickerElement = this._register(new MutableDisposable()); constructor( - @IKeymapService private readonly keymapService: IKeymapService, + @IKeyboardLayoutService private readonly keyboardLayoutService: IKeyboardLayoutService, @IStatusbarService private readonly statusbarService: IStatusbarService, ) { super(); - let layout = this.keymapService.getCurrentKeyboardLayout(); + let layout = this.keyboardLayoutService.getCurrentKeyboardLayout(); if (layout) { let layoutInfo = parseKeyboardLayoutDescription(layout); const text = nls.localize('keyboardLayout', "Layout: {0}", layoutInfo.label); @@ -49,8 +49,8 @@ export class KeyboardLayoutPickerContribution extends Disposable implements IWor ); } - this._register(keymapService.onDidChangeKeyboardMapper(() => { - let layout = this.keymapService.getCurrentKeyboardLayout(); + this._register(keyboardLayoutService.onDidChangeKeyboardLayout(() => { + let layout = this.keyboardLayoutService.getCurrentKeyboardLayout(); let layoutInfo = parseKeyboardLayoutDescription(layout); if (this.pickerElement.value) { @@ -101,7 +101,7 @@ export class KeyboardLayoutPickerAction extends Action { actionLabel: string, @IFileService private readonly fileService: IFileService, @IQuickInputService private readonly quickInputService: IQuickInputService, - @IKeymapService private readonly keymapService: IKeymapService, + @IKeyboardLayoutService private readonly keyboardLayoutService: IKeyboardLayoutService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IEditorService private readonly editorService: IEditorService @@ -110,8 +110,8 @@ export class KeyboardLayoutPickerAction extends Action { } async run(): Promise { - let layouts = this.keymapService.getAllKeyboardLayouts(); - let currentLayout = this.keymapService.getCurrentKeyboardLayout(); + let layouts = this.keyboardLayoutService.getAllKeyboardLayouts(); + let currentLayout = this.keyboardLayoutService.getCurrentKeyboardLayout(); let layoutConfig = this.configurationService.getValue('keyboard.layout'); let isAutoDetect = layoutConfig === 'autodetect'; @@ -169,7 +169,8 @@ export class KeyboardLayoutPickerAction extends Action { } return this.editorService.openEditor({ resource: stat.resource, - mode: 'jsonc' + mode: 'jsonc', + options: { pinned: true } }); }, (error) => { throw new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", file.toString(), error)); diff --git a/src/vs/workbench/contrib/preferences/browser/media/preferences.css b/src/vs/workbench/contrib/preferences/browser/media/preferences.css index 2476aeea7..89e322320 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/preferences.css +++ b/src/vs/workbench/contrib/preferences/browser/media/preferences.css @@ -194,28 +194,6 @@ justify-content: center; } -.monaco-editor .settings-group-title-widget .title-container.collapsed .codicon::before { - transform: rotate(-90deg); -} - -.monaco-editor .codicon-edit { - transform: rotate(-90deg); - width:16px; - height: 16px; - cursor: pointer; -} - -.monaco-editor .codicon-edit.hidden { - display: none; - visibility: hidden; -} - .monaco-editor .dim-configuration { color: #b1b1b1; } - -.monaco-editor .floating-click-widget { - padding: 10px; - border-radius: 5px; - cursor: pointer; -} diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 58ce2825e..f9f59178f 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -124,6 +124,7 @@ .settings-editor > .settings-body { position: relative; + margin-top: 14px; } .settings-editor > .settings-body > .no-results-message { @@ -247,7 +248,6 @@ } .settings-editor > .settings-body .settings-tree-container { - margin-top: 14px; border-spacing: 0; border-collapse: separate; position: relative; @@ -344,13 +344,11 @@ } .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides { - opacity: 0.9; font-style: italic; margin-right: 4px; } .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title .setting-item-ignored { - opacity: 0.9; font-style: italic; } diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 8c59d7911..511b2c236 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -33,13 +33,14 @@ import { KeybindingsEditor } from 'vs/workbench/contrib/preferences/browser/keyb import { ConfigureLanguageBasedSettingsAction } from 'vs/workbench/contrib/preferences/browser/preferencesActions'; import { PreferencesEditor } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; import { SettingsEditor2, SettingsFocusContext } from 'vs/workbench/contrib/preferences/browser/settingsEditor2'; -import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, KEYBINDINGS_EDITOR_COMMAND_ADD, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { PreferencesContribution } from 'vs/workbench/contrib/preferences/common/preferencesContribution'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; +import { preferencesOpenSettingsIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; @@ -95,7 +96,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( // Register Preferences Editor Input Factory class PreferencesEditorInputFactory extends AbstractSideBySideEditorInputFactory { - protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { return new PreferencesEditorInput(name, description, secondaryInput, primaryInput); } } @@ -277,7 +278,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon super({ id: '_workbench.openUserSettingsEditor', title: OPEN_SETTINGS2_ACTION_TITLE, - icon: { id: 'codicon/go-to-file' }, + icon: preferencesOpenSettingsIcon, menu: [{ id: MenuId.EditorTitle, when: ResourceContextKey.Resource.isEqualTo(that.environmentService.settingsResource.toString()), @@ -295,7 +296,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon super({ id: SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, title: { value: nls.localize('openSettingsJson', "Open Settings (JSON)"), original: 'Open Settings (JSON)' }, - icon: { id: 'codicon/go-to-file' }, + icon: preferencesOpenSettingsIcon, menu: [{ id: MenuId.EditorTitle, when: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR.toNegated()), @@ -815,7 +816,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: 'workbench.action.openGlobalKeybindings', title: { value: nls.localize('openGlobalKeybindings', "Open Keyboard Shortcuts"), original: 'Open Keyboard Shortcuts' }, category, - icon: { id: 'codicon/go-to-file' }, + icon: preferencesOpenSettingsIcon, keybinding: { when: null, weight: KeybindingWeight.WorkbenchContrib, @@ -832,8 +833,9 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon ] }); } - run(accessor: ServicesAccessor) { - return accessor.get(IPreferencesService).openGlobalKeybindingSettings(false); + run(accessor: ServicesAccessor, args: string | undefined) { + const query = typeof args === 'string' ? args : undefined; + return accessor.get(IPreferencesService).openGlobalKeybindingSettings(false, { query }); } }); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { @@ -871,7 +873,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: 'workbench.action.openGlobalKeybindingsFile', title: { value: nls.localize('openGlobalKeybindingsFile', "Open Keyboard Shortcuts (JSON)"), original: 'Open Keyboard Shortcuts (JSON)' }, category, - icon: { id: 'codicon/go-to-file' }, + icon: preferencesOpenSettingsIcon, menu: [ { id: MenuId.CommandPalette }, { @@ -960,7 +962,20 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon handler: (accessor, args: any) => { const editorPane = accessor.get(IEditorService).activeEditorPane; if (editorPane instanceof KeybindingsEditor) { - editorPane.defineKeybinding(editorPane.activeKeybindingEntry!); + editorPane.defineKeybinding(editorPane.activeKeybindingEntry!, false); + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_ADD, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_A), + handler: (accessor, args: any) => { + const editorPane = accessor.get(IEditorService).activeEditorPane; + if (editorPane instanceof KeybindingsEditor) { + editorPane.defineKeybinding(editorPane.activeKeybindingEntry!, true); } } }); @@ -1091,7 +1106,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS), - primary: KeyCode.DownArrow, + primary: KeyMod.CtrlCmd | KeyCode.DownArrow, handler: (accessor, args: any) => { const editorPane = accessor.get(IEditorService).activeEditorPane; if (editorPane instanceof KeybindingsEditor) { @@ -1109,7 +1124,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon command: { id: commandId, title: OPEN_SETTINGS2_ACTION_TITLE, - icon: { id: 'codicon/go-to-file' } + icon: preferencesOpenSettingsIcon }, when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource!.toString()), WorkbenchStateContext.isEqualTo('workspace')), group: 'navigation', @@ -1134,7 +1149,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon command: { id: commandId, title: OPEN_SETTINGS2_ACTION_TITLE, - icon: { id: 'codicon/go-to-file' } + icon: preferencesOpenSettingsIcon }, when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.getFolderSettingsResource(folder.uri)!.toString())), group: 'navigation', diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts index 8b095ecb5..e6955647c 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts @@ -55,7 +55,7 @@ export class ConfigureLanguageBasedSettingsAction extends Action { if (pick) { const modeId = this.modeService.getModeIdForLanguageName(pick.label.toLowerCase()); if (typeof modeId === 'string') { - return this.preferencesService.openGlobalSettings(true, { editSetting: `[${modeId}]` }); + return this.preferencesService.openGlobalSettings(true, { revealSetting: { key: `[${modeId}]`, edit: true } }); } } return undefined; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index 8c39361fd..8f4719506 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -111,7 +111,8 @@ export class PreferencesEditor extends EditorPane { placeholder: nls.localize('SearchSettingsWidget.Placeholder', "Search Settings"), focusKey: this.searchFocusContextKey, showResultCount: true, - ariaLive: 'assertive' + ariaLive: 'assertive', + history: [], })); this._register(this.searchWidget.onDidChange(value => this.onInputChanged())); this._register(this.searchWidget.onFocus(() => this.lastFocusedWidget = this.searchWidget)); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts b/src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts new file mode 100644 index 000000000..ec9d0a958 --- /dev/null +++ b/src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const settingsGroupExpandedIcon = registerIcon('settings-group-expanded', Codicon.chevronDown, localize('settingsGroupExpandedIcon', 'Icon for an expanded section in the split JSON settings editor.')); +export const settingsGroupCollapsedIcon = registerIcon('settings-group-collapsed', Codicon.chevronRight, localize('settingsGroupCollapsedIcon', 'Icon for an collapsed section in the split JSON settings editor.')); +export const settingsScopeDropDownIcon = registerIcon('settings-folder-dropdown', Codicon.triangleDown, localize('settingsScopeDropDownIcon', 'Icon for the folder dropdown button in the split JSON settings editor.')); +export const settingsMoreActionIcon = registerIcon('settings-more-action', Codicon.gear, localize('settingsMoreActionIcon', 'Icon for the \'more actions\' action in the settings UI.')); + +export const keybindingsRecordKeysIcon = registerIcon('keybindings-record-keys', Codicon.recordKeys, localize('keybindingsRecordKeysIcon', 'Icon for the \'record keys\' action in the keybinding UI.')); +export const keybindingsSortIcon = registerIcon('keybindings-sort', Codicon.sortPrecedence, localize('keybindingsSortIcon', 'Icon for the \'sort by precedence\' toggle in the keybinding UI.')); + +export const keybindingsEditIcon = registerIcon('keybindings-edit', Codicon.edit, localize('keybindingsEditIcon', 'Icon for the edit action in the keybinding UI.')); +export const keybindingsAddIcon = registerIcon('keybindings-add', Codicon.add, localize('keybindingsAddIcon', 'Icon for the add action in the keybinding UI.')); + +export const settingsEditIcon = registerIcon('settings-edit', Codicon.edit, localize('settingsEditIcon', 'Icon for the edit action in the settings UI.')); +export const settingsAddIcon = registerIcon('settings-add', Codicon.add, localize('settingsAddIcon', 'Icon for the add action in the settings UI.')); + +export const settingsRemoveIcon = registerIcon('settings-remove', Codicon.close, localize('settingsRemoveIcon', 'Icon for the remove action in the settings UI.')); +export const settingsDiscardIcon = registerIcon('settings-discard', Codicon.discard, localize('preferencesDiscardIcon', 'Icon for the discard action in the settings UI.')); + +export const preferencesClearInputIcon = registerIcon('preferences-clear-input', Codicon.clearAll, localize('preferencesClearInput', 'Icon for clear input in the settings and keybinding UI.')); +export const preferencesOpenSettingsIcon = registerIcon('preferences-open-settings', Codicon.goToFile, localize('preferencesOpenSettings', 'Icon for open settings commands.')); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index b13338f99..49511397c 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -25,12 +25,15 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations'; -import { DefaultSettingsHeaderWidget, EditPreferenceWidget, SettingsGroupTitleWidget, SettingsHeaderWidget, preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; +import { DefaultSettingsHeaderWidget, EditPreferenceWidget, SettingsGroupTitleWidget, SettingsHeaderWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { IFilterResult, IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditorModel, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultSettingsEditorModel, SettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IMarkerService, IMarkerData, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { FindDecorations } from 'vs/editor/contrib/find/findDecorations'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { settingsEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; export interface IPreferencesRenderer extends IDisposable { readonly preferencesModel: IPreferencesEditorModel; @@ -569,15 +572,10 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr private createDecoration(range: IRange): IModelDeltaDecoration { return { range, - options: FilteredMatchesRenderer._FIND_MATCH + options: FindDecorations._FIND_MATCH_DECORATION }; } - private static readonly _FIND_MATCH = ModelDecorationOptions.register({ - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - className: 'findMatch' - }); - private computeHiddenRanges(filteredGroups: ISettingsGroup[] | undefined, allSettingsGroups: ISettingsGroup[]): IRange[] { // Hide the contents of hidden groups const notMatchesRanges: IRange[] = []; @@ -614,15 +612,10 @@ export class HighlightMatchesRenderer extends Disposable { this.decorationIds = this.editor.deltaDecorations(this.decorationIds, matches.map(match => this.createDecoration(match))); } - private static readonly _FIND_MATCH = ModelDecorationOptions.register({ - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - className: 'findMatch' - }); - private createDecoration(range: IRange): IModelDeltaDecoration { return { range, - options: HighlightMatchesRenderer._FIND_MATCH + options: FindDecorations._FIND_MATCH_DECORATION }; } @@ -746,7 +739,7 @@ class EditSettingRenderer extends Disposable { const decorations = this.editor.getLineDecorations(line); if (decorations) { for (const { options } of decorations) { - if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf(preferencesEditIcon.classNames) === -1) { + if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf(ThemeIcon.asClassName(settingsEditIcon)) === -1) { return false; } } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index a736bea30..67b174bdd 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IInputOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { HistoryInputBox, IHistoryInputOptions } from 'vs/base/browser/ui/inputbox/inputBox'; import { Widget } from 'vs/base/browser/ui/widget'; import { Action, IAction } from 'vs/base/common/actions'; import { Emitter, Event } from 'vs/base/common/event'; @@ -21,22 +21,23 @@ import { Position } from 'vs/editor/common/core/position'; import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { Schemas } from 'vs/base/common/network'; import { activeContrastBorder, badgeBackground, badgeForeground, contrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; -import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND } from 'vs/workbench/common/theme'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ISettingsGroup, IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { isEqual } from 'vs/base/common/resources'; -import { registerIcon, Codicon } from 'vs/base/common/codicons'; import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { settingsEditIcon, settingsGroupCollapsedIcon, settingsGroupExpandedIcon, settingsScopeDropDownIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; +import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget'; export class SettingsHeaderWidget extends Widget implements IViewZone { @@ -171,13 +172,23 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { this._register(focusTracker.onDidFocus(() => this.toggleFocus(true))); this._register(focusTracker.onDidBlur(() => this.toggleFocus(false))); - this.icon = DOM.append(this.titleContainer, DOM.$('.codicon.codicon-chevron-down')); + this.icon = DOM.append(this.titleContainer, DOM.$('')); this.title = DOM.append(this.titleContainer, DOM.$('.title')); this.title.textContent = this.settingsGroup.title + ` (${this.settingsGroup.sections.reduce((count, section) => count + section.settings.length, 0)})`; + this.updateTwisty(false); this.layout(); } + private getTwistyIcon(isCollapsed: boolean): ThemeIcon { + return isCollapsed ? settingsGroupCollapsedIcon : settingsGroupExpandedIcon; + } + + private updateTwisty(collapse: boolean) { + this.icon.classList.remove(...ThemeIcon.asClassNameArray(this.getTwistyIcon(!collapse))); + this.icon.classList.add(...ThemeIcon.asClassNameArray(this.getTwistyIcon(collapse))); + } + render() { if (!this.settingsGroup.range) { // #61352 @@ -193,6 +204,7 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { toggleCollapse(collapse: boolean) { this.titleContainer.classList.toggle('collapsed', collapse); + this.updateTwisty(collapse); } toggleFocus(focus: boolean): void { @@ -257,6 +269,7 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { private collapse(collapse: boolean) { if (collapse !== this.isCollapsed()) { this.titleContainer.classList.toggle('collapsed', collapse); + this.updateTwisty(collapse); this._onToggled.fire(collapse); } } @@ -345,7 +358,7 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem { this.container = container; this.labelElement = DOM.$('.action-title'); this.detailsElement = DOM.$('.action-details'); - this.dropDownElement = DOM.$('.dropdown-icon.codicon.codicon-triangle-down.hide'); + this.dropDownElement = DOM.$('.dropdown-icon.hide' + ThemeIcon.asCSSSelector(settingsScopeDropDownIcon)); this.anchorElement = DOM.$('a.action-label.folder-settings', { role: 'button', 'aria-haspopup': 'true', @@ -604,7 +617,7 @@ export class SettingsTargetsWidget extends Widget { } } -export interface SearchOptions extends IInputOptions { +export interface SearchOptions extends IHistoryInputOptions { focusKey?: IContextKey; showResultCount?: boolean; ariaLive?: string; @@ -617,7 +630,7 @@ export class SearchWidget extends Widget { private countElement!: HTMLElement; private searchContainer!: HTMLElement; - inputBox!: InputBox; + inputBox!: HistoryInputBox; private controlsDiv!: HTMLElement; private readonly _onDidChange: Emitter = this._register(new Emitter()); @@ -629,7 +642,8 @@ export class SearchWidget extends Widget { constructor(parent: HTMLElement, protected options: SearchOptions, @IContextViewService private readonly contextViewService: IContextViewService, @IInstantiationService protected instantiationService: IInstantiationService, - @IThemeService private readonly themeService: IThemeService + @IThemeService private readonly themeService: IThemeService, + @IContextKeyService private readonly contextKeyService: IContextKeyService ) { super(); this.create(parent); @@ -678,8 +692,8 @@ export class SearchWidget extends Widget { this._register(this.inputBox.onDidChange(value => this._onDidChange.fire(value))); } - protected createInputBox(parent: HTMLElement): InputBox { - const box = this._register(new InputBox(parent, this.contextViewService, this.options)); + protected createInputBox(parent: HTMLElement): HistoryInputBox { + const box = this._register(new ContextScopedHistoryInputBox(parent, this.contextViewService, this.options, this.contextKeyService)); this._register(attachInputBoxStyler(box, this.themeService)); return box; @@ -746,8 +760,6 @@ export class SearchWidget extends Widget { } } -export const preferencesEditIcon = registerIcon('preferences-edit', Codicon.edit, localize('preferencesEditIcon', 'Icon for the edit action in preferences.')); - export class EditPreferenceWidget extends Disposable { private _line: number = -1; @@ -785,7 +797,7 @@ export class EditPreferenceWidget extends Disposable { this._line = line; newDecoration.push({ options: { - glyphMarginClassName: preferencesEditIcon.classNames, + glyphMarginClassName: ThemeIcon.asClassName(settingsEditIcon), glyphMarginHoverMessage: new MarkdownString().appendText(hoverMessage), stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, }, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 941cd7b5b..7abb3fca7 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -31,12 +31,11 @@ import { IEditorModel } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { badgeBackground, badgeForeground, contrastBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IUserDataAutoSyncEnablementService, IUserDataSyncService, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorMemento, IEditorOpenContext, IEditorPane } from 'vs/workbench/common/editor'; @@ -47,12 +46,13 @@ import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickE import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, ID_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS } from 'vs/workbench/contrib/preferences/common/preferences'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingsEditorOptions, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IUserDataSyncWorkbenchService } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { preferencesClearInputIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; export const enum SettingsFocusContext { Search, @@ -91,7 +91,7 @@ export class SettingsEditor2 extends EditorPane { private static CONFIG_SCHEMA_UPDATE_DELAYER = 500; private static readonly SUGGESTIONS: string[] = [ - `@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices', '@tag:sync', `@${EXTENSION_SETTING_TAG}` + `@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices', '@tag:sync', `@${ID_SETTING_TAG}`, `@${EXTENSION_SETTING_TAG}`, `@${FEATURE_SETTING_TAG}scm`, `@${FEATURE_SETTING_TAG}explorer`, `@${FEATURE_SETTING_TAG}search`, `@${FEATURE_SETTING_TAG}debug`, `@${FEATURE_SETTING_TAG}extensions`, `@${FEATURE_SETTING_TAG}terminal`, `@${FEATURE_SETTING_TAG}task`, `@${FEATURE_SETTING_TAG}problems`, `@${FEATURE_SETTING_TAG}output`, `@${FEATURE_SETTING_TAG}comments`, `@${FEATURE_SETTING_TAG}remote`, `@${FEATURE_SETTING_TAG}timeline` ]; private static shouldSettingUpdateFast(type: SettingValueType | SettingValueType[]): boolean { @@ -173,7 +173,6 @@ export class SettingsEditor2 extends EditorPane { @IStorageService private readonly storageService: IStorageService, @INotificationService private readonly notificationService: INotificationService, @IEditorGroupsService protected editorGroupService: IEditorGroupsService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService ) { @@ -202,8 +201,6 @@ export class SettingsEditor2 extends EditorPane { this.onConfigUpdate(e.affectedKeys); } })); - - storageKeysSyncRegistryService.registerStorageKey({ key: SETTINGS_AUTOSAVE_NOTIFIED_KEY, version: 1 }); } get minimumWidth(): number { return 375; } @@ -439,7 +436,7 @@ export class SettingsEditor2 extends EditorPane { const searchContainer = DOM.append(this.headerContainer, $('.search-container')); - const clearInputAction = new Action(SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Settings Search Input"), 'codicon-clear-all', false, () => { this.clearSearchResults(); return Promise.resolve(null); }); + const clearInputAction = new Action(SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Settings Search Input"), ThemeIcon.asClassName(preferencesClearInputIcon), false, () => { this.clearSearchResults(); return Promise.resolve(null); }); this.searchWidget = this._register(this.instantiationService.createInstance(SuggestEnabledInput, `${SettingsEditor2.ID}.searchbox`, searchContainer, { triggerCharacters: ['@'], @@ -664,7 +661,7 @@ export class SettingsEditor2 extends EditorPane { this.settingRenderers = this.instantiationService.createInstance(SettingTreeRenderers); this._register(this.settingRenderers.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value, e.type))); this._register(this.settingRenderers.onDidOpenSettings(settingKey => { - this.openSettingsFile({ editSetting: settingKey }); + this.openSettingsFile({ revealSetting: { key: settingKey, edit: true } }); })); this._register(this.settingRenderers.onDidClickSettingLink(settingName => this.onDidClickSetting(settingName))); this._register(this.settingRenderers.onDidFocusSetting(element => { @@ -737,8 +734,8 @@ export class SettingsEditor2 extends EditorPane { private notifyNoSaveNeeded() { if (!this.storageService.getBoolean(SETTINGS_AUTOSAVE_NOTIFIED_KEY, StorageScope.GLOBAL, false)) { - this.storageService.store(SETTINGS_AUTOSAVE_NOTIFIED_KEY, true, StorageScope.GLOBAL); - this.notificationService.info(localize('settingsNoSaveNeeded', "Your changes are automatically saved as you edit.")); + this.storageService.store(SETTINGS_AUTOSAVE_NOTIFIED_KEY, true, StorageScope.GLOBAL, StorageTarget.USER); + this.notificationService.info(localize('settingsNoSaveNeeded', "Changes to settings are saved automatically.")); } } @@ -968,7 +965,7 @@ export class SettingsEditor2 extends EditorPane { const groups = this.defaultSettingsEditorModel.settingsGroups.slice(1); // Without commonlyUsed const dividedGroups = collections.groupBy(groups, g => g.extensionInfo ? 'extension' : 'core'); - const settingsResult = resolveSettingsTree(tocData, dividedGroups.core); + const settingsResult = resolveSettingsTree(tocData, dividedGroups.core, this.logService); const resolvedSettingsRoot = settingsResult.tree; // Warn for settings not included in layout @@ -982,7 +979,7 @@ export class SettingsEditor2 extends EditorPane { this.hasWarnedMissingSettings = true; } - const commonlyUsed = resolveSettingsTree(commonlyUsedData, dividedGroups.core); + const commonlyUsed = resolveSettingsTree(commonlyUsedData, dividedGroups.core, this.logService); resolvedSettingsRoot.children!.unshift(commonlyUsed.tree); resolvedSettingsRoot.children!.push(resolveExtensionsSettings(dividedGroups.extension || [])); @@ -1138,18 +1135,22 @@ export class SettingsEditor2 extends EditorPane { private triggerSearch(query: string): Promise { this.viewState.tagFilters = new Set(); this.viewState.extensionFilters = new Set(); + this.viewState.featureFilters = new Set(); + this.viewState.idFilters = new Set(); if (query) { const parsedQuery = parseQuery(query); query = parsedQuery.query; parsedQuery.tags.forEach(tag => this.viewState.tagFilters!.add(tag)); parsedQuery.extensionFilters.forEach(extensionId => this.viewState.extensionFilters!.add(extensionId)); + parsedQuery.featureFilters!.forEach(feature => this.viewState.featureFilters!.add(feature)); + parsedQuery.idFilters!.forEach(id => this.viewState.idFilters!.add(id)); } if (query && query !== '@') { query = this.parseSettingFromJSON(query) || query; return this.triggerFilterPreferences(query); } else { - if ((this.viewState.tagFilters && this.viewState.tagFilters.size) || (this.viewState.extensionFilters && this.viewState.extensionFilters.size)) { + if (this.viewState.tagFilters.size || this.viewState.extensionFilters.size || this.viewState.featureFilters.size || this.viewState.idFilters.size) { this.searchResultModel = this.createFilterModel(); } else { this.searchResultModel = null; @@ -1306,9 +1307,11 @@ export class SettingsEditor2 extends EditorPane { this.tocTreeModel.update(); } - this.tocTree.setFocus([]); - this.viewState.filterToCategory = undefined; - this.tocTree.expandAll(); + if (type === SearchResultIdx.Local) { + this.tocTree.setFocus([]); + this.viewState.filterToCategory = undefined; + this.tocTree.expandAll(); + } this.refreshTOCTree(); this.renderTree(undefined, true); @@ -1382,12 +1385,12 @@ export class SettingsEditor2 extends EditorPane { } private layoutTrees(dimension: DOM.Dimension): void { - const listHeight = dimension.height - (76 + 11 /* header height + padding*/); + const listHeight = dimension.height - (72 + 11 /* header height + editor padding */); const settingsTreeHeight = listHeight - 14; this.settingsTreeContainer.style.height = `${settingsTreeHeight}px`; this.settingsTree.layout(settingsTreeHeight, dimension.width); - const tocTreeHeight = listHeight - 17; + const tocTreeHeight = settingsTreeHeight - 1; this.tocTreeContainer.style.height = `${tocTreeHeight}px`; this.tocTree.layout(tocTreeHeight); } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts index b2eb1cb29..86b50e989 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts @@ -4,23 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { ISetting } from 'vs/workbench/services/preferences/common/preferences'; - -export interface ITOCEntry { +export interface ITOCEntry { id: string; label: string; - - children?: ITOCEntry[]; - settings?: Array; + children?: ITOCEntry[]; + settings?: Array; } -export const commonlyUsedData: ITOCEntry = { +export const commonlyUsedData: ITOCEntry = { id: 'commonlyUsed', label: localize('commonlyUsed', "Commonly Used"), - settings: ['files.autoSave', 'editor.fontSize', 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', 'editor.wordWrap', 'files.exclude', 'files.associations'] + settings: ['files.autoSave', 'editor.fontSize', 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', 'editor.wordWrap', 'files.exclude', 'files.associations', 'workbench.editor.enablePreview'] }; -export const tocData: ITOCEntry = { +export const tocData: ITOCEntry = { id: 'root', label: 'root', children: [ diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 2618f9f41..3b26e64a1 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -38,7 +38,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; -import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { ISettingsEditorViewState, settingKeyToDisplayFormat, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; @@ -55,6 +55,8 @@ import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; import { IListService, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ILogService } from 'vs/platform/log/common/log'; +import { settingsMoreActionIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; const $ = DOM.$; @@ -257,15 +259,15 @@ function getListDisplayValue(element: SettingsTreeSettingElement): IListDataItem }); } -export function resolveSettingsTree(tocData: ITOCEntry, coreSettingsGroups: ISettingsGroup[]): { tree: ITOCEntry, leftoverSettings: Set } { +export function resolveSettingsTree(tocData: ITOCEntry, coreSettingsGroups: ISettingsGroup[], logService: ILogService): { tree: ITOCEntry, leftoverSettings: Set } { const allSettings = getFlatSettings(coreSettingsGroups); return { - tree: _resolveSettingsTree(tocData, allSettings), + tree: _resolveSettingsTree(tocData, allSettings, logService), leftoverSettings: allSettings }; } -export function resolveExtensionsSettings(groups: ISettingsGroup[]): ITOCEntry { +export function resolveExtensionsSettings(groups: ISettingsGroup[]): ITOCEntry { const settingsGroupToEntry = (group: ISettingsGroup) => { const flatSettings = arrays.flatten( group.sections.map(section => section.settings)); @@ -288,17 +290,17 @@ export function resolveExtensionsSettings(groups: ISettingsGroup[]): ITOCEntry { }; } -function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set): ITOCEntry { - let children: ITOCEntry[] | undefined; +function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set, logService: ILogService): ITOCEntry { + let children: ITOCEntry[] | undefined; if (tocData.children) { children = tocData.children - .map(child => _resolveSettingsTree(child, allSettings)) + .map(child => _resolveSettingsTree(child, allSettings, logService)) .filter(child => (child.children && child.children.length) || (child.settings && child.settings.length)); } let settings: ISetting[] | undefined; if (tocData.settings) { - settings = arrays.flatten(tocData.settings.map(pattern => getMatchingSettings(allSettings, pattern))); + settings = arrays.flatten(tocData.settings.map(pattern => getMatchingSettings(allSettings, pattern, logService))); } if (!children && !settings) { @@ -313,7 +315,7 @@ function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set): I }; } -function getMatchingSettings(allSettings: Set, pattern: string): ISetting[] { +function getMatchingSettings(allSettings: Set, pattern: string, logService: ILogService): ISetting[] { const result: ISetting[] = []; allSettings.forEach(s => { @@ -323,17 +325,20 @@ function getMatchingSettings(allSettings: Set, pattern: string): ISett } }); + if (!result.length) { + logService.warn(`Settings pattern "${pattern}" doesn't match any settings`); + } return result.sort((a, b) => a.key.localeCompare(b.key)); } const settingPatternCache = new Map(); -function createSettingMatchRegExp(pattern: string): RegExp { +export function createSettingMatchRegExp(pattern: string): RegExp { pattern = escapeRegExpCharacters(pattern) .replace(/\\\*/g, '.*'); - return new RegExp(`^${pattern}`, 'i'); + return new RegExp(`^${pattern}$`, 'i'); } function settingMatches(s: ISetting, pattern: string): boolean { @@ -631,22 +636,12 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const toolbar = new ToolBar(container, this._contextMenuService, { toggleMenuTitle, - renderDropdownAsChildElement: true + renderDropdownAsChildElement: true, + moreIcon: ThemeIcon.asCSSIcon(settingsMoreActionIcon) // change icon from ellipsis to gear }); return toolbar; } - private fixToolbarIcon(toolbar: ToolBar): void { - const button = toolbar.getElement().querySelector('.codicon-toolbar-more'); - if (button) { - (button).tabIndex = 0; - - // change icon from ellipsis to gear - (button).classList.add('codicon-gear'); - (button).classList.remove('codicon-toolbar-more'); - } - } - protected renderSettingElement(node: ITreeNode, index: number, template: ISettingItemTemplate | ISettingBoolItemTemplate): void { const element = node.element; template.context = element; @@ -654,7 +649,6 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const actions = this.disposableActionFactory(element.setting); actions.forEach(a => template.elementDisposables?.add(a)); template.toolbar.setActions([], [...this.settingActions, ...actions]); - this.fixToolbarIcon(template.toolbar); const setting = element.setting; @@ -1332,7 +1326,8 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre const description = (enumDescriptions && enumDescriptions[index] && (enumDescriptionsAreMarkdown ? fixSettingLinks(enumDescriptions[index], false) : enumDescriptions[index])); return { text: enumItemLabels && enumItemLabels[index] ? enumItemLabels[index] : data, - description: enumItemLabels && enumItemLabels[index] ? `[${data}] ${description}` : description, + detail: enumItemLabels && enumItemLabels[index] ? data : '', + description, descriptionIsMarkdown: enumDescriptionsAreMarkdown, descriptionMarkdownActionHandler: { callback: (content) => { @@ -1897,8 +1892,11 @@ export class SettingsTree extends WorkbenchObjectTree { // applying an opacity to the link color. const fgWithOpacity = new Color(new RGBA(foregroundColor.rgba.r, foregroundColor.rgba.g, foregroundColor.rgba.b, 0.9)); collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description { color: ${fgWithOpacity}; }`); - collector.addRule(`.settings-editor > .settings-body .settings-toc-container .monaco-list-row:not(.selected) { color: ${fgWithOpacity}; }`); + + // Hack for subpixel antialiasing + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides, + .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title .setting-item-ignored { color: ${fgWithOpacity}; }`); } const errorColor = theme.getColor(errorForeground); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 12a988e55..e2f231a83 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as arrays from 'vs/base/common/arrays'; -import { isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { escapeRegExpCharacters, isFalsyOrWhitespace } from 'vs/base/common/strings'; import { isArray, withUndefinedAsNull, isUndefinedOrNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; -import { ITOCEntry, knownAcronyms, knownTermMappings } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; +import { ITOCEntry, knownAcronyms, knownTermMappings, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; import { IExtensionSetting, ISearchResult, ISetting, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -25,6 +25,8 @@ export interface ISettingsEditorViewState { settingsTarget: SettingsTarget; tagFilters?: Set; extensionFilters?: Set; + featureFilters?: Set; + idFilters?: Set; filterToCategory?: SettingsTreeGroupElement; } @@ -288,12 +290,49 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { return Array.from(extensionFilters).some(extensionId => extensionId.toLowerCase() === this.setting.extensionInfo!.id.toLowerCase()); } + + matchesAnyFeature(featureFilters?: Set): boolean { + if (!featureFilters || !featureFilters.size) { + return true; + } + + const features = tocData.children!.find(child => child.id === 'features'); + + return Array.from(featureFilters).some(filter => { + if (features && features.children) { + const feature = features.children.find(feature => 'features/' + filter === feature.id); + if (feature) { + const patterns = feature.settings?.map(setting => createSettingMatchRegExp(setting)); + return patterns && !this.setting.extensionInfo && patterns.some(pattern => pattern.test(this.setting.key.toLowerCase())); + } else { + return false; + } + } else { + return false; + } + }); + } + + matchesAnyId(idFilters?: Set): boolean { + if (!idFilters || !idFilters.size) { + return true; + } + return idFilters.has(this.setting.key); + } +} + + +function createSettingMatchRegExp(pattern: string): RegExp { + pattern = escapeRegExpCharacters(pattern) + .replace(/\\\*/g, '.*'); + + return new RegExp(`^${pattern}$`, 'i'); } export class SettingsTreeModel { protected _root!: SettingsTreeGroupElement; private _treeElementsBySettingName = new Map(); - private _tocRoot!: ITOCEntry; + private _tocRoot!: ITOCEntry; constructor( protected _viewState: ISettingsEditorViewState, @@ -349,14 +388,14 @@ export class SettingsTreeModel { }); } - private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement { + private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement { const depth = parent ? this.getDepth(parent) + 1 : 0; const element = new SettingsTreeGroupElement(tocEntry.id, undefined, tocEntry.label, depth, false); const children: SettingsTreeGroupChild[] = []; if (tocEntry.settings) { - const settingChildren = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(s, element)) + const settingChildren = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(s, element)) .filter(el => el.setting.deprecationMessage ? el.isConfigured : true); children.push(...settingChildren); } @@ -608,8 +647,9 @@ export class SearchResultModel extends SettingsTreeModel { // Save time, filter children in the search model instead of relying on the tree filter, which still requires heights to be calculated. const isRemote = !!this.environmentService.remoteAuthority; + this.root.children = this.root.children - .filter(child => child instanceof SettingsTreeSettingElement && child.matchesAllTags(this._viewState.tagFilters) && child.matchesScope(this._viewState.settingsTarget, isRemote) && child.matchesAnyExtension(this._viewState.extensionFilters)); + .filter(child => child instanceof SettingsTreeSettingElement && child.matchesAllTags(this._viewState.tagFilters) && child.matchesScope(this._viewState.settingsTarget, isRemote) && child.matchesAnyExtension(this._viewState.extensionFilters) && child.matchesAnyId(this._viewState.idFilters) && (this.containsValidFeature() ? child.matchesAnyFeature(this._viewState.featureFilters) : true)); if (this.newExtensionSearchResults && this.newExtensionSearchResults.filterMatches.length) { const resultExtensionIds = this.newExtensionSearchResults.filterMatches @@ -623,6 +663,22 @@ export class SearchResultModel extends SettingsTreeModel { } } + private containsValidFeature(): boolean { + if (!this._viewState.featureFilters || !this._viewState.featureFilters.size || !tocData.children) { + return false; + } + + const features = tocData.children.find(child => child.id === 'features'); + + if (features && features.children) { + return Array.from(this._viewState.featureFilters).some(filter => { + return features.children?.find(feature => 'features/' + filter === feature.id); + }); + } else { + return false; + } + } + private getFlatSettings(): ISetting[] { const flatSettings: ISetting[] = []; arrays.coalesce(this.getUniqueResults()) @@ -639,13 +695,19 @@ export interface IParsedQuery { tags: string[]; query: string; extensionFilters: string[]; + idFilters: string[]; + featureFilters: string[]; } const tagRegex = /(^|\s)@tag:("([^"]*)"|[^"]\S*)/g; const extensionRegex = /(^|\s)@ext:("([^"]*)"|[^"]\S*)?/g; +const featureRegex = /(^|\s)@feature:("([^"]*)"|[^"]\S*)?/g; +const idRegex = /(^|\s)@id:("([^"]*)"|[^"]\S*)?/g; export function parseQuery(query: string): IParsedQuery { const tags: string[] = []; const extensions: string[] = []; + const features: string[] = []; + const ids: string[] = []; query = query.replace(tagRegex, (_, __, quotedTag, tag) => { tags.push(tag || quotedTag); return ''; @@ -664,11 +726,29 @@ export function parseQuery(query: string): IParsedQuery { return ''; }); + query = query.replace(featureRegex, (_, __, quotedFeature, feature) => { + const featureQuery: string = feature || quotedFeature; + if (featureQuery) { + features.push(...featureQuery.split(',').map(s => s.trim()).filter(s => !isFalsyOrWhitespace(s))); + } + return ''; + }); + + query = query.replace(idRegex, (_, __, quotedId, id) => { + const idRegex: string = id || quotedId; + if (idRegex) { + ids.push(...idRegex.split(',').map(s => s.trim()).filter(s => !isFalsyOrWhitespace(s))); + } + return ''; + }); + query = query.trim(); return { tags, extensionFilters: extensions, + featureFilters: features, + idFilters: ids, query }; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index 944f05ef4..9994ccc1b 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -23,8 +23,8 @@ import { localize } from 'vs/nls'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { editorWidgetBorder, focusBorder, foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listFocusBackground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, textLinkActiveForeground, textLinkForeground, textPreformatForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; -import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; +import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { settingsDiscardIcon, settingsEditIcon, settingsRemoveIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; const $ = DOM.$; export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "The foreground color for a section header or active title.")); @@ -496,14 +496,14 @@ export class ListSettingWidget extends AbstractListSettingWidget protected getActionsForItem(item: IListDataItem, idx: number): IAction[] { return [ { - class: preferencesEditIcon.classNames, + class: ThemeIcon.asClassName(settingsEditIcon), enabled: true, id: 'workbench.action.editListItem', tooltip: this.getLocalizedStrings().editActionTooltip, run: () => this.editSetting(idx) }, { - class: 'codicon-close', + class: ThemeIcon.asClassName(settingsRemoveIcon), enabled: true, id: 'workbench.action.removeListItem', tooltip: this.getLocalizedStrings().deleteActionTooltip, @@ -740,7 +740,7 @@ export class ObjectSettingWidget extends AbstractListSettingWidget { styleController: id => new DefaultStyleController(DOM.createStyleSheet(container), id), accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider), collapseByDefault: true, - horizontalScrolling: false + horizontalScrolling: false, + hideTwistiesOfChildlessElements: true }; super( diff --git a/src/vs/workbench/contrib/preferences/common/preferences.ts b/src/vs/workbench/contrib/preferences/common/preferences.ts index 62f57e04a..928106092 100644 --- a/src/vs/workbench/contrib/preferences/common/preferences.ts +++ b/src/vs/workbench/contrib/preferences/common/preferences.ts @@ -6,10 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ISettingsEditorModel, ISearchResult } from 'vs/workbench/services/preferences/common/preferences'; -import { IEditorPane } from 'vs/workbench/common/editor'; -import { IKeybindingItemEntry } from 'vs/workbench/services/preferences/common/keybindingsEditorModel'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Event } from 'vs/base/common/event'; export interface IWorkbenchSettingsConfiguration { workbench: { @@ -43,29 +40,6 @@ export interface ISearchProvider { searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise; } -export interface IKeybindingsEditorPane extends IEditorPane { - - readonly activeKeybindingEntry: IKeybindingItemEntry | null; - readonly onDefineWhenExpression: Event; - readonly onLayout: Event; - - search(filter: string): void; - focusSearch(): void; - clearSearchResults(): void; - focusKeybindings(): void; - recordSearchKeys(): void; - toggleSortByPrecedence(): void; - selectKeybinding(keybindingEntry: IKeybindingItemEntry): void; - defineKeybinding(keybindingEntry: IKeybindingItemEntry): Promise; - defineWhenExpression(keybindingEntry: IKeybindingItemEntry): void; - updateKeybinding(keybindingEntry: IKeybindingItemEntry, key: string, when: string | undefined): Promise; - removeKeybinding(keybindingEntry: IKeybindingItemEntry): Promise; - resetKeybinding(keybindingEntry: IKeybindingItemEntry): Promise; - copyKeybinding(keybindingEntry: IKeybindingItemEntry): Promise; - copyKeybindingCommand(keybindingEntry: IKeybindingItemEntry): Promise; - showSimilarKeybindings(keybindingEntry: IKeybindingItemEntry): void; -} - export const SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'settings.action.clearSearchResults'; export const SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU = 'settings.action.showContextMenu'; @@ -83,6 +57,7 @@ export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'keybindings.edit export const KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS = 'keybindings.editor.recordSearchKeys'; export const KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE = 'keybindings.editor.toggleSortByPrecedence'; export const KEYBINDINGS_EDITOR_COMMAND_DEFINE = 'keybindings.editor.defineKeybinding'; +export const KEYBINDINGS_EDITOR_COMMAND_ADD = 'keybindings.editor.addKeybinding'; export const KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN = 'keybindings.editor.defineWhenExpression'; export const KEYBINDINGS_EDITOR_COMMAND_REMOVE = 'keybindings.editor.removeKeybinding'; export const KEYBINDINGS_EDITOR_COMMAND_RESET = 'keybindings.editor.resetKeybinding'; @@ -95,5 +70,6 @@ export const KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS = 'keybindings.editor.show export const MODIFIED_SETTING_TAG = 'modified'; export const EXTENSION_SETTING_TAG = 'ext:'; - +export const FEATURE_SETTING_TAG = 'feature:'; +export const ID_SETTING_TAG = 'id:'; export const KEYBOARD_LAYOUT_OPEN_PICKER = 'workbench.action.openKeyboardLayoutPicker'; diff --git a/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts b/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts index 8113f3d37..b2ae0a9e8 100644 --- a/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts +++ b/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts @@ -147,7 +147,9 @@ suite('SettingsTree', () => { { tags: [], extensionFilters: [], - query: '' + query: '', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -155,7 +157,9 @@ suite('SettingsTree', () => { { tags: ['modified'], extensionFilters: [], - query: '' + query: '', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -163,7 +167,9 @@ suite('SettingsTree', () => { { tags: ['foo'], extensionFilters: [], - query: '' + query: '', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -171,7 +177,9 @@ suite('SettingsTree', () => { { tags: ['modified'], extensionFilters: [], - query: 'foo' + query: 'foo', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -179,7 +187,9 @@ suite('SettingsTree', () => { { tags: ['foo', 'modified'], extensionFilters: [], - query: '' + query: '', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -187,7 +197,9 @@ suite('SettingsTree', () => { { tags: ['foo', 'modified'], extensionFilters: [], - query: 'my query' + query: 'my query', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -195,7 +207,9 @@ suite('SettingsTree', () => { { tags: ['modified'], extensionFilters: [], - query: 'test query' + query: 'test query', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -203,7 +217,9 @@ suite('SettingsTree', () => { { tags: ['modified'], extensionFilters: [], - query: 'test' + query: 'test', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -211,7 +227,9 @@ suite('SettingsTree', () => { { tags: [], extensionFilters: [], - query: 'query has @ for some reason' + query: 'query has @ for some reason', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -219,7 +237,9 @@ suite('SettingsTree', () => { { tags: [], extensionFilters: ['github.vscode-pull-request-github'], - query: '' + query: '', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -227,7 +247,47 @@ suite('SettingsTree', () => { { tags: [], extensionFilters: ['github.vscode-pull-request-github', 'vscode.git'], - query: '' + query: '', + featureFilters: [], + idFilters: [] + }); + testParseQuery( + '@feature:scm', + { + tags: [], + extensionFilters: [], + featureFilters: ['scm'], + query: '', + idFilters: [] + }); + + testParseQuery( + '@feature:scm,terminal', + { + tags: [], + extensionFilters: [], + featureFilters: ['scm', 'terminal'], + query: '', + idFilters: [] + }); + testParseQuery( + '@id:files.autoSave', + { + tags: [], + extensionFilters: [], + featureFilters: [], + query: '', + idFilters: ['files.autoSave'] + }); + + testParseQuery( + '@id:files.autoSave,terminal.integrated.commandsToSkipShell', + { + tags: [], + extensionFilters: [], + featureFilters: [], + query: '', + idFilters: ['files.autoSave', 'terminal.integrated.commandsToSkipShell'] }); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts index d24b9139c..d742a1d28 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @@ -22,12 +22,14 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess'; -import { stripCodicons } from 'vs/base/common/codicons'; +import { Codicon, stripCodicons } from 'vs/base/common/codicons'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { TriggerAction } from 'vs/platform/quickinput/browser/pickerQuickAccess'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider { @@ -60,7 +62,8 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce @ITelemetryService telemetryService: ITelemetryService, @INotificationService notificationService: INotificationService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, + @IPreferencesService private readonly preferencesService: IPreferencesService, ) { super({ showAlias: !Language.isDefaultVariant(), @@ -91,7 +94,17 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce return [ ...this.getCodeEditorCommandPicks(), ...this.getGlobalCommandPicks(disposables) - ]; + ].map(c => ({ + ...c, + buttons: [{ + iconClass: Codicon.gear.classNames, + tooltip: localize('configure keybinding', "Configure Keybinding"), + }], + trigger: (): TriggerAction => { + this.preferencesService.openGlobalKeybindingSettings(false, { query: `@command:${c.commandId}` }); + return TriggerAction.CLOSE_PICKER; + }, + })); } private getGlobalCommandPicks(disposables: DisposableStore): ICommandQuickPick[] { @@ -100,7 +113,7 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce const globalCommandsMenu = this.menuService.createMenu(MenuId.CommandPalette, scopedContextKeyService); const globalCommandsMenuActions = globalCommandsMenu.getActions() .reduce((r, [, actions]) => [...r, ...actions], >[]) - .filter(action => action instanceof MenuItemAction) as MenuItemAction[]; + .filter(action => action instanceof MenuItemAction && action.enabled) as MenuItemAction[]; for (const action of globalCommandsMenuActions) { diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index 386eb37bb..c5a10561f 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -36,7 +36,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private updateMode: string | undefined; private debugConsoleWordWrap: boolean | undefined; private accessibilitySupport: 'on' | 'off' | 'auto' | undefined; - private enableExperimentalProxyLoginDialog: boolean | undefined; + private enableExperimentalProxyLoginDialog = true; // default constructor( @IHostService private readonly hostService: IHostService, diff --git a/src/vs/workbench/contrib/remote/browser/media/tunnelView.css b/src/vs/workbench/contrib/remote/browser/media/tunnelView.css index 659c87549..55776ca99 100644 --- a/src/vs/workbench/contrib/remote/browser/media/tunnelView.css +++ b/src/vs/workbench/contrib/remote/browser/media/tunnelView.css @@ -3,6 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.customview-tree .tunnel-view-label { +.pane-body:not(.wide) .ports-view .monaco-icon-label, +.pane-body:not(.wide) .ports-view .monaco-icon-label { flex: 1; } + +.pane-body:not(.wide) .ports-view .monaco-list .monaco-list-row:hover:not(.highlighted) .monaco-icon-label, +.pane-body:not(.wide) .ports-view .monaco-list .monaco-list-row.focused .monaco-icon-label { + flex: 1; +} + +.pane-body.wide .ports-view .monaco-list .monaco-list-row .actionBarContainer, +.ports-view .monaco-list .monaco-list-row .actionBarContainer { + flex: 1 0 auto; +} + +.pane-body:not(.wide) .ports-view .monaco-list .monaco-list-row .actionBarContainer { + flex: 0 0 auto; +} + +.ports-view.actions-right .monaco-list .monaco-list-row .actionBarContainer, +.pane-body:not(.wide) .ports-view .monaco-list .monaco-list-row .actionBarContainer { + text-align: right; +} + +.pane-body.wide .ports-view .monaco-action-bar { + margin-left: 10px; +} diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index c6d98b601..29ab926a3 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -13,7 +13,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { FilterViewPaneContainer } from 'vs/workbench/browser/parts/views/viewsViewlet'; @@ -54,7 +54,8 @@ import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/e import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { RemoteStatusIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator'; import { inQuickPickContextKeyValue } from 'vs/workbench/browser/quickaccess'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export interface HelpInformation { extensionDescription: IExtensionDescription; @@ -135,12 +136,12 @@ class HelpTreeRenderer implements ITreeRenderer { - hasChildren(element: any) { +class HelpDataSource implements IAsyncDataSource { + hasChildren(element: HelpModel) { return element instanceof HelpModel; } - getChildren(element: any) { + getChildren(element: HelpModel) { if (element instanceof HelpModel && element.items) { return element.items; } @@ -149,14 +150,15 @@ class HelpDataSource implements IAsyncDataSource { } } -const getStartedIcon = registerIcon('remote-explorer-get-started', Codicon.star); -const documentationIcon = registerIcon('remote-explorer-documentation', Codicon.book); -const feedbackIcon = registerIcon('remote-explorer-feedback', Codicon.twitter); -const reviewIssuesIcon = registerIcon('remote-explorer-review-issues', Codicon.issues); -const reportIssuesIcon = registerIcon('remote-explorer-report-issues', Codicon.comment); +const getStartedIcon = registerIcon('remote-explorer-get-started', Codicon.star, nls.localize('getStartedIcon', 'Getting started icon in the remote explorer view.')); +const documentationIcon = registerIcon('remote-explorer-documentation', Codicon.book, nls.localize('documentationIcon', 'Documentation icon in the remote explorer view.')); +const feedbackIcon = registerIcon('remote-explorer-feedback', Codicon.twitter, nls.localize('feedbackIcon', 'Feedback icon in the remote explorer view.')); +const reviewIssuesIcon = registerIcon('remote-explorer-review-issues', Codicon.issues, nls.localize('reviewIssuesIcon', 'Review issue icon in the remote explorer view.')); +const reportIssuesIcon = registerIcon('remote-explorer-report-issues', Codicon.comment, nls.localize('reportIssuesIcon', 'Report issue icon in the remote explorer view.')); +const remoteExplorerViewIcon = registerIcon('remote-explorer-view-icon', Codicon.remoteExplorer, nls.localize('remoteExplorerViewIcon', 'View icon of the remote explorer view.')); interface IHelpItem { - icon: Codicon, + icon: ThemeIcon, iconClasses: string[]; label: string; handleClick(): Promise; @@ -297,14 +299,14 @@ class HelpItemValue { abstract class HelpItemBase implements IHelpItem { public iconClasses: string[] = []; constructor( - public icon: Codicon, + public icon: ThemeIcon, public label: string, public values: HelpItemValue[], private quickInputService: IQuickInputService, private environmentService: IWorkbenchEnvironmentService, private remoteExplorerService: IRemoteExplorerService ) { - this.iconClasses.push(...icon.classNamesArray); + this.iconClasses.push(...ThemeIcon.asClassNameArray(icon)); this.iconClasses.push('remote-help-tree-node-item-icon'); } @@ -351,7 +353,7 @@ abstract class HelpItemBase implements IHelpItem { class HelpItem extends HelpItemBase { constructor( - icon: Codicon, + icon: ThemeIcon, label: string, values: HelpItemValue[], quickInputService: IQuickInputService, @@ -369,7 +371,7 @@ class HelpItem extends HelpItemBase { class IssueReporterItem extends HelpItemBase { constructor( - icon: Codicon, + icon: ThemeIcon, label: string, values: HelpItemValue[], quickInputService: IQuickInputService, @@ -388,7 +390,7 @@ class IssueReporterItem extends HelpItemBase { class HelpPanel extends ViewPane { static readonly ID = '~remote.helpPanel'; static readonly TITLE = nls.localize('remote.help', "Help and feedback"); - private tree!: WorkbenchAsyncDataTree; + private tree!: WorkbenchAsyncDataTree; constructor( protected viewModel: IViewModel, @@ -418,7 +420,7 @@ class HelpPanel extends ViewPane { treeContainer.classList.add('remote-help-content'); container.appendChild(treeContainer); - this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, + this.tree = >this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'RemoteHelp', treeContainer, new HelpTreeVirtualDelegate(), @@ -439,7 +441,7 @@ class HelpPanel extends ViewPane { this.tree.setInput(model); this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, 75, true)(e => { - e.element.handleClick(); + e.element?.handleClick(); })); } @@ -572,7 +574,7 @@ Registry.as(Extensions.ViewContainersRegistry).register matches = /^details(@(\d+))?$/.exec(group); if (matches) { - return -500; + return -500 + Number(matches[2]); } matches = /^help(@(\d+))?$/.exec(group); @@ -583,7 +585,7 @@ Registry.as(Extensions.ViewContainersRegistry).register return; } }, - icon: 'codicon-remote-explorer', + icon: remoteExplorerViewIcon, order: 4 }, ViewContainerLocation.Sidebar); diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index 4eb693366..aaabe66b0 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -5,9 +5,9 @@ import * as nls from 'vs/nls'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { Extensions, IViewDescriptorService, IViewsRegistry, IViewsService } from 'vs/workbench/common/views'; -import { IRemoteExplorerService, MakeAddress, mapHasTunnelLocalhostOrAllInterfaces, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; -import { forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanelDescriptor, TunnelViewModel } from 'vs/workbench/contrib/remote/browser/tunnelView'; +import { Extensions, IViewContainersRegistry, IViewDescriptorService, IViewsRegistry, IViewsService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; +import { IRemoteExplorerService, makeAddress, mapHasAddressLocalhostOrAllInterfaces, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { PORT_AUTO_FORWARD_SETTING, forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanel, TunnelPanelDescriptor, TunnelViewModel } from 'vs/workbench/contrib/remote/browser/tunnelView'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -15,25 +15,40 @@ import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarA import { UrlFinder } from 'vs/workbench/contrib/remote/browser/urlFinder'; import Severity from 'vs/base/common/severity'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { INotificationService, IPromptChoice } from 'vs/platform/notification/common/notification'; +import { INotificationHandle, INotificationService, IPromptChoice } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { OperatingSystem } from 'vs/base/common/platform'; +import { RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { Codicon } from 'vs/base/common/codicons'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; +import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { optional } from 'vs/platform/instantiation/common/instantiation'; export const VIEWLET_ID = 'workbench.view.remote'; export class ForwardedPortsView extends Disposable implements IWorkbenchContribution { private contextKeyListener?: IDisposable; + private _activityBadge?: IDisposable; private entryAccessor: IStatusbarEntryAccessor | undefined; + private readonly tasExperimentService: ITASExperimentService | undefined; constructor( @IContextKeyService private readonly contextKeyService: IContextKeyService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, - @IStatusbarService private readonly statusbarService: IStatusbarService + @IActivityService private readonly activityService: IActivityService, + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, ) { super(); + this.tasExperimentService = tasExperimentService; this._register(Registry.as(Extensions.ViewsRegistry).registerViewWelcomeContent(TUNNEL_VIEW_ID, { content: `Forwarded ports allow you to access your running services locally.\n[Forward a Port](command:${ForwardPortAction.INLINE_ID})`, })); @@ -41,17 +56,41 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu this.enableForwardedPortsView(); } - private enableForwardedPortsView() { + private async usePanelTreatment(): Promise { + if (this.tasExperimentService) { + return !!(await this.tasExperimentService.getTreatment('portspanel')); + } + return false; + } + + private async getViewContainer(): Promise { + if (await this.usePanelTreatment()) { + return Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ + id: TunnelPanel.ID, + name: nls.localize('ports', "Ports"), + icon: Codicon.plug, + ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [TunnelPanel.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), + storageId: TunnelPanel.ID, + hideIfEmpty: true, + order: 5 + }, ViewContainerLocation.Panel); + } else { + return this.viewDescriptorService.getViewContainerById(VIEWLET_ID); + } + } + + private async enableForwardedPortsView() { if (this.contextKeyListener) { this.contextKeyListener.dispose(); this.contextKeyListener = undefined; } const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService); + if (this.environmentService.remoteAuthority && viewEnabled) { - const tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService), this.environmentService); + const viewContainer = await this.getViewContainer(); + const tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService, this.configurationService), this.environmentService); const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - const viewContainer = this.viewDescriptorService.getViewContainerById(VIEWLET_ID); if (viewContainer) { viewsRegistry.registerViews([tunnelPanelDescriptor!], viewContainer); } @@ -68,18 +107,38 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu const disposable = Registry.as(Extensions.ViewsRegistry).onViewsRegistered(e => { if (e.find(view => view.views.find(viewDescriptor => viewDescriptor.id === TUNNEL_VIEW_ID))) { this._register(this.remoteExplorerService.tunnelModel.onForwardPort(() => { + this.updateActivityBadge(); this.updateStatusBar(); })); this._register(this.remoteExplorerService.tunnelModel.onClosePort(() => { + this.updateActivityBadge(); this.updateStatusBar(); })); + this.updateActivityBadge(); this.updateStatusBar(); disposable.dispose(); } }); } + private async updateActivityBadge() { + if (this._activityBadge) { + this._activityBadge.dispose(); + } + if (!(await this.usePanelTreatment())) { + return; + } + if (this.remoteExplorerService.tunnelModel.forwarded.size > 0) { + const viewContainer = this.viewDescriptorService.getViewContainerByViewId(TUNNEL_VIEW_ID); + if (viewContainer) { + this._activityBadge = this.activityService.showViewContainerActivity(viewContainer.id, { + badge: new NumberBadge(this.remoteExplorerService.tunnelModel.forwarded.size, n => n === 1 ? nls.localize('1forwardedPort', "1 forwarded port") : nls.localize('nForwardedPorts', "{0} forwarded ports", n)) + }); + } + } + } + private updateStatusBar() { if (!this.entryAccessor) { this._register(this.entryAccessor = this.statusbarService.addEntry(this.entry, 'status.forwardedPorts', nls.localize('status.forwardedPorts', "Forwarded Ports"), StatusbarAlignment.LEFT, 40)); @@ -92,18 +151,13 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu let text: string; let tooltip: string; const count = this.remoteExplorerService.tunnelModel.forwarded.size + this.remoteExplorerService.tunnelModel.detected.size; + text = `${count}`; if (count === 0) { - text = nls.localize('remote.forwardedPorts.statusbarTextNone', "No Ports Available"); - tooltip = text; + tooltip = nls.localize('remote.forwardedPorts.statusbarTextNone', "No Ports Forwarded"); } else { - if (count === 1) { - text = nls.localize('remote.forwardedPorts.statusbarTextSingle', "1 Port Available"); - } else { - text = nls.localize('remote.forwardedPorts.statusbarTextMultiple', "{0} Ports Available", count); - } const allTunnels = Array.from(this.remoteExplorerService.tunnelModel.forwarded.values()); allTunnels.push(...Array.from(this.remoteExplorerService.tunnelModel.detected.values())); - tooltip = nls.localize('remote.forwardedPorts.statusbarTooltip', "Available Ports: {0}", + tooltip = nls.localize('remote.forwardedPorts.statusbarTooltip', "Forwarded Ports: {0}", allTunnels.map(forwarded => forwarded.remotePort).join(', ')); } return { @@ -117,40 +171,134 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu export class AutomaticPortForwarding extends Disposable implements IWorkbenchContribution { - private contextServiceListener?: IDisposable; - private urlFinder?: UrlFinder; - private static AUTO_FORWARD_SETTING = 'remote.autoForwardPorts'; constructor( - @ITerminalService private readonly terminalService: ITerminalService, - @INotificationService private readonly notificationService: INotificationService, - @IOpenerService private readonly openerService: IOpenerService, - @IViewsService private readonly viewsService: IViewsService, - @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IDebugService private readonly debugService: IDebugService + @ITerminalService readonly terminalService: ITerminalService, + @INotificationService readonly notificationService: INotificationService, + @IOpenerService readonly openerService: IOpenerService, + @IViewsService readonly viewsService: IViewsService, + @IRemoteExplorerService readonly remoteExplorerService: IRemoteExplorerService, + @IWorkbenchEnvironmentService readonly environmentService: IWorkbenchEnvironmentService, + @IContextKeyService readonly contextKeyService: IContextKeyService, + @IConfigurationService readonly configurationService: IConfigurationService, + @IDebugService readonly debugService: IDebugService, + @IRemoteAgentService readonly remoteAgentService: IRemoteAgentService ) { super(); + if (!this.environmentService.remoteAuthority) { + return; + } + + remoteAgentService.getEnvironment().then(environment => { + if (environment?.os === OperatingSystem.Windows) { + this._register(new WindowsAutomaticPortForwarding(terminalService, notificationService, openerService, + remoteExplorerService, contextKeyService, configurationService, debugService)); + } else if (environment?.os === OperatingSystem.Linux) { + this._register(new LinuxAutomaticPortForwarding(configurationService, remoteExplorerService, notificationService, openerService, contextKeyService)); + } + }); + } +} + +class ForwardedPortNotifier extends Disposable { + private lastNotifyTime: Date; + private static COOL_DOWN = 5000; // milliseconds + private lastNotification: INotificationHandle | undefined; + + constructor(private readonly notificationService: INotificationService, + private readonly remoteExplorerService: IRemoteExplorerService, + private readonly openerService: IOpenerService) { + super(); + this.lastNotifyTime = new Date(); + this.lastNotifyTime.setFullYear(this.lastNotifyTime.getFullYear() - 1); + } + + public async notify(tunnels: RemoteTunnel[]) { + const tunnel = await this.portNumberHeuristicDelay(tunnels); + if (tunnel) { + if (Date.now() - this.lastNotifyTime.getTime() > ForwardedPortNotifier.COOL_DOWN) { + this.showNotification(tunnel); + } + } + } + + private newerTunnel: RemoteTunnel | undefined; + private async portNumberHeuristicDelay(tunnels: RemoteTunnel[]): Promise { + if (tunnels.length === 0) { + return; + } + tunnels = tunnels.sort((a, b) => a.tunnelRemotePort - b.tunnelRemotePort); + const firstTunnel = tunnels.shift()!; + // Heuristic. + if (firstTunnel.tunnelRemotePort % 1000 === 0) { + this.newerTunnel = firstTunnel; + return firstTunnel; + // 9229 is the node inspect port + } else if (firstTunnel.tunnelRemotePort < 10000 && firstTunnel.tunnelRemotePort !== 9229) { + this.newerTunnel = firstTunnel; + return firstTunnel; + } + + this.newerTunnel = undefined; + return new Promise(resolve => { + setTimeout(() => { + if (this.newerTunnel) { + resolve(undefined); + } else { + resolve(firstTunnel); + } + }, 3000); + }); + } + + private showNotification(tunnel: RemoteTunnel) { + const address = makeAddress(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort); + const message = nls.localize('remote.tunnelsView.automaticForward', "Your service running on port {0} is available. [See all forwarded ports](command:{1}.focus)", + tunnel.tunnelRemotePort, TunnelPanel.ID); + const browserChoice: IPromptChoice = { + label: OpenPortInBrowserAction.LABEL, + run: () => OpenPortInBrowserAction.run(this.remoteExplorerService.tunnelModel, this.openerService, address) + }; + this.lastNotification = this.notificationService.prompt(Severity.Info, message, [browserChoice], { neverShowAgain: { id: 'remote.tunnelsView.autoForwardNeverShow', isSecondary: true } }); + this.lastNotifyTime = new Date(); + this.lastNotification.onDidClose(() => { + this.lastNotification = undefined; + }); + } +} + +class WindowsAutomaticPortForwarding extends Disposable { + private contextServiceListener?: IDisposable; + private urlFinder?: UrlFinder; + private notifier: ForwardedPortNotifier; + + constructor( + private readonly terminalService: ITerminalService, + readonly notificationService: INotificationService, + readonly openerService: IOpenerService, + private readonly remoteExplorerService: IRemoteExplorerService, + private readonly contextKeyService: IContextKeyService, + private readonly configurationService: IConfigurationService, + private readonly debugService: IDebugService + ) { + super(); + this.notifier = new ForwardedPortNotifier(notificationService, remoteExplorerService, openerService); this._register(configurationService.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration(AutomaticPortForwarding.AUTO_FORWARD_SETTING)) { + if (e.affectsConfiguration(PORT_AUTO_FORWARD_SETTING)) { this.tryStartStopUrlFinder(); } })); - if (this.environmentService.remoteAuthority) { - this.contextServiceListener = this._register(this.contextKeyService.onDidChangeContext(e => { - if (e.affectsSome(new Set(forwardedPortsViewEnabled.keys()))) { - this.tryStartStopUrlFinder(); - } - })); - this.tryStartStopUrlFinder(); - } + this.contextServiceListener = this._register(this.contextKeyService.onDidChangeContext(e => { + if (e.affectsSome(new Set(forwardedPortsViewEnabled.keys()))) { + this.tryStartStopUrlFinder(); + } + })); + this.tryStartStopUrlFinder(); } private tryStartStopUrlFinder() { - if (this.configurationService.getValue(AutomaticPortForwarding.AUTO_FORWARD_SETTING)) { + if (this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING)) { this.startUrlFinder(); } else { this.stopUrlFinder(); @@ -166,31 +314,12 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon } this.urlFinder = this._register(new UrlFinder(this.terminalService, this.debugService)); this._register(this.urlFinder.onDidMatchLocalUrl(async (localUrl) => { - if (mapHasTunnelLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, localUrl.host, localUrl.port)) { + if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, localUrl.host, localUrl.port)) { return; } const forwarded = await this.remoteExplorerService.forward(localUrl); if (forwarded) { - const address = MakeAddress(forwarded.tunnelRemoteHost, forwarded.tunnelRemotePort); - const message = nls.localize('remote.tunnelsView.automaticForward', "Your service running on port {0} is available.", - forwarded.tunnelRemotePort); - const browserChoice: IPromptChoice = { - label: OpenPortInBrowserAction.LABEL, - run: () => OpenPortInBrowserAction.run(this.remoteExplorerService.tunnelModel, this.openerService, address) - }; - const showChoice: IPromptChoice = { - label: nls.localize('remote.tunnelsView.showView', "Show Forwarded Ports"), - run: () => { - const remoteAuthority = this.environmentService.remoteAuthority; - const explorerType: string[] | undefined = remoteAuthority ? [remoteAuthority.split('+')[0]] : undefined; - if (explorerType) { - this.remoteExplorerService.targetType = explorerType; - } - this.viewsService.openViewContainer(VIEWLET_ID); - }, - isSecondary: true - }; - this.notificationService.prompt(Severity.Info, message, [browserChoice, showChoice], { neverShowAgain: { id: 'remote.tunnelsView.autoForwardNeverShow', isSecondary: true } }); + this.notifier.notify([forwarded]); } })); } @@ -202,3 +331,114 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon } } } + +class LinuxAutomaticPortForwarding extends Disposable { + private candidateListener: IDisposable | undefined; + private autoForwarded: Set = new Set(); + private notifier: ForwardedPortNotifier; + private initialCandidates: Set = new Set(); + private contextServiceListener: IDisposable | undefined; + + constructor( + private readonly configurationService: IConfigurationService, + readonly remoteExplorerService: IRemoteExplorerService, + readonly notificationService: INotificationService, + readonly openerService: IOpenerService, + readonly contextKeyService: IContextKeyService + ) { + super(); + this.notifier = new ForwardedPortNotifier(notificationService, remoteExplorerService, openerService); + this._register(configurationService.onDidChangeConfiguration(async (e) => { + if (e.affectsConfiguration(PORT_AUTO_FORWARD_SETTING)) { + await this.startStopCandidateListener(); + } + })); + + this.contextServiceListener = this._register(this.contextKeyService.onDidChangeContext(async (e) => { + if (e.affectsSome(new Set(forwardedPortsViewEnabled.keys()))) { + await this.startStopCandidateListener(); + } + })); + + this.startStopCandidateListener(); + } + + private async startStopCandidateListener() { + if (this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING)) { + await this.startCandidateListener(); + } else { + this.stopCandidateListener(); + } + } + + private stopCandidateListener() { + if (this.candidateListener) { + this.candidateListener.dispose(); + this.candidateListener = undefined; + } + } + + private async startCandidateListener() { + if (this.candidateListener || !forwardedPortsViewEnabled.getValue(this.contextKeyService)) { + return; + } + if (this.contextServiceListener) { + this.contextServiceListener.dispose(); + } + + if (!this.remoteExplorerService.tunnelModel.environmentTunnelsSet) { + await new Promise(resolve => this.remoteExplorerService.tunnelModel.onEnvironmentTunnelsSet(() => resolve())); + } + + // Capture list of starting candidates so we don't auto forward them later. + this.setInitialCandidates(); + + this.candidateListener = this._register(this.remoteExplorerService.tunnelModel.onCandidatesChanged(this.handleCandidateUpdate, this)); + } + + private setInitialCandidates() { + this.remoteExplorerService.tunnelModel.candidates.forEach(async (value) => { + this.initialCandidates.add(makeAddress(value.host, value.port)); + }); + } + + private async forwardCandidates(): Promise { + const allTunnels = (await Promise.all(this.remoteExplorerService.tunnelModel.candidates.map(async (value) => { + const address = makeAddress(value.host, value.port); + if (this.initialCandidates.has(address)) { + return undefined; + } + if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.detected, value.host, value.port)) { + return undefined; + } + if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, value.host, value.port)) { + return undefined; + } + const forwarded = await this.remoteExplorerService.forward(value); + if (forwarded) { + this.autoForwarded.add(address); + } + return forwarded; + }))).filter(tunnel => !!tunnel); + if (allTunnels.length === 0) { + return undefined; + } + return allTunnels; + } + + private async handleCandidateUpdate(removed: Map) { + removed.forEach((value, key) => { + if (this.autoForwarded.has(key)) { + this.remoteExplorerService.close(value); + this.autoForwarded.delete(key); + } else if (this.initialCandidates.has(key)) { + this.initialCandidates.delete(key); + } + }); + + const tunnels = await this.forwardCandidates(); + if (tunnels) { + this.notifier.notify(tunnels); + } + } +} diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 83e5499b9..f3ee1a1f6 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -23,21 +23,24 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot import { IHostService } from 'vs/workbench/services/host/browser/host'; import { isWeb } from 'vs/base/common/platform'; import { once } from 'vs/base/common/functional'; +import { truncate } from 'vs/base/common/strings'; export class RemoteStatusIndicator extends Disposable implements IWorkbenchContribution { - private static REMOTE_ACTIONS_COMMAND_ID = 'workbench.action.remote.showMenu'; - private static CLOSE_REMOTE_COMMAND_ID = 'workbench.action.remote.close'; - private static SHOW_CLOSE_REMOTE_COMMAND_ID = !isWeb; // web does not have a "Close Remote" command + private static readonly REMOTE_ACTIONS_COMMAND_ID = 'workbench.action.remote.showMenu'; + private static readonly CLOSE_REMOTE_COMMAND_ID = 'workbench.action.remote.close'; + private static readonly SHOW_CLOSE_REMOTE_COMMAND_ID = !isWeb; // web does not have a "Close Remote" command + + private static readonly REMOTE_STATUS_LABEL_MAX_LENGTH = 40; private remoteStatusEntry: IStatusbarEntryAccessor | undefined; - private remoteMenu = this._register(this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService)); + private readonly remoteMenu = this._register(this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService)); private hasRemoteActions = false; - private remoteAuthority = this.environmentService.remoteAuthority; + private readonly remoteAuthority = this.environmentService.remoteAuthority; private connectionState: 'initializing' | 'connected' | 'disconnected' | undefined = undefined; - private connectionStateContextKey = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', '').bindTo(this.contextKeyService); + private readonly connectionStateContextKey = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', '').bindTo(this.contextKeyService); constructor( @IStatusbarService private readonly statusbarService: IStatusbarService, @@ -189,7 +192,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr // Remote indicator: show if provided via options const remoteIndicator = this.environmentService.options?.windowIndicator; if (remoteIndicator) { - this.renderRemoteStatusIndicator(remoteIndicator.label, remoteIndicator.tooltip, remoteIndicator.command); + this.renderRemoteStatusIndicator(truncate(remoteIndicator.label, RemoteStatusIndicator.REMOTE_STATUS_LABEL_MAX_LENGTH), remoteIndicator.tooltip, remoteIndicator.command); } // Remote Authority: show connection state @@ -200,10 +203,10 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr this.renderRemoteStatusIndicator(nls.localize('host.open', "Opening Remote..."), nls.localize('host.open', "Opening Remote..."), undefined, true /* progress */); break; case 'disconnected': - this.renderRemoteStatusIndicator(`$(alert) ${nls.localize('disconnectedFrom', "Disconnected from {0}", hostLabel)}`, nls.localize('host.tooltipDisconnected', "Disconnected from {0}", hostLabel)); + this.renderRemoteStatusIndicator(`$(alert) ${nls.localize('disconnectedFrom', "Disconnected from {0}", truncate(hostLabel, RemoteStatusIndicator.REMOTE_STATUS_LABEL_MAX_LENGTH))}`, nls.localize('host.tooltipDisconnected', "Disconnected from {0}", hostLabel)); break; default: - this.renderRemoteStatusIndicator(`$(remote) ${hostLabel}`, nls.localize('host.tooltip', "Editing on {0}", hostLabel)); + this.renderRemoteStatusIndicator(`$(remote) ${truncate(hostLabel, RemoteStatusIndicator.REMOTE_STATUS_LABEL_MAX_LENGTH)}`, nls.localize('host.tooltip', "Editing on {0}", hostLabel)); } } diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 235dce546..7ed9fa790 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -26,7 +26,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IMenuService, MenuId, IMenu, MenuRegistry, MenuItemAction, ILocalizedString, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { createAndFillInContextMenuActions, createAndFillInActionBarActions, MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IRemoteExplorerService, TunnelModel, MakeAddress, TunnelType, ITunnelItem, Tunnel, mapHasTunnelLocalhostOrAllInterfaces, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { IRemoteExplorerService, TunnelModel, makeAddress, TunnelType, ITunnelItem, Tunnel, mapHasAddressLocalhostOrAllInterfaces, TUNNEL_VIEW_ID, parseAddress } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; @@ -42,8 +42,10 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { Codicon } from 'vs/base/common/codicons'; export const forwardedPortsViewEnabled = new RawContextKey('forwardedPortsViewEnabled', false); +export const PORT_AUTO_FORWARD_SETTING = 'remote.autoForwardPorts'; class TunnelTreeVirtualDelegate implements IListVirtualDelegate { getHeight(element: ITunnelItem): number { @@ -61,7 +63,7 @@ export interface ITunnelViewModel { readonly detected: TunnelItem[]; readonly candidates: TunnelItem[]; readonly input: TunnelItem; - groups(): Promise; + groupsAndForwarded(): Promise; } export class TunnelViewModel extends Disposable implements ITunnelViewModel { @@ -72,7 +74,8 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { private _candidates: Map = new Map(); constructor( - @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService) { + @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, + @IConfigurationService private readonly configurationService: IConfigurationService) { super(); this.model = remoteExplorerService.tunnelModel; this._register(this.model.onForwardPort(() => this._onForwardedPortsChanged.fire())); @@ -84,22 +87,19 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { tunnelType: TunnelType.Add, remoteHost: 'localhost', remotePort: 0, - description: '' + description: '', + iconClasses: undefined }; } - async groups(): Promise { - const groups: ITunnelGroup[] = []; + async groupsAndForwarded(): Promise { + const groups: (ITunnelGroup | TunnelItem)[] = []; this._candidates = new Map(); (await this.model.candidates).forEach(candidate => { - this._candidates.set(MakeAddress(candidate.host, candidate.port), candidate); + this._candidates.set(makeAddress(candidate.host, candidate.port), candidate); }); if ((this.model.forwarded.size > 0) || this.remoteExplorerService.getEditableData(undefined)) { - groups.push({ - label: nls.localize('remote.tunnelsView.forwarded', "Forwarded"), - tunnelType: TunnelType.Forwarded, - items: this.forwarded - }); + groups.push(...this.forwarded); } if (this.model.detected.size > 0) { groups.push({ @@ -108,13 +108,15 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { items: this.detected }); } - const candidates = await this.candidates; - if (candidates.length > 0) { - groups.push({ - label: nls.localize('remote.tunnelsView.candidates', "Not Forwarded"), - tunnelType: TunnelType.Candidate, - items: candidates - }); + if (!this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING)) { + const candidates = await this.candidates; + if (candidates.length > 0) { + groups.push({ + label: nls.localize('remote.tunnelsView.candidates', "Not Forwarded"), + tunnelType: TunnelType.Candidate, + items: candidates + }); + } } if (groups.length === 0) { groups.push(this._input); @@ -123,7 +125,7 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { } private addProcessInfoFromCandidate(tunnelItem: ITunnelItem) { - const key = MakeAddress(tunnelItem.remoteHost, tunnelItem.remotePort); + const key = makeAddress(tunnelItem.remoteHost, tunnelItem.remotePort); if (this._candidates.has(key)) { tunnelItem.description = this._candidates.get(key)!.detail; } @@ -158,8 +160,8 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { get candidates(): TunnelItem[] { const candidates: TunnelItem[] = []; this._candidates.forEach(value => { - if (!mapHasTunnelLocalhostOrAllInterfaces(this.model.forwarded, value.host, value.port) && - !mapHasTunnelLocalhostOrAllInterfaces(this.model.detected, value.host, value.port)) { + if (!mapHasAddressLocalhostOrAllInterfaces(this.model.forwarded, value.host, value.port) && + !mapHasAddressLocalhostOrAllInterfaces(this.model.detected, value.host, value.port)) { candidates.push(new TunnelItem(TunnelType.Candidate, value.host, value.port, undefined, undefined, false, undefined, value.detail)); } }); @@ -179,6 +181,7 @@ interface ITunnelTemplateData { elementDisposable: IDisposable; container: HTMLElement; iconLabel: IconLabel; + icon: HTMLElement; actionBar: ActionBar; } @@ -211,6 +214,7 @@ class TunnelTreeRenderer extends Disposable implements ITreeRendererelement).items) { @@ -387,7 +405,7 @@ interface ITunnelGroup { class TunnelItem implements ITunnelItem { static createFromTunnel(tunnel: Tunnel, type: TunnelType = TunnelType.Forwarded, closeable?: boolean) { - return new TunnelItem(type, tunnel.remoteHost, tunnel.remotePort, tunnel.localAddress, tunnel.localPort, closeable === undefined ? tunnel.closeable : closeable, tunnel.name, tunnel.description); + return new TunnelItem(type, tunnel.remoteHost, tunnel.remotePort, tunnel.localAddress, tunnel.localPort, closeable === undefined ? tunnel.closeable : closeable, tunnel.name, tunnel.description ?? tunnel.runningProcess, tunnel.source); } constructor( @@ -399,6 +417,7 @@ class TunnelItem implements ITunnelItem { public closeable?: boolean, public name?: string, private _description?: string, + private source?: string ) { } get label(): string { if (this.name) { @@ -414,13 +433,23 @@ class TunnelItem implements ITunnelItem { if (address.length < 16) { return address; } - let url: URL | undefined; + let displayAddress: string = address; try { - url = new URL(address); + if (!address.startsWith('http')) { + address = `http://${address}`; + } + const url = new URL(address); + if (url && url.host) { + const lastDotIndex = url.host.lastIndexOf('.'); + const secondLastDotIndex = lastDotIndex !== -1 ? url.host.substring(0, lastDotIndex).lastIndexOf('.') : -1; + if (secondLastDotIndex !== -1) { + displayAddress = `${url.protocol}//...${url.host.substring(secondLastDotIndex + 1)}`; + } + } } catch (e) { // Address isn't a valid url and can't be compacted. } - return url && url.host.length > 0 ? url.host : address; + return displayAddress; } set description(description: string | undefined) { @@ -428,13 +457,30 @@ class TunnelItem implements ITunnelItem { } get description(): string | undefined { - if (this._description) { - return this._description; - } else if (this.name) { - return nls.localize('remote.tunnelsView.forwardedPortDescription0', "{0} \u2192 {1}", this.remotePort, this.localAddress); + const description: string[] = []; + + if (this.name && this.localAddress) { + description.push(nls.localize('remote.tunnelsView.forwardedPortDescription0', "{0} \u2192 {1}", this.remotePort, TunnelItem.compactLongAddress(this.localAddress))); } + + if (this._description) { + description.push(this._description); + } + + if (this.source) { + description.push(this.source); + } + + if (description.length > 0) { + return description.join(' \u2022 '); + } + return undefined; } + + get iconClasses(): string | undefined { + return this.tunnelType === TunnelType.Detected || this.tunnelType === TunnelType.Forwarded ? Codicon.plug.classNames : undefined; + } } export const TunnelTypeContextKey = new RawContextKey('tunnelType', TunnelType.Add); @@ -507,6 +553,7 @@ export class TunnelPanel extends ViewPane { const panelContainer = dom.append(container, dom.$('.tree-explorer-viewlet-tree-view')); const treeContainer = dom.append(panelContainer, dom.$('.customview-tree')); + treeContainer.classList.add('ports-view'); treeContainer.classList.add('file-icon-themable-tree', 'show-file-icons'); const renderer = new TunnelTreeRenderer(TunnelPanel.ID, this.menuService, this.contextKeyService, this.instantiationService, this.contextViewService, this.themeService, this.remoteExplorerService); @@ -682,9 +729,13 @@ export class TunnelPanelDescriptor implements IViewDescriptor { readonly canToggleVisibility = true; readonly hideByDefault = false; readonly workspace = true; + // group is not actually used for views that are not extension contributed. Use order instead. readonly group = 'details@0'; + // -500 comes from the remote explorer viewOrderDelegate + readonly order = -500; readonly remoteAuthority?: string | string[]; readonly canMoveView = true; + readonly containerIcon = Codicon.plug; constructor(viewModel: ITunnelViewModel, environmentService: IWorkbenchEnvironmentService) { this.ctorDescriptor = new SyncDescriptor(TunnelPanel, [viewModel]); @@ -740,16 +791,8 @@ export namespace ForwardPortAction { export const TREEITEM_LABEL = nls.localize('remote.tunnel.forwardItem', "Forward Port"); const forwardPrompt = nls.localize('remote.tunnel.forwardPrompt', "Port number or address (eg. 3000 or 10.10.10.10:2000)."); - function parseInput(value: string): { host: string, port: number } | undefined { - const matches = value.match(/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\:|localhost:)?([0-9]+)$/); - if (!matches) { - return undefined; - } - return { host: matches[1]?.substring(0, matches[1].length - 1) || 'localhost', port: Number(matches[2]) }; - } - function validateInput(value: string): string | null { - const parsed = parseInput(value); + const parsed = parseAddress(value); if (!parsed) { return invalidPortString; } else if (parsed.port >= maxPortNumber) { @@ -774,7 +817,7 @@ export namespace ForwardPortAction { remoteExplorerService.setEditable(undefined, { onFinish: async (value, success) => { let parsed: { host: string, port: number } | undefined; - if (success && (parsed = parseInput(value))) { + if (success && (parsed = parseAddress(value))) { remoteExplorerService.forward({ host: parsed.host, port: parsed.port }).then(tunnel => error(notificationService, tunnel, parsed!.host, parsed!.port)); } remoteExplorerService.setEditable(undefined, null); @@ -798,7 +841,7 @@ export namespace ForwardPortAction { validateInput: (value) => Promise.resolve(validateInput(value)) }); let parsed: { host: string, port: number } | undefined; - if (value && (parsed = parseInput(value))) { + if (value && (parsed = parseAddress(value))) { remoteExplorerService.forward({ host: parsed.host, port: parsed.port }).then(tunnel => error(notificationService, tunnel, parsed!.host, parsed!.port)); } }; @@ -864,10 +907,15 @@ export namespace OpenPortInBrowserAction { export function handler(): ICommandHandler { return async (accessor, arg) => { + let key: string | undefined; if (arg instanceof TunnelItem) { + key = makeAddress(arg.remoteHost, arg.remotePort); + } else if (arg.tunnelRemoteHost && arg.tunnelRemotePort) { + key = makeAddress(arg.tunnelRemoteHost, arg.tunnelRemotePort); + } + if (key) { const model = accessor.get(IRemoteExplorerService).tunnelModel; const openerService = accessor.get(IOpenerService); - const key = MakeAddress(arg.remoteHost, arg.remotePort); return run(model, openerService, key); } }; @@ -877,12 +925,56 @@ export namespace OpenPortInBrowserAction { const tunnel = model.forwarded.get(key) || model.detected.get(key); let address: string | undefined; if (tunnel && tunnel.localAddress && (address = model.address(tunnel.remoteHost, tunnel.remotePort))) { - return openerService.open(URI.parse('http://' + address)); + if (!address.startsWith('http')) { + address = `http://${address}`; + } + return openerService.open(URI.parse(address)); } return Promise.resolve(); } } +namespace OpenPortInBrowserCommandPaletteAction { + export const ID = 'remote.tunnel.openCommandPalette'; + export const LABEL = nls.localize('remote.tunnel.openCommandPalette', "Open Port in Browser"); + + interface QuickPickTunnel extends IQuickPickItem { + tunnel?: TunnelItem; + } + + export function handler(): ICommandHandler { + return async (accessor, arg) => { + const model = accessor.get(IRemoteExplorerService).tunnelModel; + const quickPickService = accessor.get(IQuickInputService); + const openerService = accessor.get(IOpenerService); + const commandService = accessor.get(ICommandService); + const options: QuickPickTunnel[] = [...model.forwarded, ...model.detected].map(value => { + const tunnelItem = TunnelItem.createFromTunnel(value[1]); + return { + label: tunnelItem.label, + description: tunnelItem.description, + tunnel: tunnelItem + }; + }); + if (options.length === 0) { + options.push({ + label: nls.localize('remote.tunnel.openCommandPaletteNone', "No ports currently forwarded. Open the Ports view to get started.") + }); + } else { + options.push({ + label: nls.localize('remote.tunnel.openCommandPaletteView', "Open the Ports view...") + }); + } + const picked = await quickPickService.pick(options, { placeHolder: nls.localize('remote.tunnel.openCommandPalettePick', "Choose the port to open") }); + if (picked && picked.tunnel) { + return OpenPortInBrowserAction.run(model, openerService, makeAddress(picked.tunnel.remoteHost, picked.tunnel.remotePort)); + } else if (picked) { + return commandService.executeCommand(`${TUNNEL_VIEW_ID}.focus`); + } + }; + } +} + namespace CopyAddressAction { export const INLINE_ID = 'remote.tunnel.copyAddressInline'; export const COMMANDPALETTE_ID = 'remote.tunnel.copyAddressCommandPalette'; @@ -923,18 +1015,6 @@ namespace CopyAddressAction { } } -namespace RefreshTunnelViewAction { - export const ID = 'remote.tunnel.refresh'; - export const LABEL = nls.localize('remote.tunnel.refreshView', "Refresh"); - - export function handler(): ICommandHandler { - return (accessor, arg) => { - const remoteExplorerService = accessor.get(IRemoteExplorerService); - return remoteExplorerService.refresh(); - }; - } -} - namespace ChangeLocalPortAction { export const ID = 'remote.tunnel.changeLocalPort'; export const LABEL = nls.localize('remote.tunnel.changeLocalPort', "Change Local Port"); @@ -962,7 +1042,7 @@ namespace ChangeLocalPortAction { const numberValue = Number(value); const newForward = await remoteExplorerService.forward({ host: context.remoteHost, port: context.remotePort }, numberValue, context.name); if (newForward && newForward.tunnelLocalPort !== numberValue) { - notificationService.warn(nls.localize('remote.tunnel.changeLocalPortNumber', "The local port {0} is not available. Port number {1} has been used instead", value, newForward.tunnelLocalPort)); + notificationService.warn(nls.localize('remote.tunnel.changeLocalPortNumber', "The local port {0} is not available. Port number {1} has been used instead", value, newForward.tunnelLocalPort ?? newForward.localAddress)); } } }, @@ -1001,6 +1081,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ CommandsRegistry.registerCommand(ClosePortAction.COMMANDPALETTE_ID, ClosePortAction.commandPaletteHandler()); CommandsRegistry.registerCommand(OpenPortInBrowserAction.ID, OpenPortInBrowserAction.handler()); +CommandsRegistry.registerCommand(OpenPortInBrowserCommandPaletteAction.ID, OpenPortInBrowserCommandPaletteAction.handler()); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CopyAddressAction.INLINE_ID, weight: KeybindingWeight.WorkbenchContrib + tunnelViewCommandsWeightBonus, @@ -1009,7 +1090,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: CopyAddressAction.inlineHandler() }); CommandsRegistry.registerCommand(CopyAddressAction.COMMANDPALETTE_ID, CopyAddressAction.commandPaletteHandler()); -CommandsRegistry.registerCommand(RefreshTunnelViewAction.ID, RefreshTunnelViewAction.handler()); CommandsRegistry.registerCommand(ChangeLocalPortAction.ID, ChangeLocalPortAction.handler()); MenuRegistry.appendMenuItem(MenuId.CommandPalette, ({ @@ -1033,22 +1113,20 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, ({ }, when: forwardedPortsViewEnabled })); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, ({ + command: { + id: OpenPortInBrowserCommandPaletteAction.ID, + title: OpenPortInBrowserCommandPaletteAction.LABEL + }, + when: forwardedPortsViewEnabled +})); MenuRegistry.appendMenuItem(MenuId.TunnelTitle, ({ group: 'navigation', order: 0, command: { id: ForwardPortAction.INLINE_ID, title: ForwardPortAction.LABEL, - icon: { id: 'codicon/plus' } - } -})); -MenuRegistry.appendMenuItem(MenuId.TunnelTitle, ({ - group: 'navigation', - order: 1, - command: { - id: RefreshTunnelViewAction.ID, - title: RefreshTunnelViewAction.LABEL, - icon: { id: 'codicon/refresh' } + icon: Codicon.plus } })); MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ @@ -1111,7 +1189,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ command: { id: OpenPortInBrowserAction.ID, title: OpenPortInBrowserAction.LABEL, - icon: { id: 'codicon/globe' } + icon: Codicon.globe }, when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Detected)) })); @@ -1120,7 +1198,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ command: { id: ForwardPortAction.INLINE_ID, title: ForwardPortAction.TREEITEM_LABEL, - icon: { id: 'codicon/plus' } + icon: Codicon.plus }, when: TunnelTypeContextKey.isEqualTo(TunnelType.Candidate) })); @@ -1129,7 +1207,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ command: { id: ClosePortAction.INLINE_ID, title: ClosePortAction.LABEL, - icon: { id: 'codicon/x' } + icon: Codicon.x }, when: TunnelCloseableContextKey })); diff --git a/src/vs/workbench/contrib/remote/browser/urlFinder.ts b/src/vs/workbench/contrib/remote/browser/urlFinder.ts index bc74bb759..e9c21167a 100644 --- a/src/vs/workbench/contrib/remote/browser/urlFinder.ts +++ b/src/vs/workbench/contrib/remote/browser/urlFinder.ts @@ -18,6 +18,12 @@ export class UrlFinder extends Disposable { * http://0.0.0.0:4000 - Elixir Phoenix */ private static readonly localUrlRegex = /\b\w{2,20}:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0|:\d{2,5})[\w\-\.\~:\/\?\#[\]\@!\$&\(\)\*\+\,\;\=]*/gim; + /** + * https://github.com/microsoft/vscode-remote-release/issues/3949 + */ + private static readonly localPythonServerRegex = /HTTP\son\s(127\.0\.0\.1|0\.0\.0\.0)\sport\s(\d+)/; + + private static readonly excludeTerminals = ['Dev Containers']; private _onDidMatchLocalUrl: Emitter<{ host: string, port: number }> = new Emitter(); public readonly onDidMatchLocalUrl = this._onDidMatchLocalUrl.event; @@ -27,14 +33,10 @@ export class UrlFinder extends Disposable { super(); // Terminal terminalService.terminalInstances.forEach(instance => { - this.listeners.set(instance, instance.onData(data => { - this.processData(data); - })); + this.registerTerminalInstance(instance); }); this._register(terminalService.onInstanceCreated(instance => { - this.listeners.set(instance, instance.onData(data => { - this.processData(data); - })); + this.registerTerminalInstance(instance); })); this._register(terminalService.onInstanceDisposed(instance => { this.listeners.get(instance)?.dispose(); @@ -57,6 +59,14 @@ export class UrlFinder extends Disposable { })); } + private registerTerminalInstance(instance: ITerminalInstance) { + if (!UrlFinder.excludeTerminals.includes(instance.title)) { + this.listeners.set(instance, instance.onData(data => { + this.processData(data); + })); + } + } + private replPositions: Map = new Map(); private processNewReplElements(session: IDebugSession) { const oldReplPosition = this.replPositions.get(session.getId()); @@ -90,25 +100,33 @@ export class UrlFinder extends Disposable { // strip ANSI terminal codes data = data.replace(UrlFinder.terminalCodesRegex, ''); const urlMatches = data.match(UrlFinder.localUrlRegex) || []; - urlMatches.forEach((match) => { - // check if valid url - const serverUrl = new URL(match); - if (serverUrl) { - // check if the port is a valid integer value - const port = parseFloat(serverUrl.port!); - if (!isNaN(port) && Number.isInteger(port) && port > 0 && port <= 65535) { - // normalize the host name - let host = serverUrl.hostname; - if (host !== '0.0.0.0' && host !== '127.0.0.1') { - host = 'localhost'; + if (urlMatches && urlMatches.length > 0) { + urlMatches.forEach((match) => { + // check if valid url + const serverUrl = new URL(match); + if (serverUrl) { + // check if the port is a valid integer value + const port = parseFloat(serverUrl.port!); + if (!isNaN(port) && Number.isInteger(port) && port > 0 && port <= 65535) { + // normalize the host name + let host = serverUrl.hostname; + if (host !== '0.0.0.0' && host !== '127.0.0.1') { + host = 'localhost'; + } + // Exclude node inspect, except when using default port + if (port !== 9229 && data.startsWith('Debugger listening on')) { + return; + } + this._onDidMatchLocalUrl.fire({ port, host }); } - // Exclude node inspect, except when using default port - if (port !== 9229 && data.startsWith('Debugger listening on')) { - return; - } - this._onDidMatchLocalUrl.fire({ port, host }); } + }); + } else { + // Try special python case + const pythonMatch = data.match(UrlFinder.localPythonServerRegex); + if (pythonMatch && pythonMatch.length === 3) { + this._onDidMatchLocalUrl.fire({ host: pythonMatch[1], port: Number(pythonMatch[2]) }); } - }); + } } } diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 1728f76ea..93911f192 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -131,7 +131,7 @@ Registry.as(ConfigurationExtensions.Configuration) }, 'remote.autoForwardPorts': { type: 'boolean', - markdownDescription: localize('remote.autoForwardPorts', "When enabled, URLs with ports (ex. `http://127.0.0.1:3000`) that are printed to your terminals are automatically forwarded."), + markdownDescription: localize('remote.autoForwardPorts', "When enabled, new running processes are detected and ports that they listen on are automatically forwarded."), default: true } } diff --git a/src/vs/workbench/contrib/remote/common/tunnelFactory.ts b/src/vs/workbench/contrib/remote/common/tunnelFactory.ts index ab8c80e67..6da6a2745 100644 --- a/src/vs/workbench/contrib/remote/common/tunnelFactory.ts +++ b/src/vs/workbench/contrib/remote/common/tunnelFactory.ts @@ -3,31 +3,39 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITunnelService, TunnelOptions, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService, TunnelOptions, RemoteTunnel, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; +import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService'; export class TunnelFactoryContribution extends Disposable implements IWorkbenchContribution { constructor( @ITunnelService tunnelService: ITunnelService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IOpenerService openerService: IOpenerService, + @IRemoteExplorerService remoteExplorerService: IRemoteExplorerService ) { super(); const tunnelFactory = environmentService.options?.tunnelProvider?.tunnelFactory; if (tunnelFactory) { this._register(tunnelService.setTunnelProvider({ - forwardPort: (tunnelOptions: TunnelOptions): Promise | undefined => { - const tunnelPromise = tunnelFactory(tunnelOptions); + forwardPort: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise | undefined => { + const tunnelPromise = tunnelFactory(tunnelOptions, tunnelCreationOptions); if (!tunnelPromise) { return undefined; } return new Promise(resolve => { - tunnelPromise.then(tunnel => { + tunnelPromise.then(async (tunnel) => { + const localAddress = tunnel.localAddress.startsWith('http') ? tunnel.localAddress : `http://${tunnel.localAddress}`; const remoteTunnel: RemoteTunnel = { tunnelRemotePort: tunnel.remoteAddress.port, tunnelRemoteHost: tunnel.remoteAddress.host, - localAddress: tunnel.localAddress, + // The tunnel factory may give us an inaccessible local address. + // To make sure this doesn't happen, resolve the uri immediately. + localAddress: (await openerService.resolveExternalUri(URI.parse(localAddress))).resolved.toString(), dispose: tunnel.dispose }; resolve(remoteTunnel); @@ -35,6 +43,7 @@ export class TunnelFactoryContribution extends Disposable implements IWorkbenchC }); } })); + remoteExplorerService.setTunnelInformation(undefined); } } } diff --git a/src/vs/workbench/contrib/sash/browser/sash.contribution.ts b/src/vs/workbench/contrib/sash/browser/sash.contribution.ts index c4b13519c..c63a2d4be 100644 --- a/src/vs/workbench/contrib/sash/browser/sash.contribution.ts +++ b/src/vs/workbench/contrib/sash/browser/sash.contribution.ts @@ -11,6 +11,8 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { SashSizeController, minSize, maxSize } from 'vs/workbench/contrib/sash/browser/sash'; import { isIPad } from 'vs/base/browser/browser'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { sashHoverBorder } from 'vs/platform/theme/common/colorRegistry'; // Sash size contribution Registry.as(WorkbenchExtensions.Workbench) @@ -30,3 +32,13 @@ Registry.as(ConfigurationExtensions.Configuration) }, } }); + +registerThemingParticipant((theme, collector) => { + const sashHoverBorderColor = theme.getColor(sashHoverBorder); + collector.addRule(` + .monaco-sash:hover, + .monaco-sash.active { + background: ${sashHoverBorderColor} + } + `); +}); diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index b826a548c..ffd41e575 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -19,7 +19,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { URI } from 'vs/base/common/uri'; import { ISCMService, ISCMRepository, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { registerThemingParticipant, IColorTheme, ICssStyleCollector, themeColorFromId, IThemeService } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, IColorTheme, ICssStyleCollector, themeColorFromId, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerColor, transparent } from 'vs/platform/theme/common/colorRegistry'; import { Color, RGBA } from 'vs/base/common/color'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; @@ -48,6 +48,7 @@ import { ISplice } from 'vs/base/common/sequence'; import { createStyleSheet } from 'vs/base/browser/dom'; import { ITextFileEditorModel, IResolvedTextFileEditorModel, ITextFileService, isTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { EncodingMode } from 'vs/workbench/common/editor'; +import { gotoNextLocation, gotoPreviousLocation } from 'vs/platform/theme/common/iconRegistry'; class DiffActionRunner extends ActionRunner { @@ -246,8 +247,8 @@ class DirtyDiffWidget extends PeekViewWidget { protected _fillHead(container: HTMLElement): void { super._fillHead(container); - const previous = this.instantiationService.createInstance(UIEditorAction, this.editor, new ShowPreviousChangeAction(), 'codicon-arrow-up'); - const next = this.instantiationService.createInstance(UIEditorAction, this.editor, new ShowNextChangeAction(), 'codicon-arrow-down'); + const previous = this.instantiationService.createInstance(UIEditorAction, this.editor, new ShowPreviousChangeAction(), ThemeIcon.asClassName(gotoPreviousLocation)); + const next = this.instantiationService.createInstance(UIEditorAction, this.editor, new ShowNextChangeAction(), ThemeIcon.asClassName(gotoNextLocation)); this._disposables.add(previous); this._disposables.add(next); @@ -563,20 +564,57 @@ export class DirtyDiffController extends Disposable implements IEditorContributi private session: IDisposable = Disposable.None; private mouseDownInfo: { lineNumber: number } | null = null; private enabled = false; + private gutterActionDisposables = new DisposableStore(); + private stylesheet: HTMLStyleElement; constructor( private editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); this.enabled = !contextKeyService.getContextKeyValue('isInDiffEditor'); + this.stylesheet = createStyleSheet(); + this._register(toDisposable(() => this.stylesheet.remove())); if (this.enabled) { this.isDirtyDiffVisible = isDirtyDiffVisible.bindTo(contextKeyService); - this._register(editor.onMouseDown(e => this.onEditorMouseDown(e))); - this._register(editor.onMouseUp(e => this.onEditorMouseUp(e))); this._register(editor.onDidChangeModel(() => this.close())); + + const onDidChangeGutterAction = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorationsGutterAction')); + this._register(onDidChangeGutterAction(this.onDidChangeGutterAction, this)); + this.onDidChangeGutterAction(); + } + } + + private onDidChangeGutterAction(): void { + const gutterAction = this.configurationService.getValue<'diff' | 'none'>('scm.diffDecorationsGutterAction'); + + this.gutterActionDisposables.dispose(); + this.gutterActionDisposables = new DisposableStore(); + + if (gutterAction === 'diff') { + this.gutterActionDisposables.add(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); + this.gutterActionDisposables.add(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); + this.stylesheet.textContent = ` + .monaco-editor .dirty-diff-glyph { + cursor: pointer; + } + + .monaco-editor .margin-view-overlays .dirty-diff-glyph:hover::before { + width: 9px; + left: -6px; + } + + .monaco-editor .margin-view-overlays .dirty-diff-deleted:hover::after { + bottom: 0; + border-top-width: 0; + border-bottom-width: 0; + } + `; + } else { + this.stylesheet.textContent = ``; } } @@ -798,6 +836,11 @@ export class DirtyDiffController extends Disposable implements IEditorContributi return model.changes; } + + dispose(): void { + this.gutterActionDisposables.dispose(); + super.dispose(); + } } export const editorGutterModifiedBackground = registerColor('editorGutter.modifiedBackground', { diff --git a/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css b/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css index 472114eed..dd0921f4d 100644 --- a/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css +++ b/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css @@ -5,7 +5,6 @@ .monaco-editor .dirty-diff-glyph { margin-left: 5px; - cursor: pointer; z-index: 5; } @@ -39,20 +38,6 @@ transition: height 80ms linear; } -.monaco-editor .margin-view-overlays .dirty-diff-glyph:hover::before { - position: absolute; - content: ''; - height: 100%; - width: 9px; - left: -6px; -} - -.monaco-editor .margin-view-overlays .dirty-diff-deleted:hover::after { - bottom: 0; - border-top-width: 0; - border-bottom-width: 0; -} - /* Hide glyph decorations when inside the inline diff editor */ .monaco-editor.modified-in-monaco-diff-editor .margin-view-overlays > div > .dirty-diff-glyph { display: none; diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index db8fb95f8..98a0f62f8 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -23,6 +23,7 @@ import { SCMViewPaneContainer } from 'vs/workbench/contrib/scm/browser/scmViewPa import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { SCMViewPane } from 'vs/workbench/contrib/scm/browser/scmViewPane'; import { SCMViewService } from 'vs/workbench/contrib/scm/browser/scmViewService'; import { SCMRepositoriesViewPane } from 'vs/workbench/contrib/scm/browser/scmRepositoriesViewPane'; @@ -38,12 +39,14 @@ ModesRegistry.registerLanguage({ Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(DirtyDiffWorkbenchController, LifecyclePhase.Restored); +const sourceControlViewIcon = registerIcon('source-control-view-icon', Codicon.sourceControl, localize('sourceControlViewIcon', 'View icon of the source control view.')); + const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: localize('source control', "Source Control"), ctorDescriptor: new SyncDescriptor(SCMViewPaneContainer), storageId: 'workbench.scm.views.state', - icon: Codicon.sourceControl.classNames, + icon: sourceControlViewIcon, alwaysUseContainerInfo: true, order: 2, hideIfEmpty: true @@ -65,7 +68,7 @@ viewsRegistry.registerViews([{ canMoveView: true, weight: 80, order: -999, - containerIcon: Codicon.sourceControl.classNames + containerIcon: sourceControlViewIcon }], viewContainer); viewsRegistry.registerViews([{ @@ -80,7 +83,7 @@ viewsRegistry.registerViews([{ order: -1000, when: ContextKeyExpr.and(ContextKeyExpr.has('scm.providerCount'), ContextKeyExpr.notEquals('scm.providerCount', 0)), // readonly when = ContextKeyExpr.or(ContextKeyExpr.equals('config.scm.alwaysShowProviders', true), ContextKeyExpr.and(ContextKeyExpr.notEquals('scm.providerCount', 0), ContextKeyExpr.notEquals('scm.providerCount', 1))); - containerIcon: Codicon.sourceControl.classNames + containerIcon: sourceControlViewIcon }], viewContainer); Registry.as(WorkbenchExtensions.Workbench) @@ -138,6 +141,16 @@ Registry.as(ConfigurationExtensions.Configuration).regis description: localize('scm.diffDecorationsGutterVisibility', "Controls the visibility of the Source Control diff decorator in the gutter."), default: 'always' }, + 'scm.diffDecorationsGutterAction': { + type: 'string', + enum: ['diff', 'none'], + enumDescriptions: [ + localize('scm.diffDecorationsGutterAction.diff', "Show the inline diff peek view on click."), + localize('scm.diffDecorationsGutterAction.none', "Do nothing.") + ], + description: localize('scm.diffDecorationsGutterAction', "Controls the behavior of Source Control diff gutter decorations."), + default: 'diff' + }, 'scm.alwaysShowActions': { type: 'boolean', description: localize('alwaysShowActions', "Controls whether inline actions are always visible in the Source Control view."), diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts index 75d93fa6e..ffcb36be9 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/scm'; -import { basename } from 'vs/base/common/resources'; import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; import { append, $ } from 'vs/base/browser/dom'; import { ISCMRepository, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; @@ -20,6 +19,8 @@ import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IListRenderer } from 'vs/base/browser/ui/list/list'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { basename } from 'vs/base/common/resources'; interface RepositoryTemplate { readonly label: HTMLElement; @@ -42,7 +43,8 @@ export class RepositoryRenderer implements ICompressibleTreeRenderer | ISCMResource; @@ -655,26 +656,30 @@ export class SCMTreeKeyboardNavigationLabelProvider implements ICompressibleKeyb } } +function getSCMResourceId(element: TreeElement): string { + if (ResourceTree.isResourceNode(element)) { + const group = element.context; + return `folder:${group.provider.id}/${group.id}/$FOLDER/${element.uri.toString()}`; + } else if (isSCMRepository(element)) { + const provider = element.provider; + return `repo:${provider.id}`; + } else if (isSCMInput(element)) { + const provider = element.repository.provider; + return `input:${provider.id}`; + } else if (isSCMResource(element)) { + const group = element.resourceGroup; + const provider = group.provider; + return `resource:${provider.id}/${group.id}/${element.sourceUri.toString()}`; + } else { + const provider = element.provider; + return `group:${provider.id}/${element.id}`; + } +} + class SCMResourceIdentityProvider implements IIdentityProvider { getId(element: TreeElement): string { - if (ResourceTree.isResourceNode(element)) { - const group = element.context; - return `folder:${group.provider.id}/${group.id}/$FOLDER/${element.uri.toString()}`; - } else if (isSCMRepository(element)) { - const provider = element.provider; - return `repo:${provider.id}`; - } else if (isSCMInput(element)) { - const provider = element.repository.provider; - return `input:${provider.id}`; - } else if (isSCMResource(element)) { - const group = element.resourceGroup; - const provider = group.provider; - return `resource:${provider.id}/${group.id}/${element.sourceUri.toString()}`; - } else { - const provider = element.provider; - return `group:${provider.id}/${element.id}`; - } + return getSCMResourceId(element); } } @@ -728,15 +733,24 @@ interface IRepositoryItem { dispose(): void; } +interface ITreeViewState { + readonly collapsed: string[]; +} + function isRepositoryItem(item: IRepositoryItem | IGroupItem): item is IRepositoryItem { return Array.isArray((item as IRepositoryItem).groupItems); } -function asTreeElement(node: IResourceNode, forceIncompressible: boolean): ICompressedTreeElement { +function asTreeElement(node: IResourceNode, forceIncompressible: boolean, viewState?: ITreeViewState): ICompressedTreeElement { + const element = (node.childrenCount === 0 && node.element) ? node.element : node; + const collapsed = viewState ? viewState.collapsed.indexOf(getSCMResourceId(element)) > -1 : false; + return { - element: (node.childrenCount === 0 && node.element) ? node.element : node, - children: Iterable.map(node.children, node => asTreeElement(node, false)), - incompressible: !!node.element || forceIncompressible + element, + children: Iterable.map(node.children, node => asTreeElement(node, false, viewState)), + incompressible: !!node.element || forceIncompressible, + collapsed, + collapsible: node.childrenCount > 0 }; } @@ -788,12 +802,21 @@ class ViewModel { } } + private _treeViewStateIsStale = false; + get treeViewState(): ITreeViewState | undefined { + if (this.visible && this._treeViewStateIsStale) { + this.updateViewState(); + this._treeViewStateIsStale = false; + } + + return this._treeViewState; + } + private items = new Map(); private visibilityDisposables = new DisposableStore(); private scrollTop: number | undefined; private alwaysShowRepositories = false; private firstVisible = true; - private repositoryCollapseStates: Map | undefined; private viewSubMenuAction: SCMViewSubMenuAction | undefined; private disposables = new DisposableStore(); @@ -802,6 +825,7 @@ class ViewModel { private inputRenderer: InputRenderer, private _mode: ViewModelMode, private _sortKey: ViewModelSortKey, + private _treeViewState: ITreeViewState | undefined, @IInstantiationService protected instantiationService: IInstantiationService, @IEditorService protected editorService: IEditorService, @IConfigurationService protected configurationService: IConfigurationService, @@ -815,6 +839,8 @@ class ViewModel { configurationService.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables); this.onDidChangeConfiguration(); + + this.disposables.add(this.tree.onDidChangeCollapseState(() => this._treeViewStateIsStale = true)); } private onDidChangeConfiguration(e?: IConfigurationChangeEvent): void { @@ -862,7 +888,7 @@ class ViewModel { } private createGroupItem(group: ISCMResourceGroup): IGroupItem { - const tree = new ResourceTree(group, group.provider.rootUri || URI.file('/')); + const tree = new ResourceTree(group, group.provider.rootUri || URI.file('/'), this.uriIdentityService.extUri); const resources: ISCMResource[] = [...group.elements]; const disposable = combinedDisposable( group.onDidChange(() => this.tree.refilter()), @@ -907,7 +933,6 @@ class ViewModel { this.visibilityDisposables = new DisposableStore(); this.scmViewService.onDidChangeVisibleRepositories(this._onDidChangeVisibleRepositories, this, this.visibilityDisposables); this._onDidChangeVisibleRepositories({ added: this.scmViewService.visibleRepositories, removed: Iterable.empty() }); - this.repositoryCollapseStates = undefined; if (typeof this.scrollTop === 'number') { this.tree.scrollTop = this.scrollTop; @@ -917,13 +942,7 @@ class ViewModel { this.editorService.onDidActiveEditorChange(this.onDidActiveEditorChange, this, this.visibilityDisposables); this.onDidActiveEditorChange(); } else { - if (this.items.size > 1) { - this.repositoryCollapseStates = new Map(); - - for (const [, item] of this.items) { - this.repositoryCollapseStates.set(item.element, this.tree.isCollapsed(item.element)); - } - } + this.updateViewState(); this.visibilityDisposables.dispose(); this._onDidChangeVisibleRepositories({ added: Iterable.empty(), removed: [...this.items.keys()] }); @@ -939,12 +958,12 @@ class ViewModel { if (!this.alwaysShowRepositories && (this.items.size === 1 && (!item || isRepositoryItem(item)))) { const item = Iterable.first(this.items.values())!; - this.tree.setChildren(null, this.render(item).children); + this.tree.setChildren(null, this.render(item, this._treeViewState).children); } else if (item) { - this.tree.setChildren(item.element, this.render(item).children); + this.tree.setChildren(item.element, this.render(item, this._treeViewState).children); } else { const items = coalesce(this.scmViewService.visibleRepositories.map(r => this.items.get(r))); - this.tree.setChildren(null, items.map(item => this.render(item))); + this.tree.setChildren(null, items.map(item => this.render(item, this._treeViewState))); } if (focusedInput) { @@ -958,7 +977,7 @@ class ViewModel { this._onDidChangeRepositoryCollapseState.fire(); } - private render(item: IRepositoryItem | IGroupItem): ICompressedTreeElement { + private render(item: IRepositoryItem | IGroupItem, treeViewState?: ITreeViewState): ICompressedTreeElement { if (isRepositoryItem(item)) { const children: ICompressedTreeElement[] = []; const hasSomeChanges = item.groupItems.some(item => item.element.elements.length > 0); @@ -968,20 +987,40 @@ class ViewModel { children.push({ element: item.element.input, incompressible: true, collapsible: false }); } - children.push(...item.groupItems.map(i => this.render(i))); + children.push(...item.groupItems.map(i => this.render(i, treeViewState))); } - const collapsed = this.repositoryCollapseStates?.get(item.element); + const collapsed = treeViewState ? treeViewState.collapsed.indexOf(getSCMResourceId(item.element)) > -1 : false; + return { element: item.element, children, incompressible: true, collapsed, collapsible: true }; } else { const children = this.mode === ViewModelMode.List ? Iterable.map(item.resources, element => ({ element, incompressible: true })) - : Iterable.map(item.tree.root.children, node => asTreeElement(node, true)); + : Iterable.map(item.tree.root.children, node => asTreeElement(node, true, treeViewState)); - return { element: item.element, children, incompressible: true, collapsible: true }; + const collapsed = treeViewState ? treeViewState.collapsed.indexOf(getSCMResourceId(item.element)) > -1 : false; + + return { element: item.element, children, incompressible: true, collapsed, collapsible: true }; } } + private updateViewState(): void { + const collapsed: string[] = []; + const visit = (node: ITreeNode) => { + if (node.element && node.collapsible && node.collapsed) { + collapsed.push(getSCMResourceId(node.element)); + } + + for (const child of node.children) { + visit(child); + } + }; + + visit(this.tree.getNode()); + + this._treeViewState = { collapsed }; + } + private onDidActiveEditorChange(): void { if (!this.configurationService.getValue('scm.autoReveal')) { return; @@ -1000,13 +1039,17 @@ class ViewModel { } for (const repository of this.scmViewService.visibleRepositories) { - const item = this.items.get(repository)!; + const item = this.items.get(repository); + + if (!item) { + continue; + } // go backwards from last group for (let j = item.groupItems.length - 1; j >= 0; j--) { const groupItem = item.groupItems[j]; const resource = this.mode === ViewModelMode.Tree - ? groupItem.tree.getNode(uri)?.element // TODO@Joao URI identity? + ? groupItem.tree.getNode(uri)?.element : groupItem.resources.find(r => this.uriIdentityService.extUri.isEqual(r.sourceUri, uri)); if (resource) { @@ -1020,12 +1063,14 @@ class ViewModel { } focus() { - for (const repository of this.scmViewService.visibleRepositories) { - const widget = this.inputRenderer.getRenderedInputWidget(repository.input); + if (this.tree.getFocus().length === 0) { + for (const repository of this.scmViewService.visibleRepositories) { + const widget = this.inputRenderer.getRenderedInputWidget(repository.input); - if (widget) { - widget.focus(); - return; + if (widget) { + widget.focus(); + return; + } } } @@ -1184,7 +1229,7 @@ export class ToggleViewModeAction extends Action { } private onDidChangeMode(mode: ViewModelMode): void { - const iconClass = mode === ViewModelMode.List ? 'codicon-list-tree' : 'codicon-list-flat'; + const iconClass = ThemeIcon.asClassName(mode === ViewModelMode.List ? Codicon.listTree : Codicon.listFlat); this.class = `scm-action toggle-view-mode ${iconClass}`; this.checked = this.viewModel.mode === this.mode; } @@ -1444,7 +1489,8 @@ class SCMInputWidget extends Disposable { padding: { top: 3, bottom: 3 }, quickSuggestions: false, scrollbar: { alwaysConsumeMouseWheel: false }, - overflowWidgetsDomNode + overflowWidgetsDomNode, + renderWhitespace: 'none' }; const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { @@ -1612,7 +1658,7 @@ class SCMCollapseAction extends Action { this.enabled = isAnyProviderCollapsible; this.allCollapsed = isAnyProviderCollapsible && this.viewModel.areAllProvidersCollapsed(); this.label = this.allCollapsed ? localize('expand all', "Expand All Repositories") : localize('collapse all', "Collapse All Repositories"); - this.class = this.allCollapsed ? Codicon.expandAll.classNames : Codicon.collapseAll.classNames; + this.class = ThemeIcon.asClassName(this.allCollapsed ? Codicon.expandAll : Codicon.collapseAll); } } @@ -1689,7 +1735,7 @@ export class SCMViewPane extends ViewPane { const actionRunner = new RepositoryPaneActionRunner(() => this.getSelectedResources()); this._register(actionRunner); - this._register(actionRunner.onDidBeforeRun(() => this.tree.domFocus())); + this._register(actionRunner.onBeforeRun(() => this.tree.domFocus())); const renderers: ICompressibleTreeRenderer[] = [ this.instantiationService.createInstance(RepositoryRenderer, actionViewItemProvider), @@ -1731,13 +1777,22 @@ export class SCMViewPane extends ViewPane { append(this.listContainer, overflowWidgetsDomNode); let viewMode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree; - const storageMode = this.storageService.get(`scm.viewMode`, StorageScope.WORKSPACE) as ViewModelMode; + const storageMode = this.storageService.get(`scm.viewMode`, StorageScope.WORKSPACE) as ViewModelMode; if (typeof storageMode === 'string') { viewMode = storageMode; } - this.viewModel = this.instantiationService.createInstance(ViewModel, this.tree, this.inputRenderer, viewMode, ViewModelSortKey.Path); + let viewState: ITreeViewState | undefined; + + const storageViewState = this.storageService.get(`scm.viewState`, StorageScope.WORKSPACE); + if (storageViewState) { + try { + viewState = JSON.parse(storageViewState); + } catch {/* noop */ } + } + + this.viewModel = this.instantiationService.createInstance(ViewModel, this.tree, this.inputRenderer, viewMode, ViewModelSortKey.Path, viewState); this._register(this.viewModel); this.listContainer.classList.add('file-icon-themable-tree'); @@ -1754,6 +1809,12 @@ export class SCMViewPane extends ViewPane { this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.alwaysShowRepositories'))(this.updateActions, this)); this.updateActions(); + + this._register(this.storageService.onWillSaveState(e => { + if (e.reason === WillSaveStateReason.SHUTDOWN) { + this.storageService.store(`scm.viewState`, JSON.stringify(this.viewModel.treeViewState), StorageScope.WORKSPACE, StorageTarget.MACHINE); + } + })); } private updateIndentStyles(theme: IFileIconTheme): void { @@ -1765,7 +1826,7 @@ export class SCMViewPane extends ViewPane { private onDidChangeMode(): void { this.updateIndentStyles(this.themeService.getFileIconTheme()); - this.storageService.store(`scm.viewMode`, this.viewModel.mode, StorageScope.WORKSPACE); + this.storageService.store(`scm.viewMode`, this.viewModel.mode, StorageScope.WORKSPACE, StorageTarget.USER); } layoutBody(height: number | undefined = this.layoutCache.height, width: number | undefined = this.layoutCache.width): void { @@ -1839,7 +1900,7 @@ export class SCMViewPane extends ViewPane { return this.viewModel.getViewActionsContext(); } - private async open(e: IOpenEvent): Promise { + private async open(e: IOpenEvent): Promise { if (!e.element) { return; } else if (isSCMRepository(e.element)) { @@ -1878,13 +1939,17 @@ export class SCMViewPane extends ViewPane { } // ISCMResource - await e.element.open(!!e.editorOptions.preserveFocus); + if (e.element.command?.id === API_OPEN_EDITOR_COMMAND_ID || e.element.command?.id === API_OPEN_DIFF_EDITOR_COMMAND_ID) { + await this.commandService.executeCommand(e.element.command.id, ...(e.element.command.arguments || []), e); + } else { + await e.element.open(!!e.editorOptions.preserveFocus); - if (e.editorOptions.pinned) { - const activeEditorPane = this.editorService.activeEditorPane; + if (e.editorOptions.pinned) { + const activeEditorPane = this.editorService.activeEditorPane; - if (activeEditorPane) { - activeEditorPane.group.pinEditor(activeEditorPane.input); + if (activeEditorPane) { + activeEditorPane.group.pinEditor(activeEditorPane.input); + } } } @@ -1937,7 +2002,7 @@ export class SCMViewPane extends ViewPane { } const actionRunner = new RepositoryPaneActionRunner(() => this.getSelectedResources()); - actionRunner.onDidBeforeRun(() => this.tree.domFocus()); + actionRunner.onBeforeRun(() => this.tree.domFocus()); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, diff --git a/src/vs/workbench/contrib/scm/browser/scmViewService.ts b/src/vs/workbench/contrib/scm/browser/scmViewService.ts index 2704f6a7b..78ac23a15 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewService.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewService.ts @@ -5,10 +5,21 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { ISCMViewService, ISCMRepository, ISCMService, ISCMViewVisibleRepositoryChangeEvent, ISCMMenus } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMViewService, ISCMRepository, ISCMService, ISCMViewVisibleRepositoryChangeEvent, ISCMMenus, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; import { Iterable } from 'vs/base/common/iterator'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { SCMMenus } from 'vs/workbench/contrib/scm/browser/menus'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { debounce } from 'vs/base/common/decorators'; + +function getProviderStorageKey(provider: ISCMProvider): string { + return `${provider.contextValue}:${provider.label}${provider.rootUri ? `:${provider.rootUri.toString()}` : ''}`; +} + +export interface ISCMViewServiceState { + readonly all: string[]; + readonly visible: number[]; +} export class SCMViewService implements ISCMViewService { @@ -16,6 +27,9 @@ export class SCMViewService implements ISCMViewService { readonly menus: ISCMMenus; + private didFinishLoading: boolean = false; + private provisionalVisibleRepository: ISCMRepository | undefined; + private previousState: ISCMViewServiceState | undefined; private disposables = new DisposableStore(); private _visibleRepositoriesSet = new Set(); @@ -84,7 +98,8 @@ export class SCMViewService implements ISCMViewService { constructor( @ISCMService private readonly scmService: ISCMService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IStorageService private readonly storageService: IStorageService ) { this.menus = instantiationService.createInstance(SCMMenus); @@ -94,13 +109,63 @@ export class SCMViewService implements ISCMViewService { for (const repository of scmService.repositories) { this.onDidAddRepository(repository); } + + try { + this.previousState = JSON.parse(storageService.get('scm:view:visibleRepositories', StorageScope.WORKSPACE, '')); + this.eventuallyFinishLoading(); + } catch { + // noop + } + + storageService.onWillSaveState(this.onWillSaveState, this, this.disposables); } private onDidAddRepository(repository: ISCMRepository): void { + if (!this.didFinishLoading) { + this.eventuallyFinishLoading(); + } + + let removed: Iterable = Iterable.empty(); + + if (this.previousState) { + const index = this.previousState.all.indexOf(getProviderStorageKey(repository.provider)); + + if (index === -1) { // saw a repo we did not expect + const added: ISCMRepository[] = []; + for (const repo of this.scmService.repositories) { // all should be visible + if (!this._visibleRepositoriesSet.has(repo)) { + added.push(repository); + } + } + + this._visibleRepositories = [...this.scmService.repositories]; + this._visibleRepositoriesSet = new Set(this.scmService.repositories); + this._onDidChangeRepositories.fire({ added, removed: Iterable.empty() }); + this.finishLoading(); + return; + } + + const visible = this.previousState.visible.indexOf(index) > -1; + + if (!visible) { + if (this._visibleRepositories.length === 0) { // should make it visible, until other repos come along + this.provisionalVisibleRepository = repository; + } else { + return; + } + } else { + if (this.provisionalVisibleRepository) { + this._visibleRepositories = []; + this._visibleRepositoriesSet = new Set(); + removed = [this.provisionalVisibleRepository]; + this.provisionalVisibleRepository = undefined; + } + } + } + this._visibleRepositories.push(repository); this._visibleRepositoriesSet.add(repository); - - this._onDidChangeRepositories.fire({ added: [repository], removed: Iterable.empty() }); + this._onDidChangeRepositories.fire({ added: [repository], removed }); if (!this._focusedRepository) { this.focus(repository); @@ -108,6 +173,10 @@ export class SCMViewService implements ISCMViewService { } private onDidRemoveRepository(repository: ISCMRepository): void { + if (!this.didFinishLoading) { + this.eventuallyFinishLoading(); + } + const index = this._visibleRepositories.indexOf(repository); if (index > -1) { @@ -166,6 +235,32 @@ export class SCMViewService implements ISCMViewService { this._onDidFocusRepository.fire(repository); } + private onWillSaveState(): void { + if (!this.didFinishLoading) { // don't remember state, if the workbench didn't really finish loading + return; + } + + const all = this.scmService.repositories.map(r => getProviderStorageKey(r.provider)); + const visible = this.visibleRepositories.map(r => all.indexOf(getProviderStorageKey(r.provider))); + const raw = JSON.stringify({ all, visible }); + + this.storageService.store('scm:view:visibleRepositories', raw, StorageScope.WORKSPACE, StorageTarget.MACHINE); + } + + @debounce(2000) + private eventuallyFinishLoading(): void { + this.finishLoading(); + } + + private finishLoading(): void { + if (this.didFinishLoading) { + return; + } + + this.didFinishLoading = true; + this.previousState = undefined; + } + dispose(): void { this.disposables.dispose(); this._onDidChangeRepositories.dispose(); diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 7ce0990af..407c50843 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -34,7 +34,8 @@ export interface ISCMResource { readonly resourceGroup: ISCMResourceGroup; readonly sourceUri: URI; readonly decorations: ISCMResourceDecorations; - readonly contextValue?: string; + readonly contextValue: string | undefined; + readonly command: Command | undefined; open(preserveFocus: boolean): Promise; } diff --git a/src/vs/workbench/contrib/scm/common/scmService.ts b/src/vs/workbench/contrib/scm/common/scmService.ts index 43da9ea76..48af74fb9 100644 --- a/src/vs/workbench/contrib/scm/common/scmService.ts +++ b/src/vs/workbench/contrib/scm/common/scmService.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository, IInputValidator, ISCMInputChangeEvent, SCMInputChangeReason } from './scm'; import { ILogService } from 'vs/platform/log/common/log'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { HistoryNavigator2 } from 'vs/base/common/history'; class SCMInput implements ISCMInput { @@ -97,7 +97,7 @@ class SCMInput implements ISCMInput { } if (this.repository.provider.rootUri) { - this.storageService.store(historyKey, JSON.stringify([...this.historyNavigator]), StorageScope.GLOBAL); + this.storageService.store(historyKey, JSON.stringify([...this.historyNavigator]), StorageScope.GLOBAL, StorageTarget.USER); } }); } diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index 83a6db8d7..c85764d07 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -24,6 +24,8 @@ import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/b import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { mergeSort } from 'vs/base/common/arrays'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { dirname } from 'vs/base/common/resources'; const REPLACE_PREVIEW = 'replacePreview'; @@ -93,7 +95,8 @@ export class ReplaceService implements IReplaceService { @ITextFileService private readonly textFileService: ITextFileService, @IEditorService private readonly editorService: IEditorService, @ITextModelService private readonly textModelResolverService: ITextModelService, - @IBulkEditService private readonly bulkEditorService: IBulkEditService + @IBulkEditService private readonly bulkEditorService: IBulkEditService, + @ILabelService private readonly labelService: ILabelService ) { } replace(match: Match): Promise; @@ -113,6 +116,7 @@ export class ReplaceService implements IReplaceService { leftResource: fileMatch.resource, rightResource: toReplaceResource(fileMatch.resource), label: nls.localize('fileReplaceChanges', "{0} ↔ {1} (Replace Preview)", fileMatch.name(), fileMatch.name()), + description: this.labelService.getUriLabel(dirname(fileMatch.resource), { relative: true }), options: { preserveFocus, pinned, diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 041daae25..b0ccc8847 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -28,8 +28,8 @@ import { defaultQuickAccessContextKeyValue } from 'vs/workbench/browser/quickacc import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { Extensions as ViewExtensions, IViewsRegistry, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; -import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; -import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition, IExplorerService, VIEWLET_ID as VIEWLET_ID_FILES } from 'vs/workbench/contrib/files/common/files'; +import { getMultiSelectedResources, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; +import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition, VIEWLET_ID as VIEWLET_ID_FILES } from 'vs/workbench/contrib/files/common/files'; import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions'; import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, togglePreserveCaseCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction, ExpandAllAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; @@ -509,11 +509,11 @@ const viewContainer = Registry.as(ViewExtensions.ViewCo name: nls.localize('name', "Search"), ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), hideIfEmpty: true, - icon: searchViewIcon.classNames, + icon: searchViewIcon, order: 1 }, ViewContainerLocation.Sidebar); -const viewDescriptor = { id: VIEW_ID, containerIcon: 'codicon-search', name: nls.localize('search', "Search"), ctorDescriptor: new SyncDescriptor(SearchView), canToggleVisibility: false, canMoveView: true }; +const viewDescriptor = { id: VIEW_ID, containerIcon: searchViewIcon, name: nls.localize('search', "Search"), ctorDescriptor: new SyncDescriptor(SearchView), canToggleVisibility: false, canMoveView: true }; // Register search default location to sidebar Registry.as(ViewExtensions.ViewsRegistry).registerViews([viewDescriptor], viewContainer); @@ -824,7 +824,7 @@ configurationRegistry.registerConfiguration({ nls.localize('search.actionsPositionAuto', "Position the actionbar to the right when the search view is narrow, and immediately after the content when the search view is wide."), nls.localize('search.actionsPositionRight', "Always position the actionbar to the right."), ], - default: 'auto', + default: 'right', description: nls.localize('search.actionsPosition', "Controls the positioning of the actionbar on rows in the search view.") }, 'search.searchOnType': { @@ -861,7 +861,7 @@ configurationRegistry.registerConfiguration({ 'search.searchEditor.reusePriorSearchConfiguration': { type: 'boolean', default: false, - markdownDescription: nls.localize({ key: 'search.searchEditor.reusePriorSearchConfiguration', comment: ['"Search Editor" is a type that editor that can display search results. "includes, excludes, and flags" just refers to settings that affect search. For example, the "search.exclude" setting.'] }, "When enabled, new Search Editors will reuse the includes, excludes, and flags of the previously opened Search Editor") + markdownDescription: nls.localize({ key: 'search.searchEditor.reusePriorSearchConfiguration', comment: ['"Search Editor" is a type of editor that can display search results. "includes, excludes, and flags" refers to the "files to include" and "files to exclude" input boxes, and the flags that control whether a query is case-sensitive or a regex.'] }, "When enabled, new Search Editors will reuse the includes, excludes, and flags of the previously opened Search Editor") }, 'search.searchEditor.defaultNumberOfContextLines': { type: ['number', 'null'], diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index d2f108408..4e136901b 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -29,6 +29,7 @@ import { SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/sea import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; import { searchRefreshIcon, searchCollapseAllIcon, searchExpandAllIcon, searchClearIcon, searchReplaceAllIcon, searchReplaceIcon, searchRemoveIcon, searchStopIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export function isSearchViewFocused(viewsService: IViewsService): boolean { const searchView = getSearchView(viewsService); @@ -153,7 +154,7 @@ export abstract class FindOrReplaceInFilesAction extends Action { const searchAndReplaceWidget = openedView.searchAndReplaceWidget; searchAndReplaceWidget.toggleReplace(this.expandSearchReplaceWidget); - const updatedText = openedView.updateTextFromSelection({ allowUnselectedWord: !this.expandSearchReplaceWidget }); + const updatedText = openedView.updateTextFromFindWidgetOrSelection({ allowUnselectedWord: !this.expandSearchReplaceWidget }); openedView.searchAndReplaceWidget.focus(undefined, updatedText, updatedText); } }); @@ -182,7 +183,7 @@ export const FindInFilesCommand: ICommandHandler = (accessor, args: IFindInFiles if (typeof args.query === 'string') { openedView.setSearchParameters(args); } else { - updatedText = openedView.updateTextFromSelection({ allowUnselectedWord: typeof args.replace !== 'string' }); + updatedText = openedView.updateTextFromFindWidgetOrSelection({ allowUnselectedWord: typeof args.replace !== 'string' }); } openedView.searchAndReplaceWidget.focus(undefined, updatedText, updatedText); } @@ -279,7 +280,7 @@ export class RefreshAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'search-action ' + searchRefreshIcon.classNames); + super(id, label, 'search-action ' + ThemeIcon.asClassName(searchRefreshIcon)); } get enabled(): boolean { @@ -309,7 +310,7 @@ export class CollapseDeepestExpandedLevelAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'search-action ' + searchCollapseAllIcon.classNames); + super(id, label, 'search-action ' + ThemeIcon.asClassName(searchCollapseAllIcon)); this.update(); } @@ -365,7 +366,7 @@ export class ExpandAllAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'search-action ' + searchExpandAllIcon.classNames); + super(id, label, 'search-action ' + ThemeIcon.asClassName(searchExpandAllIcon)); this.update(); } @@ -449,7 +450,7 @@ export class ClearSearchResultsAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'search-action ' + searchClearIcon.classNames); + super(id, label, 'search-action ' + ThemeIcon.asClassName(searchClearIcon)); this.update(); } @@ -475,7 +476,7 @@ export class CancelSearchAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'search-action ' + searchStopIcon.classNames); + super(id, label, 'search-action ' + ThemeIcon.asClassName(searchStopIcon)); this.update(); } @@ -610,7 +611,7 @@ export class RemoveAction extends AbstractSearchAndReplaceAction { private viewer: WorkbenchObjectTree, private element: RenderableMatch ) { - super('remove', RemoveAction.LABEL, searchRemoveIcon.classNames); + super('remove', RemoveAction.LABEL, ThemeIcon.asClassName(searchRemoveIcon)); } run(): Promise { @@ -650,7 +651,7 @@ export class ReplaceAllAction extends AbstractSearchAndReplaceAction { private fileMatch: FileMatch, @IKeybindingService keyBindingService: IKeybindingService ) { - super(Constants.ReplaceAllInFileActionId, appendKeyBindingLabel(ReplaceAllAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFileActionId), keyBindingService), searchReplaceAllIcon.classNames); + super(Constants.ReplaceAllInFileActionId, appendKeyBindingLabel(ReplaceAllAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFileActionId), keyBindingService), ThemeIcon.asClassName(searchReplaceAllIcon)); } run(): Promise { @@ -674,7 +675,7 @@ export class ReplaceAllInFolderAction extends AbstractSearchAndReplaceAction { constructor(private viewer: WorkbenchObjectTree, private folderMatch: FolderMatch, @IKeybindingService keyBindingService: IKeybindingService ) { - super(Constants.ReplaceAllInFolderActionId, appendKeyBindingLabel(ReplaceAllInFolderAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFolderActionId), keyBindingService), searchReplaceAllIcon.classNames); + super(Constants.ReplaceAllInFolderActionId, appendKeyBindingLabel(ReplaceAllInFolderAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFolderActionId), keyBindingService), ThemeIcon.asClassName(searchReplaceAllIcon)); } run(): Promise { @@ -701,7 +702,7 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction { @IConfigurationService private readonly configurationService: IConfigurationService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { - super(Constants.ReplaceActionId, appendKeyBindingLabel(ReplaceAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceActionId), keyBindingService), searchReplaceIcon.classNames); + super(Constants.ReplaceActionId, appendKeyBindingLabel(ReplaceAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceActionId), keyBindingService), ThemeIcon.asClassName(searchReplaceIcon)); } async run(): Promise { diff --git a/src/vs/workbench/contrib/search/browser/searchIcons.ts b/src/vs/workbench/contrib/search/browser/searchIcons.ts index 2138fbaa3..8c6d633bd 100644 --- a/src/vs/workbench/contrib/search/browser/searchIcons.ts +++ b/src/vs/workbench/contrib/search/browser/searchIcons.ts @@ -3,25 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -export const searchDetailsIcon = registerIcon('search-details', Codicon.ellipsis); +export const searchDetailsIcon = registerIcon('search-details', Codicon.ellipsis, localize('searchDetailsIcon', 'Icon to make search details visible.')); -export const searchShowContextIcon = registerIcon('search-show-context', Codicon.listSelection); -export const searchHideReplaceIcon = registerIcon('search-hide-replace', Codicon.chevronRight); -export const searchShowReplaceIcon = registerIcon('search-show-replace', Codicon.chevronDown); -export const searchReplaceAllIcon = registerIcon('search-replace-all', Codicon.replaceAll); -export const searchReplaceIcon = registerIcon('search-replace', Codicon.replace); -export const searchRemoveIcon = registerIcon('search-remove', Codicon.close); +export const searchShowContextIcon = registerIcon('search-show-context', Codicon.listSelection, localize('searchShowContextIcon', 'Icon for toggle the context in the search editor.')); +export const searchHideReplaceIcon = registerIcon('search-hide-replace', Codicon.chevronRight, localize('searchHideReplaceIcon', 'Icon to collapse the replace section in the search view.')); +export const searchShowReplaceIcon = registerIcon('search-show-replace', Codicon.chevronDown, localize('searchShowReplaceIcon', 'Icon to expand the replace section in the search view.')); +export const searchReplaceAllIcon = registerIcon('search-replace-all', Codicon.replaceAll, localize('searchReplaceAllIcon', 'Icon for replace all in the search view.')); +export const searchReplaceIcon = registerIcon('search-replace', Codicon.replace, localize('searchReplaceIcon', 'Icon for replace in the search view.')); +export const searchRemoveIcon = registerIcon('search-remove', Codicon.close, localize('searchRemoveIcon', 'Icon to remove a search result.')); -export const searchRefreshIcon = registerIcon('search-refresh', Codicon.refresh); -export const searchCollapseAllIcon = registerIcon('search-collapse-results', Codicon.collapseAll); -export const searchExpandAllIcon = registerIcon('search-expand-results', Codicon.expandAll); -export const searchClearIcon = registerIcon('search-clear-results', Codicon.clearAll); -export const searchStopIcon = Codicon.searchStop; +export const searchRefreshIcon = registerIcon('search-refresh', Codicon.refresh, localize('searchRefreshIcon', 'Icon for refresh in the search view.')); +export const searchCollapseAllIcon = registerIcon('search-collapse-results', Codicon.collapseAll, localize('searchCollapseAllIcon', 'Icon for collapse results in the search view.')); +export const searchExpandAllIcon = registerIcon('search-expand-results', Codicon.expandAll, localize('searchExpandAllIcon', 'Icon for expand results in the search view.')); +export const searchClearIcon = registerIcon('search-clear-results', Codicon.clearAll, localize('searchClearIcon', 'Icon for clear results in the search view.')); +export const searchStopIcon = registerIcon('search-stop', Codicon.searchStop, localize('searchStopIcon', 'Icon for stop in the search view.')); -export const searchViewIcon = Codicon.search; - -export const searchNewEditorIcon = registerIcon('search-new-editor', Codicon.newFile); -export const searchGotoFileIcon = registerIcon('search-goto-file', Codicon.goToFile); +export const searchViewIcon = registerIcon('search-view-icon', Codicon.search, localize('searchViewIcon', 'View icon of the search view.')); +export const searchNewEditorIcon = registerIcon('search-new-editor', Codicon.newFile, localize('searchNewEditorIcon', 'Icon for the action to open a new search editor.')); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 71a2cae44..5a42e7b88 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -22,6 +22,7 @@ import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchview'; import { ICodeEditor, isCodeEditor, isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { CommonFindController } from 'vs/editor/contrib/find/findController'; import * as nls from 'vs/nls'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; @@ -37,7 +38,7 @@ import { IProgressService, IProgressStep, IProgress } from 'vs/platform/progress import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ITextQuery, SearchSortOrder, SearchCompletionExitCode } from 'vs/workbench/services/search/common/search'; import { ISearchHistoryService, ISearchHistoryValues } from 'vs/workbench/contrib/search/common/searchHistoryService'; import { diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, editorFindMatchHighlight, editorFindMatchHighlightBorder, listActiveSelectionForeground, foreground } from 'vs/platform/theme/common/colorRegistry'; -import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ResourceLabels } from 'vs/workbench/browser/labels'; @@ -59,7 +60,7 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { MultiCursorSelectionController } from 'vs/editor/contrib/multicursor/multicursor'; import { Selection } from 'vs/editor/common/core/selection'; @@ -219,7 +220,7 @@ export class SearchView extends ViewPane { this.viewModel = this._register(this.searchWorkbenchService.searchModel); this.queryBuilder = this.instantiationService.createInstance(QueryBuilder); this.memento = new Memento(this.id, storageService); - this.viewletState = this.memento.getMemento(StorageScope.WORKSPACE); + this.viewletState = this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.USER); this._register(this.fileService.onDidFilesChange(e => this.onFilesChanged(e))); this._register(this.textFileService.untitled.onDidDispose(model => this.onUntitledDidDispose(model.resource))); @@ -282,7 +283,7 @@ export class SearchView extends ViewPane { // Toggle query details button this.toggleQueryDetailsButton = dom.append(this.queryDetails, - $('.more' + searchDetailsIcon.cssSelector, { tabindex: 0, role: 'button', title: nls.localize('moreSearch', "Toggle Search Details") })); + $('.more' + ThemeIcon.asCSSSelector(searchDetailsIcon), { tabindex: 0, role: 'button', title: nls.localize('moreSearch', "Toggle Search Details") })); this._register(dom.addDisposableListener(this.toggleQueryDetailsButton, dom.EventType.CLICK, e => { dom.EventHelper.stop(e); @@ -888,28 +889,63 @@ export class SearchView extends ViewPane { } } - updateTextFromSelection({ allowUnselectedWord = true, allowSearchOnType = true }): boolean { - let updatedText = false; - const seedSearchStringFromSelection = this.configurationService.getValue('editor').find!.seedSearchStringFromSelection; - if (seedSearchStringFromSelection) { - let selectedText = this.getSearchTextFromEditor(allowUnselectedWord); - if (selectedText) { - if (this.searchWidget.searchInput.getRegex()) { - selectedText = strings.escapeRegExpCharacters(selectedText); - } - - if (allowSearchOnType && !this.viewModel.searchResult.isDirty) { - this.searchWidget.setValue(selectedText); - } else { - this.pauseSearching = true; - this.searchWidget.setValue(selectedText); - this.pauseSearching = false; - } - updatedText = true; + updateTextFromFindWidgetOrSelection({ allowUnselectedWord = true, allowSearchOnType = true }): boolean { + const activeEditor = this.editorService.activeTextEditorControl; + if (isCodeEditor(activeEditor)) { + const controller = CommonFindController.get(activeEditor as ICodeEditor); + if (controller.isFindInputFocused()) { + return this.updateTextFromFindWidget(controller, { allowSearchOnType }); } } - return updatedText; + return this.updateTextFromSelection({ allowUnselectedWord, allowSearchOnType }); + } + + private updateTextFromFindWidget(controller: CommonFindController, { allowSearchOnType = true }): boolean { + if (!this.searchConfig.seedWithNearestWord && (window.getSelection()?.toString() ?? '') === '') { + return false; + } + + const searchString = controller.getState().searchString; + if (searchString === '') { + return false; + } + + this.searchWidget.searchInput.setCaseSensitive(controller.getState().matchCase); + this.searchWidget.searchInput.setWholeWords(controller.getState().wholeWord); + this.searchWidget.searchInput.setRegex(controller.getState().isRegex); + this.updateText(searchString, allowSearchOnType); + + return true; + } + + private updateTextFromSelection({ allowUnselectedWord = true, allowSearchOnType = true }): boolean { + const seedSearchStringFromSelection = this.configurationService.getValue('editor').find!.seedSearchStringFromSelection; + if (!seedSearchStringFromSelection) { + return false; + } + + let selectedText = this.getSearchTextFromEditor(allowUnselectedWord); + if (selectedText === null) { + return false; + } + + if (this.searchWidget.searchInput.getRegex()) { + selectedText = strings.escapeRegExpCharacters(selectedText); + } + + this.updateText(selectedText, allowSearchOnType); + return true; + } + + private updateText(text: string, allowSearchOnType: boolean = true) { + if (allowSearchOnType && !this.viewModel.searchResult.isDirty) { + this.searchWidget.setValue(text); + } else { + this.pauseSearching = true; + this.searchWidget.setValue(text); + this.pauseSearching = false; + } } focusNextInputBox(): void { diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index c566e5d38..a7dbe426c 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -25,7 +25,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; import { attachFindReplaceInputBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; import { appendKeyBindingLabel, isSearchViewFocused, getSearchView } from 'vs/workbench/contrib/search/browser/searchActions'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; @@ -34,6 +34,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { IViewsService } from 'vs/workbench/common/views'; import { searchReplaceAllIcon, searchHideReplaceIcon, searchShowContextIcon, searchShowReplaceIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; +import { ToggleSearchEditorContextLinesCommandId } from 'vs/workbench/contrib/searchEditor/browser/constants'; /** Specified in searchview.css */ export const SingleLineInputHeight = 24; @@ -56,7 +57,7 @@ class ReplaceAllAction extends Action { static readonly ID: string = 'search.action.replaceAll'; constructor(private _searchWidget: SearchWidget) { - super(ReplaceAllAction.ID, '', searchReplaceAllIcon.classNames, false); + super(ReplaceAllAction.ID, '', ThemeIcon.asClassName(searchReplaceAllIcon), false); } set searchWidget(searchWidget: SearchWidget) { @@ -293,9 +294,9 @@ export class SearchWidget extends Widget { }; this.toggleReplaceButton = this._register(new Button(parent, opts)); this.toggleReplaceButton.element.setAttribute('aria-expanded', 'false'); - this.toggleReplaceButton.element.classList.add(...searchHideReplaceIcon.classNamesArray); - this.toggleReplaceButton.icon = 'toggle-replace-button'; - // TODO@joh need to dispose this listener eventually + this.toggleReplaceButton.element.classList.add('toggle-replace-button'); + this.toggleReplaceButton.icon = ThemeIcon.asCSSIcon(searchHideReplaceIcon); + // TODO@joao need to dispose this listener eventually this.toggleReplaceButton.onDidClick(() => this.onToggleReplaceButton()); this.toggleReplaceButton.element.title = nls.localize('search.replace.toggle.button.title', "Toggle Replace"); } @@ -351,7 +352,11 @@ export class SearchWidget extends Widget { this._register(this.searchInputFocusTracker.onDidBlur(() => this.searchInputBoxFocused.set(false))); - this.showContextCheckbox = new Checkbox({ isChecked: false, title: nls.localize('showContext', "Show Context"), icon: searchShowContextIcon }); + this.showContextCheckbox = new Checkbox({ + isChecked: false, + title: appendKeyBindingLabel(nls.localize('showContext', "Toggle Context Lines"), this.keyBindingService.lookupKeybinding(ToggleSearchEditorContextLinesCommandId), this.keyBindingService), + icon: ThemeIcon.asCSSIcon(searchShowContextIcon) + }); this._register(this.showContextCheckbox.onChange(() => this.onContextLinesChanged())); if (options.showContextToggle) { @@ -431,11 +436,11 @@ export class SearchWidget extends Widget { private onToggleReplaceButton(): void { this.replaceContainer.classList.toggle('disabled'); if (this.isReplaceShown()) { - this.toggleReplaceButton.element.classList.remove(...searchHideReplaceIcon.classNamesArray); - this.toggleReplaceButton.element.classList.add(...searchShowReplaceIcon.classNamesArray); + this.toggleReplaceButton.element.classList.remove(...ThemeIcon.asClassNameArray(searchHideReplaceIcon)); + this.toggleReplaceButton.element.classList.add(...ThemeIcon.asClassNameArray(searchShowReplaceIcon)); } else { - this.toggleReplaceButton.element.classList.remove(...searchShowReplaceIcon.classNamesArray); - this.toggleReplaceButton.element.classList.add(...searchHideReplaceIcon.classNamesArray); + this.toggleReplaceButton.element.classList.remove(...ThemeIcon.asClassNameArray(searchShowReplaceIcon)); + this.toggleReplaceButton.element.classList.add(...ThemeIcon.asClassNameArray(searchHideReplaceIcon)); } this.toggleReplaceButton.element.setAttribute('aria-expanded', this.isReplaceShown() ? 'true' : 'false'); this.updateReplaceActiveState(); diff --git a/src/vs/workbench/contrib/search/common/searchHistoryService.ts b/src/vs/workbench/contrib/search/common/searchHistoryService.ts index bb5a9209e..018799b96 100644 --- a/src/vs/workbench/contrib/search/common/searchHistoryService.ts +++ b/src/vs/workbench/contrib/search/common/searchHistoryService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { isEmptyObject } from 'vs/base/common/types'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -61,7 +61,7 @@ export class SearchHistoryService implements ISearchHistoryService { if (isEmptyObject(history)) { this.storageService.remove(SearchHistoryService.SEARCH_HISTORY_KEY, StorageScope.WORKSPACE); } else { - this.storageService.store(SearchHistoryService.SEARCH_HISTORY_KEY, JSON.stringify(history), StorageScope.WORKSPACE); + this.storageService.store(SearchHistoryService.SEARCH_HISTORY_KEY, JSON.stringify(history), StorageScope.WORKSPACE, StorageTarget.USER); } } } diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 0c6e0d194..3c7044d06 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -31,6 +31,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { compareFileNames, compareFileExtensions, comparePaths } from 'vs/base/common/comparers'; import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; export class Match { @@ -698,7 +699,7 @@ export class SearchResult extends Disposable { private _folderMatches: FolderMatchWithResource[] = []; private _otherFilesMatch: FolderMatch | null = null; - private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forUris(); + private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forUris(key => this.uriIdentityService.extUri.ignorePathCasing(key)); private _showHighlights: boolean = false; private _query: ITextQuery | null = null; @@ -713,6 +714,7 @@ export class SearchResult extends Disposable { @ITelemetryService private readonly telemetryService: ITelemetryService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, ) { super(); this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); @@ -743,7 +745,7 @@ export class SearchResult extends Disposable { .then(() => this._isDirty = false); this._rangeHighlightDecorations.removeHighlightRange(); - this._folderMatchesMap = TernarySearchTree.forUris(); + this._folderMatchesMap = TernarySearchTree.forUris(key => this.uriIdentityService.extUri.ignorePathCasing(key)); if (!query) { return; @@ -965,7 +967,7 @@ export class SearchResult extends Disposable { private disposeMatches(): void { this.folderMatches().forEach(folderMatch => folderMatch.dispose()); this._folderMatches = []; - this._folderMatchesMap = TernarySearchTree.forUris(); + this._folderMatchesMap = TernarySearchTree.forUris(key => this.uriIdentityService.extUri.ignorePathCasing(key)); this._rangeHighlightDecorations.removeHighlightRange(); } diff --git a/src/vs/workbench/contrib/search/test/browser/queryBuilder.test.ts b/src/vs/workbench/contrib/search/test/browser/queryBuilder.test.ts index 0a86fa7f4..9bd262f90 100644 --- a/src/vs/workbench/contrib/search/test/browser/queryBuilder.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/queryBuilder.test.ts @@ -10,13 +10,14 @@ import { URI as uri } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IWorkspaceContextService, toWorkspaceFolder, toWorkspaceFolders, Workspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, toWorkspaceFolder, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { ISearchPathsInfo, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IFileQuery, IFolderQuery, IPatternInfo, ITextQuery, QueryType } from 'vs/workbench/services/search/common/search'; import { TestPathService, TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; const DEFAULT_EDITOR_CONFIG = {}; const DEFAULT_USER_CONFIG = { useRipgrep: true, useIgnoreFiles: true, useGlobalIgnoreFiles: true }; diff --git a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts index 363eab383..9dc2993ce 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts @@ -17,6 +17,10 @@ import { isWindows } from 'vs/base/common/platform'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; suite('Search - Viewlet', () => { let instantiation: TestInstantiationService; @@ -25,6 +29,7 @@ suite('Search - Viewlet', () => { instantiation = new TestInstantiationService(); instantiation.stub(IModelService, stubModelService(instantiation)); instantiation.set(IWorkspaceContextService, new TestContextService(TestWorkspace)); + instantiation.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); }); test('Data Source', function () { diff --git a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts b/src/vs/workbench/contrib/search/test/common/searchModel.test.ts index 545d7de99..0a99a6a7a 100644 --- a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/common/searchModel.test.ts @@ -21,6 +21,10 @@ import { SearchModel } from 'vs/workbench/contrib/search/common/searchModel'; import * as process from 'vs/base/common/process'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; const nullEvent = new class { id: number = -1; @@ -72,6 +76,7 @@ suite('SearchModel', () => { instantiationService.stub(IModelService, stubModelService(instantiationService)); instantiationService.stub(ISearchService, {}); instantiationService.stub(ISearchService, 'textSearch', Promise.resolve({ results: [] })); + instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); const config = new TestConfigurationService(); config.setUserConfiguration('search', { searchOnType: true }); diff --git a/src/vs/workbench/contrib/search/test/common/searchResult.test.ts b/src/vs/workbench/contrib/search/test/common/searchResult.test.ts index f46f158ac..e147ce757 100644 --- a/src/vs/workbench/contrib/search/test/common/searchResult.test.ts +++ b/src/vs/workbench/contrib/search/test/common/searchResult.test.ts @@ -18,6 +18,10 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { NullLogService } from 'vs/platform/log/common/log'; const lineOneRange = new OneLineRange(1, 0, 1); @@ -29,6 +33,7 @@ suite('SearchResult', () => { instantiationService = new TestInstantiationService(); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModelService, stubModelService(instantiationService)); + instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); instantiationService.stubPromise(IReplaceService, {}); instantiationService.stubPromise(IReplaceService, 'replace', null); }); diff --git a/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts b/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts index 92b450d22..e6a021cc7 100644 --- a/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts +++ b/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts @@ -6,12 +6,13 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IWorkspaceContextService, toWorkspaceFolder, Workspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ISearchPathsInfo, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; import { TestEnvironmentService, TestNativePathService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { assertEqualSearchPathResults, getUri, patternsToIExpression, globalGlob, fixPath } from 'vs/workbench/contrib/search/test/browser/queryBuilder.test'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; const DEFAULT_EDITOR_CONFIG = {}; const DEFAULT_USER_CONFIG = { useRipgrep: true, useIgnoreFiles: true, useGlobalIgnoreFiles: true }; diff --git a/src/vs/workbench/contrib/searchEditor/browser/constants.ts b/src/vs/workbench/contrib/searchEditor/browser/constants.ts index b37c41a7a..e4d4b412b 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/constants.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/constants.ts @@ -15,3 +15,4 @@ export const SearchEditorID = 'workbench.editor.searchEditor'; export const OpenNewEditorCommandId = 'search.action.openNewEditor'; export const OpenEditorCommandId = 'search.action.openEditor'; +export const ToggleSearchEditorContextLinesCommandId = 'toggleSearchEditorContextLines'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index 08518d3e3..cc6faa97d 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -15,7 +15,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -41,7 +41,6 @@ const FocusQueryEditorWidgetCommandId = 'search.action.focusQueryEditorWidget'; const ToggleSearchEditorCaseSensitiveCommandId = 'toggleSearchEditorCaseSensitive'; const ToggleSearchEditorWholeWordCommandId = 'toggleSearchEditorWholeWord'; const ToggleSearchEditorRegexCommandId = 'toggleSearchEditorRegex'; -const ToggleSearchEditorContextLinesCommandId = 'toggleSearchEditorContextLines'; const IncreaseSearchEditorContextLinesCommandId = 'increaseSearchEditorContextLines'; const DecreaseSearchEditorContextLinesCommandId = 'decreaseSearchEditorContextLines'; @@ -161,60 +160,6 @@ Registry.as(EditorInputExtensions.EditorInputFactor //#endregion //#region Commands -KeybindingsRegistry.registerCommandAndKeybindingRule(Object.assign({ - id: ToggleSearchEditorCaseSensitiveCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor, SearchConstants.SearchInputBoxFocusedKey), - handler: toggleSearchEditorCaseSensitiveCommand -}, ToggleCaseSensitiveKeybinding)); - -KeybindingsRegistry.registerCommandAndKeybindingRule(Object.assign({ - id: ToggleSearchEditorWholeWordCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor, SearchConstants.SearchInputBoxFocusedKey), - handler: toggleSearchEditorWholeWordCommand -}, ToggleWholeWordKeybinding)); - -KeybindingsRegistry.registerCommandAndKeybindingRule(Object.assign({ - id: ToggleSearchEditorRegexCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor, SearchConstants.SearchInputBoxFocusedKey), - handler: toggleSearchEditorRegexCommand -}, ToggleRegexKeybinding)); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: ToggleSearchEditorContextLinesCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor), - handler: toggleSearchEditorContextLinesCommand, - primary: KeyMod.Alt | KeyCode.KEY_L, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L } -}); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: IncreaseSearchEditorContextLinesCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor), - handler: (accessor: ServicesAccessor) => modifySearchEditorContextLinesCommand(accessor, true), - primary: KeyMod.Alt | KeyCode.US_EQUAL -}); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: DecreaseSearchEditorContextLinesCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor), - handler: (accessor: ServicesAccessor) => modifySearchEditorContextLinesCommand(accessor, false), - primary: KeyMod.Alt | KeyCode.US_MINUS -}); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: SelectAllSearchEditorMatchesCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor), - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, - handler: selectAllSearchEditorMatchesCommand -}); - CommandsRegistry.registerCommand( CleanSearchEditorStateCommandId, (accessor: ServicesAccessor) => { @@ -289,7 +234,6 @@ registerAction2(class extends Action2 { title: { value: localize('searchEditor.deleteResultBlock', "Delete File Results"), original: 'Delete File Results' }, keybinding: { weight: KeybindingWeight.EditorContrib, - when: SearchEditorConstants.InSearchEditor, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Backspace, }, precondition: SearchEditorConstants.InSearchEditor, @@ -416,13 +360,10 @@ registerAction2(class extends Action2 { id: FocusQueryEditorWidgetCommandId, title: { value: localize('search.action.focusQueryEditorWidget', "Focus Search Editor Input"), original: 'Focus Search Editor Input' }, category, - menu: { - id: MenuId.CommandPalette, - when: ActiveEditorContext.isEqualTo(SearchEditorConstants.SearchEditorID) - }, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, keybinding: { primary: KeyCode.Escape, - when: SearchEditorConstants.InSearchEditor, weight: KeybindingWeight.EditorContrib } }); @@ -435,4 +376,134 @@ registerAction2(class extends Action2 { } } }); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: ToggleSearchEditorCaseSensitiveCommandId, + title: { value: localize('searchEditor.action.toggleSearchEditorCaseSensitive', "Toggle Match Case"), original: 'Toggle Match Case' }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: Object.assign({ + weight: KeybindingWeight.WorkbenchContrib, + when: SearchConstants.SearchInputBoxFocusedKey, + }, ToggleCaseSensitiveKeybinding) + }); + } + run(accessor: ServicesAccessor) { + toggleSearchEditorCaseSensitiveCommand(accessor); + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: ToggleSearchEditorWholeWordCommandId, + title: { value: localize('searchEditor.action.toggleSearchEditorWholeWord', "Toggle Match Whole Word"), original: 'Toggle Match Whole Word' }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: Object.assign({ + weight: KeybindingWeight.WorkbenchContrib, + when: SearchConstants.SearchInputBoxFocusedKey, + }, ToggleWholeWordKeybinding) + }); + } + run(accessor: ServicesAccessor) { + toggleSearchEditorWholeWordCommand(accessor); + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: ToggleSearchEditorRegexCommandId, + title: { value: localize('searchEditor.action.toggleSearchEditorRegex', "Toggle Use Regular Expression"), original: 'Toggle Use Regular Expression"' }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: Object.assign({ + weight: KeybindingWeight.WorkbenchContrib, + when: SearchConstants.SearchInputBoxFocusedKey, + }, ToggleRegexKeybinding) + }); + } + run(accessor: ServicesAccessor) { + toggleSearchEditorRegexCommand(accessor); + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: SearchEditorConstants.ToggleSearchEditorContextLinesCommandId, + title: { value: localize('searchEditor.action.toggleSearchEditorContextLines', "Toggle Context Lines"), original: 'Toggle Context Lines"' }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.Alt | KeyCode.KEY_L, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L } + } + }); + } + run(accessor: ServicesAccessor) { + toggleSearchEditorContextLinesCommand(accessor); + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: IncreaseSearchEditorContextLinesCommandId, + title: { original: 'Increase Context Lines', value: localize('searchEditor.action.increaseSearchEditorContextLines', "Increase Context Lines") }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.Alt | KeyCode.US_EQUAL + } + }); + } + run(accessor: ServicesAccessor) { modifySearchEditorContextLinesCommand(accessor, true); } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: DecreaseSearchEditorContextLinesCommandId, + title: { original: 'Decrease Context Lines', value: localize('searchEditor.action.decreaseSearchEditorContextLines', "Decrease Context Lines") }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.Alt | KeyCode.US_MINUS + } + }); + } + run(accessor: ServicesAccessor) { modifySearchEditorContextLinesCommand(accessor, false); } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: SelectAllSearchEditorMatchesCommandId, + title: { original: 'Select All Matches', value: localize('searchEditor.action.selectAllSearchEditorMatches', "Select All Matches") }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, + } + }); + } + run(accessor: ServicesAccessor) { + selectAllSearchEditorMatchesCommand(accessor); + } +}); //#endregion diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 7b7ddcc6e..caf6d8012 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -34,7 +34,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { inputBorder, registerColor, searchEditorFindMatch, searchEditorFindMatchBorder } from 'vs/platform/theme/common/colorRegistry'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; -import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; @@ -139,7 +139,7 @@ export class SearchEditor extends BaseTextEditor { this.includesExcludesContainer = DOM.append(this.queryEditorContainer, DOM.$('.includes-excludes')); // // Toggle query details button - this.toggleQueryDetailsButton = DOM.append(this.includesExcludesContainer, DOM.$('.expand' + searchDetailsIcon.cssSelector, { tabindex: 0, role: 'button', title: localize('moreSearch', "Toggle Search Details") })); + this.toggleQueryDetailsButton = DOM.append(this.includesExcludesContainer, DOM.$('.expand' + ThemeIcon.asCSSSelector(searchDetailsIcon), { tabindex: 0, role: 'button', title: localize('moreSearch', "Toggle Search Details") })); this._register(DOM.addDisposableListener(this.toggleQueryDetailsButton, DOM.EventType.CLICK, e => { DOM.EventHelper.stop(e); this.toggleIncludesExcludes(); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts index 0c124a1f0..f1d1426f4 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts @@ -28,6 +28,7 @@ import { OpenNewEditorCommandId } from 'vs/workbench/contrib/searchEditor/browse import { OpenSearchEditorArgs } from 'vs/workbench/contrib/searchEditor/browser/searchEditor.contribution'; import { EditorsOrder } from 'vs/workbench/common/editor'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export const toggleSearchEditorCaseSensitiveCommand = (accessor: ServicesAccessor) => { const editorService = accessor.get(IEditorService); @@ -85,7 +86,7 @@ export class OpenSearchEditorAction extends Action { constructor(id: string, label: string, @IInstantiationService private readonly instantiationService: IInstantiationService, ) { - super(id, label, searchNewEditorIcon.classNames); + super(id, label, ThemeIcon.asClassName(searchNewEditorIcon)); } update() { diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts index a75dbd61e..c5a551a47 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts @@ -16,7 +16,7 @@ import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EditorInput, GroupIdentifier, IEditorInput, IMoveResult, IRevertOptions, ISaveOptions, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, EditorResourceAccessor } from 'vs/workbench/common/editor'; import { Memento } from 'vs/workbench/common/memento'; @@ -65,7 +65,7 @@ export class SearchEditorInput extends EditorInput { public get config(): Readonly { return this._config; } public set config(value: Readonly) { this._config = value; - this.memento.getMemento(StorageScope.WORKSPACE).searchConfig = value; + this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE).searchConfig = value; this._onDidChangeLabel.fire(); } @@ -276,7 +276,7 @@ export class SearchEditorInput extends EditorInput { const searchFileName = (query.replace(/[^\w \-_]+/g, '_') || 'Search') + SEARCH_EDITOR_EXT; - return joinPath(this.fileDialogService.defaultFilePath(this.pathService.defaultUriScheme) || (await this.pathService.userHome()), searchFileName); + return joinPath(await this.fileDialogService.defaultFilePath(this.pathService.defaultUriScheme), searchFileName); } } @@ -298,7 +298,7 @@ export const getOrMakeSearchEditorInput = ( const reuseOldSettings = searchEditorSettings.reusePriorSearchConfiguration; const defaultNumberOfContextLines = searchEditorSettings.defaultNumberOfContextLines; - const priorConfig: SearchConfiguration = reuseOldSettings ? new Memento(SearchEditorInput.ID, storageService).getMemento(StorageScope.WORKSPACE).searchConfig : {}; + const priorConfig: SearchConfiguration = reuseOldSettings ? new Memento(SearchEditorInput.ID, storageService).getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE).searchConfig : {}; const defaultConfig = defaultSearchConfig(); const config = { ...defaultConfig, ...priorConfig, ...existingData.config }; diff --git a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts index fd8440e40..48b60f977 100644 --- a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts @@ -15,10 +15,9 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; import { IQuickPickItem, IQuickInputService, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { Codicon } from 'vs/base/common/codicons'; +import { Event } from 'vs/base/common/event'; -interface ISnippetPick extends IQuickPickItem { - snippet: Snippet; -} class Args { @@ -91,7 +90,7 @@ class InsertSnippetAction extends EditorAction { const clipboardService = accessor.get(IClipboardService); const quickInputService = accessor.get(IQuickInputService); - const snippet = await new Promise(async (resolve, reject) => { + const snippet = await new Promise(async (resolve) => { const { lineNumber, column } = editor.getPosition(); let { snippet, name, langId } = Args.fromUser(arg); @@ -129,44 +128,13 @@ class InsertSnippetAction extends EditorAction { if (name) { // take selected snippet - (await snippetService.getSnippets(languageId)).every(snippet => { - if (snippet.name !== name) { - return true; - } - resolve(snippet); - return false; - }); + const snippet = (await snippetService.getSnippets(languageId)).find(snippet => snippet.name === name); + resolve(snippet); + } else { // let user pick a snippet - const snippets = (await snippetService.getSnippets(languageId)).sort(Snippet.compare); - const picks: QuickPickInput[] = []; - let prevSnippet: Snippet | undefined; - for (const snippet of snippets) { - const pick: ISnippetPick = { - label: snippet.prefix, - detail: snippet.description, - snippet - }; - if (!prevSnippet || prevSnippet.snippetSource !== snippet.snippetSource) { - let label = ''; - switch (snippet.snippetSource) { - case SnippetSource.User: - label = nls.localize('sep.userSnippet', "User Snippets"); - break; - case SnippetSource.Extension: - label = nls.localize('sep.extSnippet', "Extension Snippets"); - break; - case SnippetSource.Workspace: - label = nls.localize('sep.workspaceSnippet', "Workspace Snippets"); - break; - } - picks.push({ type: 'separator', label }); - - } - picks.push(pick); - prevSnippet = snippet; - } - return quickInputService.pick(picks, { matchOnDetail: true }).then(pick => resolve(pick && pick.snippet), reject); + const snippet = await this._pickSnippet(snippetService, quickInputService, languageId); + resolve(snippet); } }); @@ -179,6 +147,80 @@ class InsertSnippetAction extends EditorAction { } SnippetController2.get(editor).insert(snippet.codeSnippet, { clipboardText }); } + + private async _pickSnippet(snippetService: ISnippetsService, quickInputService: IQuickInputService, languageId: LanguageId): Promise { + + interface ISnippetPick extends IQuickPickItem { + snippet: Snippet; + } + + const snippets = (await snippetService.getSnippets(languageId, { includeDisabledSnippets: true, includeNoPrefixSnippets: true })).sort(Snippet.compare); + + const makeSnippetPicks = () => { + const result: QuickPickInput[] = []; + let prevSnippet: Snippet | undefined; + for (const snippet of snippets) { + const pick: ISnippetPick = { + label: snippet.prefix || snippet.name, + detail: snippet.description, + snippet + }; + if (!prevSnippet || prevSnippet.snippetSource !== snippet.snippetSource) { + let label = ''; + switch (snippet.snippetSource) { + case SnippetSource.User: + label = nls.localize('sep.userSnippet', "User Snippets"); + break; + case SnippetSource.Extension: + label = nls.localize('sep.extSnippet', "Extension Snippets"); + break; + case SnippetSource.Workspace: + label = nls.localize('sep.workspaceSnippet', "Workspace Snippets"); + break; + } + result.push({ type: 'separator', label }); + } + + if (snippet.snippetSource === SnippetSource.Extension) { + const isEnabled = snippetService.isEnabled(snippet); + if (isEnabled) { + pick.buttons = [{ + iconClass: Codicon.eyeClosed.classNames, + tooltip: nls.localize('disableSnippet', 'Hide from IntelliSense') + }]; + } else { + pick.description = nls.localize('isDisabled', "(hidden from IntelliSense)"); + pick.buttons = [{ + iconClass: Codicon.eye.classNames, + tooltip: nls.localize('enable.snippet', 'Show in IntelliSense') + }]; + } + } + + result.push(pick); + prevSnippet = snippet; + } + return result; + }; + + const picker = quickInputService.createQuickPick(); + picker.placeholder = nls.localize('pick.placeholder', "Select a snippet"); + picker.matchOnDescription = true; + picker.ignoreFocusOut = false; + picker.onDidTriggerItemButton(ctx => { + const isEnabled = snippetService.isEnabled(ctx.item.snippet); + snippetService.updateEnablement(ctx.item.snippet, !isEnabled); + picker.items = makeSnippetPicks(); + }); + picker.items = makeSnippetPicks(); + picker.show(); + + // wait for an item to be picked or the picker to become hidden + await Promise.race([Event.toPromise(picker.onDidAccept), Event.toPromise(picker.onDidHide)]); + const result = picker.selectedItems[0]?.snippet; + picker.dispose(); + return result; + } } registerEditorAction(InsertSnippetAction); diff --git a/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts b/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts index c7adf5c4f..7ccba80fa 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts @@ -13,15 +13,24 @@ import { SnippetFile, Snippet } from 'vs/workbench/contrib/snippets/browser/snip export const ISnippetsService = createDecorator('snippetService'); +export interface ISnippetGetOptions { + includeDisabledSnippets?: boolean; + includeNoPrefixSnippets?: boolean; +} + export interface ISnippetsService { readonly _serviceBrand: undefined; getSnippetFiles(): Promise>; - getSnippets(languageId: LanguageId): Promise; + isEnabled(snippet: Snippet): boolean; - getSnippetsSync(languageId: LanguageId): Snippet[]; + updateEnablement(snippet: Snippet, enabled: boolean): void; + + getSnippets(languageId: LanguageId, opt?: ISnippetGetOptions): Promise; + + getSnippetsSync(languageId: LanguageId, opt?: ISnippetGetOptions): Snippet[]; } const languageScopeSchemaId = 'vscode://schemas/snippets'; @@ -56,7 +65,7 @@ const languageScopeSchema: IJSONSchema = { description: nls.localize('snippetSchema.json', 'User snippet configuration'), additionalProperties: { type: 'object', - required: ['prefix', 'body'], + required: ['body'], properties: snippetSchemaProperties, additionalProperties: false } @@ -76,7 +85,7 @@ const globalSchema: IJSONSchema = { description: nls.localize('snippetSchema.json', 'User snippet configuration'), additionalProperties: { type: 'object', - required: ['prefix', 'body'], + required: ['body'], properties: { ...snippetSchemaProperties, scope: { diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts b/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts index a9e0f87a0..98d24e31a 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts @@ -15,6 +15,9 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IdleValue } from 'vs/base/common/async'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { relativePath } from 'vs/base/common/resources'; +import { isObject } from 'vs/base/common/types'; +import { Iterable } from 'vs/base/common/iterator'; class SnippetBodyInsights { @@ -86,9 +89,9 @@ export class Snippet { readonly body: string, readonly source: string, readonly snippetSource: SnippetSource, + readonly snippetIdentifier?: string ) { - // - this.prefixLow = prefix ? prefix.toLowerCase() : prefix; + this.prefixLow = prefix.toLowerCase(); this._bodyInsights = new IdleValue(() => new SnippetBodyInsights(this.body)); } @@ -121,14 +124,14 @@ export class Snippet { interface JsonSerializedSnippet { - body: string; + body: string | string[]; scope: string; - prefix: string | string[]; + prefix: string | string[] | undefined; description: string; } function isJsonSerializedSnippet(thing: any): thing is JsonSerializedSnippet { - return Boolean((thing).body) && Boolean((thing).prefix); + return isObject(thing) && Boolean((thing).body); } interface JsonSerializedSnippets { @@ -242,18 +245,21 @@ export class SnippetFile { let { prefix, body, description } = snippet; + if (!prefix) { + prefix = ''; + } + if (Array.isArray(body)) { body = body.join('\n'); } + if (typeof body !== 'string') { + return; + } if (Array.isArray(description)) { description = description.join('\n'); } - if ((typeof prefix !== 'string' && !Array.isArray(prefix)) || typeof body !== 'string') { - return; - } - let scopes: string[]; if (this.defaultScopes) { scopes = this.defaultScopes; @@ -280,17 +286,17 @@ export class SnippetFile { } } - let prefixes = Array.isArray(prefix) ? prefix : [prefix]; - prefixes.forEach(p => { + for (const _prefix of Array.isArray(prefix) ? prefix : Iterable.single(prefix)) { bucket.push(new Snippet( scopes, name, - p, + _prefix, description, body, source, - this.source + this.source, + this._extension && `${relativePath(this._extension.extensionLocation, this.location)}/${name}` )); - }); + } } } diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index 3976dd126..a1bace7ed 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -19,12 +19,16 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { IWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ISnippetsService } from 'vs/workbench/contrib/snippets/browser/snippets.contribution'; +import { ISnippetGetOptions, ISnippetsService } from 'vs/workbench/contrib/snippets/browser/snippets.contribution'; import { Snippet, SnippetFile, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchModeService'; import { SnippetCompletionProvider } from './snippetCompletionProvider'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { ResourceMap } from 'vs/base/common/map'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { isStringArray } from 'vs/base/common/types'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; namespace snippetExt { @@ -123,13 +127,52 @@ function watch(service: IFileService, resource: URI, callback: () => any): IDisp ); } +class SnippetEnablement { + + private static _key = 'snippets.ignoredSnippets'; + + private readonly _ignored: Set; + + constructor( + @IStorageService private readonly _storageService: IStorageService, + ) { + + const raw = _storageService.get(SnippetEnablement._key, StorageScope.GLOBAL, ''); + let data: string[] | undefined; + try { + data = JSON.parse(raw); + } catch { } + + this._ignored = isStringArray(data) ? new Set(data) : new Set(); + } + + isIgnored(id: string): boolean { + return this._ignored.has(id); + } + + updateIgnored(id: string, value: boolean): void { + let changed = false; + if (this._ignored.has(id) && !value) { + this._ignored.delete(id); + changed = true; + } else if (!this._ignored.has(id) && value) { + this._ignored.add(id); + changed = true; + } + if (changed) { + this._storageService.store(SnippetEnablement._key, JSON.stringify(Array.from(this._ignored)), StorageScope.GLOBAL, StorageTarget.USER); + } + } +} + class SnippetsService implements ISnippetsService { - readonly _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _disposables = new DisposableStore(); private readonly _pendingWork: Promise[] = []; - private readonly _files = new Map(); + private readonly _files = new ResourceMap(); + private readonly _enablement: SnippetEnablement; constructor( @IEnvironmentService private readonly _environmentService: IEnvironmentService, @@ -139,6 +182,7 @@ class SnippetsService implements ISnippetsService { @IFileService private readonly _fileService: IFileService, @IExtensionResourceLoaderService private readonly _extensionResourceLoaderService: IExtensionResourceLoaderService, @ILifecycleService lifecycleService: ILifecycleService, + @IInstantiationService instantiationService: IInstantiationService, ) { this._pendingWork.push(Promise.resolve(lifecycleService.when(LifecyclePhase.Restored).then(() => { this._initExtensionSnippets(); @@ -147,12 +191,24 @@ class SnippetsService implements ISnippetsService { }))); setSnippetSuggestSupport(new SnippetCompletionProvider(this._modeService, this)); + + this._enablement = instantiationService.createInstance(SnippetEnablement); } dispose(): void { this._disposables.dispose(); } + isEnabled(snippet: Snippet): boolean { + return !snippet.snippetIdentifier || !this._enablement.isIgnored(snippet.snippetIdentifier); + } + + updateEnablement(snippet: Snippet, enabled: boolean): void { + if (snippet.snippetIdentifier) { + this._enablement.updateIgnored(snippet.snippetIdentifier, !enabled); + } + } + private _joinSnippets(): Promise { const promises = this._pendingWork.slice(0); this._pendingWork.length = 0; @@ -164,26 +220,27 @@ class SnippetsService implements ISnippetsService { return this._files.values(); } - getSnippets(languageId: LanguageId): Promise { - return this._joinSnippets().then(() => { - const result: Snippet[] = []; - const promises: Promise[] = []; + async getSnippets(languageId: LanguageId, opts?: ISnippetGetOptions): Promise { + await this._joinSnippets(); - const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); - if (languageIdentifier) { - const langName = languageIdentifier.language; - for (const file of this._files.values()) { - promises.push(file.load() - .then(file => file.select(langName, result)) - .catch(err => this._logService.error(err, file.location.toString())) - ); - } + const result: Snippet[] = []; + const promises: Promise[] = []; + + const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); + if (languageIdentifier) { + const langName = languageIdentifier.language; + for (const file of this._files.values()) { + promises.push(file.load() + .then(file => file.select(langName, result)) + .catch(err => this._logService.error(err, file.location.toString())) + ); } - return Promise.all(promises).then(() => result); - }); + } + await Promise.all(promises); + return this._filterSnippets(result, opts); } - getSnippetsSync(languageId: LanguageId): Snippet[] { + getSnippetsSync(languageId: LanguageId, opts?: ISnippetGetOptions): Snippet[] { const result: Snippet[] = []; const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); if (languageIdentifier) { @@ -191,11 +248,18 @@ class SnippetsService implements ISnippetsService { for (const file of this._files.values()) { // kick off loading (which is a noop in case it's already loaded) // and optimistically collect snippets - file.load().catch(err => { /*ignore*/ }); + file.load().catch(_err => { /*ignore*/ }); file.select(langName, result); } } - return result; + return this._filterSnippets(result, opts); + } + + private _filterSnippets(snippets: Snippet[], opts?: ISnippetGetOptions): Snippet[] { + return snippets.filter(snippet => { + return (snippet.prefix || opts?.includeNoPrefixSnippets) // prefix or no-prefix wanted + && (this.isEnabled(snippet) || opts?.includeDisabledSnippets); // enabled or disabled wanted + }); } // --- loading, watching @@ -203,7 +267,7 @@ class SnippetsService implements ISnippetsService { private _initExtensionSnippets(): void { snippetExt.point.setHandler(extensions => { - for (let [key, value] of this._files) { + for (const [key, value] of this._files) { if (value.source === SnippetSource.Extension) { this._files.delete(key); } @@ -216,8 +280,7 @@ class SnippetsService implements ISnippetsService { continue; } - const resource = validContribution.location.toString(); - const file = this._files.get(resource); + const file = this._files.get(validContribution.location); if (file) { if (file.defaultScopes) { file.defaultScopes.push(validContribution.language); @@ -226,7 +289,7 @@ class SnippetsService implements ISnippetsService { } } else { const file = new SnippetFile(SnippetSource.Extension, validContribution.location, validContribution.language ? [validContribution.language] : undefined, extension.description, this._fileService, this._extensionResourceLoaderService); - this._files.set(file.location.toString(), file); + this._files.set(file.location, file); if (this._environmentService.isExtensionDevelopment) { file.load().then(file => { @@ -267,28 +330,28 @@ class SnippetsService implements ISnippetsService { updateWorkspaceSnippets(); } - private _initWorkspaceFolderSnippets(workspace: IWorkspace, bucket: DisposableStore): Promise { - let promises = workspace.folders.map(folder => { + private async _initWorkspaceFolderSnippets(workspace: IWorkspace, bucket: DisposableStore): Promise { + const promises = workspace.folders.map(async folder => { const snippetFolder = folder.toResource('.vscode'); - return this._fileService.exists(snippetFolder).then(value => { - if (value) { - this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket); - } else { - // watch - bucket.add(this._fileService.onDidFilesChange(e => { - if (e.contains(snippetFolder, FileChangeType.ADDED)) { - this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket); - } - })); - } - }); + const value = await this._fileService.exists(snippetFolder); + if (value) { + this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket); + } else { + // watch + bucket.add(this._fileService.onDidFilesChange(e => { + if (e.contains(snippetFolder, FileChangeType.ADDED)) { + this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket); + } + })); + } }); - return Promise.all(promises); + await Promise.all(promises); } - private _initUserSnippets(): Promise { + private async _initUserSnippets(): Promise { const userSnippetsFolder = this._environmentService.snippetsHome; - return this._fileService.createFolder(userSnippetsFolder).then(() => this._initFolderSnippets(SnippetSource.User, userSnippetsFolder, this._disposables)); + await this._fileService.createFolder(userSnippetsFolder); + return await this._initFolderSnippets(SnippetSource.User, userSnippetsFolder, this._disposables); } private _initFolderSnippets(source: SnippetSource, folder: URI, bucket: DisposableStore): Promise { @@ -315,15 +378,14 @@ class SnippetsService implements ISnippetsService { private _addSnippetFile(uri: URI, source: SnippetSource): IDisposable { const ext = resources.extname(uri); - const key = uri.toString(); if (source === SnippetSource.User && ext === '.json') { const langName = resources.basename(uri).replace(/\.json/, ''); - this._files.set(key, new SnippetFile(source, uri, [langName], undefined, this._fileService, this._extensionResourceLoaderService)); + this._files.set(uri, new SnippetFile(source, uri, [langName], undefined, this._fileService, this._extensionResourceLoaderService)); } else if (ext === '.code-snippets') { - this._files.set(key, new SnippetFile(source, uri, undefined, undefined, this._fileService, this._extensionResourceLoaderService)); + this._files.set(uri, new SnippetFile(source, uri, undefined, undefined, this._fileService, this._extensionResourceLoaderService)); } return { - dispose: () => this._files.delete(key) + dispose: () => this._files.delete(uri) }; } } diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts index cb3b8cbc6..10419ba6c 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts @@ -16,8 +16,7 @@ import { CompletionContext, CompletionTriggerKind } from 'vs/editor/common/modes class SimpleSnippetService implements ISnippetsService { declare readonly _serviceBrand: undefined; - constructor(readonly snippets: Snippet[]) { - } + constructor(readonly snippets: Snippet[]) { } getSnippets() { return Promise.resolve(this.getSnippetsSync()); } @@ -27,6 +26,12 @@ class SimpleSnippetService implements ISnippetsService { getSnippetFiles(): any { throw new Error(); } + isEnabled(): boolean { + throw new Error(); + } + updateEnablement(): void { + throw new Error(); + } } suite('SnippetsService', function () { diff --git a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts index dc2cdaeaf..76c12fa6a 100644 --- a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts @@ -102,7 +102,7 @@ class PartsSplash { layoutInfo, baseTheme }), - { encoding: 'utf8', overwriteEncoding: true } + { encoding: 'utf8' } ); if (baseTheme !== this._lastBaseTheme || colorInfo.editorBackground !== this._lastBackground) { diff --git a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index 273f4cc82..d7452466e 100644 --- a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -9,8 +9,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ISurveyData, IProductService } from 'vs/platform/product/common/productService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; @@ -26,7 +25,6 @@ class LanguageSurvey extends Disposable { constructor( data: ISurveyData, storageService: IStorageService, - storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, notificationService: INotificationService, telemetryService: ITelemetryService, modelService: IModelService, @@ -43,14 +41,6 @@ class LanguageSurvey extends Disposable { const EDITED_LANGUAGE_COUNT_KEY = `${data.surveyId}.editedCount`; const EDITED_LANGUAGE_DATE_KEY = `${data.surveyId}.editedDate`; - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: SESSION_COUNT_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: LAST_SESSION_DATE_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: SKIP_VERSION_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: IS_CANDIDATE_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: EDITED_LANGUAGE_COUNT_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: EDITED_LANGUAGE_DATE_KEY, version: 1 }); - const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, ''); if (skipVersion) { return; @@ -65,8 +55,8 @@ class LanguageSurvey extends Disposable { models.forEach(m => { if (m.getMode() === data.languageId && date !== storageService.get(EDITED_LANGUAGE_DATE_KEY, StorageScope.GLOBAL)) { const editedCount = storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; - storageService.store(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL); - storageService.store(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL); + storageService.store(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL, StorageTarget.USER); } }); }, 250)); @@ -80,8 +70,8 @@ class LanguageSurvey extends Disposable { } const sessionCount = storageService.getNumber(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; - storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL); - storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL); + storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL, StorageTarget.USER); if (sessionCount < 9) { return; @@ -94,10 +84,10 @@ class LanguageSurvey extends Disposable { const isCandidate = storageService.getBoolean(IS_CANDIDATE_KEY, StorageScope.GLOBAL, false) || Math.random() < data.userProbability; - storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL, StorageTarget.USER); if (!isCandidate) { - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); return; } @@ -113,23 +103,23 @@ class LanguageSurvey extends Disposable { telemetryService.publicLog(`${data.surveyId}.survey/takeShortSurvey`); telemetryService.getTelemetryInfo().then(info => { openerService.open(URI.parse(`${data.surveyUrl}?o=${encodeURIComponent(platform)}&v=${encodeURIComponent(productService.version)}&m=${encodeURIComponent(info.machineId)}`)); - storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); }); } }, { label: nls.localize('remindLater', "Remind Me later"), run: () => { telemetryService.publicLog(`${data.surveyId}.survey/remindMeLater`); - storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL); + storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL, StorageTarget.USER); } }, { label: nls.localize('neverAgain', "Don't Show Again"), isSecondary: true, run: () => { telemetryService.publicLog(`${data.surveyId}.survey/dontShowAgain`); - storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); } }], { sticky: true } @@ -141,7 +131,6 @@ class LanguageSurveysContribution implements IWorkbenchContribution { constructor( @IStorageService storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IModelService modelService: IModelService, @@ -155,7 +144,7 @@ class LanguageSurveysContribution implements IWorkbenchContribution { productService.surveys .filter(surveyData => surveyData.surveyId && surveyData.editCount && surveyData.languageId && surveyData.surveyUrl && surveyData.userProbability) - .map(surveyData => new LanguageSurvey(surveyData, storageService, storageKeysSyncRegistryService, notificationService, telemetryService, modelService, textFileService, openerService, productService)); + .map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService, openerService, productService)); } } diff --git a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts index 7f8e32d28..243eb7500 100644 --- a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts @@ -8,8 +8,7 @@ import { language } from 'vs/base/common/platform'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IProductService } from 'vs/platform/product/common/productService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; @@ -27,7 +26,6 @@ class NPSContribution implements IWorkbenchContribution { constructor( @IStorageService storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IOpenerService openerService: IOpenerService, @@ -37,12 +35,6 @@ class NPSContribution implements IWorkbenchContribution { return; } - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: SESSION_COUNT_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: LAST_SESSION_DATE_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: SKIP_VERSION_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: IS_CANDIDATE_KEY, version: 1 }); - const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, ''); if (skipVersion) { return; @@ -56,8 +48,8 @@ class NPSContribution implements IWorkbenchContribution { } const sessionCount = (storageService.getNumber(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) || 0) + 1; - storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL); - storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL); + storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL, StorageTarget.USER); if (sessionCount < 9) { return; @@ -66,10 +58,10 @@ class NPSContribution implements IWorkbenchContribution { const isCandidate = storageService.getBoolean(IS_CANDIDATE_KEY, StorageScope.GLOBAL, false) || Math.random() < PROBABILITY; - storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL, StorageTarget.USER); if (!isCandidate) { - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); return; } @@ -81,18 +73,18 @@ class NPSContribution implements IWorkbenchContribution { run: () => { telemetryService.getTelemetryInfo().then(info => { openerService.open(URI.parse(`${productService.npsSurveyUrl}?o=${encodeURIComponent(platform)}&v=${encodeURIComponent(productService.version)}&m=${encodeURIComponent(info.machineId)}`)); - storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); }); } }, { label: nls.localize('remindLater', "Remind Me later"), - run: () => storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL) + run: () => storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL, StorageTarget.USER) }, { label: nls.localize('neverAgain', "Don't Show Again"), run: () => { - storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); } }], { sticky: true } diff --git a/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts index 2aecc6350..0fbc6faa5 100644 --- a/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts @@ -16,7 +16,7 @@ export class NoOpWorkspaceTagsService implements IWorkspaceTagsService { return Promise.resolve({}); } - getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { + async getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): Promise { return undefined; } diff --git a/src/vs/workbench/contrib/tags/common/workspaceTags.ts b/src/vs/workbench/contrib/tags/common/workspaceTags.ts index d1683b977..487eb6caf 100644 --- a/src/vs/workbench/contrib/tags/common/workspaceTags.ts +++ b/src/vs/workbench/contrib/tags/common/workspaceTags.ts @@ -20,7 +20,7 @@ export interface IWorkspaceTagsService { * Returns an id for the workspace, different from the id returned by the context service. A hash based * on the folder uri or workspace configuration, not time-based, and undefined for empty workspaces. */ - getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined; + getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): Promise; getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise; } diff --git a/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts b/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts index df781b92f..726575e82 100644 --- a/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts +++ b/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as crypto from 'crypto'; +import { sha1Hex } from 'vs/base/browser/hash'; import { onUnexpectedError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { IFileService, IFileStat } from 'vs/platform/files/common/files'; @@ -20,10 +20,8 @@ import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsSer import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { IProductService } from 'vs/platform/product/common/productService'; -export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: boolean = false): string[] { - return getRemotes(text, stripEndingDotGit).map(r => { - return crypto.createHash('sha1').update(r).digest('hex'); - }); +export async function getHashedRemotesFromConfig(text: string, stripEndingDotGit: boolean = false): Promise { + return Promise.all(getRemotes(text, stripEndingDotGit).map(remote => sha1Hex(remote))); } export class WorkspaceTags implements IWorkbenchContribution { @@ -76,7 +74,7 @@ export class WorkspaceTags implements IWorkbenchContribution { private async getWorkspaceInformation(): Promise { const workspace = this.contextService.getWorkspace(); const state = this.contextService.getWorkbenchState(); - const telemetryId = this.workspaceTagsService.getTelemetryWorkspaceId(workspace, state); + const telemetryId = await this.workspaceTagsService.getTelemetryWorkspaceId(workspace, state); return this.telemetryService.getTelemetryInfo().then(info => { return { id: workspace.id, diff --git a/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts index 35b943f87..bf203f81c 100644 --- a/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as crypto from 'crypto'; +import { sha1Hex } from 'vs/base/browser/hash'; import { IFileService, IResolveFileResult, IFileStat } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -14,6 +14,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/workspaceTags'; import { getHashedRemotesFromConfig } from 'vs/workbench/contrib/tags/electron-browser/workspaceTags'; import { IProductService } from 'vs/platform/product/common/productService'; +import { splitLines } from 'vs/base/common/strings'; const MetaModulesToLookFor = [ // Azure packages @@ -137,9 +138,9 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { return this._tags; } - getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { - function createHash(uri: URI): string { - return crypto.createHash('sha1').update(uri.scheme === Schemas.file ? uri.fsPath : uri.toString()).digest('hex'); + async getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): Promise { + function createHash(uri: URI): Promise { + return sha1Hex(uri.scheme === Schemas.file ? uri.fsPath : uri.toString()); } let workspaceId: string | undefined; @@ -148,11 +149,11 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { workspaceId = undefined; break; case WorkbenchState.FOLDER: - workspaceId = createHash(workspace.folders[0].uri); + workspaceId = await createHash(workspace.folders[0].uri); break; case WorkbenchState.WORKSPACE: if (workspace.configuration) { - workspaceId = createHash(workspace.configuration); + workspaceId = await createHash(workspace.configuration); } } @@ -291,13 +292,13 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.py.playwright" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } */ - private resolveWorkspaceTags(): Promise { + private async resolveWorkspaceTags(): Promise { const tags: Tags = Object.create(null); const state = this.contextService.getWorkbenchState(); const workspace = this.contextService.getWorkspace(); - tags['workspace.id'] = this.getTelemetryWorkspaceId(workspace, state); + tags['workspace.id'] = await this.getTelemetryWorkspaceId(workspace, state); const { filesToOpenOrCreate, filesToDiff } = this.environmentService.configuration; tags['workbench.filesToOpenOrCreate'] = filesToOpenOrCreate && filesToOpenOrCreate.length || 0; @@ -412,7 +413,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { } const requirementsTxtPromises = getFilePromises('requirements.txt', this.fileService, this.textFileService, content => { - const dependencies: string[] = content.value.split(/\r\n|\r|\n/); + const dependencies: string[] = splitLines(content.value); for (let dependency of dependencies) { // Dependencies in requirements.txt can have 3 formats: `foo==3.1, foo>=3.1, foo` const format1 = dependency.split('=='); @@ -423,7 +424,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { }); const pipfilePromises = getFilePromises('pipfile', this.fileService, this.textFileService, content => { - let dependencies: string[] = content.value.split(/\r\n|\r|\n/); + let dependencies: string[] = splitLines(content.value); // We're only interested in the '[packages]' section of the Pipfile dependencies = dependencies.slice(dependencies.indexOf('[packages]') + 1); diff --git a/src/vs/workbench/contrib/tags/test/electron-browser/workspaceTags.test.ts b/src/vs/workbench/contrib/tags/test/electron-browser/workspaceTags.test.ts index ed3c428da..faad40a9e 100644 --- a/src/vs/workbench/contrib/tags/test/electron-browser/workspaceTags.test.ts +++ b/src/vs/workbench/contrib/tags/test/electron-browser/workspaceTags.test.ts @@ -13,35 +13,35 @@ function hash(value: string): string { suite('Telemetry - WorkspaceTags', () => { - test('Single remote hashed', function () { - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git')), [hash('github3.com/username/repository.git')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git')), [hash('git.server.org/project.git')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('user@git.server.org:project.git')), [hash('git.server.org/project.git')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('/opt/git/project.git')), []); + test('Single remote hashed', async function () { + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git')), [hash('github3.com/username/repository.git')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git')), [hash('git.server.org/project.git')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('user@git.server.org:project.git')), [hash('git.server.org/project.git')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('/opt/git/project.git')), []); // Strip .git - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git'), true), [hash('github3.com/username/repository')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git'), true), [hash('git.server.org/project')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('user@git.server.org:project.git'), true), [hash('git.server.org/project')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('/opt/git/project.git'), true), []); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git'), true), [hash('github3.com/username/repository')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git'), true), [hash('git.server.org/project')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('user@git.server.org:project.git'), true), [hash('git.server.org/project')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('/opt/git/project.git'), true), []); // Compare Striped .git with no .git - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git'), true), getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository'))); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git'), true), getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project'))); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('user@git.server.org:project.git'), true), [hash('git.server.org/project')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('/opt/git/project.git'), true), getHashedRemotesFromConfig(remote('/opt/git/project'))); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git'), true), await getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository'))); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git'), true), await getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project'))); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('user@git.server.org:project.git'), true), [hash('git.server.org/project')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('/opt/git/project.git'), true), await getHashedRemotesFromConfig(remote('/opt/git/project'))); }); - test('Multiple remotes hashed', function () { + test('Multiple remotes hashed', async function () { const config = ['https://github.com/microsoft/vscode.git', 'https://git.example.com/gitproject.git'].map(remote).join(' '); - assert.deepStrictEqual(getHashedRemotesFromConfig(config), [hash('github.com/microsoft/vscode.git'), hash('git.example.com/gitproject.git')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(config), [hash('github.com/microsoft/vscode.git'), hash('git.example.com/gitproject.git')]); // Strip .git - assert.deepStrictEqual(getHashedRemotesFromConfig(config, true), [hash('github.com/microsoft/vscode'), hash('git.example.com/gitproject')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(config, true), [hash('github.com/microsoft/vscode'), hash('git.example.com/gitproject')]); // Compare Striped .git with no .git const noDotGitConfig = ['https://github.com/microsoft/vscode', 'https://git.example.com/gitproject'].map(remote).join(' '); - assert.deepStrictEqual(getHashedRemotesFromConfig(config, true), getHashedRemotesFromConfig(noDotGitConfig)); + assert.deepStrictEqual(await getHashedRemotesFromConfig(config, true), await getHashedRemotesFromConfig(noDotGitConfig)); }); function remote(url: string): string { diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 54b59c57c..64f74982d 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -19,8 +19,6 @@ import { ValidationStatus, ValidationState } from 'vs/base/common/parsers'; import * as UUID from 'vs/base/common/uuid'; import * as Platform from 'vs/base/common/platform'; import { LRUCache, Touch } from 'vs/base/common/map'; - -import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -28,13 +26,13 @@ import { IFileService, IFileStat } from 'vs/platform/files/common/files'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ProblemMatcherRegistry, NamedProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IProgressService, IProgressOptions, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IDialogService, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -42,7 +40,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; -import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output'; @@ -78,9 +76,10 @@ import { ITextEditorSelection, TextEditorSelectionRevealType } from 'vs/platform import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; -import { isWorkspaceFolder, TaskQuickPickEntry, QUICKOPEN_DETAIL_CONFIG, TaskQuickPick, QUICKOPEN_SKIP_CONFIG } from 'vs/workbench/contrib/tasks/browser/taskQuickPick'; +import { isWorkspaceFolder, TaskQuickPickEntry, QUICKOPEN_DETAIL_CONFIG, TaskQuickPick, QUICKOPEN_SKIP_CONFIG, configureTaskIcon } from 'vs/workbench/contrib/tasks/browser/taskQuickPick'; import { ILogService } from 'vs/platform/log/common/log'; import { once } from 'vs/base/common/functional'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history'; const PROBLEM_MATCHER_NEVER_CONFIG = 'task.problemMatchers.neverPrompt'; @@ -238,7 +237,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @IWorkspaceContextService protected readonly contextService: IWorkspaceContextService, @ITelemetryService protected readonly telemetryService: ITelemetryService, @ITextFileService private readonly textFileService: ITextFileService, - @ILifecycleService lifecycleService: ILifecycleService, @IModelService protected readonly modelService: IModelService, @IExtensionService private readonly extensionService: IExtensionService, @IQuickInputService private readonly quickInputService: IQuickInputService, @@ -248,7 +246,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @IProgressService private readonly progressService: IProgressService, @IOpenerService private readonly openerService: IOpenerService, @IHostService private readonly _hostService: IHostService, - @IDialogService private readonly dialogService: IDialogService, + @IDialogService protected readonly dialogService: IDialogService, @INotificationService private readonly notificationService: INotificationService, @IContextKeyService protected readonly contextKeyService: IContextKeyService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @@ -308,7 +306,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.updateWorkspaceTasks(TaskRunSource.ConfigurationChange); })); this._taskRunningState = TASK_RUNNING_STATE.bindTo(contextKeyService); - this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown()))); this._onDidStateChange = this._register(new Emitter()); this.registerCommands(); this.configurationResolverService.contributeVariable('defaultBuildTask', async (): Promise => { @@ -518,7 +515,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private disposeTaskSystemListeners(): void { + protected disposeTaskSystemListeners(): void { if (this._taskSystemListener) { this._taskSystemListener.dispose(); } @@ -580,7 +577,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!values) { return undefined; } - return values.find(task => task.matches(key, compareId)); + values = values.filter(task => task.matches(key, compareId)).sort(task => task._source.kind === TaskSourceKind.Extension ? 1 : -1); + return values.length > 0 ? values[0] : undefined; }); } @@ -801,6 +799,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return tasks; } + public removeRecentlyUsedTask(taskRecentlyUsedKey: string) { + if (this.getRecentlyUsedTasks().has(taskRecentlyUsedKey)) { + this.getRecentlyUsedTasks().delete(taskRecentlyUsedKey); + this.saveRecentlyUsedTasks(); + } + } + private setTaskLRUCacheLimit() { const quickOpenHistoryLimit = this.configurationService.getValue(QUICKOPEN_HISTORY_LIMIT_CONFIG); if (this._recentlyUsedTasks) { @@ -845,7 +850,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer for (const key of keys) { keyValues.push([key, this._recentlyUsedTasks.get(key, Touch.None)!]); } - this.storageService.store(AbstractTaskService.RecentlyUsedTasks_KeyV2, JSON.stringify(keyValues), StorageScope.WORKSPACE); + this.storageService.store(AbstractTaskService.RecentlyUsedTasks_KeyV2, JSON.stringify(keyValues), StorageScope.WORKSPACE, StorageTarget.USER); } private openDocumentation(): void { @@ -981,7 +986,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer newValue = Object.create(null); } newValue[type] = true; - return this.configurationService.updateValue(PROBLEM_MATCHER_NEVER_CONFIG, newValue, ConfigurationTarget.USER); + return this.configurationService.updateValue(PROBLEM_MATCHER_NEVER_CONFIG, newValue); } private attachProblemMatcher(task: ContributedTask | CustomTask): Promise { @@ -1593,7 +1598,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.modelService, this.configurationResolverService, this.telemetryService, this.contextService, this.environmentService, AbstractTaskService.OutputChannelId, this.fileService, this.terminalInstanceService, - this.pathService, this.viewDescriptorService, this.logService, + this.pathService, this.viewDescriptorService, this.logService, this.configurationService, (workspaceFolder: IWorkspaceFolder | undefined) => { if (workspaceFolder) { return this.getTaskSystemInfo(workspaceFolder.uri.scheme); @@ -1699,7 +1704,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return; } - if (!contributed) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { result.add(key, ...folderTasks.set.tasks); } else { let configurations = folderTasks.configurations; @@ -1864,31 +1869,35 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected abstract updateWorkspaceTasks(runSource: TaskRunSource | void): void; protected computeWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise> { - if (this.workspaceFolders.length === 0) { - return Promise.resolve(new Map()); - } else { - let promises: Promise[] = []; - for (let folder of this.workspaceFolders) { - promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined)); + let promises: Promise[] = []; + for (let folder of this.workspaceFolders) { + promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined)); + } + return Promise.all(promises).then(async (values) => { + let result = new Map(); + for (let value of values) { + if (value) { + result.set(value.workspaceFolder.uri.toString(), value); + } } - return Promise.all(promises).then(async (values) => { - let result = new Map(); - for (let value of values) { - if (value) { - result.set(value.workspaceFolder.uri.toString(), value); - } - } - const userTasks = await this.computeUserTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined); - if (userTasks) { - result.set(USER_TASKS_GROUP_KEY, userTasks); - } - const workspaceFileTasks = await this.computeWorkspaceFileTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined); + let folder = this.workspaceFolders.length > 0 ? this.workspaceFolders[0] : undefined; + if (!folder) { + const userhome = await this.pathService.userHome(); + folder = new WorkspaceFolder({ uri: userhome, name: resources.basename(userhome), index: 0 }); + } + const userTasks = await this.computeUserTasks(folder, runSource).then((value) => value, () => undefined); + if (userTasks) { + result.set(USER_TASKS_GROUP_KEY, userTasks); + } + + if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { + const workspaceFileTasks = await this.computeWorkspaceFileTasks(folder, runSource).then((value) => value, () => undefined); if (workspaceFileTasks && this._workspace && this._workspace.configuration) { result.set(this._workspace.configuration.toString(), workspaceFileTasks); } - return result; - }); - } + } + return result; + }); } private get jsonTasksSupported(): boolean { @@ -2129,64 +2138,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }; } - public beforeShutdown(): boolean | Promise { - if (!this._taskSystem) { - return false; - } - if (!this._taskSystem.isActiveSync()) { - return false; - } - // The terminal service kills all terminal on shutdown. So there - // is nothing we can do to prevent this here. - if (this._taskSystem instanceof TerminalTaskSystem) { - return false; - } - - let terminatePromise: Promise; - if (this._taskSystem.canAutoTerminate()) { - terminatePromise = Promise.resolve({ confirmed: true }); - } else { - terminatePromise = this.dialogService.confirm({ - message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'), - primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"), - type: 'question' - }); - } - - return terminatePromise.then(res => { - if (res.confirmed) { - return this._taskSystem!.terminateAll().then((responses) => { - let success = true; - let code: number | undefined = undefined; - for (let response of responses) { - success = success && response.success; - // We only have a code in the old output runner which only has one task - // So we can use the first code. - if (code === undefined && response.code !== undefined) { - code = response.code; - } - } - if (success) { - this._taskSystem = undefined; - this.disposeTaskSystemListeners(); - return false; // no veto - } else if (code && code === TerminateResponseCode.ProcessNotFound) { - return this.dialogService.confirm({ - message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'), - primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"), - type: 'info' - }).then(res => !res.confirmed); - } - return true; // veto - }, (err) => { - return true; // veto - }); - } - - return true; // veto - }); - } - private handleError(err: any): void { let showOutput = true; if (err instanceof TaskError) { @@ -2222,17 +2173,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } private canRunCommand(): boolean { - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - this.notificationService.prompt( - Severity.Info, - nls.localize('TaskService.noWorkspace', "Tasks are only available on a workspace folder."), - [{ - label: nls.localize('TaskService.learnMore', "Learn More"), - run: () => this.openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks')) - }] - ); - return false; - } return true; } @@ -2262,7 +2202,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } for (let task of tasks) { let entry: TaskQuickPickEntry = TaskQuickPickEntry(task); - entry.buttons = [{ iconClass: 'codicon-gear', tooltip: nls.localize('configureTask', "Configure Task") }]; + entry.buttons = [{ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") }]; if (selectedEntry && (task === selectedEntry.task)) { entries.unshift(selectedEntry); } else { @@ -2446,7 +2386,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer label: nls.localize('TaskService.notAgain', "Don't Show Again"), isSecondary: true, run: () => { - this.storageService.store(AbstractTaskService.IgnoreTask010DonotShowAgain_key, true, StorageScope.WORKSPACE); + this.storageService.store(AbstractTaskService.IgnoreTask010DonotShowAgain_key, true, StorageScope.WORKSPACE, StorageTarget.USER); this._showIgnoreMessage = false; } }] @@ -2541,7 +2481,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } this.showQuickPick(tasks ? tasks : taskResult!.tasks, placeholder, { - label: nls.localize('TaskService.noEntryToRunSlow', 'No task to run found. Configure Tasks...'), + label: nls.localize('TaskService.noEntryToRunSlow', '$(plus) Configure a Task'), task: null }, true). @@ -2551,7 +2491,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } else { this.showTwoLevelQuickPick(placeholder, { - label: nls.localize('TaskService.noEntryToRun', 'No configured tasks. Configure Tasks...'), + label: nls.localize('TaskService.noEntryToRun', '$(plus) Configure a Task'), task: null }). then(pickThen); @@ -2926,8 +2866,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } if (this.isTaskEntry(selection)) { this.configureTask(selection.task); - } else { + } else if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { this.openTaskFile(selection.folder.toResource('.vscode/tasks.json'), TaskSourceKind.Workspace); + } else { + const resource = this.getResourceForKind(TaskSourceKind.User); + if (resource) { + this.openTaskFile(resource, TaskSourceKind.User); + } } } @@ -2969,46 +2914,22 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return taskPromise.then((taskMap) => { let entries: QuickPickInput[] = []; let needsCreateOrOpen: boolean = true; - if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { - let tasks = taskMap.all(); - if (tasks.length > 0) { - tasks = tasks.sort((a, b) => a._label.localeCompare(b._label)); - for (let task of tasks) { - entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this.showDetail() ? task.configurationProperties.detail : undefined }); - if (!ContributedTask.is(task)) { - needsCreateOrOpen = false; - } + let tasks = taskMap.all(); + if (tasks.length > 0) { + tasks = tasks.sort((a, b) => a._label.localeCompare(b._label)); + for (let task of tasks) { + entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this.showDetail() ? task.configurationProperties.detail : undefined }); + if (!ContributedTask.is(task)) { + needsCreateOrOpen = false; } } - if (needsCreateOrOpen) { - let label = stats[0] !== undefined ? openLabel : createLabel; - if (entries.length) { - entries.push({ type: 'separator' }); - } - entries.push({ label, folder: this.contextService.getWorkspace().folders[0] }); - } - } else { - let folders = this.contextService.getWorkspace().folders; - let index = 0; - for (let folder of folders) { - let tasks = taskMap.get(folder); - if (tasks.length > 0) { - tasks = tasks.slice().sort((a, b) => a._label.localeCompare(b._label)); - for (let i = 0; i < tasks.length; i++) { - let entry: TaskQuickPickEntryType = { label: tasks[i]._label, task: tasks[i], description: this.getTaskDescription(tasks[i]) }; - if (i === 0) { - entries.push({ type: 'separator', label: folder.name }); - } - entries.push(entry); - } - } else { - let label = stats[index] !== undefined ? openLabel : createLabel; - let entry: TaskQuickPickEntryType = { label, folder: folder }; - entries.push({ type: 'separator', label: folder.name }); - entries.push(entry); - } - index++; + } + if (needsCreateOrOpen) { + let label = stats[0] !== undefined ? openLabel : createLabel; + if (entries.length) { + entries.push({ type: 'separator' }); } + entries.push({ label, folder: this.contextService.getWorkspace().folders[0] }); } if ((entries.length === 1) && !needsCreateOrOpen) { tokenSource.cancel(); diff --git a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts index 4b20d3ac1..40899ecaf 100644 --- a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts +++ b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts @@ -9,7 +9,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ITaskService, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; import { forEach } from 'vs/base/common/collections'; import { RunOnOptions, Task, TaskRunSource, TASKS_CATEGORY } from 'vs/workbench/contrib/tasks/common/tasks'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { Action2 } from 'vs/platform/actions/common/actions'; @@ -110,14 +110,14 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut label: nls.localize('allow', "Allow and run"), run: () => { resolve(true); - storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, true, StorageScope.WORKSPACE); + storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, true, StorageScope.WORKSPACE, StorageTarget.MACHINE); } }, { label: nls.localize('disallow', "Disallow"), run: () => { resolve(false); - storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, false, StorageScope.WORKSPACE); + storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, false, StorageScope.WORKSPACE, StorageTarget.MACHINE); } }, { @@ -156,6 +156,6 @@ export class ManageAutomaticTaskRunning extends Action2 { return; } - storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, value === allowItem, StorageScope.WORKSPACE); + storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, value === allowItem, StorageScope.WORKSPACE, StorageTarget.MACHINE); } } diff --git a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts index cd19578ce..a423d7665 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts @@ -9,12 +9,15 @@ import { Task, ContributedTask, CustomTask, ConfiguringTask, TaskSorter, KeyedTa import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import * as Types from 'vs/base/common/types'; import { ITaskService, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; -import { IQuickPickItem, QuickPickInput, IQuickPick } from 'vs/base/parts/quickinput/common/quickInput'; +import { IQuickPickItem, QuickPickInput, IQuickPick, IQuickInputButton } from 'vs/base/parts/quickinput/common/quickInput'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { Disposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { Codicon } from 'vs/base/common/codicons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail'; export const QUICKOPEN_SKIP_CONFIG = 'task.quickOpen.skip'; @@ -32,6 +35,9 @@ export interface TaskTwoLevelQuickPickEntry extends IQuickPickItem { const SHOW_ALL: string = nls.localize('taskQuickPick.showAll', "Show All Tasks..."); +export const configureTaskIcon = registerIcon('tasks-list-configure', Codicon.gear, nls.localize('configureTaskIcon', 'Configration icon in the tasks selection list.')); +const removeTaskIcon = registerIcon('tasks-remove', Codicon.close, nls.localize('removeTaskIcon', 'Icon for remove in the tasks selection list.')); + export class TaskQuickPick extends Disposable { private sorter: TaskSorter; private topLevelEntries: QuickPickInput[] | undefined; @@ -63,16 +69,17 @@ export class TaskQuickPick extends Disposable { return ''; } - private createTaskEntry(task: Task | ConfiguringTask): TaskTwoLevelQuickPickEntry { + private createTaskEntry(task: Task | ConfiguringTask, extraButtons: IQuickInputButton[] = []): TaskTwoLevelQuickPickEntry { const entry: TaskTwoLevelQuickPickEntry = { label: this.guessTaskLabel(task), description: this.taskService.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined }; - entry.buttons = [{ iconClass: 'codicon-gear', tooltip: nls.localize('configureTask', "Configure Task") }]; + entry.buttons = [{ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") }, ...extraButtons]; return entry; } - private createEntriesForGroup(entries: QuickPickInput[], tasks: (Task | ConfiguringTask)[], groupLabel: string) { + private createEntriesForGroup(entries: QuickPickInput[], tasks: (Task | ConfiguringTask)[], + groupLabel: string, extraButtons: IQuickInputButton[] = []) { entries.push({ type: 'separator', label: groupLabel }); tasks.forEach(task => { - entries.push(this.createTaskEntry(task)); + entries.push(this.createTaskEntry(task, extraButtons)); }); } @@ -143,7 +150,11 @@ export class TaskQuickPick extends Disposable { let dedupedConfiguredTasks: (Task | ConfiguringTask)[] = dedupeAndPrune.configuredTasks; recentTasks = dedupeAndPrune.recentTasks; if (recentTasks.length > 0) { - this.createEntriesForGroup(this.topLevelEntries, recentTasks, nls.localize('recentlyUsed', 'recently used')); + const removeRecentButton: IQuickInputButton = { + iconClass: ThemeIcon.asClassName(removeTaskIcon), + tooltip: nls.localize('removeRecent', 'Remove Recently Used Task') + }; + this.createEntriesForGroup(this.topLevelEntries, recentTasks, nls.localize('recentlyUsed', 'recently used'), [removeRecentButton]); } if (configuredTasks.length > 0) { if (dedupedConfiguredTasks.length > 0) { @@ -171,6 +182,18 @@ export class TaskQuickPick extends Disposable { picker.onDidTriggerItemButton(async (context) => { let task = context.item.task; + if (task && !Types.isString(task) && context.button.iconClass === ThemeIcon.asClassName(removeTaskIcon)) { + const key = task.getRecentlyUsedKey(); + if (key) { + this.taskService.removeRecentlyUsedTask(key); + const indexToRemove = picker.items.indexOf(context.item); + if (indexToRemove >= 0) { + picker.items = [...picker.items.slice(0, indexToRemove), ...picker.items.slice(indexToRemove + 1)]; + } + } + return; + } + this.quickInputService.cancel(); if (ContributedTask.is(task)) { this.taskService.customize(task, undefined, true); @@ -226,10 +249,10 @@ export class TaskQuickPick extends Disposable { private async doPickerSecondLevel(picker: IQuickPick, type: string) { picker.busy = true; - picker.value = ''; if (type === SHOW_ALL) { picker.items = (await this.taskService.tasks()).sort((a, b) => this.sorter.compare(a, b)).map(task => this.createTaskEntry(task)); } else { + picker.value = ''; picker.items = await this.getEntriesForProvider(type); } picker.busy = false; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index f96226900..b011e99c7 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -47,6 +47,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { env as processEnv, cwd as processCwd } from 'vs/base/common/process'; import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { ILogService } from 'vs/platform/log/common/log'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; interface TerminalData { terminal: ITerminalInstance; @@ -204,6 +205,7 @@ export class TerminalTaskSystem implements ITaskSystem { private pathService: IPathService, private viewDescriptorService: IViewDescriptorService, private logService: ILogService, + private configurationService: IConfigurationService, taskSystemInfoResolver: TaskSystemInfoResolver, ) { @@ -247,7 +249,7 @@ export class TerminalTaskSystem implements ITaskSystem { } try { - const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this.executeTask(task, resolver, trigger) }; + const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this.executeTask(task, resolver, trigger, new Set()) }; executeResult.promise.then(summary => { this.lastTask = this.currentTask; }); @@ -435,7 +437,21 @@ export class TerminalTaskSystem implements ITaskSystem { return Promise.all(promises); } - private async executeTask(task: Task, resolver: ITaskResolver, trigger: string, alreadyResolved?: Map): Promise { + + private showDependencyCycleMessage(task: Task) { + this.log(nls.localize('dependencyCycle', + 'There is a dependency cycle. See task "{0}".', + task._label + )); + this.showOutput(); + } + + private async executeTask(task: Task, resolver: ITaskResolver, trigger: string, encounteredDependencies: Set, alreadyResolved?: Map): Promise { + if (encounteredDependencies.has(task.getCommonTaskId())) { + this.showDependencyCycleMessage(task); + return {}; + } + alreadyResolved = alreadyResolved ?? new Map(); let promises: Promise[] = []; if (task.configurationProperties.dependsOn) { @@ -446,7 +462,8 @@ export class TerminalTaskSystem implements ITaskSystem { let promise = this.activeTasks[key] ? this.activeTasks[key].promise : undefined; if (!promise) { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.DependsOnStarted, task)); - promise = this.executeDependencyTask(dependencyTask, resolver, trigger, alreadyResolved); + encounteredDependencies.add(task.getCommonTaskId()); + promise = this.executeDependencyTask(dependencyTask, resolver, trigger, encounteredDependencies, alreadyResolved); } promises.push(promise); if (task.configurationProperties.dependsOrder === DependsOrder.sequence) { @@ -472,6 +489,7 @@ export class TerminalTaskSystem implements ITaskSystem { if ((ContributedTask.is(task) || CustomTask.is(task)) && (task.command)) { return Promise.all(promises).then((summaries): Promise | ITaskSummary => { + encounteredDependencies.delete(task.getCommonTaskId()); for (let summary of summaries) { if (summary.exitCode !== 0) { this.removeInstances(task); @@ -486,6 +504,7 @@ export class TerminalTaskSystem implements ITaskSystem { }); } else { return Promise.all(promises).then((summaries): ITaskSummary => { + encounteredDependencies.delete(task.getCommonTaskId()); for (let summary of summaries) { if (summary.exitCode !== 0) { return { exitCode: summary.exitCode }; @@ -496,11 +515,11 @@ export class TerminalTaskSystem implements ITaskSystem { } } - private async executeDependencyTask(task: Task, resolver: ITaskResolver, trigger: string, alreadyResolved?: Map): Promise { + private async executeDependencyTask(task: Task, resolver: ITaskResolver, trigger: string, encounteredDependencies: Set, alreadyResolved?: Map): Promise { // If the task is a background task with a watching problem matcher, we don't wait for the whole task to finish, // just for the problem matcher to go inactive. if (!task.configurationProperties.isBackground) { - return this.executeTask(task, resolver, trigger, alreadyResolved); + return this.executeTask(task, resolver, trigger, encounteredDependencies, alreadyResolved); } const inactivePromise = new Promise(resolve => { @@ -511,7 +530,7 @@ export class TerminalTaskSystem implements ITaskSystem { } }); }); - return Promise.race([inactivePromise, this.executeTask(task, resolver, trigger, alreadyResolved)]); + return Promise.race([inactivePromise, this.executeTask(task, resolver, trigger, encounteredDependencies, alreadyResolved)]); } private async resolveAndFindExecutable(systemInfo: TaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, cwd: string | undefined, envPath: string | undefined): Promise { @@ -856,7 +875,6 @@ export class TerminalTaskSystem implements ITaskSystem { }); promise = new Promise((resolve, reject) => { const onExit = terminal!.onExit((exitCode) => { - onData.dispose(); onExit.dispose(); let key = task.getMapKey(); this.removeFromActiveTasks(task); @@ -887,8 +905,12 @@ export class TerminalTaskSystem implements ITaskSystem { // There is nothing else to do here. } } - startStopProblemMatcher.done(); - startStopProblemMatcher.dispose(); + // Hack to work around #92868 until terminal is fixed. + setTimeout(() => { + onData.dispose(); + startStopProblemMatcher.done(); + startStopProblemMatcher.dispose(); + }, 100); if (!processStartedSignaled && terminal) { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId!)); processStartedSignaled = true; @@ -1019,9 +1041,17 @@ export class TerminalTaskSystem implements ITaskSystem { if (!shellSpecified) { // Under Mac remove -l to not start it as a login shell. if (platform === Platform.Platform.Mac) { - let index = shellArgs.indexOf('-l'); - if (index !== -1) { - shellArgs.splice(index, 1); + // Background on -l on osx https://github.com/microsoft/vscode/issues/107563 + const osxShellArgs = this.configurationService.inspect('terminal.integrated.shellArgs.osx'); + if ((osxShellArgs.user === undefined) && (osxShellArgs.userLocal === undefined) && (osxShellArgs.userLocalValue === undefined) + && (osxShellArgs.userRemote === undefined) && (osxShellArgs.userRemoteValue === undefined) + && (osxShellArgs.userValue === undefined) && (osxShellArgs.workspace === undefined) + && (osxShellArgs.workspaceFolder === undefined) && (osxShellArgs.workspaceFolderValue === undefined) + && (osxShellArgs.workspaceValue === undefined)) { + let index = shellArgs.indexOf('-l'); + if (index !== -1) { + shellArgs.splice(index, 1); + } } } toAdd.push('-c'); diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index 46574e022..5e6c6f746 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -14,6 +14,7 @@ import { ILineMatcher, createLineMatcher, ProblemMatcher, ProblemMatch, ApplyToK import { IMarkerService, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { generateUuid } from 'vs/base/common/uuid'; import { IFileService } from 'vs/platform/files/common/files'; +import { isWindows } from 'vs/base/common/platform'; export const enum ProblemCollectorEventKind { BackgroundProcessingBegins = 'backgroundProcessingBegins', @@ -282,7 +283,7 @@ export abstract class AbstractProblemCollector implements IDisposable { let existingMarker; if (!markersPerResource.has(key)) { markersPerResource.set(key, marker); - } else if (((existingMarker = markersPerResource.get(key)) !== undefined) && existingMarker.message.length < marker.message.length) { + } else if (((existingMarker = markersPerResource.get(key)) !== undefined) && (existingMarker.message.length < marker.message.length) && isWindows) { // Most likely https://github.com/microsoft/vscode/issues/77475 // Heuristic dictates that when the key is the same and message is smaller, we have hit this limitation. markersPerResource.set(key, marker); diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index 19e45d105..8b38d041a 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -77,6 +77,7 @@ export interface ITaskService { taskTypes(): string[]; getWorkspaceTasks(runSource?: TaskRunSource): Promise>; readRecentTasks(): Promise<(Task | ConfiguringTask)[]>; + removeRecentlyUsedTask(taskRecentlyUsedKey: string): void; /** * @param alias The task's name, label or defined identifier. */ diff --git a/src/vs/workbench/contrib/tasks/electron-browser/taskService.ts b/src/vs/workbench/contrib/tasks/electron-browser/taskService.ts index 70145f1b9..64fe0389f 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/taskService.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/taskService.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import * as Objects from 'vs/base/common/objects'; import * as semver from 'vs/base/common/semver/semver'; import { IStringDictionary } from 'vs/base/common/collections'; -import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { WorkbenchState, IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ITaskSystem } from 'vs/workbench/contrib/tasks/common/taskSystem'; import { ExecutionEngine, TaskRunSource } from 'vs/workbench/contrib/tasks/common/tasks'; import * as TaskConfig from '../common/taskConfiguration'; @@ -15,6 +16,36 @@ import { ProcessRunnerDetector } from 'vs/workbench/contrib/tasks/node/processRu import { AbstractTaskService } from 'vs/workbench/contrib/tasks/browser/abstractTaskService'; import { TaskFilter, ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { TerminalTaskSystem } from 'vs/workbench/contrib/tasks/browser/terminalTaskSystem'; +import { IConfirmationResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { TerminateResponseCode } from 'vs/base/common/processes'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; +import { IOutputService } from 'vs/workbench/contrib/output/common/output'; +import { ITerminalService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; interface WorkspaceFolderConfigurationResult { workspaceFolder: IWorkspaceFolder; @@ -24,6 +55,69 @@ interface WorkspaceFolderConfigurationResult { export class TaskService extends AbstractTaskService { private _configHasErrors: boolean = false; + constructor(@IConfigurationService configurationService: IConfigurationService, + @IMarkerService markerService: IMarkerService, + @IOutputService outputService: IOutputService, + @IPanelService panelService: IPanelService, + @IViewsService viewsService: IViewsService, + @ICommandService commandService: ICommandService, + @IEditorService editorService: IEditorService, + @IFileService fileService: IFileService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @ITelemetryService telemetryService: ITelemetryService, + @ITextFileService textFileService: ITextFileService, + @ILifecycleService lifecycleService: ILifecycleService, + @IModelService modelService: IModelService, + @IExtensionService extensionService: IExtensionService, + @IQuickInputService quickInputService: IQuickInputService, + @IConfigurationResolverService configurationResolverService: IConfigurationResolverService, + @ITerminalService terminalService: ITerminalService, + @IStorageService storageService: IStorageService, + @IProgressService progressService: IProgressService, + @IOpenerService openerService: IOpenerService, + @IHostService _hostService: IHostService, + @IDialogService dialogService: IDialogService, + @INotificationService notificationService: INotificationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @ITerminalInstanceService terminalInstanceService: ITerminalInstanceService, + @IPathService pathService: IPathService, + @ITextModelService textModelResolverService: ITextModelService, + @IPreferencesService preferencesService: IPreferencesService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @ILogService logService: ILogService) { + super(configurationService, + markerService, + outputService, + panelService, + viewsService, + commandService, + editorService, + fileService, + contextService, + telemetryService, + textFileService, + modelService, + extensionService, + quickInputService, + configurationResolverService, + terminalService, + storageService, + progressService, + openerService, + _hostService, + dialogService, + notificationService, + contextKeyService, + environmentService, + terminalInstanceService, + pathService, + textModelResolverService, + preferencesService, + viewDescriptorService, + logService); + this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown()))); + } protected getTaskSystem(): ITaskSystem { if (this._taskSystem) { @@ -133,6 +227,64 @@ export class TaskService extends AbstractTaskService { } return result; } + + public beforeShutdown(): boolean | Promise { + if (!this._taskSystem) { + return false; + } + if (!this._taskSystem.isActiveSync()) { + return false; + } + // The terminal service kills all terminal on shutdown. So there + // is nothing we can do to prevent this here. + if (this._taskSystem instanceof TerminalTaskSystem) { + return false; + } + + let terminatePromise: Promise; + if (this._taskSystem.canAutoTerminate()) { + terminatePromise = Promise.resolve({ confirmed: true }); + } else { + terminatePromise = this.dialogService.confirm({ + message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'), + primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"), + type: 'question' + }); + } + + return terminatePromise.then(res => { + if (res.confirmed) { + return this._taskSystem!.terminateAll().then((responses) => { + let success = true; + let code: number | undefined = undefined; + for (let response of responses) { + success = success && response.success; + // We only have a code in the old output runner which only has one task + // So we can use the first code. + if (code === undefined && response.code !== undefined) { + code = response.code; + } + } + if (success) { + this._taskSystem = undefined; + this.disposeTaskSystemListeners(); + return false; // no veto + } else if (code && code === TerminateResponseCode.ProcessNotFound) { + return this.dialogService.confirm({ + message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'), + primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"), + type: 'info' + }).then(res => !res.confirmed); + } + return true; // veto + }, (err) => { + return true; // veto + }); + } + + return true; // veto + }); + } } registerSingleton(ITaskService, TaskService, true); diff --git a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts index 778410948..9e1ecd640 100644 --- a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts @@ -10,12 +10,13 @@ import * as UUID from 'vs/base/common/uuid'; import * as Platform from 'vs/base/common/platform'; import { ValidationStatus } from 'vs/base/common/parsers'; import { ProblemMatcher, FileLocationKind, ProblemPattern, ApplyToKind } from 'vs/workbench/contrib/tasks/common/problemMatcher'; -import { WorkspaceFolder, Workspace, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { WorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; import * as Tasks from 'vs/workbench/contrib/tasks/common/tasks'; import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask, TaskConfigSource } from 'vs/workbench/contrib/tasks/common/taskConfiguration'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IContext } from 'vs/platform/contextkey/common/contextkey'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; const workspaceFolder: WorkspaceFolder = new WorkspaceFolder({ uri: URI.file('/workspace/folderOne'), diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 004f9217c..511c82963 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -31,7 +31,7 @@ type TelemetryData = { ext: string; path: number; reason?: number; - whitelistedjson?: string; + allowlistedjson?: string; }; type FileTelemetryDataFragment = { @@ -39,13 +39,13 @@ type FileTelemetryDataFragment = { ext: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; path: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; reason?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - whitelistedjson?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + allowlistedjson?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; }; export class TelemetryContribution extends Disposable implements IWorkbenchContribution { - private static WHITELIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; - private static WHITELIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; + private static ALLOWLIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; + private static ALLOWLIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; constructor( @ITelemetryService private readonly telemetryService: ITelemetryService, @@ -184,7 +184,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr for (const folder of folders) { if (isEqualOrParent(resource, folder.toResource('.vscode'))) { const filename = basename(resource); - if (TelemetryContribution.WHITELIST_WORKSPACE_JSON.indexOf(filename) > -1) { + if (TelemetryContribution.ALLOWLIST_WORKSPACE_JSON.indexOf(filename) > -1) { return `.vscode/${filename}`; } } @@ -202,11 +202,11 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr ext, path: hash(path), reason, - whitelistedjson: undefined as string | undefined + allowlistedjson: undefined as string | undefined }; - if (ext === '.json' && TelemetryContribution.WHITELIST_JSON.indexOf(fileName) > -1) { - telemetryData['whitelistedjson'] = fileName; + if (ext === '.json' && TelemetryContribution.ALLOWLIST_JSON.indexOf(fileName) > -1) { + telemetryData['allowlistedjson'] = fileName; } return telemetryData; diff --git a/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts b/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts index 09750a14e..d91501663 100644 --- a/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts +++ b/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts @@ -7,6 +7,8 @@ import { IEnvironmentVariableInfo, IMergedEnvironmentVariableCollection, IMerged import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { localize } from 'vs/nls'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { Codicon } from 'vs/base/common/codicons'; export class EnvironmentVariableInfoStale implements IEnvironmentVariableInfo { readonly requiresAction = true; @@ -53,8 +55,8 @@ export class EnvironmentVariableInfoStale implements IEnvironmentVariableInfo { return info; } - getIcon(): string { - return 'warning'; + getIcon(): ThemeIcon { + return Codicon.warning; } getActions(): { label: string, iconClass?: string, run: () => void, commandId: string }[] { @@ -83,8 +85,8 @@ export class EnvironmentVariableInfoChangesActive implements IEnvironmentVariabl return message + '\n\n```\n' + changes.join('\n') + '\n```'; } - getIcon(): string { - return 'info'; + getIcon(): ThemeIcon { + return Codicon.info; } } diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts index 5c0e8243f..fb420ef52 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts @@ -49,6 +49,8 @@ export const unixLineAndColumnMatchIndex = 11; // Each line and column clause have 6 groups (ie no. of expressions in round brackets) export const lineAndColumnClauseGroupCount = 6; +const MAX_LENGTH = 2000; + export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider { constructor( private readonly _xterm: Terminal, @@ -85,6 +87,9 @@ export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider } const text = getXtermLineContent(this._xterm.buffer.active, startLine, endLine, this._xterm.cols); + if (text.length > MAX_LENGTH) { + return []; + } // clone regex to do a global search on text const rex = new RegExp(this._localLinkRegex, 'g'); diff --git a/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css b/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css index da4ecfc43..5732fb11c 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css +++ b/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css @@ -11,6 +11,10 @@ transition: background-color 800ms linear; } +.monaco-workbench .pane-body.integrated-terminal .xterm-viewport { + scrollbar-width: thin; +} + .monaco-workbench .pane-body.integrated-terminal .xterm-viewport::-webkit-scrollbar { width: 10px; } diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 8c394244f..43de91e60 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -196,7 +196,3 @@ padding: 0 22px 0 6px; } -/* HACK: Can remove when fixed upstream https://github.com/xtermjs/xterm.js/issues/3058 */ -.xterm-helper-textarea { - border: 0px; -} diff --git a/src/vs/workbench/contrib/terminal/browser/media/xterm.css b/src/vs/workbench/contrib/terminal/browser/media/xterm.css index 712baa284..1ac20b3df 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/xterm.css +++ b/src/vs/workbench/contrib/terminal/browser/media/xterm.css @@ -62,10 +62,10 @@ } .xterm .xterm-helper-textarea { - /* - * HACK: to fix IE's blinking cursor - * Move textarea out of the screen to the far left, so that the cursor is not visible. - */ + padding: 0; + border: 0; + margin: 0; + /* Move textarea out of the screen to the far left, so that the cursor is not visible */ position: absolute; opacity: 0; left: -9999em; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 34e4327a1..61d5525a7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -36,6 +36,7 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess'; import { terminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; +import { terminalViewIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; // Register services registerSingleton(ITerminalService, TerminalService, true); @@ -63,7 +64,7 @@ configurationRegistry.registerConfiguration(terminalConfiguration); const VIEW_CONTAINER = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: TERMINAL_VIEW_ID, name: nls.localize('terminal', "Terminal"), - icon: 'codicon-terminal', + icon: terminalViewIcon, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [TERMINAL_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: TERMINAL_VIEW_ID, focusCommand: { id: TERMINAL_COMMAND_ID.FOCUS }, @@ -74,7 +75,7 @@ Registry.as(panel.Extensions.Panels).setDefaultPanelId(TERM Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: TERMINAL_VIEW_ID, name: nls.localize('terminal', "Terminal"), - containerIcon: 'codicon-terminal', + containerIcon: terminalViewIcon, canToggleVisibility: false, canMoveView: true, ctorDescriptor: new SyncDescriptor(TerminalViewPane) @@ -150,16 +151,17 @@ if (BrowserFeatures.clipboard.readText) { win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V] }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); - // An extra Windows-only ctrl+v keybinding is used for pwsh that sends ctrl+v directly to the - // shell, this gets handled by PSReadLine which properly handles multi-line pastes. This is - // disabled in accessibility mode as PowerShell does not run PSReadLine when it detects a screen - // reader. - if (platform.isWindows) { - registerSendSequenceKeybinding(String.fromCharCode('V'.charCodeAt(0) - CTRL_LETTER_OFFSET), { // ctrl+v - when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, ContextKeyExpr.equals(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, WindowsShellType.PowerShell), CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()), - primary: KeyMod.CtrlCmd | KeyCode.KEY_V - }); - } +} + +// An extra Windows-only ctrl+v keybinding is used for pwsh that sends ctrl+v directly to the +// shell, this gets handled by PSReadLine which properly handles multi-line pastes. This is +// disabled in accessibility mode as PowerShell does not run PSReadLine when it detects a screen +// reader. This works even when clipboard.readText is not supported. +if (platform.isWindows) { + registerSendSequenceKeybinding(String.fromCharCode('V'.charCodeAt(0) - CTRL_LETTER_OFFSET), { // ctrl+v + when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, ContextKeyExpr.equals(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, WindowsShellType.PowerShell), CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()), + primary: KeyMod.CtrlCmd | KeyCode.KEY_V + }); } // Delete word left: ctrl+w diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index bb382e0f9..1f58d65ea 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -59,7 +59,6 @@ export interface ITerminalTab { onDisposed: Event; onInstancesChanged: Event; - setWillFocus(focus: boolean): void; focusPreviousPane(): void; focusNextPane(): void; resizePane(direction: Direction): void; @@ -71,6 +70,11 @@ export interface ITerminalTab { split(shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; } +export const enum TerminalConnectionState { + Connecting, + Connected +} + export interface ITerminalService { readonly _serviceBrand: undefined; @@ -79,6 +83,7 @@ export interface ITerminalService { terminalInstances: ITerminalInstance[]; terminalTabs: ITerminalTab[]; isProcessSupportRegistered: boolean; + readonly connectionState: TerminalConnectionState; initializeTerminals(): Promise; onActiveTabChanged: Event; @@ -95,6 +100,7 @@ export interface ITerminalService { onActiveInstanceChanged: Event; onRequestAvailableShells: Event; onDidRegisterProcessSupport: Event; + onDidChangeConnectionState: Event; /** * Creates a terminal. @@ -416,6 +422,11 @@ export interface ITerminalInstance { */ notifyFindWidgetFocusChanged(isFocused: boolean): void; + /** + * Notifies the terminal to refresh its focus state based on the active document elemnet in DOM + */ + refreshFocusState(): void; + /** * Focuses the terminal instance if it's able to (xterm.js instance exists). * diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 953b22493..63264f9ab 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -9,7 +9,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { TERMINAL_VIEW_ID, ITerminalConfigHelper, TitleEventSource, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, IRemoteTerminalAttachTarget } from 'vs/workbench/contrib/terminal/common/terminal'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IQuickInputService, IPickOptions, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -24,7 +24,7 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { ITerminalInstance, ITerminalService, Direction, IRemoteTerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstance, ITerminalService, Direction, IRemoteTerminalService, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal'; import { Action2, registerAction2, ILocalizedString } from 'vs/platform/actions/common/actions'; import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess'; import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions'; @@ -41,6 +41,9 @@ import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewIte import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions'; import { ILabelService } from 'vs/platform/label/common/label'; import { RemoteNameContext } from 'vs/workbench/browser/contextkeys'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { killTerminalIcon, newTerminalIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; +import { Codicon } from 'vs/base/common/codicons'; async function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { switch (configHelper.config.splitCwd) { @@ -95,7 +98,7 @@ export class KillTerminalAction extends Action { id: string, label: string, @ITerminalService private readonly _terminalService: ITerminalService ) { - super(id, label, 'terminal-action codicon-trash'); + super(id, label, 'terminal-action ' + ThemeIcon.asClassName(killTerminalIcon)); } async run() { @@ -175,7 +178,7 @@ export class CreateNewTerminalAction extends Action { @ICommandService private readonly _commandService: ICommandService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService ) { - super(id, label, 'terminal-action codicon-add'); + super(id, label, 'terminal-action ' + ThemeIcon.asClassName(newTerminalIcon)); } async run(event?: any) { @@ -216,8 +219,8 @@ export class SplitTerminalAction extends Action { public static readonly ID = TERMINAL_COMMAND_ID.SPLIT; public static readonly LABEL = localize('workbench.action.terminal.split', "Split Terminal"); public static readonly SHORT_LABEL = localize('workbench.action.terminal.split.short', "Split"); - public static readonly HORIZONTAL_CLASS = 'terminal-action codicon-split-horizontal'; - public static readonly VERTICAL_CLASS = 'terminal-action codicon-split-vertical'; + public static readonly HORIZONTAL_CLASS = 'terminal-action ' + Codicon.splitHorizontal.classNames; + public static readonly VERTICAL_CLASS = 'terminal-action ' + Codicon.splitVertical.classNames; constructor( id: string, label: string, @@ -295,6 +298,23 @@ export class SelectDefaultShellWindowsTerminalAction extends Action { } } +export class ConfigureTerminalSettingsAction extends Action { + + public static readonly ID = TERMINAL_COMMAND_ID.CONFIGURE_TERMINAL_SETTINGS; + public static readonly LABEL = localize('workbench.action.terminal.openSettings', "Configure Terminal Settings"); + + constructor( + id: string, label: string, + @IPreferencesService private readonly _preferencesService: IPreferencesService + ) { + super(id, label); + } + + async run() { + this._preferencesService.openSettings(false, '@feature:terminal'); + } +} + const terminalIndexRe = /^([0-9]+): /; export class SwitchTerminalAction extends Action { @@ -307,6 +327,7 @@ export class SwitchTerminalAction extends Action { @ITerminalService private readonly _terminalService: ITerminalService, @ITerminalContributionService private readonly _contributions: ITerminalContributionService, @ICommandService private readonly _commands: ICommandService, + @IPreferencesService private readonly preferencesService: IPreferencesService ) { super(id, label, 'terminal-action switch-terminal'); } @@ -323,7 +344,11 @@ export class SwitchTerminalAction extends Action { this._terminalService.refreshActiveTab(); return this._terminalService.selectDefaultShell(); } - + if (item === ConfigureTerminalSettingsAction.LABEL) { + const settingsAction = new ConfigureTerminalSettingsAction(ConfigureTerminalSettingsAction.ID, ConfigureTerminalSettingsAction.LABEL, this.preferencesService); + settingsAction.run(); + this._terminalService.refreshActiveTab(); + } const indexMatches = terminalIndexRe.exec(item); if (indexMatches) { this._terminalService.setActiveTabByIndex(Number(indexMatches[1]) - 1); @@ -357,6 +382,7 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { this._register(_terminalService.onActiveTabChanged(this._updateItems, this)); this._register(_terminalService.onInstanceTitleChanged(this._updateItems, this)); this._register(_terminalService.onTabDisposed(this._updateItems, this)); + this._register(_terminalService.onDidChangeConnectionState(this._updateItems, this)); this._register(attachSelectBoxStyler(this.selectBox, this._themeService)); } @@ -374,7 +400,10 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { } function getTerminalSelectOpenItems(terminalService: ITerminalService, contributions: ITerminalContributionService): ISelectOptionItem[] { - const items = terminalService.getTabLabels().map(label => { text: label }); + const items = terminalService.connectionState === TerminalConnectionState.Connected ? + terminalService.getTabLabels().map(label => { text: label }) : + [{ text: localize('terminalConnectingLabel', "Starting...") }]; + items.push({ text: SwitchTerminalActionViewItem.SEPARATOR, isDisabled: true }); for (const contributed of contributions.terminalTypes) { @@ -382,6 +411,7 @@ function getTerminalSelectOpenItems(terminalService: ITerminalService, contribut } items.push({ text: SelectDefaultShellWindowsTerminalAction.LABEL }); + items.push({ text: ConfigureTerminalSettingsAction.LABEL }); return items; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 6dc113471..105d4cbfc 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITerminalConfiguration, ITerminalFont, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro, IShellLaunchConfig, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_FONT_WEIGHT, DEFAULT_BOLD_FONT_WEIGHT, FontWeight } from 'vs/workbench/contrib/terminal/common/terminal'; import Severity from 'vs/base/common/severity'; import { INotificationService, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; @@ -20,7 +20,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IProductService } from 'vs/platform/product/common/productService'; import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; const MINIMUM_FONT_SIZE = 6; const MAXIMUM_FONT_SIZE = 25; @@ -51,7 +50,6 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { @ITelemetryService private readonly telemetryService: ITelemetryService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IProductService private readonly productService: IProductService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { this._updateConfig(); this._configurationService.onDidChangeConfiguration(e => { @@ -59,9 +57,6 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { this._updateConfig(); } }); - - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: 'terminalConfigHelper/launchRecommendationsIgnore', version: 1 }); } public setLinuxDistro(linuxDistro: LinuxDistro) { @@ -211,7 +206,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { public setWorkspaceShellAllowed(isAllowed: boolean): void { this._onWorkspacePermissionsChanged.fire(isAllowed); - this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, isAllowed, StorageScope.WORKSPACE); + this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, isAllowed, StorageScope.WORKSPACE, StorageTarget.MACHINE); } public isWorkspaceShellAllowed(defaultValue: boolean | undefined = undefined): boolean | undefined { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalIcons.ts b/src/vs/workbench/contrib/terminal/browser/terminalIcons.ts new file mode 100644 index 000000000..88d2b3131 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalIcons.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const terminalViewIcon = registerIcon('terminal-view-icon', Codicon.terminal, localize('terminalViewIcon', 'View icon of the terminal view.')); + +export const renameTerminalIcon = registerIcon('terminal-rename', Codicon.gear, localize('renameTerminalIcon', 'Icon for rename in the terminal quick menu.')); +export const killTerminalIcon = registerIcon('terminal-kill', Codicon.trash, localize('killTerminalIcon', 'Icon for killing a terminal instance.')); +export const newTerminalIcon = registerIcon('terminal-new', Codicon.add, localize('newTerminalIcon', 'Icon for creating a new terminal instance.')); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 675990117..08dfc59ed 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -20,12 +20,12 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { IShellLaunchConfig, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalLaunchError, IProcessDataEvent, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalLaunchError, IProcessDataEvent, ITerminalDimensionsOverride, SHOW_TERMINAL_CONFIG_PROMPT } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; @@ -45,6 +45,8 @@ import { EnvironmentVariableInfoWidget } from 'vs/workbench/contrib/terminal/bro import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { TerminalLaunchHelpAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -186,6 +188,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @INotificationService private readonly _notificationService: INotificationService, + @IPreferencesService private readonly _preferencesService: IPreferencesService, @IViewsService private readonly _viewsService: IViewsService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IClipboardService private readonly _clipboardService: IClipboardService, @@ -541,9 +544,29 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return false; } - // Skip processing by xterm.js of keyboard events that resolve to commands described - // within commandsToSkipShell - if (resolveResult && this._skipTerminalCommands.some(k => k === resolveResult.commandId)) { + // for keyboard events that resolve to commands described + // within commandsToSkipShell, either alert or skip processing by xterm.js + if (resolveResult && this._skipTerminalCommands.some(k => k === resolveResult.commandId) && !this._configHelper.config.sendKeybindingsToShell) { + // don't alert when terminal is opened or closed + if (this._storageService.getBoolean(SHOW_TERMINAL_CONFIG_PROMPT, StorageScope.GLOBAL, true) && + resolveResult.commandId !== 'workbench.action.terminal.toggleTerminal' && + resolveResult.commandId !== 'workbench.action.terminal.new' && + resolveResult.commandId !== 'workbench.action.togglePanel' && + resolveResult.commandId !== 'workbench.action.terminal.focus') { + this._notificationService.prompt( + Severity.Info, + nls.localize('configure terminal settings', "Some keybindings are dispatched to the workbench by default."), + [ + { + label: nls.localize('configureTerminalSettings', "Configure Terminal Settings"), + run: () => { + this._preferencesService.openSettings(false, '@id:terminal.integrated.commandsToSkipShell,terminal.integrated.sendKeybindingsToShell,terminal.integrated.allowChords'); + } + } as IPromptChoice + ] + ); + this._storageService.store(SHOW_TERMINAL_CONFIG_PROMPT, false, StorageScope.GLOBAL, StorageTarget.USER); + } event.preventDefault(); return false; } @@ -564,6 +587,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return false; } + // Fallback to force ctrl+v to paste on browsers that do not support + // navigator.clipboard.readText + if (!BrowserFeatures.clipboard.readText && event.key === 'v' && event.ctrlKey) { + return false; + } + return true; }); this._register(dom.addDisposableListener(xterm.element, 'mousedown', () => { @@ -656,7 +685,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { { label: nls.localize('dontShowAgain', "Don't Show Again"), isSecondary: true, - run: () => this._storageService.store(NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, true, StorageScope.GLOBAL) + run: () => this._storageService.store(NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, true, StorageScope.GLOBAL, StorageTarget.MACHINE) } as IPromptChoice ]; this._notificationService.prompt( @@ -728,6 +757,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._terminalFocusContextKey.set(terminalFocused); } + public refreshFocusState() { + this.notifyFindWidgetFocusChanged(false); + } + public dispose(immediate?: boolean): void { this._logService.trace(`terminalInstance#dispose (id: ${this.id})`); @@ -1404,7 +1437,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // maximize on Windows/Linux would fire an event saying that the terminal was not // visible. if (this._xterm.getOption('rendererType') === 'canvas') { - this._xtermCore._renderService._onIntersectionChange({ intersectionRatio: 1 }); + this._xtermCore._renderService?._onIntersectionChange({ intersectionRatio: 1 }); // HACK: Force a refresh of the screen to ensure links are refresh corrected. // This can probably be removed when the above hack is fixed in Chromium. this._xterm.refresh(0, this._xterm.rows - 1); @@ -1594,13 +1627,17 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = .monaco-workbench .pane-body.integrated-terminal .find-focused .xterm .xterm-viewport, .monaco-workbench .pane-body.integrated-terminal .xterm.focus .xterm-viewport, .monaco-workbench .pane-body.integrated-terminal .xterm:focus .xterm-viewport, - .monaco-workbench .pane-body.integrated-terminal .xterm:hover .xterm-viewport { background-color: ${scrollbarSliderBackgroundColor} !important; }` - ); + .monaco-workbench .pane-body.integrated-terminal .xterm:hover .xterm-viewport { background-color: ${scrollbarSliderBackgroundColor} !important; } + .monaco-workbench .pane-body.integrated-terminal .xterm-viewport { scrollbar-color: ${scrollbarSliderBackgroundColor} transparent; } + `); } const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground); if (scrollbarSliderHoverBackgroundColor) { - collector.addRule(`.monaco-workbench .pane-body.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover { background-color: ${scrollbarSliderHoverBackgroundColor}; }`); + collector.addRule(` + .monaco-workbench .pane-body.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover { background-color: ${scrollbarSliderHoverBackgroundColor}; } + .monaco-workbench .pane-body.integrated-terminal .xterm-viewport:hover { scrollbar-color: ${scrollbarSliderHoverBackgroundColor} transparent; } + `); } const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts b/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts index 6c4af6350..7a283761a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts @@ -10,6 +10,8 @@ import { matchesFuzzy } from 'vs/base/common/filters'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { killTerminalIcon, renameTerminalIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; export class TerminalQuickAccessProvider extends PickerQuickAccessProvider { @@ -39,11 +41,11 @@ export class TerminalQuickAccessProvider extends PickerQuickAccessProvider | undefined; + private _connectionState: TerminalConnectionState; public get configHelper(): ITerminalConfigHelper { return this._configHelper; } @@ -97,6 +99,9 @@ export class TerminalService implements ITerminalService { public get onRequestAvailableShells(): Event { return this._onRequestAvailableShells.event; } private readonly _onDidRegisterProcessSupport = new Emitter(); public get onDidRegisterProcessSupport(): Event { return this._onDidRegisterProcessSupport.event; } + private readonly _onDidChangeConnectionState = new Emitter(); + public get onDidChangeConnectionState(): Event { return this._onDidChangeConnectionState.event; } + public get connectionState(): TerminalConnectionState { return this._connectionState; } constructor( @IContextKeyService private _contextKeyService: IContextKeyService, @@ -134,6 +139,42 @@ export class TerminalService implements ITerminalService { this._handleInstanceContextKeys(); this._processSupportContextKey = KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED.bindTo(this._contextKeyService); this._processSupportContextKey.set(!isWeb || this._remoteAgentService.getConnection() !== null); + + const enableTerminalReconnection = this.configHelper.config.enablePersistentSessions; + const serverSpawn = this.configHelper.config.serverSpawn; + if (!!this._environmentService.remoteAuthority && enableTerminalReconnection && serverSpawn) { + this._remoteTerminalsInitialized = this._reconnectToRemoteTerminals(); + this._connectionState = TerminalConnectionState.Connecting; + } else { + this._connectionState = TerminalConnectionState.Connected; + } + } + + private async _reconnectToRemoteTerminals(): Promise { + const remoteTerms = await this._remoteTerminalService.listTerminals(true); + const workspace = this._workspaceContextService.getWorkspace(); + const unattachedWorkspaceRemoteTerms = remoteTerms + .filter(term => term.workspaceId === workspace.id) + .filter(term => !this.isAttachedToTerminal(term)); + + /* __GDPR__ + "terminalReconnection" : { + "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + const data = { + count: unattachedWorkspaceRemoteTerms.length + }; + this._telemetryService.publicLog('terminalReconnection', data); + if (unattachedWorkspaceRemoteTerms.length > 0) { + // Reattach to all remote terminals + for (let term of unattachedWorkspaceRemoteTerms) { + this.createTerminal({ remoteAttach: term }); + } + } + + this._connectionState = TerminalConnectionState.Connected; + this._onDidChangeConnectionState.fire(); } public setNativeWindowsDelegate(delegate: ITerminalNativeWindowsDelegate): void { @@ -226,7 +267,7 @@ export class TerminalService implements ITerminalService { } public getTabLabels(): string[] { - return this._terminalTabs.map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`); + return this._terminalTabs.filter(tab => tab.terminalInstances.length > 0).map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`); } public getFindState(): FindReplaceState { @@ -349,45 +390,13 @@ export class TerminalService implements ITerminalService { } public async initializeTerminals(): Promise { - const enableTerminalReconnection = this.configHelper.config.enablePersistentSessions; - const serverSpawn = this.configHelper.config.serverSpawn; - if (!!this._environmentService.remoteAuthority && enableTerminalReconnection && serverSpawn) { - let emptyTab: TerminalTab | undefined; + if (this._remoteTerminalsInitialized) { + await this._remoteTerminalsInitialized; + if (!this.terminalTabs.length) { - emptyTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, undefined); - this._terminalTabs.push(emptyTab); - this._onInstanceTitleChanged.fire(undefined); + this.createTerminal(undefined); } - - const remoteTerms = await this._remoteTerminalService.listTerminals(true); - const workspace = this._workspaceContextService.getWorkspace(); - const unattachedWorkspaceRemoteTerms = remoteTerms - .filter(term => term.workspaceId === workspace.id) - .filter(term => !this.isAttachedToTerminal(term)); - - /* __GDPR__ - "terminalReconnection" : { - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - const data = { - count: unattachedWorkspaceRemoteTerms.length - }; - this._telemetryService.publicLog('terminalReconnection', data); - if (unattachedWorkspaceRemoteTerms.length > 0) { - // Reattach to all remote terminals - this.createTerminal({ remoteAttach: unattachedWorkspaceRemoteTerms[0] }, emptyTab); - for (let term of unattachedWorkspaceRemoteTerms.slice(1)) { - this.createTerminal({ remoteAttach: term }); - } - } else if (this.terminalTabs.length === 1 && this.terminalTabs[0] === emptyTab) { - // No terminals to reconnect to, and the only terminal tab is our placeholder - this.createTerminal(undefined, emptyTab); - } else if (emptyTab) { - // There is another tab, need to remove the empty tab - this._removeTab(emptyTab); - } - } else if (this.terminalTabs.length === 0) { + } else if (!this._environmentService.remoteAuthority && this.terminalTabs.length === 0) { // Local, just create a terminal this.createTerminal(); } @@ -664,7 +673,7 @@ export class TerminalService implements ITerminalService { return instance; } - public createTerminal(shell: IShellLaunchConfig = {}, terminalTab?: TerminalTab): ITerminalInstance { + public createTerminal(shell: IShellLaunchConfig = {}): ITerminalInstance { if (!this.isProcessSupportRegistered) { throw new Error('Could not create terminal when process support is not registered'); } @@ -675,14 +684,11 @@ export class TerminalService implements ITerminalService { return instance; } - if (terminalTab) { - terminalTab.addInstance(shell); - } else { - terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, shell); - this._terminalTabs.push(terminalTab); - } + const terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, shell); + this._terminalTabs.push(terminalTab); const instance = terminalTab.terminalInstances[0]; + terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed)); terminalTab.addDisposable(terminalTab.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged)); this._initInstanceListeners(instance); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index ac96823f3..6e8e1f58d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { IShellLaunchConfig, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; -import * as nls from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { SplitView, Orientation, IView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; @@ -219,7 +218,6 @@ export class TerminalTab extends Disposable implements ITerminalTab { private _activeInstanceIndex: number; private _isVisible: boolean = false; - private _willFocus: boolean = false; public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } @@ -249,16 +247,6 @@ export class TerminalTab extends Disposable implements ITerminalTab { } } - /** - * Focus the current active instance, or if there isn't one yet, the first instance added - */ - setWillFocus(focus: boolean): void { - this._willFocus = focus; - if (focus && this.activeInstance) { - this.activeInstance.focusWhenReady(); - } - } - public addInstance(shellLaunchConfigOrInstance: IShellLaunchConfig | ITerminalInstance): void { let instance: ITerminalInstance; if ('id' in shellLaunchConfigOrInstance) { @@ -274,10 +262,6 @@ export class TerminalTab extends Disposable implements ITerminalTab { } this._onInstancesChanged.fire(); - - if (this.terminalInstances.length === 1 && this._willFocus) { - instance.focusWhenReady(); - } } public dispose(): void { @@ -387,10 +371,6 @@ export class TerminalTab extends Disposable implements ITerminalTab { } public get title(): string { - if (!this.terminalInstances.length) { - return nls.localize('terminal.integrated.starting', "Starting..."); - } - let title = this.terminalInstances[0].title; for (let i = 1; i < this.terminalInstances.length; i++) { if (this.terminalInstances[i].title) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index ca60145b4..5a9b02d8e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -3,15 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { disposableTimeout } from 'vs/base/common/async'; import { Color } from 'vs/base/common/color'; import { debounce } from 'vs/base/common/decorators'; import { Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { XTermAttributes, XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; -import { IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; -import type { IBuffer, IBufferCell, ITerminalAddon, Terminal } from 'xterm'; +import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import type { IBuffer, IBufferCell, IDisposable, ITerminalAddon, Terminal } from 'xterm'; const ESC = '\x1b'; const CSI = `${ESC}[`; @@ -21,7 +23,6 @@ const DELETE_CHAR = `${CSI}X`; const DELETE_REST_OF_LINE = `${CSI}K`; const CSI_STYLE_RE = /^\x1b\[[0-9;]*m/; const CSI_MOVE_RE = /^\x1b\[([0-9]*)(;[35])?O?([DC])/; -const PASSWORD_INPUT_RE = /(?:\W|^)(?:pat|token|password|passphrase|passwd)(\W.*:|:)/i; const NOT_WORD_RE = /[^a-z0-9]/i; const statsBufferSize = 24; @@ -171,6 +172,12 @@ export interface IPrediction { */ readonly affectsStyle?: boolean; + /** + * If set to false, the prediction will not be cleared if no input is + * received from the server. + */ + readonly clearAfterTimeout?: boolean; + /** * Returns a sequence to apply the prediction. * @param buffer to write to @@ -292,6 +299,8 @@ class StringReader { * after it. */ class HardBoundary implements IPrediction { + public readonly clearAfterTimeout = false; + public apply() { return ''; } @@ -314,12 +323,10 @@ class HardBoundary implements IPrediction { * through its `matches` request. */ class TentativeBoundary implements IPrediction { - private expected?: Cursor; - constructor(private readonly inner: IPrediction) { } + constructor(public readonly inner: IPrediction) { } public apply(buffer: IBuffer, cursor: Cursor) { - this.expected = cursor.clone(); - this.inner.apply(buffer, this.expected); + this.inner.apply(buffer, cursor); return ''; } @@ -328,10 +335,6 @@ class TentativeBoundary implements IPrediction { } public rollForwards(cursor: Cursor, withInput: string) { - if (this.expected) { - cursor.moveTo(this.expected); - } - return withInput; } @@ -340,13 +343,16 @@ class TentativeBoundary implements IPrediction { } } +export const isTenativeCharacterPrediction = (p: unknown): p is (TentativeBoundary & { inner: CharacterPrediction }) => + p instanceof TentativeBoundary && p.inner instanceof CharacterPrediction; + /** * Prediction for a single alphanumeric character. */ class CharacterPrediction implements IPrediction { public readonly affectsStyle = true; - protected appliedAt?: { + public appliedAt?: { pos: ICoordinate; oldAttributes: string; oldChar: string; @@ -407,7 +413,7 @@ class BackspacePrediction implements IPrediction { pos: ICoordinate; oldAttributes: string; oldChar: string; - eol: boolean; + isLastChar: boolean; }; constructor(private readonly terminal: Terminal) { } @@ -415,13 +421,13 @@ class BackspacePrediction implements IPrediction { public apply(_: IBuffer, cursor: Cursor) { // at eol if everything to the right is whitespace (zsh will emit a "clear line" code in this case) // todo: can be optimized if `getTrimmedLength` is exposed from xterm - const eol = !cursor.getLine()?.translateToString(undefined, cursor.x).trim(); + const isLastChar = !cursor.getLine()?.translateToString(undefined, cursor.x).trim(); const pos = cursor.coordinate; const move = cursor.shift(-1); const cell = cursor.getCell(); this.appliedAt = cell - ? { eol, pos, oldAttributes: attributesToSeq(cell), oldChar: cell.getChars() } - : { eol, pos, oldAttributes: '', oldChar: '' }; + ? { isLastChar, pos, oldAttributes: attributesToSeq(cell), oldChar: cell.getChars() } + : { isLastChar, pos, oldAttributes: '', oldChar: '' }; return move + DELETE_CHAR; } @@ -444,7 +450,7 @@ class BackspacePrediction implements IPrediction { } public matches(input: StringReader) { - if (this.appliedAt?.eol) { + if (this.appliedAt?.isLastChar) { const r1 = input.eatGradually(`\b${CSI}K`); if (r1 !== MatchResult.Failure) { return r1; @@ -637,6 +643,20 @@ export class PredictionStats extends Disposable { }; } + /** + * Gets the maximum observed latency. + */ + public get maxLatency() { + let max = -Infinity; + for (const [latency, correct] of this.stats) { + if (correct) { + max = Math.max(latency, max); + } + } + + return max; + } + constructor(timeline: PredictionTimeline) { super(); this._register(timeline.onPredictionAdded(p => this.addedAtTime.set(p, Date.now()))); @@ -690,10 +710,18 @@ export class PredictionTimeline { private readonly succeededEmitter = new Emitter(); public readonly onPredictionSucceeded = this.succeededEmitter.event; + private get currentGenerationPredictions() { + return this.expected.filter(({ gen }) => gen === this.expected[0].gen).map(({ p }) => p); + } + public get isShowingPredictions() { return this.showPredictions; } + public get length() { + return this.expected.length; + } + constructor(public readonly terminal: Terminal, private readonly style: TypeAheadStyle) { } public setShowPredictions(show: boolean) { @@ -709,7 +737,7 @@ export class PredictionTimeline { return; } - const toApply = this.expected.filter(({ gen }) => gen === this.expected[0].gen).map(({ p }) => p); + const toApply = this.currentGenerationPredictions; if (show) { this.cursor = undefined; this.style.expectIncomingStyle(toApply.reduce((count, p) => p.affectsStyle ? count + 1 : count, 0)); @@ -719,6 +747,19 @@ export class PredictionTimeline { } } + /** + * Undoes any predictions written and resets expectations. + */ + public undoAllPredictions() { + const buffer = this.getActiveBuffer(); + if (this.showPredictions && buffer) { + this.terminal.write(this.currentGenerationPredictions.reverse() + .map(p => p.rollback(this.getCursor(buffer))).join('')); + } + + this.expected = []; + } + /** * Should be called when input is incoming to the temrinal. */ @@ -754,7 +795,7 @@ export class PredictionTimeline { ReadLoop: while (this.expected.length && reader.remaining > 0) { emitPredictionOmitted(); - const prediction = this.expected[0].p; + const { p: prediction, gen } = this.expected[0]; const cursor = this.getCursor(buffer); let beforeTestReaderIndex = reader.index; switch (prediction.matches(reader)) { @@ -762,7 +803,13 @@ export class PredictionTimeline { // if the input character matches what the next prediction expected, undo // the prediction and write the real character out. const eaten = input.slice(beforeTestReaderIndex, reader.index); - output += prediction.rollForwards?.(cursor, eaten); + if (gen === startingGen) { + output += prediction.rollForwards?.(cursor, eaten); + } else { + prediction.apply(buffer, this.getCursor(buffer)); // move cursor for additional apply + output += eaten; + } + this.succeededEmitter.fire(prediction); this.expected.shift(); break; @@ -860,21 +907,30 @@ export class PredictionTimeline { * pty output/ */ public addBoundary(): void; - public addBoundary(buffer: IBuffer, prediction: IPrediction): void; + public addBoundary(buffer: IBuffer, prediction: IPrediction): boolean; public addBoundary(buffer?: IBuffer, prediction?: IPrediction) { + let applied = false; if (buffer && prediction) { - this.addPrediction(buffer, prediction); + applied = this.addPrediction(buffer, prediction); } this.currentGen++; + return applied; } /** * Peeks the last prediction written. */ - public peekEnd() { + public peekEnd(): IPrediction | undefined { return this.expected[this.expected.length - 1]?.p; } + /** + * Peeks the first pending prediction. + */ + public peekStart(): IPrediction | undefined { + return this.expected[0]?.p; + } + public getCursor(buffer: IBuffer) { if (!this.cursor) { if (this.showPredictions) { @@ -895,29 +951,37 @@ export class PredictionTimeline { return buffer.type === 'normal' ? buffer : undefined; } } + +/** + * Gets the escape sequence args to restore state/appearence in the cell. + */ +const attributesToArgs = (cell: XTermAttributes) => { + if (cell.isAttributeDefault()) { return [0]; } + + const args = []; + if (cell.isBold()) { args.push(1); } + if (cell.isDim()) { args.push(2); } + if (cell.isItalic()) { args.push(3); } + if (cell.isUnderline()) { args.push(4); } + if (cell.isBlink()) { args.push(5); } + if (cell.isInverse()) { args.push(7); } + if (cell.isInvisible()) { args.push(8); } + + if (cell.isFgRGB()) { args.push(38, 2, cell.getFgColor() >>> 24, (cell.getFgColor() >>> 16) & 0xFF, cell.getFgColor() & 0xFF); } + if (cell.isFgPalette()) { args.push(38, 5, cell.getFgColor()); } + if (cell.isFgDefault()) { args.push(39); } + + if (cell.isBgRGB()) { args.push(48, 2, cell.getBgColor() >>> 24, (cell.getBgColor() >>> 16) & 0xFF, cell.getBgColor() & 0xFF); } + if (cell.isBgPalette()) { args.push(48, 5, cell.getBgColor()); } + if (cell.isBgDefault()) { args.push(49); } + + return args; +}; + /** * Gets the escape sequence to restore state/appearence in the cell. */ -const attributesToSeq = (cell: XTermAttributes) => cell.isAttributeDefault() - ? `${CSI}0m` - : [ - cell.isBold() && `${CSI}1m`, - cell.isDim() && `${CSI}2m`, - cell.isItalic() && `${CSI}3m`, - cell.isUnderline() && `${CSI}4m`, - cell.isBlink() && `${CSI}5m`, - cell.isInverse() && `${CSI}7m`, - cell.isInvisible() && `${CSI}8m`, - - cell.isFgRGB() && `${CSI}38;2;${cell.getFgColor() >>> 24};${(cell.getFgColor() >>> 16) & 0xFF};${cell.getFgColor() & 0xFF}m`, - cell.isFgPalette() && `${CSI}38;5;${cell.getFgColor()}m`, - cell.isFgDefault() && `${CSI}39m`, - - cell.isBgRGB() && `${CSI}48;2;${cell.getBgColor() >>> 24};${(cell.getBgColor() >>> 16) & 0xFF};${cell.getBgColor() & 0xFF}m`, - cell.isBgPalette() && `${CSI}48;5;${cell.getBgColor()}m`, - cell.isBgDefault() && `${CSI}49m`, - ].filter(seq => !!seq).join(''); - +const attributesToSeq = (cell: XTermAttributes) => `${CSI}${attributesToArgs(cell).join(';')}m`; const arrayHasPrefixAt = (a: ReadonlyArray, ai: number, b: ReadonlyArray) => { if (a.length - ai > b.length) { @@ -968,7 +1032,7 @@ const getColorWidth = (params: (number | number[])[], pos: number) => { return advance; }; -class TypeAheadStyle { +class TypeAheadStyle implements IDisposable { private static compileArgs(args: ReadonlyArray) { return `${CSI}${args.join(';')}m`; } @@ -984,8 +1048,9 @@ class TypeAheadStyle { public apply!: string; public undo!: string; + private csiHandler?: IDisposable; - constructor(value: ITerminalConfiguration['localEchoStyle']) { + constructor(value: ITerminalConfiguration['localEchoStyle'], private readonly terminal: Terminal) { this.onUpdate(value); } @@ -998,9 +1063,38 @@ class TypeAheadStyle { } /** - * Should be called when an attribut eupdate happens in the terminal. + * Starts tracking for CSI changes in the terminal. */ - public onDidWriteSGR(args: (number | number[])[]) { + public startTracking() { + this.expectedIncomingStyles = 0; + this.onDidWriteSGR(attributesToArgs(core(this.terminal)._inputHandler._curAttrData)); + this.csiHandler = this.terminal.parser.registerCsiHandler({ final: 'm' }, args => { + this.onDidWriteSGR(args); + return false; + }); + } + + /** + * Stops tracking terminal CSI changes. + */ + @debounce(2000) + public debounceStopTracking() { + this.stopTracking(); + } + + /** + * @inheritdoc + */ + public dispose() { + this.stopTracking(); + } + + private stopTracking() { + this.csiHandler?.dispose(); + this.csiHandler = undefined; + } + + private onDidWriteSGR(args: (number | number[])[]) { const originalUndo = this.undoArgs; for (let i = 0; i < args.length;) { const px = args[i]; @@ -1089,41 +1183,70 @@ class TypeAheadStyle { } } +const compileExcludeRegexp = (programs = DEFAULT_LOCAL_ECHO_EXCLUDE) => + new RegExp(`\\b(${programs.map(escapeRegExpCharacters).join('|')})\\b`, 'i'); + +export const enum CharPredictState { + /** No characters typed on this line yet */ + Unknown, + /** Has a pending character prediction */ + HasPendingChar, + /** Character validated on this line */ + Validated, +} + export class TypeAheadAddon extends Disposable implements ITerminalAddon { - private typeaheadStyle = new TypeAheadStyle(this.config.config.localEchoStyle); + private typeaheadStyle?: TypeAheadStyle; private typeaheadThreshold = this.config.config.localEchoLatencyThreshold; - protected lastRow?: { y: number; startingX: number }; - private timeline?: PredictionTimeline; + private excludeProgramRe = compileExcludeRegexp(this.config.config.localEchoExcludePrograms); + protected lastRow?: { y: number; startingX: number; charState: CharPredictState }; + protected timeline?: PredictionTimeline; + private terminalTitle = ''; public stats?: PredictionStats; + /** + * Debounce that clears predictions after a timeout if the PTY doesn't apply them. + */ + private clearPredictionDebounce?: IDisposable; + constructor( private readonly processManager: ITerminalProcessManager, private readonly config: TerminalConfigHelper, @ITelemetryService private readonly telemetryService: ITelemetryService, ) { super(); + this._register(toDisposable(() => this.clearPredictionDebounce?.dispose())); } public activate(terminal: Terminal): void { + const style = this.typeaheadStyle = this._register(new TypeAheadStyle(this.config.config.localEchoStyle, terminal)); const timeline = this.timeline = new PredictionTimeline(terminal, this.typeaheadStyle); const stats = this.stats = this._register(new PredictionStats(this.timeline)); timeline.setShowPredictions(this.typeaheadThreshold === 0); - this._register(terminal.parser.registerCsiHandler({ final: 'm' }, args => { - this.typeaheadStyle.onDidWriteSGR(args); - return false; - })); this._register(terminal.onData(e => this.onUserData(e))); + this._register(terminal.onTitleChange(title => { + this.terminalTitle = title; + this.reevaluatePredictorState(stats, timeline); + })); this._register(terminal.onResize(() => { timeline.setShowPredictions(false); timeline.clearCursor(); this.reevaluatePredictorState(stats, timeline); })); this._register(this.config.onConfigChanged(() => { - this.typeaheadStyle.onUpdate(this.config.config.localEchoStyle); + style.onUpdate(this.config.config.localEchoStyle); this.typeaheadThreshold = this.config.config.localEchoLatencyThreshold; + this.excludeProgramRe = compileExcludeRegexp(this.config.config.localEchoExcludePrograms); this.reevaluatePredictorState(stats, timeline); })); + this._register(this.timeline.onPredictionSucceeded(p => { + if (this.lastRow?.charState === CharPredictState.HasPendingChar && isTenativeCharacterPrediction(p) && p.inner.appliedAt) { + if (p.inner.appliedAt.pos.y + p.inner.appliedAt.pos.baseY === this.lastRow.y) { + this.lastRow.charState = CharPredictState.Validated; + } + } + })); this._register(this.processManager.onBeforeProcessData(e => this.onBeforeProcessData(e))); let nextStatsSend: any; @@ -1135,10 +1258,36 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { }, statsSendTelemetryEvery); } + if (timeline.length === 0) { + style.debounceStopTracking(); + } + this.reevaluatePredictorState(stats, timeline); })); } + private deferClearingPredictions() { + if (!this.stats || !this.timeline) { + return; + } + + this.clearPredictionDebounce?.dispose(); + if (this.timeline.length === 0 || this.timeline.peekStart()?.clearAfterTimeout === false) { + this.clearPredictionDebounce = undefined; + return; + } + + this.clearPredictionDebounce = disposableTimeout( + () => { + this.timeline?.undoAllPredictions(); + if (this.lastRow?.charState === CharPredictState.HasPendingChar) { + this.lastRow.charState = CharPredictState.Unknown; + } + }, + Math.max(500, this.stats.maxLatency * 3 / 2), + ); + } + /** * Note on debounce: * @@ -1147,8 +1296,14 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { * terminal cursor is not updated, causes issues. */ @debounce(100) - private reevaluatePredictorState(stats: PredictionStats, timeline: PredictionTimeline) { - if (this.typeaheadThreshold < 0) { + protected reevaluatePredictorState(stats: PredictionStats, timeline: PredictionTimeline) { + this.reevaluatePredictorStateNow(stats, timeline); + } + + protected reevaluatePredictorStateNow(stats: PredictionStats, timeline: PredictionTimeline) { + if (this.excludeProgramRe.test(this.terminalTitle)) { + timeline.setShowPredictions(false); + } else if (this.typeaheadThreshold < 0) { timeline.setShowPredictions(false); } else if (this.typeaheadThreshold === 0) { timeline.setShowPredictions(true); @@ -1201,7 +1356,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { // the user gave input, and mark all additions before that as tentative. const actualY = buffer.baseY + buffer.cursorY; if (actualY !== this.lastRow?.y) { - this.lastRow = { y: actualY, startingX: buffer.cursorX }; + this.lastRow = { y: actualY, startingX: buffer.cursorX, charState: CharPredictState.Unknown }; } else { this.lastRow.startingX = Math.min(this.lastRow.startingX, buffer.cursorX); } @@ -1232,7 +1387,16 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { if (reader.eatCharCode(32, 126)) { // alphanum const char = data[reader.index - 1]; - if (this.timeline.addPrediction(buffer, new CharacterPrediction(this.typeaheadStyle, char)) && this.timeline.getCursor(buffer).x === terminal.cols) { + const prediction = new CharacterPrediction(this.typeaheadStyle!, char); + let applied: boolean; + if (this.lastRow.charState === CharPredictState.Unknown) { + applied = this.timeline.addBoundary(buffer, new TentativeBoundary(prediction)); + this.lastRow.charState = CharPredictState.HasPendingChar; + } else { + applied = this.timeline.addPrediction(buffer, prediction); + } + + if (applied && this.timeline.getCursor(buffer).x >= terminal.cols) { this.timeline.addBoundary(buffer, new TentativeBoundary(new LinewrapPrediction())); } continue; @@ -1269,6 +1433,11 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { this.timeline.addBoundary(buffer, new HardBoundary()); break; } + + if (this.timeline.length === 1) { + this.deferClearingPredictions(); + this.typeaheadStyle!.startTracking(); + } } private onBeforeProcessData(event: IBeforeProcessDataEvent): void { @@ -1280,12 +1449,6 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { event.data = this.timeline.beforeServerInput(event.data); // console.log('emitted data:', JSON.stringify(event.data)); - // If there's something that looks like a password prompt, omit giving - // input. This is approximate since there's no TTY "password here" code, - // but should be enough to cover common cases like sudo - if (PASSWORD_INPUT_RE.test(event.data)) { - const terminal = this.timeline.terminal; - this.timeline.addBoundary(terminal.buffer.active, new HardBoundary()); - } + this.deferClearingPredictions(); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 74fc5f854..2d2f0a1b2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -19,7 +19,7 @@ import { URI } from 'vs/base/common/uri'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { DataTransfers } from 'vs/base/browser/dnd'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalService, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -67,10 +67,6 @@ export class TerminalViewPane extends ViewPane { } this._onDidChangeViewWelcomeState.fire(); }); - - this._register(this.onDidBlur(() => { - this._terminalService.getActiveTab()?.setWillFocus(false); - })); } protected renderBody(container: HTMLElement): void { @@ -133,6 +129,9 @@ export class TerminalViewPane extends ViewPane { } } else { this._terminalService.getActiveTab()?.setVisible(false); + this._terminalService.terminalInstances.forEach(instance => { + instance.notifyFindWidgetFocusChanged(false); + }); } })); @@ -217,7 +216,25 @@ export class TerminalViewPane extends ViewPane { } public focus() { - this._terminalService.getActiveTab()?.setWillFocus(true); + if (this._terminalService.connectionState === TerminalConnectionState.Connecting) { + // If the terminal is waiting to reconnect to remote terminals, then there is no TerminalInstance yet that can + // be focused. So wait for connection to finish, then focus. + const activeElement = document.activeElement; + this._register(this._terminalService.onDidChangeConnectionState(() => { + // Only focus the terminal if the activeElement has not changed since focus() was called + // TODO hack + if (document.activeElement === activeElement) { + this._focus(); + } + })); + + return; + } + this._focus(); + } + + private _focus() { + this._terminalService.getActiveInstance()?.focusWhenReady(); } public focusFindWidget() { diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts b/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts index 3bd498445..913e4de79 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts @@ -12,6 +12,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import * as dom from 'vs/base/browser/dom'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IHoverService, IHoverOptions } from 'vs/workbench/services/hover/browser/hover'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export class EnvironmentVariableInfoWidget extends Widget implements ITerminalWidget { readonly id = 'env-var-info'; @@ -34,7 +35,7 @@ export class EnvironmentVariableInfoWidget extends Widget implements ITerminalWi attach(container: HTMLElement): void { this._container = container; this._domNode = document.createElement('div'); - this._domNode.classList.add('terminal-env-var-info', 'codicon', `codicon-${this._info.getIcon()}`); + this._domNode.classList.add('terminal-env-var-info', ...ThemeIcon.asClassNameArray(this._info.getIcon())); if (this.requiresAction) { this._domNode.classList.add('requires-action'); } diff --git a/src/vs/workbench/contrib/terminal/common/environmentVariable.ts b/src/vs/workbench/contrib/terminal/common/environmentVariable.ts index bb32eafc8..196f18495 100644 --- a/src/vs/workbench/contrib/terminal/common/environmentVariable.ts +++ b/src/vs/workbench/contrib/terminal/common/environmentVariable.ts @@ -6,6 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { IProcessEnvironment } from 'vs/base/common/platform'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export const IEnvironmentVariableService = createDecorator('environmentVariableService'); @@ -98,6 +99,6 @@ export type ISerializableEnvironmentVariableCollection = [string, IEnvironmentVa export interface IEnvironmentVariableInfo { readonly requiresAction: boolean; getInfo(): string; - getIcon(): string; + getIcon(): ThemeIcon; getActions?(): { label: string, iconClass?: string, run: () => void, commandId: string }[]; } diff --git a/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts b/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts index 8373296bf..e1d984407 100644 --- a/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts +++ b/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts @@ -6,7 +6,7 @@ import { IEnvironmentVariableService, IMergedEnvironmentVariableCollection, ISerializableEnvironmentVariableCollection, IEnvironmentVariableCollectionWithPersistence } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { Event, Emitter } from 'vs/base/common/event'; import { debounce, throttle } from 'vs/base/common/decorators'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection'; import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; @@ -85,7 +85,7 @@ export class EnvironmentVariableService implements IEnvironmentVariableService { } }); const stringifiedJson = JSON.stringify(collectionsJson); - this._storageService.store(ENVIRONMENT_VARIABLE_COLLECTIONS_KEY, stringifiedJson, StorageScope.WORKSPACE); + this._storageService.store(ENVIRONMENT_VARIABLE_COLLECTIONS_KEY, stringifiedJson, StorageScope.WORKSPACE, StorageTarget.MACHINE); } @debounce(1000) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 3b32d9e9f..ef7ff2e50 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -50,7 +50,7 @@ export const KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED = new RawContextKey; localEchoStyle: 'bold' | 'dim' | 'italic' | 'underlined' | 'inverted' | string; serverSpawn: boolean; enablePersistentSessions: boolean; } +export const DEFAULT_LOCAL_ECHO_EXCLUDE: ReadonlyArray = ['vim', 'vi', 'nano', 'tmux']; + export interface ITerminalConfigHelper { config: ITerminalConfiguration; @@ -486,6 +490,7 @@ export const enum TERMINAL_COMMAND_ID { TOGGLE = 'workbench.action.terminal.toggleTerminal', KILL = 'workbench.action.terminal.kill', QUICK_KILL = 'workbench.action.terminal.quickKill', + CONFIGURE_TERMINAL_SETTINGS = 'workbench.action.terminal.openSettings', COPY_SELECTION = 'workbench.action.terminal.copySelection', SELECT_ALL = 'workbench.action.terminal.selectAll', DELETE_WORD_LEFT = 'workbench.action.terminal.deleteWordLeft', diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index eba3dd720..ed21243bf 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -6,7 +6,7 @@ import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { localize } from 'vs/nls'; import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT } from 'vs/workbench/contrib/terminal/common/terminal'; +import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_LOCAL_ECHO_EXCLUDE } from 'vs/workbench/contrib/terminal/common/terminal'; import { isMacintosh, isWindows, Platform } from 'vs/base/common/platform'; export const terminalConfiguration: IConfigurationNode = { @@ -15,6 +15,11 @@ export const terminalConfiguration: IConfigurationNode = { title: localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"), type: 'object', properties: { + 'terminal.integrated.sendKeybindingsToShell': { + markdownDescription: localize('terminal.integrated.sendKeybindingsToShell', "Dispatches most keybindings to the terminal instead of the workbench, overriding `#terminal.integrated.commandsToSkipShell#`, which can be used alternatively for fine tuning."), + type: 'boolean', + default: false + }, 'terminal.integrated.automationShell.linux': { markdownDescription: localize({ key: 'terminal.integrated.automationShell.linux', @@ -211,7 +216,7 @@ export const terminalConfiguration: IConfigurationNode = { localize('terminal.integrated.rendererType.auto', "Let VS Code guess which renderer to use."), localize('terminal.integrated.rendererType.canvas', "Use the standard GPU/canvas-based renderer."), localize('terminal.integrated.rendererType.dom', "Use the fallback DOM-based renderer."), - localize('terminal.integrated.rendererType.experimentalWebgl', "Use the experimental webgl-based renderer. Note that this has some [known issues](https://github.com/xtermjs/xterm.js/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Faddon%2Fwebgl) and this will only be enabled for new terminals (not hot swappable like the other renderers).") + localize('terminal.integrated.rendererType.experimentalWebgl', "Use the experimental webgl-based renderer. Note that this has some [known issues](https://github.com/xtermjs/xterm.js/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Faddon%2Fwebgl).") ], default: 'auto', description: localize('terminal.integrated.rendererType', "Controls how the terminal is rendered.") @@ -358,6 +363,15 @@ export const terminalConfiguration: IConfigurationNode = { minimum: -1, default: 30, }, + 'terminal.integrated.localEchoExcludePrograms': { + description: localize('terminal.integrated.localEchoExcludePrograms', "Experimental: local echo will be disabled when any of these program names are found in the terminal title."), + type: 'array', + items: { + type: 'string', + uniqueItems: true + }, + default: DEFAULT_LOCAL_ECHO_EXCLUDE, + }, 'terminal.integrated.localEchoStyle': { description: localize('terminal.integrated.localEchoStyle', "Experimental: terminal style of locally echoed text; either a font style or an RGB color."), default: 'dim', diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 069f76a47..92dcbb35b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -93,7 +93,8 @@ export function shouldSetLangEnvVariable(env: platform.IProcessEnvironment, dete return true; } if (detectLocale === 'auto') { - return !env['LANG'] || (env['LANG'].search(/\.UTF\-8$/) === -1 && env['LANG'].search(/\.utf8$/) === -1); + const lang = env['LANG']; + return !lang || (lang.search(/\.UTF\-8$/) === -1 && lang.search(/\.utf8$/) === -1 && lang.search(/\.euc.+/) === -1); } return false; // 'off' } diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index 8dc3b6c15..4a006c386 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -152,7 +152,7 @@ async function validateShellPaths(label: string, potentialPaths: string[]): Prom } try { const result = await stat(normalize(current)); - if (result.isFile || result.isSymbolicLink) { + if (result.isFile() || result.isSymbolicLink()) { return { label, path: current diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts index eed4382af..d8263b558 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts @@ -8,7 +8,6 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; -import { StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; suite('Workbench - TerminalConfigHelper', () => { let fixture: HTMLElement; @@ -30,7 +29,7 @@ suite('Workbench - TerminalConfigHelper', () => { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); - const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.setLinuxDistro(LinuxDistro.Fedora); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, '\'DejaVu Sans Mono\', monospace', 'Fedora should have its font overridden when terminal.integrated.fontFamily not set'); @@ -40,7 +39,7 @@ suite('Workbench - TerminalConfigHelper', () => { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); - const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.setLinuxDistro(LinuxDistro.Ubuntu); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, '\'Ubuntu Mono\', monospace', 'Ubuntu should have its font overridden when terminal.integrated.fontFamily not set'); @@ -50,7 +49,7 @@ suite('Workbench - TerminalConfigHelper', () => { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); - const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, 'foo', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set'); }); @@ -68,7 +67,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 10 } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 10, 'terminal.integrated.fontSize should be selected over editor.fontSize'); @@ -81,12 +80,12 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 0 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.setLinuxDistro(LinuxDistro.Ubuntu); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 8, 'The minimum terminal font size (with adjustment) should be used when terminal.integrated.fontSize less than it'); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it'); @@ -99,7 +98,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 1500 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 25, 'The maximum terminal font size should be used when terminal.integrated.fontSize more than it'); @@ -112,12 +111,12 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: null } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.setLinuxDistro(LinuxDistro.Ubuntu); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize + 2, 'The default editor font size (with adjustment) should be used when terminal.integrated.fontSize is not set'); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set'); }); @@ -135,7 +134,7 @@ suite('Workbench - TerminalConfigHelper', () => { lineHeight: 2 } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().lineHeight, 2, 'terminal.integrated.lineHeight should be selected over editor.lineHeight'); @@ -149,7 +148,7 @@ suite('Workbench - TerminalConfigHelper', () => { lineHeight: 0 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().lineHeight, 1, 'editor.lineHeight should be 1 when terminal.integrated.lineHeight not set'); }); @@ -162,7 +161,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -174,7 +173,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontFamily: 'sans-serif' } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -186,7 +185,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontFamily: 'serif' } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); @@ -202,7 +201,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -218,7 +217,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -234,7 +233,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts index f50415e5e..0814572f0 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts @@ -7,8 +7,8 @@ import * as assert from 'assert'; import { Terminal } from 'xterm'; import { SinonStub, stub, useFakeTimers } from 'sinon'; import { Emitter } from 'vs/base/common/event'; -import { IPrediction, PredictionStats, TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; -import { IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { CharPredictState, IPrediction, PredictionStats, TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; +import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -92,7 +92,8 @@ suite('Workbench - Terminal Typeahead', () => { setup(() => { config = upcastPartial({ localEchoStyle: 'italic', - localEchoLatencyThreshold: 0 + localEchoLatencyThreshold: 0, + localEchoExcludePrograms: DEFAULT_LOCAL_ECHO_EXCLUDE, }); publicLog = stub(); addon = new TestTypeAheadAddon( @@ -100,6 +101,7 @@ suite('Workbench - Terminal Typeahead', () => { upcastPartial({ config, onConfigChanged: onConfigChanged.event }), upcastPartial({ publicLog }) ); + addon.unlockMakingPredictions(); }); teardown(() => { @@ -149,8 +151,7 @@ suite('Workbench - Terminal Typeahead', () => { `${CSI}?25l`, // hide cursor `${CSI}2;7H`, // move cursor cursor `${CSI}X`, // delete character - `${CSI}1m`, // reset style - `${CSI}38;5;1m`, // reset style + `${CSI}1;38;5;1m`, // reset style 'q', // new character `${CSI}?25h`, // show cursor ].join('')); @@ -244,29 +245,56 @@ suite('Workbench - Terminal Typeahead', () => { t.expectWritten(`${CSI}2;6H${CSI}X`); }); - test('avoids predicting password input', () => { + test('waits for first valid prediction on a line', () => { + const t = createMockTerminal({ lines: ['hello|'] }); + addon.lockMakingPredictions(); + addon.activate(t.terminal); + + t.onData('o'); + t.expectWritten(''); + expectProcessed('o', 'o'); + + t.onData('o'); + t.expectWritten(`${CSI}3mo${CSI}23m`); + }); + + test('disables on title change', () => { const t = createMockTerminal({ lines: ['hello|'] }); addon.activate(t.terminal); - const tcases = ['Your password:', 'Password here:', 'PAT:', 'Access token:']; - for (const tcase of tcases) { - expectProcessed(tcase, tcase); + addon.reevaluateNow(); + assert.strictEqual(addon.isShowing, true, 'expected to show initially'); - t.onData('mellon\r\n'); - t.expectWritten(''); - expectProcessed('\r\n', '\r\n'); + t.onTitleChange.fire('foo - VIM.exe'); + addon.reevaluateNow(); + assert.strictEqual(addon.isShowing, false, 'expected to hide when vim is open'); - t.onData('o'); // back to normal mode - t.expectWritten(`${CSI}3mo${CSI}23m`); - onBeforeProcessData.fire({ data: 'o' }); - } + t.onTitleChange.fire('foo - git.exe'); + addon.reevaluateNow(); + assert.strictEqual(addon.isShowing, true, 'expected to show again after vim closed'); }); }); }); class TestTypeAheadAddon extends TypeAheadAddon { + public unlockMakingPredictions() { + this.lastRow = { y: 1, startingX: 100, charState: CharPredictState.Validated }; + } + + public lockMakingPredictions() { + this.lastRow = undefined; + } + public unlockLeftNavigating() { - this.lastRow = { y: 1, startingX: 1 }; + this.lastRow = { y: 1, startingX: 1, charState: CharPredictState.Validated }; + } + + public reevaluateNow() { + this.reevaluatePredictorStateNow(this.stats!, this.timeline!); + } + + public get isShowing() { + return !!this.timeline?.isShowingPredictions; } } @@ -293,6 +321,7 @@ function createMockTerminal({ lines, cursorAttrs }: { }) { const written: string[] = []; const cursor = { y: 1, x: 1 }; + const onTitleChange = new Emitter(); const onData = new Emitter(); const csiEmitter = new Emitter(); @@ -316,11 +345,13 @@ function createMockTerminal({ lines, cursorAttrs }: { clearWritten: () => written.splice(0, written.length), onData: (s: string) => onData.fire(s), csiEmitter, + onTitleChange, terminal: { cols: 80, rows: 5, onResize: new Emitter().event, onData: onData.event, + onTitleChange: onTitleChange.event, parser: { registerCsiHandler(_: unknown, callback: () => void) { csiEmitter.event(callback); diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts new file mode 100644 index 000000000..4e541bc83 --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { TestService } from 'vs/workbench/contrib/testing/common/testServiceImpl'; + +registerSingleton(ITestService, TestService); diff --git a/src/vs/workbench/contrib/testing/common/testCollection.ts b/src/vs/workbench/contrib/testing/common/testCollection.ts new file mode 100644 index 000000000..ab76dab73 --- /dev/null +++ b/src/vs/workbench/contrib/testing/common/testCollection.ts @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { URI } from 'vs/base/common/uri'; +import { Location as ModeLocation } from 'vs/editor/common/modes'; +import { ExtHostTestingResource } from 'vs/workbench/api/common/extHost.protocol'; +import { TestMessageSeverity, TestRunState } from 'vs/workbench/api/common/extHostTypes'; + +/** + * Request to them main thread to run a set of tests. + */ +export interface RunTestsRequest { + tests: { testId: string; providerId: string }[]; + debug: boolean; +} + +/** + * Request from the main thread to run tests for a single provider. + */ +export interface RunTestForProviderRequest { + providerId: string; + ids: string[]; + debug: boolean; +} + +/** + * Response to a {@link RunTestsRequest} + */ +export interface RunTestsResult { + // todo +} + +export const EMPTY_TEST_RESULT: RunTestsResult = {}; + +export const collectTestResults = (results: ReadonlyArray) => { + return results[0] || {}; // todo +}; + +export interface ITestMessage { + message: string | IMarkdownString; + severity: TestMessageSeverity | undefined; + expectedOutput: string | undefined; + actualOutput: string | undefined; + location: ModeLocation | undefined; +} + +export interface ITestState { + runState: TestRunState; + duration: number | undefined; + messages: ITestMessage[]; +} + +/** + * The TestItem from .d.ts, as a plain object without children. + */ +export interface ITestItem { + label: string; + children?: never; + location: ModeLocation | undefined; + description: string | undefined; + runnable: boolean | undefined; + debuggable: boolean | undefined; + state: ITestState; +} + +/** + * TestItem-like shape, butm with an ID and children as strings. + */ +export interface InternalTestItem { + id: string; + providerId: string; + parent: string | null; + item: ITestItem; +} + +export const enum TestDiffOpType { + Add, + Update, + Remove, +} + +export type TestsDiffOp = + | [op: TestDiffOpType.Add, item: InternalTestItem] + | [op: TestDiffOpType.Update, item: InternalTestItem] + | [op: TestDiffOpType.Remove, itemId: string]; + +/** + * Utility function to get a unique string for a subscription to a resource, + * useful to keep maps of document or workspace folder subscription info. + */ +export const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`; + +/** + * Request from the ext host or main thread to indicate that tests have + * changed. It's assumed that any item upserted *must* have its children + * previously also upserted, or upserted as part of the same operation. + * Children that no longer exist in an upserted item will be removed. + */ +export type TestsDiff = TestsDiffOp[]; + +/** + * @private + */ +export interface IncrementalTestCollectionItem extends InternalTestItem { + children: Set; +} + +/** + * The IncrementalChangeCollector is used in the IncrementalTestCollection + * and called with diff changes as they're applied. This is used in the + * ext host to create a cohesive change event from a diff. + */ +export class IncrementalChangeCollector { + /** + * A node was added. + */ + public add(node: T): void { } + + /** + * A node in the collection was updated. + */ + public update(node: T): void { } + + /** + * A node was removed. + */ + public remove(node: T): void { } + + /** + * Called when the diff has been applied. + */ + public complete(): void { } +} + +/** + * Maintains tests in this extension host sent from the main thread. + */ +export abstract class AbstractIncrementalTestCollection { + /** + * Map of item IDs to test item objects. + */ + protected readonly items = new Map(); + + /** + * ID of test root items. + */ + protected readonly roots = new Set(); + + /** + * Applies the diff to the collection. + */ + public apply(diff: TestsDiff) { + const changes = this.createChangeCollector(); + + for (const op of diff) { + switch (op[0]) { + case TestDiffOpType.Add: { + const item = op[1]; + if (!item.parent) { + this.roots.add(item.id); + const created = this.createItem(item); + this.items.set(item.id, created); + changes.add(created); + } else if (this.items.has(item.parent)) { + const parent = this.items.get(item.parent)!; + parent.children.add(item.id); + const created = this.createItem(item, parent); + this.items.set(item.id, created); + changes.add(created); + } + break; + } + + case TestDiffOpType.Update: { + const item = op[1]; + const existing = this.items.get(item.id); + if (existing) { + Object.assign(existing.item, item.item); + changes.update(existing); + } + break; + } + + case TestDiffOpType.Remove: { + const toRemove = this.items.get(op[1]); + if (!toRemove) { + break; + } + + if (toRemove.parent) { + const parent = this.items.get(toRemove.parent)!; + parent.children.delete(toRemove.id); + } else { + this.roots.delete(toRemove.id); + } + + const queue: Iterable[] = [[op[1]]]; + while (queue.length) { + for (const itemId of queue.pop()!) { + const existing = this.items.get(itemId); + if (existing) { + queue.push(existing.children); + this.items.delete(itemId); + changes.remove(existing); + } + } + } + } + } + } + + changes.complete(); + } + + /** + * Called before a diff is applied to create a new change collector. + */ + protected createChangeCollector() { + return new IncrementalChangeCollector(); + } + + /** + * Creates a new item for the collection from the internal test item. + */ + protected abstract createItem(internal: InternalTestItem, parent?: T): T; +} diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts new file mode 100644 index 000000000..f4d84d583 --- /dev/null +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ExtHostTestingResource } from 'vs/workbench/api/common/extHost.protocol'; +import { RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; + +export const ITestService = createDecorator('testService'); + +export interface MainTestController { + runTests(request: RunTestForProviderRequest): Promise; +} + +export type TestDiffListener = (diff: TestsDiff) => void; + +export interface ITestService { + readonly _serviceBrand: undefined; + readonly onShouldSubscribe: Event<{ resource: ExtHostTestingResource, uri: URI }>; + readonly onShouldUnsubscribe: Event<{ resource: ExtHostTestingResource, uri: URI }>; + registerTestController(id: string, controller: MainTestController): void; + unregisterTestController(id: string): void; + runTests(req: RunTestsRequest): Promise; + publishDiff(resource: ExtHostTestingResource, uri: URI, diff: TestsDiff): void; + subscribeToDiffs(resource: ExtHostTestingResource, uri: URI, acceptDiff: TestDiffListener): IDisposable; +} diff --git a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts new file mode 100644 index 000000000..845534dd8 --- /dev/null +++ b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { groupBy } from 'vs/base/common/arrays'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { isDefined } from 'vs/base/common/types'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { ExtHostTestingResource } from 'vs/workbench/api/common/extHost.protocol'; +import { AbstractIncrementalTestCollection, collectTestResults, getTestSubscriptionKey, IncrementalTestCollectionItem, InternalTestItem, RunTestsRequest, RunTestsResult, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { ITestService, MainTestController, TestDiffListener } from 'vs/workbench/contrib/testing/common/testService'; + +export class TestService extends Disposable implements ITestService { + declare readonly _serviceBrand: undefined; + private testControllers = new Map(); + private readonly testSubscriptions = new Map; + listeners: number; + }>(); + private readonly subscribeEmitter = new Emitter<{ resource: ExtHostTestingResource, uri: URI }>(); + private readonly unsubscribeEmitter = new Emitter<{ resource: ExtHostTestingResource, uri: URI }>(); + + /** + * Fired when extension hosts should pull events from their test factories. + */ + public readonly onShouldSubscribe = this.subscribeEmitter.event; + + /** + * Fired when extension hosts should stop pulling events from their test factories. + */ + public readonly onShouldUnsubscribe = this.unsubscribeEmitter.event; + + /** + * @inheritdoc + */ + async runTests(req: RunTestsRequest): Promise { + const tests = groupBy(req.tests, (a, b) => a.providerId === b.providerId ? 0 : 1); + const requests = tests.map(group => { + const providerId = group[0].providerId; + const controller = this.testControllers.get(providerId); + return controller?.runTests({ providerId, debug: req.debug, ids: group.map(t => t.testId) }); + }).filter(isDefined); + + return collectTestResults(await Promise.all(requests)); + } + + /** + * @inheritdoc + */ + public subscribeToDiffs(resource: ExtHostTestingResource, uri: URI, acceptDiff: TestDiffListener) { + const subscriptionKey = getTestSubscriptionKey(resource, uri); + let subscription = this.testSubscriptions.get(subscriptionKey); + if (!subscription) { + subscription = { collection: new MainThreadTestCollection(), listeners: 0, onDiff: new Emitter() }; + this.subscribeEmitter.fire({ resource, uri }); + this.testSubscriptions.set(subscriptionKey, subscription); + } + + subscription.listeners++; + + const revive = subscription.collection.getReviverDiff(); + if (revive.length) { + acceptDiff(revive); + } + + const listener = subscription.onDiff.event(acceptDiff); + return toDisposable(() => { + listener.dispose(); + + if (!--subscription!.listeners) { + this.unsubscribeEmitter.fire({ resource, uri }); + this.testSubscriptions.delete(subscriptionKey); + } + }); + } + + /** + * @inheritdoc + */ + public publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff) { + const sub = this.testSubscriptions.get(getTestSubscriptionKey(resource, URI.revive(uri))); + if (sub) { + sub.collection.apply(diff); + sub.onDiff.fire(diff); + } + } + + /** + * @inheritdoc + */ + public registerTestController(id: string, controller: MainTestController): void { + this.testControllers.set(id, controller); + } + + /** + * @inheritdoc + */ + public unregisterTestController(id: string): void { + this.testControllers.delete(id); + } +} + +class MainThreadTestCollection extends AbstractIncrementalTestCollection { + /** + * Gets a diff that adds all items currently in the tree to a new collection, + * allowing it to fully hydrate. + */ + public getReviverDiff() { + const ops: TestsDiff = []; + const queue = [this.roots]; + while (queue.length) { + for (const child of queue.pop()!) { + const item = this.items.get(child)!; + ops.push([TestDiffOpType.Add, { id: item.id, providerId: item.providerId, item: item.item, parent: item.parent }]); + queue.push(item.children); + } + } + + return ops; + } + + protected createItem(internal: InternalTestItem): IncrementalTestCollectionItem { + return { ...internal, children: new Set() }; + } +} diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 769f87085..ca1165af6 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -22,6 +22,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IQuickInputService, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { DEFAULT_PRODUCT_ICON_THEME_ID } from 'vs/workbench/services/themes/browser/productIconThemeData'; +import { IGettingStartedService } from 'vs/workbench/services/gettingStarted/common/gettingStartedService'; export class SelectColorThemeAction extends Action { @@ -34,6 +35,7 @@ export class SelectColorThemeAction extends Action { @IQuickInputService private readonly quickInputService: IQuickInputService, @IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + @IGettingStartedService private readonly gettingStartedService: IGettingStartedService, @IViewletService private readonly viewletService: IViewletService ) { super(id, label); @@ -84,6 +86,7 @@ export class SelectColorThemeAction extends Action { openExtensionViewlet(this.viewletService, `category:themes ${quickpick.value}`); } else { selectTheme(theme, true); + this.gettingStartedService.progressByEvent('themeSelected'); } isCompleted = true; quickpick.hide(); @@ -302,7 +305,7 @@ class GenerateColorThemeAction extends Action { let theme = this.themeService.getColorTheme(); let colors = Registry.as(ColorRegistryExtensions.ColorContribution).getColors(); let colorIds = colors.map(c => c.id).sort(); - let resultingColors: { [key: string]: string } = {}; + let resultingColors: { [key: string]: string | null } = {}; let inherited: string[] = []; for (let colorId of colorIds) { const color = theme.getColor(colorId, false); @@ -312,12 +315,18 @@ class GenerateColorThemeAction extends Action { inherited.push(colorId); } } + const nullDefaults = []; for (let id of inherited) { const color = theme.getColor(id); if (color) { resultingColors['__' + id] = Color.Format.CSS.formatHexA(color, true); + } else { + nullDefaults.push(id); } } + for (let id of nullDefaults) { + resultingColors['__' + id] = null; + } let contents = JSON.stringify({ '$schema': colorThemeSchemaId, type: theme.type, @@ -326,7 +335,7 @@ class GenerateColorThemeAction extends Action { }, null, '\t'); contents = contents.replace(/\"__/g, '//"'); - return this.editorService.openEditor({ contents, mode: 'jsonc' }); + return this.editorService.openEditor({ contents, mode: 'jsonc', options: { pinned: true } }); } } diff --git a/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts index 0eb622bbe..d97e3c087 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts @@ -18,6 +18,7 @@ import { Color } from 'vs/base/common/color'; import { IFileService } from 'vs/platform/files/common/files'; import { basename } from 'vs/base/common/resources'; import { Schemas } from 'vs/base/common/network'; +import { splitLines } from 'vs/base/common/strings'; interface IToken { c: string; @@ -220,7 +221,7 @@ class Snapper { if (!grammar) { return []; } - let lines = content.split(/\r\n|\r|\n/); + let lines = splitLines(content); let result = this._tokenize(grammar, lines); return this._getThemesResult(grammar, lines).then((themesResult) => { diff --git a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts index b7ca9dbb2..212f827bf 100644 --- a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts +++ b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts @@ -18,11 +18,17 @@ import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ExplorerFolderContext } from 'vs/workbench/contrib/files/common/files'; import { ResourceContextKey } from 'vs/workbench/common/resources'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + + +const timelineViewIcon = registerIcon('timeline-view-icon', Codicon.history, localize('timelineViewIcon', 'View icon of the timeline view.')); +const timelineOpenIcon = registerIcon('timeline-open', Codicon.history, localize('timelineOpenIcon', 'Icon for the open timeline action.')); export class TimelinePaneDescriptor implements IViewDescriptor { readonly id = TimelinePaneId; readonly name = TimelinePane.TITLE; - readonly containerIcon = 'codicon-history'; + readonly containerIcon = timelineViewIcon; readonly ctorDescriptor = new SyncDescriptor(TimelinePane); readonly order = 2; readonly weight = 30; @@ -87,7 +93,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, ({ command: { id: OpenTimelineAction.ID, title: OpenTimelineAction.LABEL, - icon: { id: 'codicon/history' } + icon: timelineOpenIcon }, when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource) })); diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 78ae88936..64f30027e 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -42,6 +42,8 @@ import { MenuItemAction, IMenuService, MenuId, registerAction2, Action2, MenuReg import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; const ItemHeight = 22; @@ -208,7 +210,7 @@ class LoadMoreCommand { return this.loading ? localize('timeline.loadingMore', "Loading...") : localize('timeline.loadMore', "Load more"); } - get themeIcon(): { id: string; } | undefined { + get themeIcon(): ThemeIcon | undefined { return undefined; //this.loading ? { id: 'sync~spin' } : undefined; } } @@ -1150,6 +1152,11 @@ class TimelineTreeRenderer implements ITreeRenderer('updateState', StateType.Idle); @@ -117,6 +115,30 @@ export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesActio } } +interface IVersion { + major: number; + minor: number; + patch: number; +} + +function parseVersion(version: string): IVersion | undefined { + const match = /([0-9]+)\.([0-9]+)\.([0-9]+)/.exec(version); + + if (!match) { + return undefined; + } + + return { + major: parseInt(match[1]), + minor: parseInt(match[2]), + patch: parseInt(match[3]) + }; +} + +function isMajorMinorUpdate(before: IVersion, after: IVersion): boolean { + return before.major < after.major || before.minor < after.minor; +} + export class ProductContribution implements IWorkbenchContribution { private static readonly KEY = 'releaseNotes/lastVersion'; @@ -136,12 +158,13 @@ export class ProductContribution implements IWorkbenchContribution { return; } - const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, ''); + const lastVersion = parseVersion(storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, '')); + const currentVersion = parseVersion(productService.version); const shouldShowReleaseNotes = configurationService.getValue('update.showReleaseNotes'); - - // was there an update? if so, open release notes const releaseNotesUrl = productService.releaseNotesUrl; - if (shouldShowReleaseNotes && !environmentService.skipReleaseNotes && releaseNotesUrl && lastVersion && productService.version !== lastVersion) { + + // was there a major/minor update? if so, open release notes + if (shouldShowReleaseNotes && !environmentService.skipReleaseNotes && releaseNotesUrl && lastVersion && currentVersion && isMajorMinorUpdate(lastVersion, currentVersion)) { showReleaseNotes(instantiationService, productService.version) .then(undefined, () => { notificationService.prompt( @@ -160,11 +183,11 @@ export class ProductContribution implements IWorkbenchContribution { } // should we show the new license? - if (productService.licenseUrl && lastVersion && semver.satisfies(lastVersion, '<1.0.0') && semver.satisfies(productService.version, '>=1.0.0')) { + if (productService.licenseUrl && lastVersion && lastVersion.major < 1 && currentVersion && currentVersion.major >= 1) { notificationService.info(nls.localize('licenseChanged', "Our license terms have changed, please click [here]({0}) to go through them.", productService.licenseUrl)); } - storageService.store(ProductContribution.KEY, productService.version, StorageScope.GLOBAL); + storageService.store(ProductContribution.KEY, productService.version, StorageScope.GLOBAL, StorageTarget.MACHINE); }); } } @@ -185,15 +208,12 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu @IContextKeyService private readonly contextKeyService: IContextKeyService, @IProductService private readonly productService: IProductService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService + @IOpenerService private readonly openerService: IOpenerService ) { super(); this.state = updateService.state; this.updateStateContextKey = CONTEXT_UPDATE_STATE.bindTo(this.contextKeyService); - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: 'neverShowAgain:update/win32-fast-updates', version: 1 }); - this._register(updateService.onStateChange(this.onUpdateStateChange, this)); this.onUpdateStateChange(this.updateService.state); @@ -268,6 +288,10 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu } private onError(error: string): void { + if (/The request timed out|The network connection was lost/i.test(error)) { + return; + } + error = error.replace(/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/, 'See [this link](https://github.com/microsoft/vscode/issues/7426#issuecomment-425093469) for more information'); this.notificationService.notify({ @@ -398,8 +422,8 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu // if version != stored version, save version and date if (currentVersion !== lastKnownVersion) { - this.storageService.store('update/lastKnownVersion', currentVersion!, StorageScope.GLOBAL); - this.storageService.store('update/updateNotificationTime', currentMillis, StorageScope.GLOBAL); + this.storageService.store('update/lastKnownVersion', currentVersion!, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store('update/updateNotificationTime', currentMillis, StorageScope.GLOBAL, StorageTarget.MACHINE); } const updateNotificationMillis = this.storageService.getNumber('update/updateNotificationTime', StorageScope.GLOBAL, currentMillis); @@ -472,7 +496,35 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Updating) }); - CommandsRegistry.registerCommand('update.restart', () => this.updateService.quitAndInstall()); + CommandsRegistry.registerCommand('update.restart', async () => { + if (isNative && isMacintosh && typeof require !== 'undefined' && typeof require.__$__nodeRequire === 'function') { + const os = require.__$__nodeRequire('os') as { release(): string; }; + const release = os.release(); + + if (parseInt(release) >= 20) { // Big Sur + const answer = await this.dialogService.show( + Severity.Warning, + nls.localize('good luck', "'Restart to Update' is not working properly on macOS Big Sur. Click 'Quit to Update' to quit {0} and update it. Then, relaunch it from Finder.", this.productService.nameShort), + [ + nls.localize('quit', "Quit to Update"), + nls.localize('learn more', "Learn More"), + nls.localize('cancel', "Cancel") + ], + { cancelId: 2 } + ); + + if (answer.choice === 2) { + return; + } else if (answer.choice === 1) { + await this.openerService.open(URI.parse('https://github.com/microsoft/vscode/issues/109728')); + return; + } + } + } + + this.updateService.quitAndInstall(); + }); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '6_update', command: { diff --git a/src/vs/workbench/contrib/url/browser/trustedDomains.ts b/src/vs/workbench/contrib/url/browser/trustedDomains.ts index 81ec10aea..145afdf03 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomains.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomains.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IProductService } from 'vs/platform/product/common/productService'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; @@ -31,7 +31,7 @@ export const manageTrustedDomainSettingsCommand = { }, handler: async (accessor: ServicesAccessor) => { const editorService = accessor.get(IEditorService); - editorService.openEditor({ resource: TRUSTED_DOMAINS_URI, mode: 'jsonc' }); + editorService.openEditor({ resource: TRUSTED_DOMAINS_URI, mode: 'jsonc', options: { pinned: true } }); return; } }; @@ -117,7 +117,8 @@ export async function configureOpenerTrustedDomainsHandler( case 'manage': await editorService.openEditor({ resource: TRUSTED_DOMAINS_URI, - mode: 'jsonc' + mode: 'jsonc', + options: { pinned: true } }); notificationService.prompt(Severity.Info, localize('configuringURL', "Configuring trust for: {0}", resource.toString()), [{ label: 'Copy', run: () => clipboardService.writeText(resource.toString()) }]); @@ -129,7 +130,8 @@ export async function configureOpenerTrustedDomainsHandler( storageService.store( TRUSTED_DOMAINS_STORAGE_KEY, JSON.stringify([...trustedDomains, itemToTrust]), - StorageScope.GLOBAL + StorageScope.GLOBAL, + StorageTarget.USER ); return [...trustedDomains, itemToTrust]; diff --git a/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts b/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts index a04a706c8..988007b32 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts @@ -8,11 +8,10 @@ import { parse } from 'vs/base/common/json'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IFileService, IStat, IWatchOptions, IFileSystemProviderWithFileReadWriteCapability } from 'vs/platform/files/common/files'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { VSBuffer } from 'vs/base/common/buffer'; import { readTrustedDomains, TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, TRUSTED_DOMAINS_STORAGE_KEY } from 'vs/workbench/contrib/url/browser/trustedDomains'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { assertIsDefined } from 'vs/base/common/types'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -97,12 +96,8 @@ export class TrustedDomainsFileSystemProvider implements IFileSystemProviderWith @IFileService private readonly fileService: IFileService, @IStorageService private readonly storageService: IStorageService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { this.fileService.registerProvider(TRUSTED_DOMAINS_SCHEMA, this); - - this.storageKeysSyncRegistryService.registerStorageKey({ key: TRUSTED_DOMAINS_STORAGE_KEY, version: 1 }); - this.storageKeysSyncRegistryService.registerStorageKey({ key: TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, version: 1 }); } stat(resource: URI): Promise { @@ -134,11 +129,12 @@ export class TrustedDomainsFileSystemProvider implements IFileSystemProviderWith const trustedDomainsContent = VSBuffer.wrap(content).toString(); const trustedDomains = parse(trustedDomainsContent); - this.storageService.store(TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, trustedDomainsContent, StorageScope.GLOBAL); + this.storageService.store(TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, trustedDomainsContent, StorageScope.GLOBAL, StorageTarget.USER); this.storageService.store( TRUSTED_DOMAINS_STORAGE_KEY, JSON.stringify(trustedDomains) || '', - StorageScope.GLOBAL + StorageScope.GLOBAL, + StorageTarget.USER ); } catch (err) { } diff --git a/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts index 2c8428a63..c8f144bda 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts @@ -283,13 +283,15 @@ const doURLMatch = ( options.push(doURLMatch(memo, url, trustedURL, urlOffset, trustedURLOffset + 2)); } - if (trustedURL[trustedURLOffset] + trustedURL[trustedURLOffset + 1] === '.*' && url[urlOffset] === '.') { - // IP mode. Consume one segment of numbers or nothing. - let endBlockIndex = urlOffset + 1; - do { endBlockIndex++; } while (/[0-9]/.test(url[endBlockIndex])); - if (['.', ':', '/', undefined].includes(url[endBlockIndex])) { - options.push(doURLMatch(memo, url, trustedURL, endBlockIndex, trustedURLOffset + 2)); + if (trustedURL[trustedURLOffset] === '*') { + // Any match. Either consume one thing and don't advance base or consume nothing and do. + if (urlOffset + 1 === url.length) { + // If we're at the end of the input url consume one from both. + options.push(doURLMatch(memo, url, trustedURL, urlOffset + 1, trustedURLOffset + 1)); + } else { + options.push(doURLMatch(memo, url, trustedURL, urlOffset + 1, trustedURLOffset)); } + options.push(doURLMatch(memo, url, trustedURL, urlOffset, trustedURLOffset + 1)); } if (trustedURL[trustedURLOffset] + trustedURL[trustedURLOffset + 1] === ':*') { diff --git a/src/vs/workbench/contrib/url/browser/url.contribution.ts b/src/vs/workbench/contrib/url/browser/url.contribution.ts index 949c99803..5d3a01480 100644 --- a/src/vs/workbench/contrib/url/browser/url.contribution.ts +++ b/src/vs/workbench/contrib/url/browser/url.contribution.ts @@ -37,7 +37,7 @@ class OpenUrlAction extends Action2 { return quickInputService.input({ prompt: localize('urlToOpen', "URL to open") }).then(input => { if (input) { const uri = URI.parse(input); - urlService.open(uri, { trusted: true }); + urlService.open(uri, { originalUrl: input }); } }); } diff --git a/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts b/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts index 6f8ec1ad0..77144c981 100644 --- a/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts +++ b/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts @@ -116,6 +116,14 @@ suite('Link protection domain matching', () => { linkNotAllowedByRules('http://192.168.1.7:3000/', ['http://192.168.*.6:*']); }); + test('scheme match', () => { + linkAllowedByRules('http://192.168.1.7/', ['http://*']); + linkAllowedByRules('http://twitter.com', ['http://*']); + linkAllowedByRules('http://twitter.com/hello', ['http://*']); + linkNotAllowedByRules('https://192.168.1.7/', ['http://*']); + linkNotAllowedByRules('https://twitter.com/', ['http://*']); + }); + test('case normalization', () => { // https://github.com/microsoft/vscode/issues/99294 linkAllowedByRules('https://github.com/microsoft/vscode/issues/new', ['https://github.com/microsoft']); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index f41dbb1c2..8a1aa13b2 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; -import { toErrorMessage } from 'vs/base/common/errorMessage'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, dispose, MutableDisposable, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; @@ -45,15 +44,14 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { fromNow } from 'vs/base/common/date'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { Registry } from 'vs/platform/registry/common/platform'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { Codicon } from 'vs/base/common/codicons'; import { ViewContainerLocation, IViewContainersRegistry, Extensions, ViewContainer } from 'vs/workbench/common/views'; import { UserDataSyncViewPaneContainer, UserDataSyncDataViews } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncViews'; -import { IUserDataSyncWorkbenchService, getSyncAreaLabel, AccountStatus, CONTEXT_SYNC_STATE, CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE, CONFIGURE_SYNC_COMMAND_ID, SHOW_SYNC_LOG_COMMAND_ID, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { IUserDataSyncWorkbenchService, getSyncAreaLabel, AccountStatus, CONTEXT_SYNC_STATE, CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE, CONFIGURE_SYNC_COMMAND_ID, SHOW_SYNC_LOG_COMMAND_ID, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE, SYNC_VIEW_ICON } from 'vs/workbench/services/userDataSync/common/userDataSync'; const CONTEXT_CONFLICTS_SOURCES = new RawContextKey('conflictsSources', ''); @@ -259,7 +257,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await this.userDataSyncService.accept(syncResource, conflict.remoteResource, undefined, this.userDataAutoSyncEnablementService.isEnabled()); } } catch (e) { - this.notificationService.error(e); + this.notificationService.error(localize('accept failed', "Error while accepting changes. Please check [logs]({0}) for more details.", `command:${SHOW_SYNC_LOG_COMMAND_ID}`)); } } @@ -269,7 +267,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await this.userDataSyncService.accept(syncResource, conflict.localResource, undefined, this.userDataAutoSyncEnablementService.isEnabled()); } } catch (e) { - this.notificationService.error(e); + this.notificationService.error(localize('accept failed', "Error while accepting changes. Please check [logs]({0}) for more details.", `command:${SHOW_SYNC_LOG_COMMAND_ID}`)); } } @@ -447,7 +445,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await this.selectSettingsSyncService(this.userDataSyncStoreManagementService.userDataSyncStore); } await this.userDataSyncWorkbenchService.turnOn(); - this.storageService.store('sync.donotAskPreviewConfirmation', true, StorageScope.GLOBAL); + this.storageService.store('sync.donotAskPreviewConfirmation', true, StorageScope.GLOBAL, StorageTarget.MACHINE); } catch (e) { if (isPromiseCanceledError(e)) { return; @@ -482,9 +480,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } }); return; + case UserDataSyncErrorCode.Unauthorized: + this.notificationService.error(localize('auth failed', "Error while turning on Settings Sync: Authentication failed.")); + return; } } - this.notificationService.error(localize('turn on failed', "Error while starting Settings Sync: {0}", toErrorMessage(e))); + this.notificationService.error(localize('turn on failed', "Error while turning on Settings Sync. Please check [logs]({0}) for more details.", `command:${SHOW_SYNC_LOG_COMMAND_ID}`)); } } @@ -658,6 +659,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo leftResource: conflict.remoteResource, rightResource: conflict.previewResource, label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + description: localize('sideBySideDescription', "Settings Sync"), options: { preserveFocus: false, pinned: true, @@ -1047,7 +1049,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await that.turnOff(); } catch (e) { if (!isPromiseCanceledError(e)) { - that.notificationService.error(localize('turn off failed', "Error while turning off sync: {0}", toErrorMessage(e))); + that.notificationService.error(localize('turn off failed', "Error while turning off Settings Sync. Please check [logs]({0}) for more details.", `command:${SHOW_SYNC_LOG_COMMAND_ID}`)); } } } @@ -1121,7 +1123,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo UserDataSyncViewPaneContainer, [SYNC_VIEW_CONTAINER_ID] ), - icon: Codicon.sync.classNames, + icon: SYNC_VIEW_ICON, hideIfEmpty: true, }, ViewContainerLocation.Sidebar); } @@ -1251,7 +1253,7 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio this.notificationService.warn(localize('update conflicts', "Could not resolve conflicts as there is new local version available. Please try again.")); } } else { - this.notificationService.error(e); + this.notificationService.error(localize('accept failed', "Error while accepting changes. Please check [logs]({0}) for more details.", `command:${SHOW_SYNC_LOG_COMMAND_ID}`)); } } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts index bf0675cdb..3610b1ca5 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts @@ -302,7 +302,11 @@ export class UserDataSyncMergesViewPane extends TreeViewPane { if (previewResource.mergeState === MergeState.Accepted) { if (previewResource.localChange !== Change.Deleted && previewResource.remoteChange !== Change.Deleted) { // Do not open deleted preview - await this.editorService.openEditor({ resource: previewResource.accepted, label: localize('preview', "{0} (Preview)", basename(previewResource.accepted)) }); + await this.editorService.openEditor({ + resource: previewResource.accepted, + label: localize('preview', "{0} (Preview)", basename(previewResource.accepted)), + options: { pinned: true } + }); } } else { const leftResource = previewResource.remote; @@ -314,9 +318,11 @@ export class UserDataSyncMergesViewPane extends TreeViewPane { leftResource, rightResource, label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + description: localize('sideBySideDescription', "Settings Sync"), options: { preserveFocus: true, revealIfVisible: true, + pinned: true }, }); } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts index 1bf03ec3a..d26d9dd1a 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts @@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IViewsRegistry, Extensions, ITreeViewDescriptor, ITreeViewDataProvider, ITreeItem, TreeItemCollapsibleState, TreeViewItemHandleArg, ViewContainer, IViewDescriptorService } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; +import { TreeView, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ALL_SYNC_RESOURCES, SyncResource, IUserDataSyncService, ISyncResourceHandle as IResourceHandle, SyncStatus, IUserDataSyncResourceEnablementService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; @@ -34,7 +34,6 @@ import { IUserDataSyncWorkbenchService, CONTEXT_SYNC_STATE, getSyncAreaLabel, CO import { IUserDataSyncMachinesService, IUserDataSyncMachine } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { TreeView } from 'vs/workbench/contrib/views/browser/treeView'; import { flatten } from 'vs/base/common/arrays'; import { UserDataSyncMergesViewPane } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView'; import { basename } from 'vs/base/common/resources'; @@ -229,7 +228,7 @@ export class UserDataSyncDataViews extends Disposable { async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { const { resource } = <{ resource: string }>JSON.parse(handle.$treeItemHandle); const editorService = accessor.get(IEditorService); - await editorService.openEditor({ resource: URI.parse(resource) }); + await editorService.openEditor({ resource: URI.parse(resource), options: { pinned: true } }); } }); @@ -238,7 +237,7 @@ export class UserDataSyncDataViews extends Disposable { super({ id: `workbench.actions.sync.replaceCurrent`, title: localize('workbench.actions.sync.replaceCurrent', "Restore"), - icon: { id: 'codicon/discard' }, + icon: Codicon.discard, menu: { id: MenuId.ViewItemContext, when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /sync-resource-.*/i)), @@ -279,9 +278,11 @@ export class UserDataSyncDataViews extends Disposable { leftResource, rightResource, label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + description: localize('sideBySideDescription', "Settings Sync"), options: { preserveFocus: true, revealIfVisible: true, + pinned: true }, }); } diff --git a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts index bad65d90f..098549a7b 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts @@ -17,7 +17,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { Action } from 'vs/base/common/actions'; -import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-sandbox/issue'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { Disposable } from 'vs/base/common/lifecycle'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { CONTEXT_SYNC_STATE, SHOW_SYNC_LOG_COMMAND_ID, SYNC_TITLE } from 'vs/workbench/services/userDataSync/common/userDataSync'; diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts deleted file mode 100644 index b035d4cac..000000000 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ /dev/null @@ -1,1059 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./media/views'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IAction, ActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IMenuService, MenuId, MenuItemAction, registerAction2, Action2, SubmenuItemAction } from 'vs/platform/actions/common/actions'; -import { MenuEntryActionViewItem, createAndFillInContextMenuActions, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IContextKeyService, ContextKeyExpr, ContextKeyEqualsExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, IViewDescriptorService, ViewContainer, ViewContainerLocation, ResolvableTreeItem } from 'vs/workbench/common/views'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import * as DOM from 'vs/base/browser/dom'; -import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { URI } from 'vs/base/common/uri'; -import { dirname, basename } from 'vs/base/common/resources'; -import { FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon, IThemeService } from 'vs/platform/theme/common/themeService'; -import { FileKind } from 'vs/platform/files/common/files'; -import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; -import { localize } from 'vs/nls'; -import { timeout } from 'vs/base/common/async'; -import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; -import { isString } from 'vs/base/common/types'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; -import { FuzzyScore, createMatches } from 'vs/base/common/filters'; -import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; -import { isFalsyOrWhitespace } from 'vs/base/common/strings'; -import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; -import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; -import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; - -class Root implements ITreeItem { - label = { label: 'root' }; - handle = '0'; - parentHandle: string | undefined = undefined; - collapsibleState = TreeItemCollapsibleState.Expanded; - children: ITreeItem[] | undefined = undefined; -} - -const noDataProviderMessage = localize('no-dataprovider', "There is no data provider registered that can provide view data."); - -class Tree extends WorkbenchAsyncDataTree { } - -export class TreeView extends Disposable implements ITreeView { - - private isVisible: boolean = false; - private _hasIconForParentNode = false; - private _hasIconForLeafNode = false; - - private readonly collapseAllContextKey: RawContextKey; - private readonly collapseAllContext: IContextKey; - private readonly collapseAllToggleContextKey: RawContextKey; - private readonly collapseAllToggleContext: IContextKey; - private readonly refreshContextKey: RawContextKey; - private readonly refreshContext: IContextKey; - - private focused: boolean = false; - private domNode!: HTMLElement; - private treeContainer!: HTMLElement; - private _messageValue: string | undefined; - private _canSelectMany: boolean = false; - private messageElement!: HTMLDivElement; - private tree: Tree | undefined; - private treeLabels: ResourceLabels | undefined; - - private root: ITreeItem; - private elementsToRefresh: ITreeItem[] = []; - - private readonly _onDidExpandItem: Emitter = this._register(new Emitter()); - readonly onDidExpandItem: Event = this._onDidExpandItem.event; - - private readonly _onDidCollapseItem: Emitter = this._register(new Emitter()); - readonly onDidCollapseItem: Event = this._onDidCollapseItem.event; - - private _onDidChangeSelection: Emitter = this._register(new Emitter()); - readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; - - private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); - readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; - - private readonly _onDidChangeActions: Emitter = this._register(new Emitter()); - readonly onDidChangeActions: Event = this._onDidChangeActions.event; - - private readonly _onDidChangeWelcomeState: Emitter = this._register(new Emitter()); - readonly onDidChangeWelcomeState: Event = this._onDidChangeWelcomeState.event; - - private readonly _onDidChangeTitle: Emitter = this._register(new Emitter()); - readonly onDidChangeTitle: Event = this._onDidChangeTitle.event; - - private readonly _onDidChangeDescription: Emitter = this._register(new Emitter()); - readonly onDidChangeDescription: Event = this._onDidChangeDescription.event; - - private readonly _onDidCompleteRefresh: Emitter = this._register(new Emitter()); - - constructor( - readonly id: string, - private _title: string, - @IThemeService private readonly themeService: IThemeService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ICommandService private readonly commandService: ICommandService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IProgressService protected readonly progressService: IProgressService, - @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @INotificationService private readonly notificationService: INotificationService, - @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, - @IHoverService private readonly hoverService: IHoverService, - @IContextKeyService contextKeyService: IContextKeyService - ) { - super(); - this.root = new Root(); - this.collapseAllContextKey = new RawContextKey(`treeView.${this.id}.enableCollapseAll`, false); - this.collapseAllContext = this.collapseAllContextKey.bindTo(contextKeyService); - this.collapseAllToggleContextKey = new RawContextKey(`treeView.${this.id}.toggleCollapseAll`, false); - this.collapseAllToggleContext = this.collapseAllToggleContextKey.bindTo(contextKeyService); - this.refreshContextKey = new RawContextKey(`treeView.${this.id}.enableRefresh`, false); - this.refreshContext = this.refreshContextKey.bindTo(contextKeyService); - - this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); - this._register(this.themeService.onDidColorThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('explorer.decorations')) { - this.doRefresh([this.root]); /** soft refresh **/ - } - })); - this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { - if (views.some(v => v.id === this.id)) { - this.tree?.updateOptions({ overrideStyles: { listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND } }); - } - })); - this.registerActions(); - - this.create(); - } - - get viewContainer(): ViewContainer { - return this.viewDescriptorService.getViewContainerByViewId(this.id)!; - } - - get viewLocation(): ViewContainerLocation { - return this.viewDescriptorService.getViewLocationById(this.id)!; - } - - private _dataProvider: ITreeViewDataProvider | undefined; - get dataProvider(): ITreeViewDataProvider | undefined { - return this._dataProvider; - } - - set dataProvider(dataProvider: ITreeViewDataProvider | undefined) { - if (this.tree === undefined) { - this.createTree(); - } - - if (dataProvider) { - const self = this; - this._dataProvider = new class implements ITreeViewDataProvider { - private _isEmpty: boolean = true; - private _onDidChangeEmpty: Emitter = new Emitter(); - public onDidChangeEmpty: Event = this._onDidChangeEmpty.event; - - get isTreeEmpty(): boolean { - return this._isEmpty; - } - - async getChildren(node?: ITreeItem): Promise { - let children: ITreeItem[]; - if (node && node.children) { - children = node.children; - } else { - node = node ?? self.root; - children = await (node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node)); - node.children = children; - } - if (node instanceof Root) { - const oldEmpty = this._isEmpty; - this._isEmpty = children.length === 0; - if (oldEmpty !== this._isEmpty) { - this._onDidChangeEmpty.fire(); - } - } - return children; - } - }; - if (this._dataProvider.onDidChangeEmpty) { - this._register(this._dataProvider.onDidChangeEmpty(() => this._onDidChangeWelcomeState.fire())); - } - this.updateMessage(); - this.refresh(); - } else { - this._dataProvider = undefined; - this.updateMessage(); - } - - this._onDidChangeWelcomeState.fire(); - } - - private _message: string | undefined; - get message(): string | undefined { - return this._message; - } - - set message(message: string | undefined) { - this._message = message; - this.updateMessage(); - this._onDidChangeWelcomeState.fire(); - } - - get title(): string { - return this._title; - } - - set title(name: string) { - this._title = name; - this._onDidChangeTitle.fire(this._title); - } - - private _description: string | undefined; - get description(): string | undefined { - return this._description; - } - - set description(description: string | undefined) { - this._description = description; - this._onDidChangeDescription.fire(this._description); - } - - get canSelectMany(): boolean { - return this._canSelectMany; - } - - set canSelectMany(canSelectMany: boolean) { - this._canSelectMany = canSelectMany; - } - - get hasIconForParentNode(): boolean { - return this._hasIconForParentNode; - } - - get hasIconForLeafNode(): boolean { - return this._hasIconForLeafNode; - } - - get visible(): boolean { - return this.isVisible; - } - - get showCollapseAllAction(): boolean { - return !!this.collapseAllContext.get(); - } - - set showCollapseAllAction(showCollapseAllAction: boolean) { - this.collapseAllContext.set(showCollapseAllAction); - } - - get showRefreshAction(): boolean { - return !!this.refreshContext.get(); - } - - set showRefreshAction(showRefreshAction: boolean) { - this.refreshContext.set(showRefreshAction); - } - - private registerActions() { - const that = this; - this._register(registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.treeView.${that.id}.refresh`, - title: localize('refresh', "Refresh"), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.refreshContextKey), - group: 'navigation', - order: Number.MAX_SAFE_INTEGER - 1, - }, - icon: { id: 'codicon/refresh' } - }); - } - async run(): Promise { - return that.refresh(); - } - })); - this._register(registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.treeView.${that.id}.collapseAll`, - title: localize('collapseAll', "Collapse All"), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.collapseAllContextKey), - group: 'navigation', - order: Number.MAX_SAFE_INTEGER, - }, - precondition: that.collapseAllToggleContextKey, - icon: { id: 'codicon/collapse-all' } - }); - } - async run(): Promise { - if (that.tree) { - return new CollapseAllAction(that.tree, true).run(); - } - } - })); - } - - setVisibility(isVisible: boolean): void { - isVisible = !!isVisible; - if (this.isVisible === isVisible) { - return; - } - - this.isVisible = isVisible; - - if (this.tree) { - if (this.isVisible) { - DOM.show(this.tree.getHTMLElement()); - } else { - DOM.hide(this.tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it - } - - if (this.isVisible && this.elementsToRefresh.length) { - this.doRefresh(this.elementsToRefresh); - this.elementsToRefresh = []; - } - } - - this._onDidChangeVisibility.fire(this.isVisible); - } - - focus(reveal: boolean = true): void { - if (this.tree && this.root.children && this.root.children.length > 0) { - // Make sure the current selected element is revealed - const selectedElement = this.tree.getSelection()[0]; - if (selectedElement && reveal) { - this.tree.reveal(selectedElement, 0.5); - } - - // Pass Focus to Viewer - this.tree.domFocus(); - } else if (this.tree) { - this.tree.domFocus(); - } else { - this.domNode.focus(); - } - } - - show(container: HTMLElement): void { - DOM.append(container, this.domNode); - } - - private create() { - this.domNode = DOM.$('.tree-explorer-viewlet-tree-view'); - this.messageElement = DOM.append(this.domNode, DOM.$('.message')); - this.treeContainer = DOM.append(this.domNode, DOM.$('.customview-tree')); - this.treeContainer.classList.add('file-icon-themable-tree', 'show-file-icons'); - const focusTracker = this._register(DOM.trackFocus(this.domNode)); - this._register(focusTracker.onDidFocus(() => this.focused = true)); - this._register(focusTracker.onDidBlur(() => this.focused = false)); - } - - private createTree() { - const actionViewItemProvider = (action: IAction) => { - if (action instanceof MenuItemAction) { - return this.instantiationService.createInstance(MenuEntryActionViewItem, action); - } else if (action instanceof SubmenuItemAction) { - return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); - } - - return undefined; - }; - const treeMenus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); - this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); - const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.id }, () => task)); - const aligner = new Aligner(this.themeService); - const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner); - const widgetAriaLabel = this._title; - - this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer, new TreeViewDelegate(), [renderer], - dataSource, { - identityProvider: new TreeViewIdentityProvider(), - accessibilityProvider: { - getAriaLabel(element: ITreeItem): string { - if (element.accessibilityInformation) { - return element.accessibilityInformation.label; - } - - return isString(element.tooltip) ? element.tooltip : element.label ? element.label.label : ''; - }, - getRole(element: ITreeItem): string | undefined { - return element.accessibilityInformation?.role ?? 'treeitem'; - }, - getWidgetAriaLabel(): string { - return widgetAriaLabel; - } - }, - keyboardNavigationLabelProvider: { - getKeyboardNavigationLabel: (item: ITreeItem) => { - return item.label ? item.label.label : (item.resourceUri ? basename(URI.revive(item.resourceUri)) : undefined); - } - }, - expandOnlyOnTwistieClick: (e: ITreeItem) => !!e.command, - collapseByDefault: (e: ITreeItem): boolean => { - return e.collapsibleState !== TreeItemCollapsibleState.Expanded; - }, - multipleSelectionSupport: this.canSelectMany, - overrideStyles: { - listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND - } - }) as WorkbenchAsyncDataTree); - aligner.tree = this.tree; - const actionRunner = new MultipleSelectionActionRunner(this.notificationService, () => this.tree!.getSelection()); - renderer.actionRunner = actionRunner; - - this.tree.contextKeyService.createKey(this.id, true); - this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner))); - this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements))); - this._register(this.tree.onDidChangeCollapseState(e => { - if (!e.node.element) { - return; - } - - const element: ITreeItem = Array.isArray(e.node.element.element) ? e.node.element.element[0] : e.node.element.element; - if (e.node.collapsed) { - this._onDidCollapseItem.fire(element); - } else { - this._onDidExpandItem.fire(element); - } - })); - this.tree.setInput(this.root).then(() => this.updateContentAreas()); - - this._register(this.tree.onDidOpen(e => { - if (!e.browserEvent) { - return; - } - const selection = this.tree!.getSelection(); - if ((selection.length === 1) && selection[0].command) { - this.commandService.executeCommand(selection[0].command.id, ...(selection[0].command.arguments || [])); - } - })); - - } - - private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent, actionRunner: MultipleSelectionActionRunner): void { - this.hoverService.hideHover(); - const node: ITreeItem | null = treeEvent.element; - if (node === null) { - return; - } - const event: UIEvent = treeEvent.browserEvent; - - event.preventDefault(); - event.stopPropagation(); - - this.tree!.setFocus([node]); - const actions = treeMenus.getResourceContextActions(node); - if (!actions.length) { - return; - } - this.contextMenuService.showContextMenu({ - getAnchor: () => treeEvent.anchor, - - getActions: () => actions, - - getActionViewItem: (action) => { - const keybinding = this.keybindingService.lookupKeybinding(action.id); - if (keybinding) { - return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel() }); - } - return undefined; - }, - - onHide: (wasCancelled?: boolean) => { - if (wasCancelled) { - this.tree!.domFocus(); - } - }, - - getActionsContext: () => ({ $treeViewId: this.id, $treeItemHandle: node.handle }), - - actionRunner - }); - } - - protected updateMessage(): void { - if (this._message) { - this.showMessage(this._message); - } else if (!this.dataProvider) { - this.showMessage(noDataProviderMessage); - } else { - this.hideMessage(); - } - this.updateContentAreas(); - } - - private showMessage(message: string): void { - this.messageElement.classList.remove('hide'); - this.resetMessageElement(); - this._messageValue = message; - if (!isFalsyOrWhitespace(this._message)) { - this.messageElement.textContent = this._messageValue; - } - this.layout(this._height, this._width); - } - - private hideMessage(): void { - this.resetMessageElement(); - this.messageElement.classList.add('hide'); - this.layout(this._height, this._width); - } - - private resetMessageElement(): void { - DOM.clearNode(this.messageElement); - } - - private _height: number = 0; - private _width: number = 0; - layout(height: number, width: number) { - if (height && width) { - this._height = height; - this._width = width; - const treeHeight = height - DOM.getTotalHeight(this.messageElement); - this.treeContainer.style.height = treeHeight + 'px'; - if (this.tree) { - this.tree.layout(treeHeight, width); - } - } - } - - getOptimalWidth(): number { - if (this.tree) { - const parentNode = this.tree.getHTMLElement(); - const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.outline-item-label > a')); - return DOM.getLargestChildWidth(parentNode, childNodes); - } - return 0; - } - - async refresh(elements?: ITreeItem[]): Promise { - if (this.dataProvider && this.tree) { - if (this.refreshing) { - await Event.toPromise(this._onDidCompleteRefresh.event); - } - if (!elements) { - elements = [this.root]; - // remove all waiting elements to refresh if root is asked to refresh - this.elementsToRefresh = []; - } - for (const element of elements) { - element.children = undefined; // reset children - } - if (this.isVisible) { - return this.doRefresh(elements); - } else { - if (this.elementsToRefresh.length) { - const seen: Set = new Set(); - this.elementsToRefresh.forEach(element => seen.add(element.handle)); - for (const element of elements) { - if (!seen.has(element.handle)) { - this.elementsToRefresh.push(element); - } - } - } else { - this.elementsToRefresh.push(...elements); - } - } - } - return undefined; - } - - async expand(itemOrItems: ITreeItem | ITreeItem[]): Promise { - const tree = this.tree; - if (tree) { - itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems]; - await Promise.all(itemOrItems.map(element => { - return tree.expand(element, false); - })); - } - } - - setSelection(items: ITreeItem[]): void { - if (this.tree) { - this.tree.setSelection(items); - } - } - - setFocus(item: ITreeItem): void { - if (this.tree) { - this.focus(); - this.tree.setFocus([item]); - } - } - - async reveal(item: ITreeItem): Promise { - if (this.tree) { - return this.tree.reveal(item); - } - } - - private refreshing: boolean = false; - private async doRefresh(elements: ITreeItem[]): Promise { - const tree = this.tree; - if (tree && this.visible) { - this.refreshing = true; - await Promise.all(elements.map(element => tree.updateChildren(element, true, true))); - this.refreshing = false; - this._onDidCompleteRefresh.fire(); - this.updateContentAreas(); - if (this.focused) { - this.focus(false); - } - this.updateCollapseAllToggle(); - } - } - - private updateCollapseAllToggle() { - if (this.showCollapseAllAction) { - this.collapseAllToggleContext.set(!!this.root.children && (this.root.children.length > 0) && - this.root.children.some(value => value.collapsibleState !== TreeItemCollapsibleState.None)); - } - } - - private updateContentAreas(): void { - const isTreeEmpty = !this.root.children || this.root.children.length === 0; - // Hide tree container only when there is a message and tree is empty and not refreshing - if (this._messageValue && isTreeEmpty && !this.refreshing) { - this.treeContainer.classList.add('hide'); - this.domNode.setAttribute('tabindex', '0'); - } else { - this.treeContainer.classList.remove('hide'); - this.domNode.removeAttribute('tabindex'); - } - } -} - -class TreeViewIdentityProvider implements IIdentityProvider { - getId(element: ITreeItem): { toString(): string; } { - return element.handle; - } -} - -class TreeViewDelegate implements IListVirtualDelegate { - - getHeight(element: ITreeItem): number { - return TreeRenderer.ITEM_HEIGHT; - } - - getTemplateId(element: ITreeItem): string { - return TreeRenderer.TREE_TEMPLATE_ID; - } -} - -class TreeDataSource implements IAsyncDataSource { - - constructor( - private treeView: ITreeView, - private withProgress: (task: Promise) => Promise - ) { - } - - hasChildren(element: ITreeItem): boolean { - return !!this.treeView.dataProvider && (element.collapsibleState !== TreeItemCollapsibleState.None); - } - - async getChildren(element: ITreeItem): Promise { - if (this.treeView.dataProvider) { - return this.withProgress(this.treeView.dataProvider.getChildren(element)); - } - return []; - } -} - -// todo@joh,sandy make this proper and contributable from extensions -registerThemingParticipant((theme, collector) => { - - const matchBackgroundColor = theme.getColor(listFilterMatchHighlight); - if (matchBackgroundColor) { - collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); - collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); - } - const matchBorderColor = theme.getColor(listFilterMatchHighlightBorder); - if (matchBorderColor) { - collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); - collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); - } - const link = theme.getColor(textLinkForeground); - if (link) { - collector.addRule(`.tree-explorer-viewlet-tree-view > .message a { color: ${link}; }`); - } - const focusBorderColor = theme.getColor(focusBorder); - if (focusBorderColor) { - collector.addRule(`.tree-explorer-viewlet-tree-view > .message a:focus { outline: 1px solid ${focusBorderColor}; outline-offset: -1px; }`); - } - const codeBackground = theme.getColor(textCodeBlockBackground); - if (codeBackground) { - collector.addRule(`.tree-explorer-viewlet-tree-view > .message code { background-color: ${codeBackground}; }`); - } -}); - -interface ITreeExplorerTemplateData { - elementDisposable: IDisposable; - container: HTMLElement; - resourceLabel: IResourceLabel; - icon: HTMLElement; - actionBar: ActionBar; -} - -class TreeRenderer extends Disposable implements ITreeRenderer { - static readonly ITEM_HEIGHT = 22; - static readonly TREE_TEMPLATE_ID = 'treeExplorer'; - - private _actionRunner: MultipleSelectionActionRunner | undefined; - private _hoverDelegate: IHoverDelegate; - - constructor( - private treeViewId: string, - private menus: TreeMenus, - private labels: ResourceLabels, - private actionViewItemProvider: IActionViewItemProvider, - private aligner: Aligner, - @IThemeService private readonly themeService: IThemeService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @ILabelService private readonly labelService: ILabelService, - @IHoverService private readonly hoverService: IHoverService - ) { - super(); - this._hoverDelegate = { - showHover: (options: IHoverDelegateOptions): IDisposable | undefined => { - return this.hoverService.showHover(options); - } - }; - } - - get templateId(): string { - return TreeRenderer.TREE_TEMPLATE_ID; - } - - set actionRunner(actionRunner: MultipleSelectionActionRunner) { - this._actionRunner = actionRunner; - } - - renderTemplate(container: HTMLElement): ITreeExplorerTemplateData { - container.classList.add('custom-view-tree-node-item'); - - const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); - - const resourceLabel = this.labels.create(container, { supportHighlights: true, hoverDelegate: this._hoverDelegate }); - const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); - const actionBar = new ActionBar(actionsContainer, { - actionViewItemProvider: this.actionViewItemProvider - }); - - return { resourceLabel, icon, actionBar, container, elementDisposable: Disposable.None }; - } - - private getHover(label: string | undefined, resource: URI | null, node: ITreeItem): string | Promise | undefined { - if (!(node instanceof ResolvableTreeItem) || !node.hasResolve) { - if (resource) { - return undefined; - } else if (!node.tooltip) { - return label; - } else if (!isString(node.tooltip)) { - return Promise.resolve(node.tooltip); - } else { - return node.tooltip; - } - } - - return new Promise(async (resolve) => { - await node.resolve(); - resolve(node.tooltip); - }); - } - - renderElement(element: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { - templateData.elementDisposable.dispose(); - const node = element.element; - const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; - const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : (resource ? { label: basename(resource) } : undefined); - const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : undefined; - const label = treeItemLabel ? treeItemLabel.label : undefined; - const matches = (treeItemLabel && treeItemLabel.highlights && label) ? treeItemLabel.highlights.map(([start, end]) => { - if (start < 0) { - start = label.length + start; - } - if (end < 0) { - end = label.length + end; - } - if ((start >= label.length) || (end > label.length)) { - return ({ start: 0, end: 0 }); - } - if (start > end) { - const swap = start; - start = end; - end = swap; - } - return ({ start, end }); - }) : undefined; - const icon = this.themeService.getColorTheme().type === ColorScheme.LIGHT ? node.icon : node.iconDark; - const iconUrl = icon ? URI.revive(icon) : null; - const title = this.getHover(label, resource, node); - - // reset - templateData.actionBar.clear(); - templateData.icon.style.color = ''; - - if (resource || this.isFileKindThemeIcon(node.themeIcon)) { - const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); - const labelResource = resource ? resource : URI.parse('missing:_icon_resource'); - templateData.resourceLabel.setResource({ name: label, description, resource: labelResource }, { - fileKind: this.getFileKind(node), - title, - hideIcon: !!iconUrl, - fileDecorations, - extraClasses: ['custom-view-tree-node-item-resourceLabel'], - matches: matches ? matches : createMatches(element.filterData), - strikethrough: treeItemLabel?.strikethrough - }); - } else { - templateData.resourceLabel.setResource({ name: label, description }, { - title, - hideIcon: true, - extraClasses: ['custom-view-tree-node-item-resourceLabel'], - matches: matches ? matches : createMatches(element.filterData), - strikethrough: treeItemLabel?.strikethrough - }); - } - - if (iconUrl) { - templateData.icon.className = 'custom-view-tree-node-item-icon'; - templateData.icon.style.backgroundImage = DOM.asCSSUrl(iconUrl); - } else { - let iconClass: string | undefined; - if (node.themeIcon && !this.isFileKindThemeIcon(node.themeIcon)) { - iconClass = ThemeIcon.asClassName(node.themeIcon); - if (node.themeIcon.color) { - templateData.icon.style.color = this.themeService.getColorTheme().getColor(node.themeIcon.color.id)?.toString() ?? ''; - } - } - templateData.icon.className = iconClass ? `custom-view-tree-node-item-icon ${iconClass}` : ''; - templateData.icon.style.backgroundImage = ''; - } - - templateData.actionBar.context = { $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; - templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); - if (this._actionRunner) { - templateData.actionBar.actionRunner = this._actionRunner; - } - this.setAlignment(templateData.container, node); - const disposableStore = new DisposableStore(); - templateData.elementDisposable = disposableStore; - disposableStore.add(this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node))); - } - - private setAlignment(container: HTMLElement, treeItem: ITreeItem) { - container.parentElement!.classList.toggle('align-icon-with-twisty', this.aligner.alignIconWithTwisty(treeItem)); - } - - private isFileKindThemeIcon(icon: ThemeIcon | undefined): boolean { - if (icon) { - return icon.id === FileThemeIcon.id || icon.id === FolderThemeIcon.id; - } else { - return false; - } - } - - private getFileKind(node: ITreeItem): FileKind { - if (node.themeIcon) { - switch (node.themeIcon.id) { - case FileThemeIcon.id: - return FileKind.FILE; - case FolderThemeIcon.id: - return FileKind.FOLDER; - } - } - return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded ? FileKind.FOLDER : FileKind.FILE; - } - - disposeElement(resource: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { - templateData.elementDisposable.dispose(); - } - - disposeTemplate(templateData: ITreeExplorerTemplateData): void { - templateData.resourceLabel.dispose(); - templateData.actionBar.dispose(); - templateData.elementDisposable.dispose(); - } -} - -class Aligner extends Disposable { - private _tree: WorkbenchAsyncDataTree | undefined; - - constructor(private themeService: IThemeService) { - super(); - } - - set tree(tree: WorkbenchAsyncDataTree) { - this._tree = tree; - } - - public alignIconWithTwisty(treeItem: ITreeItem): boolean { - if (treeItem.collapsibleState !== TreeItemCollapsibleState.None) { - return false; - } - if (!this.hasIcon(treeItem)) { - return false; - } - - if (this._tree) { - const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); - if (this.hasIcon(parent)) { - return !!parent.children && parent.children.some(c => c.collapsibleState !== TreeItemCollapsibleState.None && !this.hasIcon(c)); - } - return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c)); - } else { - return false; - } - } - - private hasIcon(node: ITreeItem): boolean { - const icon = this.themeService.getColorTheme().type === ColorScheme.LIGHT ? node.icon : node.iconDark; - if (icon) { - return true; - } - if (node.resourceUri || node.themeIcon) { - const fileIconTheme = this.themeService.getFileIconTheme(); - const isFolder = node.themeIcon ? node.themeIcon.id === FolderThemeIcon.id : node.collapsibleState !== TreeItemCollapsibleState.None; - if (isFolder) { - return fileIconTheme.hasFileIcons && fileIconTheme.hasFolderIcons; - } - return fileIconTheme.hasFileIcons; - } - return false; - } -} - -class MultipleSelectionActionRunner extends ActionRunner { - - constructor(notificationService: INotificationService, private getSelectedResources: (() => ITreeItem[])) { - super(); - this._register(this.onDidRun(e => { - if (e.error) { - notificationService.error(localize('command-error', 'Error running command {1}: {0}. This is likely caused by the extension that contributes {1}.', e.error.message, e.action.id)); - } - })); - } - - runAction(action: IAction, context: TreeViewItemHandleArg): Promise { - const selection = this.getSelectedResources(); - let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined; - let actionInSelected: boolean = false; - if (selection.length > 1) { - selectionHandleArgs = selection.map(selected => { - if (selected.handle === context.$treeItemHandle) { - actionInSelected = true; - } - return { $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle }; - }); - } - - if (!actionInSelected) { - selectionHandleArgs = undefined; - } - - return action.run(...[context, selectionHandleArgs]); - } -} - -class TreeMenus extends Disposable implements IDisposable { - - constructor( - private id: string, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IMenuService private readonly menuService: IMenuService - ) { - super(); - } - - getResourceActions(element: ITreeItem): IAction[] { - return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).primary; - } - - getResourceContextActions(element: ITreeItem): IAction[] { - return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary; - } - - private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } { - const contextKeyService = this.contextKeyService.createScoped(); - contextKeyService.createKey('view', this.id); - contextKeyService.createKey(context.key, context.value); - - const menu = this.menuService.createMenu(menuId, contextKeyService); - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); - - menu.dispose(); - contextKeyService.dispose(); - - return result; - } -} - -export class CustomTreeView extends TreeView { - - private activated: boolean = false; - - constructor( - id: string, - title: string, - @IThemeService themeService: IThemeService, - @IInstantiationService instantiationService: IInstantiationService, - @ICommandService commandService: ICommandService, - @IConfigurationService configurationService: IConfigurationService, - @IProgressService progressService: IProgressService, - @IContextMenuService contextMenuService: IContextMenuService, - @IKeybindingService keybindingService: IKeybindingService, - @INotificationService notificationService: INotificationService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IContextKeyService contextKeyService: IContextKeyService, - @IHoverService hoverService: IHoverService, - @IExtensionService private readonly extensionService: IExtensionService, - ) { - super(id, title, themeService, instantiationService, commandService, configurationService, progressService, contextMenuService, keybindingService, notificationService, viewDescriptorService, hoverService, contextKeyService); - } - - setVisibility(isVisible: boolean): void { - super.setVisibility(isVisible); - if (this.visible) { - this.activate(); - } - } - - private activate() { - if (!this.activated) { - this.progressService.withProgress({ location: this.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) - .then(() => timeout(2000)) - .then(() => { - this.updateMessage(); - }); - this.activated = true; - } - } -} diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 316861241..142650d37 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -37,7 +37,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv private _owner: any = undefined; private readonly _scopedContextKeyService = this._register(new MutableDisposable()); - private _findWidgetVisible: IContextKey; + private _findWidgetVisible: IContextKey | undefined; public constructor( public readonly id: string, @@ -46,15 +46,13 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv extension: WebviewExtensionDescription | undefined, @ILayoutService private readonly _layoutService: ILayoutService, @IWebviewService private readonly _webviewService: IWebviewService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService + @IContextKeyService private readonly _baseContextKeyService: IContextKeyService ) { super(); this._extension = extension; this._options = initialOptions; this._contentOptions = initialContentOptions; - - this._findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(_contextKeyService); } public get isFocused() { @@ -83,15 +81,32 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv return container; } - public claim(owner: any) { + public claim(owner: any, scopedContextKeyService: IContextKeyService | undefined) { + const oldOwner = this._owner; + this._owner = owner; this.show(); + + if (oldOwner !== owner) { + const contextKeyService = (scopedContextKeyService || this._baseContextKeyService); + + // Explicitly clear before creating the new context. + // Otherwise we create the new context while the old one is still around + this._scopedContextKeyService.clear(); + this._scopedContextKeyService.value = contextKeyService.createScoped(this.container); + + this._findWidgetVisible?.reset(); + this._findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(contextKeyService); + } } public release(owner: any) { if (this._owner !== owner) { return; } + + this._scopedContextKeyService.clear(); + this._owner = undefined; this.container.style.visibility = 'hidden'; if (!this._options.retainContextWhenHidden) { @@ -130,8 +145,6 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv } webview.mountTo(this.container); - this._scopedContextKeyService.value = this._contextKeyService.createScoped(this.container); - this._findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(this._scopedContextKeyService.value); // Forward events from inner webview to outer listeners this._webviewEvents.clear(); @@ -156,6 +169,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv this._pendingMessages.forEach(msg => webview.postMessage(msg)); this._pendingMessages.clear(); } + this.container.style.visibility = 'visible'; } @@ -240,12 +254,12 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv showFind() { if (this._webview.value) { this._webview.value.showFind(); - this._findWidgetVisible.set(true); + this._findWidgetVisible?.set(true); } } hideFind() { - this._findWidgetVisible.reset(); + this._findWidgetVisible?.reset(); this._webview.value?.hideFind(); } diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 0f0773fce..726eff39c 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -58,6 +58,10 @@ }; const defaultCssRules = ` + html { + scrollbar-color: var(--vscode-scrollbarSlider-background) var(--vscode-editor-background); + } + body { background-color: transparent; color: var(--vscode-editor-foreground); @@ -439,10 +443,18 @@ // propagate focus host.onMessage('focus', () => { - const target = getActiveFrame(); - if (target) { - target.contentWindow.focus(); + const activeFrame = getActiveFrame(); + if (!activeFrame || !activeFrame.contentWindow) { + return; } + + if (document.activeElement === activeFrame) { + // We are already focused on the iframe (or one of its children) so no need + // to refocus. + return; + } + + activeFrame.contentWindow.focus(); }); // update iframe-contents @@ -573,6 +585,8 @@ newFrame.contentWindow.focus(); } + + contentWindow.addEventListener('scroll', handleInnerScroll); contentWindow.addEventListener('wheel', handleWheel); diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 0da00297f..945817c5d 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { Dimension } from 'vs/base/browser/dom'; +import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; -import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; /** * Set when the find widget in a webview is visible. @@ -145,7 +145,7 @@ export interface WebviewOverlay extends Webview { readonly container: HTMLElement; options: WebviewOptions; - claim(owner: any): void; + claim(owner: any, scopedContextKeyService: IContextKeyService | undefined): void; release(owner: any): void; getInnerWebview(): Webview | undefined; diff --git a/src/vs/workbench/contrib/webview/browser/webviewWindowDragMonitor.ts b/src/vs/workbench/contrib/webview/browser/webviewWindowDragMonitor.ts new file mode 100644 index 000000000..3bbc9e857 --- /dev/null +++ b/src/vs/workbench/contrib/webview/browser/webviewWindowDragMonitor.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; + +/** + * Allows webviews to monitor when an element in the VS Code editor is being dragged/dropped. + * + * This is required since webview end up eating the drag event. VS Code needs to see this + * event so it can handle editor element drag drop. + */ +export class WebviewWindowDragMonitor extends Disposable { + constructor(getWebview: () => Webview | undefined) { + super(); + + this._register(DOM.addDisposableListener(window, DOM.EventType.DRAG_START, () => { + getWebview()?.windowDidDragStart(); + })); + + const onDragEnd = () => { + getWebview()?.windowDidDragEnd(); + }; + + this._register(DOM.addDisposableListener(window, DOM.EventType.DRAG_END, onDragEnd)); + this._register(DOM.addDisposableListener(window, DOM.EventType.MOUSE_MOVE, currentEvent => { + if (currentEvent.buttons === 0) { + onDragEnd(); + } + })); + } +} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 6ac6a86d7..d11fa2273 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -8,96 +8,35 @@ import { addDisposableListener } from 'vs/base/browser/dom'; import { ThrottledDelayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { FileAccess, Schemas } from 'vs/base/common/network'; -import { isMacintosh } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader'; -import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; import { BaseWebview, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WebviewFindDelegate, WebviewFindWidget } from 'vs/workbench/contrib/webview/browser/webviewFindWidget'; -import { WebviewResourceRequestManager, rewriteVsCodeResourceUrls } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading'; - -class WebviewKeyboardHandler { - - private readonly _webviews = new Set(); - private readonly _isUsingNativeTitleBars: boolean; - - private readonly webviewMainService: IWebviewManagerService; - - constructor( - configurationService: IConfigurationService, - mainProcessService: IMainProcessService, - ) { - this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; - - this.webviewMainService = createChannelSender(mainProcessService.getChannel('webview')); - } - - public add(webview: WebviewTag): IDisposable { - this._webviews.add(webview); - - const disposables = new DisposableStore(); - - if (this.shouldToggleMenuShortcutsEnablement) { - this.setIgnoreMenuShortcutsForWebview(webview, true); - } - - disposables.add(addDisposableListener(webview, 'ipc-message', (event) => { - switch (event.channel) { - case 'did-focus': - this.setIgnoreMenuShortcuts(true); - break; - - case 'did-blur': - this.setIgnoreMenuShortcuts(false); - return; - } - })); - - return toDisposable(() => { - disposables.dispose(); - this._webviews.delete(webview); - }); - } - - private get shouldToggleMenuShortcutsEnablement() { - return isMacintosh || this._isUsingNativeTitleBars; - } - - private setIgnoreMenuShortcuts(value: boolean) { - for (const webview of this._webviews) { - this.setIgnoreMenuShortcutsForWebview(webview, value); - } - } - - private setIgnoreMenuShortcutsForWebview(webview: WebviewTag, value: boolean) { - if (this.shouldToggleMenuShortcutsEnablement) { - this.webviewMainService.setIgnoreMenuShortcuts(webview.getWebContentsId(), value); - } - } -} +import { WebviewIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager'; +import { rewriteVsCodeResourceUrls, WebviewResourceRequestManager } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class ElectronWebviewBasedWebview extends BaseWebview implements Webview, WebviewFindDelegate { - private static _webviewKeyboardHandler: WebviewKeyboardHandler | undefined; + private static _webviewKeyboardHandler: WebviewIgnoreMenuShortcutsManager | undefined; private static getWebviewKeyboardHandler( configService: IConfigurationService, mainProcessService: IMainProcessService, ) { if (!this._webviewKeyboardHandler) { - this._webviewKeyboardHandler = new WebviewKeyboardHandler(configService, mainProcessService); + this._webviewKeyboardHandler = new WebviewIgnoreMenuShortcutsManager(configService, mainProcessService); } return this._webviewKeyboardHandler; } @@ -124,6 +63,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme @IConfigurationService configurationService: IConfigurationService, @IMainProcessService mainProcessService: IMainProcessService, @INotificationService noficationService: INotificationService, + @INativeHostService nativeHostService: INativeHostService, ) { super(id, options, contentOptions, extension, _webviewThemeDataProvider, noficationService, _myLogService, telemetryService, environmentService); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts new file mode 100644 index 000000000..5b5e4dd75 --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { WebviewTag } from 'electron'; +import { addDisposableListener } from 'vs/base/browser/dom'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { isMacintosh } from 'vs/base/common/platform'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; +import { WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; + +export class WebviewIgnoreMenuShortcutsManager { + + private readonly _webviews = new Set(); + private readonly _isUsingNativeTitleBars: boolean; + + private readonly webviewMainService: IWebviewManagerService; + + constructor( + configurationService: IConfigurationService, + mainProcessService: IMainProcessService, + ) { + this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; + + this.webviewMainService = createChannelSender(mainProcessService.getChannel('webview')); + } + + public add(webview: WebviewTag): IDisposable { + this._webviews.add(webview); + + const disposables = new DisposableStore(); + + if (this.shouldToggleMenuShortcutsEnablement) { + this.setIgnoreMenuShortcutsForWebview(webview, true); + } + + disposables.add(addDisposableListener(webview, 'ipc-message', (event) => { + switch (event.channel) { + case WebviewMessageChannels.didFocus: + this.setIgnoreMenuShortcuts(true); + break; + + case WebviewMessageChannels.didBlur: + this.setIgnoreMenuShortcuts(false); + return; + } + })); + + return toDisposable(() => { + disposables.dispose(); + this._webviews.delete(webview); + }); + } + + private get shouldToggleMenuShortcutsEnablement() { + return isMacintosh || this._isUsingNativeTitleBars; + } + + private setIgnoreMenuShortcuts(value: boolean) { + for (const webview of this._webviews) { + this.setIgnoreMenuShortcutsForWebview(webview, value); + } + } + + private setIgnoreMenuShortcutsForWebview(webview: WebviewTag, value: boolean) { + if (this.shouldToggleMenuShortcutsEnablement) { + this.webviewMainService.setIgnoreMenuShortcuts({ webContentsId: webview.getWebContentsId() }, value); + } + } +} diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts b/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts index 3b9c5e75a..8c010272b 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts @@ -6,18 +6,23 @@ import { ThrottledDelayer } from 'vs/base/common/async'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { IRequestService } from 'vs/platform/request/common/request'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; import { WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { rewriteVsCodeResourceUrls, WebviewResourceRequestManager } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading'; +import { WindowIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; /** @@ -31,6 +36,8 @@ export class ElectronIframeWebview extends IFrameWebview { private readonly _focusDelayer = this._register(new ThrottledDelayer(10)); private _elementFocusImpl!: (options?: FocusOptions | undefined) => void; + private readonly _webviewKeyboardHandler: WindowIgnoreMenuShortcutsManager; + constructor( id: string, options: WebviewOptions, @@ -45,17 +52,30 @@ export class ElectronIframeWebview extends IFrameWebview { @IRemoteAuthorityResolverService _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @ILogService logService: ILogService, @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + @IMainProcessService mainProcessService: IMainProcessService, @INotificationService noficationService: INotificationService, + @INativeHostService nativeHostService: INativeHostService, ) { super(id, options, contentOptions, extension, webviewThemeDataProvider, noficationService, tunnelService, fileService, requestService, telemetryService, environmentService, _remoteAuthorityResolverService, logService); this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options)); + + this._webviewKeyboardHandler = new WindowIgnoreMenuShortcutsManager(configurationService, mainProcessService, nativeHostService); + + this._register(this.on(WebviewMessageChannels.didFocus, () => { + this._webviewKeyboardHandler.didFocus(); + })); + + this._register(this.on(WebviewMessageChannels.didBlur, () => { + this._webviewKeyboardHandler.didBlur(); + })); } protected createElement(options: WebviewOptions, contentOptions: WebviewContentOptions) { const element = super.createElement(options, contentOptions); - this._elementFocusImpl = element.focus.bind(element); + this._elementFocusImpl = () => element.contentWindow?.focus(); element.focus = () => { this.doFocus(); }; diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts b/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts new file mode 100644 index 000000000..00da271c3 --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isMacintosh } from 'vs/base/common/platform'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; + +export class WindowIgnoreMenuShortcutsManager { + + private readonly _isUsingNativeTitleBars: boolean; + + private readonly webviewMainService: IWebviewManagerService; + + constructor( + configurationService: IConfigurationService, + mainProcessService: IMainProcessService, + private readonly nativeHostService: INativeHostService + ) { + this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; + + this.webviewMainService = createChannelSender(mainProcessService.getChannel('webview')); + } + + public didFocus(): void { + this.setIgnoreMenuShortcuts(true); + } + + public didBlur(): void { + this.setIgnoreMenuShortcuts(false); + } + + private get shouldToggleMenuShortcutsEnablement() { + return isMacintosh || this._isUsingNativeTitleBars; + } + + protected setIgnoreMenuShortcuts(value: boolean) { + if (this.shouldToggleMenuShortcutsEnablement) { + this.webviewMainService.setIgnoreMenuShortcuts({ windowId: this.nativeHostService.windowId }, value); + } + } +} diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts index 1ec3edc79..1eebb76e4 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts @@ -8,18 +8,21 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; -import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewWindowDragMonitor } from 'vs/workbench/contrib/webview/browser/webviewWindowDragMonitor'; +import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; +import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; -import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; export class WebviewEditor extends EditorPane { @@ -35,6 +38,8 @@ export class WebviewEditor extends EditorPane { private readonly _onDidFocusWebview = this._register(new Emitter()); public get onDidFocus(): Event { return this._onDidFocusWebview.event; } + private readonly _scopedContextKeyService = this._register(new MutableDisposable()); + constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @@ -43,6 +48,7 @@ export class WebviewEditor extends EditorPane { @IWorkbenchLayoutService private readonly _workbenchLayoutService: IWorkbenchLayoutService, @IEditorDropService private readonly _editorDropService: IEditorDropService, @IHostService private readonly _hostService: IHostService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, ) { super(WebviewEditor.ID, telemetryService, themeService, storageService); } @@ -51,10 +57,17 @@ export class WebviewEditor extends EditorPane { return this.input instanceof WebviewInput ? this.input.webview : undefined; } + get scopedContextKeyService(): IContextKeyService | undefined { + return this._scopedContextKeyService.value; + } + protected createEditor(parent: HTMLElement): void { const element = document.createElement('div'); this._element = element; + this._element.id = `webview-editor-element-${generateUuid()}`; parent.appendChild(element); + + this._scopedContextKeyService.value = this._contextKeyService.createScoped(element); } public dispose(): void { @@ -139,10 +152,11 @@ export class WebviewEditor extends EditorPane { } private claimWebview(input: WebviewInput): void { - input.webview.claim(this); + input.webview.claim(this, this.scopedContextKeyService); if (this._element) { this._element.setAttribute('aria-flowto', input.webview.container.id); + DOM.setParentFlowTo(input.webview.container, this._element); } this._webviewVisibleDisposables.clear(); @@ -152,19 +166,7 @@ export class WebviewEditor extends EditorPane { containsGroup: (group) => this.group?.id === group.group.id })); - this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.DRAG_START, () => { - this.webview?.windowDidDragStart(); - })); - - const onDragEnd = () => { - this.webview?.windowDidDragEnd(); - }; - this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.DRAG_END, onDragEnd)); - this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.MOUSE_MOVE, currentEvent => { - if (currentEvent.buttons === 0) { - onDragEnd(); - } - })); + this._webviewVisibleDisposables.add(new WebviewWindowDragMonitor(() => this.webview)); this.synchronizeWebviewContainerDimensions(input.webview); this._webviewVisibleDisposables.add(this.trackFocus(input.webview)); diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts index 41e915c4e..d9beb099f 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Dimension } from 'vs/base/browser/dom'; +import * as DOM from 'vs/base/browser/dom'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -16,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -24,6 +24,7 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; import { IWebviewService, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewWindowDragMonitor } from 'vs/workbench/contrib/webview/browser/webviewWindowDragMonitor'; import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -70,7 +71,7 @@ export class WebviewViewPane extends ViewPane { this.defaultTitle = this.title; this.memento = new Memento(`webviewView.${this.id}`, storageService); - this.viewState = this.memento.getMemento(StorageScope.WORKSPACE); + this.viewState = this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility())); @@ -139,14 +140,14 @@ export class WebviewViewPane extends ViewPane { } if (this._container) { - this._webview.value.layoutWebviewOverElement(this._container, new Dimension(width, height)); + this._webview.value.layoutWebviewOverElement(this._container, new DOM.Dimension(width, height)); } } private updateTreeVisibility() { if (this.isBodyVisible()) { this.activate(); - this._webview.value?.claim(this); + this._webview.value?.claim(this, undefined); } else { this._webview.value?.release(this); } @@ -172,6 +173,9 @@ export class WebviewViewPane extends ViewPane { this._webviewDisposables.add(webview.onDidUpdateState(() => { this.viewState[storageKeys.webviewState] = webview.state; })); + + this._webviewDisposables.add(new WebviewWindowDragMonitor(() => this._webview.value)); + const source = this._webviewDisposables.add(new CancellationTokenSource()); this.withProgress(async () => { diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index 25049bac5..ff54b32e0 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -36,9 +36,11 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo for (const welcome of contribution.value) { const id = ViewIdentifierMap[welcome.view] ?? welcome.view; const { group, order } = parseGroupAndOrder(welcome, contribution); + const precondition = ContextKeyExpr.deserialize(welcome.enablement); const disposable = viewsRegistry.registerViewWelcomeContent(id, { content: welcome.contents, when: ContextKeyExpr.deserialize(welcome.when), + precondition, group, order }); diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts index ebaae9f7c..6e19480fc 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts @@ -11,6 +11,7 @@ export enum ViewsWelcomeExtensionPointFields { contents = 'contents', when = 'when', group = 'group', + enablement = 'enablement', } export interface ViewWelcome { @@ -18,6 +19,7 @@ export interface ViewWelcome { readonly [ViewsWelcomeExtensionPointFields.contents]: string; readonly [ViewsWelcomeExtensionPointFields.when]: string; readonly [ViewsWelcomeExtensionPointFields.group]: string; + readonly [ViewsWelcomeExtensionPointFields.enablement]: string; } export type ViewsWelcomeExtensionPoint = ViewWelcome[]; @@ -64,6 +66,10 @@ const viewsWelcomeExtensionPointSchema = Object.freeze(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(GettingStartedInput.ID, GettingStartedInputFactory); + +if (product.quality !== 'stable') { + Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ + ...workbenchConfigurationNodeBase, + properties: { + 'workbench.experimental.gettingStarted': { + type: 'boolean', + description: localize('gettingStartedDescription', "Enables an experimental Getting Started page, accesible via the Help menu."), + default: false, + } + } + }); +} diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css new file mode 100644 index 000000000..7c199b89d --- /dev/null +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css @@ -0,0 +1,248 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.file-icons-enabled .show-file-icons .vs_code_editor_getting_started\.md-name-file-icon.md-ext-file-icon.ext-file-icon.markdown-lang-file-icon.file-icon::before { + content: ' '; + background-image: url('../../../../browser/media/code-icon.svg'); +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide { + width: 100%; + height: 100%; + position: absolute; + transition: left 0.25s, opacity 0.25s; + left: 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories { + height: calc(100% - 50px); + display: flex; + flex-direction: column; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .gap { + flex: 150px 0 1 +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .header { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .category-title { + margin-bottom: 4px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .category-description-container { + width: 100% +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .category-progress { + margin-top: 12px; + font-size: 12px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories progress { + font-size: 12pt; + width: 100%; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories #getting-started-categories-container { + display: flex; + flex-wrap: wrap; + justify-content: center; + max-width: 900px; + margin: 32px auto; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .getting-started-category { + width: 330px; + min-height: 80px; + margin: 12px; + text-align: left; + display: flex; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .getting-started-category { + padding-right: 46px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .getting-started-category .codicon { + margin-right: 10px; + font-size: 32px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-category { + width: 330px; + display: flex; + padding: 40px 0 20px; + margin-left: 12px; + min-height: auto; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-category .codicon { + margin-left:0; + font-size: 22pt; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail #getting-started-detail-columns { + display: flex; + height: 100%; + justify-content: center; + padding: 44px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task { + display: flex; + width: 100%; + overflow: hidden; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) .task-description, +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) .actions, +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) button, +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) a { + display: none; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task.expanded { + width: 100%; + height: unset; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-title { + font-size: 14pt; +} + + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-description { + margin-top: 4px; + font-size: 13px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .actions { + margin-top: 12px; + display: flex; + align-items: center; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-next { + margin-left: auto; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon.hidden { + display: none; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon { + margin-right: 8px; + font-size: 20px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task-action { + padding: 6px 12px; + font-size: 11pt; + min-width: 100px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail #getting-started-detail-left { + min-width: 330px; + width: 40%; + max-width: 400px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail #getting-started-detail-right { + width: 66%; + text-align: center; + padding-left: 44px; + margin-bottom: 50px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button { + border: none; + color: inherit; + text-align: left; + padding: 16px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:not(:first-child) { + margin-top: 12px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:hover { + cursor: pointer; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:focus { + outline-style: solid; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .prev-button { + position: absolute; + left: 0; + font-size: 12pt; + margin: 10px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .prev-button:hover { + cursor: pointer; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .prev-button .codicon { + position: relative; + top: 2px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .skip { + display: block; + width: 150px; + margin: 0 auto; + text-align: center; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide h1 { + font-size: 32px; + font-weight: normal; + border-bottom: none; + margin: 0; + padding: 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide h2 { + font-weight: normal; + margin: 0 0 4px 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide h3 { + font-size: 18px; + font-weight: bold; + margin: 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .subtitle { + font-size: 16px; + margin: 0; + padding: 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .footer { + position: absolute; + text-align: center; + bottom: 0; + width: calc(100% - 40px); + margin-bottom: 20px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.next { + left: 100%; + opacity: 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.prev { + left: -100%; + opacity: 0; +} diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts new file mode 100644 index 000000000..637330e06 --- /dev/null +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts @@ -0,0 +1,430 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./gettingStarted'; +import 'vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started'; +import { localize } from 'vs/nls'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; +import { FileAccess, Schemas } from 'vs/base/common/network'; +import { IEditorInputFactory } from 'vs/workbench/common/editor'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { assertIsDefined } from 'vs/base/common/types'; +import { $, addDisposableListener } from 'vs/base/browser/dom'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IGettingStartedCategoryWithProgress, IGettingStartedService } from 'vs/workbench/services/gettingStarted/common/gettingStartedService'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { buttonBackground as welcomeButtonBackground, buttonHoverBackground as welcomeButtonHoverBackground, welcomePageBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePageColors'; +import { activeContrastBorder, buttonBackground, buttonForeground, buttonHoverBackground, contrastBorder, descriptionForeground, focusBorder, foreground, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +export const gettingStartedInputTypeId = 'workbench.editors.gettingStartedInput'; +const telemetryFrom = 'gettingStartedPage'; + +export class GettingStartedInput extends WalkThroughInput { + static readonly ID = gettingStartedInputTypeId; + + selectedCategory: string | undefined; + selectedTask: string | undefined; +} + +export class GettingStartedPage extends Disposable { + readonly editorInput: GettingStartedInput; + private inProgressScroll = Promise.resolve(); + + private dispatchListeners = new DisposableStore(); + + private gettingStartedCategories: IGettingStartedCategoryWithProgress[]; + private currentCategory: IGettingStartedCategoryWithProgress | undefined; + + constructor( + initialState: { selectedCategory?: string, selectedTask?: string }, + @IEditorService private readonly editorService: IEditorService, + @ICommandService private readonly commandService: ICommandService, + @IProductService private readonly productService: IProductService, + @IKeybindingService private readonly keybindingService: IKeybindingService, + @IGettingStartedService private readonly gettingStartedService: IGettingStartedService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super(); + + const resource = FileAccess.asBrowserUri('./vs_code_editor_getting_started.md', require) + .with({ + scheme: Schemas.walkThrough, + query: JSON.stringify({ moduleId: 'vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started' }) + }); + + + this.editorInput = this.instantiationService.createInstance(GettingStartedInput, { + typeId: gettingStartedInputTypeId, + name: localize('editorGettingStarted.title', "Getting Started"), + resource, + telemetryFrom, + onReady: (container: HTMLElement) => this.onReady(container) + }); + + this.editorInput.selectedCategory = initialState.selectedCategory; + this.editorInput.selectedTask = initialState.selectedTask; + + this.gettingStartedCategories = this.gettingStartedService.getCategories(); + this._register(this.dispatchListeners); + this._register(this.gettingStartedService.onDidAddTask(task => console.log('added new task', task, 'that isnt being rendered yet'))); + this._register(this.gettingStartedService.onDidAddCategory(category => console.log('added new category', category, 'that isnt being rendered yet'))); + this._register(this.gettingStartedService.onDidProgressTask(task => { + const category = this.gettingStartedCategories.find(category => category.id === task.category); + if (!category) { throw Error('Could not find category with ID: ' + task.category); } + if (category.content.type !== 'items') { throw Error('internaal error: progressing task in a non-items category'); } + const ourTask = category.content.items.find(_task => _task.id === task.id); + if (!ourTask) { + throw Error('Could not find task with ID: ' + task.id); + } + ourTask.done = task.done; + if (category.id === this.currentCategory?.id) { + const badgeelement = assertIsDefined(document.getElementById('done-task-' + task.id)); + if (task.done) { + badgeelement.classList.remove('codicon-star-empty'); + badgeelement.classList.add('codicon-star-full'); + } + else { + badgeelement.classList.add('codicon-star-empty'); + badgeelement.classList.remove('codicon-star-full'); + } + } + this.updateCategoryProgress(); + })); + } + + public openEditor(options: IEditorOptions = { pinned: true }) { + return this.editorService.openEditor(this.editorInput, options); + } + + private registerDispatchListeners(container: HTMLElement) { + this.dispatchListeners.clear(); + + container.querySelectorAll('[x-dispatch]').forEach(element => { + const [command, argument] = (element.getAttribute('x-dispatch') ?? '').split(':'); + if (command) { + this.dispatchListeners.add(addDisposableListener(element, 'click', (e) => { + + type GettingStartedActionClassification = { + command: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + argument: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + }; + type GettingStartedActionEvent = { + command: string; + argument: string | undefined; + }; + this.telemetryService.publicLog2('gettingStarted.ActionExecuted', { command, argument }); + + switch (command) { + case 'scrollPrev': { + this.scrollPrev(container); + break; + } + case 'skip': { + this.commandService.executeCommand('workbench.action.closeActiveEditor'); + break; + } + case 'selectCategory': { + const selectedCategory = this.gettingStartedCategories.find(category => category.id === argument); + if (!selectedCategory) { throw Error('Could not find category with ID ' + argument); } + if (selectedCategory.content.type === 'command') { + this.commandService.executeCommand(selectedCategory.content.command); + } else { + this.scrollToCategory(container, argument); + } + break; + } + case 'selectTask': { + this.selectTask(argument); + e.stopPropagation(); + break; + } + case 'runTaskAction': { + if (!this.currentCategory || this.currentCategory.content.type !== 'items') { + throw Error('cannot run task action for category of non items type' + this.currentCategory?.id); + } + const taskToRun = assertIsDefined(this.currentCategory?.content.items.find(task => task.id === argument)); + const commandToRun = assertIsDefined(taskToRun.button?.command); + this.commandService.executeCommand(commandToRun); + break; + } + default: { + console.error('Dispatch to', command, argument, 'not defined'); + break; + } + } + })); + } + }); + } + + private selectTask(id: string | undefined) { + const mediaElement = assertIsDefined(document.getElementById('getting-started-media')); + if (id) { + const taskElement = assertIsDefined(document.getElementById('getting-started-task-' + id)); + if (!this.currentCategory || this.currentCategory.content.type !== 'items') { + throw Error('cannot expand task for category of non items type' + this.currentCategory?.id); + } + this.editorInput.selectedTask = id; + const taskToExpand = assertIsDefined(this.currentCategory.content.items.find(task => task.id === id)); + + mediaElement.setAttribute('src', taskToExpand.media.path.toString()); + mediaElement.setAttribute('alt', taskToExpand.media.altText); + taskElement.parentElement?.querySelectorAll('.expanded').forEach(node => node.classList.remove('expanded')); + taskElement.classList.add('expanded'); + } else { + mediaElement.setAttribute('src', ''); + mediaElement.setAttribute('alt', ''); + } + } + + private onReady(container: HTMLElement) { + const categoryElements = this.gettingStartedCategories.map( + category => { + const categoryDescriptionElement = + category.content.type === 'items' ? + $('.category-description-container', {}, + $('h3.category-title', {}, category.title), + $('.category-description.description', {}, category.description), + $('.category-progress', { 'x-data-category-id': category.id, }, $('.message'), $('progress'))) : + $('.category-description-container', {}, + $('h3.category-title', {}, category.title), + $('.category-description.description', {}, category.description)); + + return $('button.getting-started-category', + { 'x-dispatch': 'selectCategory:' + category.id }, + $('.codicon.codicon-' + category.codicon, {}), categoryDescriptionElement); + }); + + const rightColumn = assertIsDefined(container.querySelector('#getting-started-detail-right')); + rightColumn.appendChild($('img#getting-started-media')); + + const categoriesContainer = assertIsDefined(document.getElementById('getting-started-categories-container')); + categoryElements.forEach(element => { + categoriesContainer.appendChild(element); + }); + + this.updateCategoryProgress(); + + assertIsDefined(document.getElementById('product-name')).textContent = this.productService.nameLong; + this.registerDispatchListeners(container); + + const categoriesSlide = assertIsDefined(document.getElementById('gettingStartedSlideCategory')); + const tasksSlide = assertIsDefined(document.getElementById('gettingStartedSlideDetails')); + + if (this.editorInput.selectedCategory) { + this.currentCategory = this.gettingStartedCategories.find(category => category.id === this.editorInput.selectedCategory); + if (!this.currentCategory) { + throw Error('Could not restore to category ' + this.editorInput.selectedCategory + ' as it was not found'); + } + this.buildCategorySlide(container, this.editorInput.selectedCategory, this.editorInput.selectedTask); + categoriesSlide.classList.add('prev'); + } else { + tasksSlide.classList.add('next'); + } + } + + private updateCategoryProgress() { + document.querySelectorAll('.category-progress').forEach(element => { + const categoryID = element.getAttribute('x-data-category-id'); + const category = this.gettingStartedCategories.find(category => category.id === categoryID); + if (!category) { throw Error('Could not find c=ategory with ID ' + categoryID); } + if (category.content.type !== 'items') { throw Error('Category with ID ' + categoryID + ' is not of items type'); } + const numDone = category.content.items.filter(task => task.done).length; + const numTotal = category.content.items.length; + + const message = assertIsDefined(element.firstChild); + const bar = assertIsDefined(element.lastChild) as HTMLProgressElement; + bar.value = numDone; + bar.max = numTotal; + if (numTotal === numDone) { + message.textContent = `All items complete!`; + } + else { + message.textContent = `${numDone} of ${numTotal} items complete`; + } + }); + } + + private async scrollToCategory(container: HTMLElement, categoryID: string) { + this.inProgressScroll = this.inProgressScroll.then(async () => { + this.clearDetialView(); + this.editorInput.selectedCategory = categoryID; + this.currentCategory = this.gettingStartedCategories.find(category => category.id === categoryID); + const slides = [...container.querySelectorAll('.gettingStartedSlide').values()]; + const currentSlide = slides.findIndex(element => !element.classList.contains('prev') && !element.classList.contains('next')); + if (currentSlide < slides.length - 1) { + this.buildCategorySlide(container, categoryID); + slides[currentSlide].classList.add('prev'); + slides[currentSlide + 1].classList.remove('next'); + } + }); + } + + private buildCategorySlide(container: HTMLElement, categoryID: string, selectedItem?: string) { + const category = this.gettingStartedCategories.find(category => category.id === categoryID); + if (!category) { throw Error('could not find category with ID ' + categoryID); } + if (category.content.type !== 'items') { throw Error('category with ID ' + categoryID + ' is not of items type'); } + + const detailTitle = assertIsDefined(document.getElementById('getting-started-detail-title')); + detailTitle.appendChild( + $('.getting-started-category', + {}, + $('.codicon.codicon-' + category.codicon, {}), + $('.category-description-container', {}, + $('h2.category-title', {}, category.title), + $('.category-description.description', {}, category.description)))); + + const categoryElements = category.content.items.map( + (task, i, arr) => $('button.getting-started-task', + { 'x-dispatch': 'selectTask:' + task.id, id: 'getting-started-task-' + task.id }, + $('.codicon' + (task.done ? '.codicon-pass-filled' : '.codicon-circle-large-outline'), { id: 'done-task-' + task.id }), + $('.task-description-container', {}, + $('h3.task-title', {}, task.title), + $('.task-description.description', {}, task.description), + $('.actions', {}, + ...( + task.button + ? [$('button.emphasis.getting-started-task-action', { 'x-dispatch': 'runTaskAction:' + task.id }, + task.button.title + this.getKeybindingLabel(task.button.command) + )] + : []), + ...( + arr[i + 1] + ? [ + $('a.task-next', + { 'x-dispatch': 'selectTask:' + arr[i + 1].id }, localize('next', "Next")), + ] : [] + )) + ))); + + const detailContainer = assertIsDefined(document.getElementById('getting-started-detail-container')); + categoryElements.forEach(element => detailContainer.appendChild(element)); + + const toExpand = category.content.items.find(item => !item.done) ?? category.content.items[0]; + this.selectTask(selectedItem ?? toExpand.id); + this.registerDispatchListeners(container); + } + + private clearDetialView() { + const detailContainer = assertIsDefined(document.getElementById('getting-started-detail-container')); + while (detailContainer.firstChild) { detailContainer.removeChild(detailContainer.firstChild); } + const detailTitle = assertIsDefined(document.getElementById('getting-started-detail-title')); + while (detailTitle.firstChild) { detailTitle.removeChild(detailTitle.firstChild); } + } + + private getKeybindingLabel(command: string) { + const binding = this.keybindingService.lookupKeybinding(command); + if (!binding) { return ''; } + else { return ` (${binding.getLabel()})`; } + } + + private async scrollPrev(container: HTMLElement) { + this.inProgressScroll = this.inProgressScroll.then(async () => { + this.currentCategory = undefined; + this.editorInput.selectedCategory = undefined; + this.editorInput.selectedTask = undefined; + this.selectTask(undefined); + const slides = [...container.querySelectorAll('.gettingStartedSlide').values()]; + const currentSlide = slides.findIndex(element => + !element.classList.contains('prev') && !element.classList.contains('next')); + if (currentSlide > 0) { + slides[currentSlide].classList.add('next'); + assertIsDefined(slides[currentSlide - 1]).classList.remove('prev'); + } + }); + } +} + +export class GettingStartedInputFactory implements IEditorInputFactory { + public canSerialize(editorInput: GettingStartedInput): boolean { + return true; + } + + public serialize(editorInput: GettingStartedInput): string { + return JSON.stringify({ selectedCategory: editorInput.selectedCategory, selectedTask: editorInput.selectedTask }); + } + + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): GettingStartedInput { + try { + const { selectedCategory, selectedTask } = JSON.parse(serializedEditorInput); + return instantiationService.createInstance(GettingStartedPage, { selectedCategory, selectedTask }).editorInput; + } catch { } + return instantiationService.createInstance(GettingStartedPage, {}).editorInput; + } +} + +registerThemingParticipant((theme, collector) => { + const backgroundColor = theme.getColor(welcomePageBackground); + if (backgroundColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePageContainer { background-color: ${backgroundColor}; }`); + } + const foregroundColor = theme.getColor(foreground); + if (foregroundColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer { color: ${foregroundColor}; }`); + } + const descriptionColor = theme.getColor(descriptionForeground); + if (descriptionColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .description { color: ${descriptionColor}; }`); + } + const buttonColor = getExtraColor(theme, welcomeButtonBackground, { dark: 'rgba(0, 0, 0, .2)', extra_dark: 'rgba(200, 235, 255, .042)', light: 'rgba(0,0,0,.04)', hc: 'black' }); + if (buttonColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button { background: ${buttonColor}; }`); + } + + const buttonHoverColor = getExtraColor(theme, welcomeButtonHoverBackground, { dark: 'rgba(200, 235, 255, .072)', extra_dark: 'rgba(200, 235, 255, .072)', light: 'rgba(0,0,0,.10)', hc: null }); + if (buttonHoverColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:hover { background: ${buttonHoverColor}; }`); + } + if (buttonColor && buttonHoverColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.expanded:hover { background: ${buttonColor}; }`); + } + + const emphasisButtonForeground = theme.getColor(buttonForeground); + if (emphasisButtonForeground) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.emphasis { color: ${emphasisButtonForeground}; }`); + } + + const emphasisButtonBackground = theme.getColor(buttonBackground); + if (emphasisButtonBackground) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.emphasis { background: ${emphasisButtonBackground}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .getting-started-category .codicon { color: ${emphasisButtonBackground} }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon { color: ${emphasisButtonBackground} } `); + } + + const emphasisButtonHoverBackground = theme.getColor(buttonHoverBackground); + if (emphasisButtonHoverBackground) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.emphasis:hover { background: ${emphasisButtonHoverBackground}; }`); + } + + const link = theme.getColor(textLinkForeground); + if (link) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a { color: ${link}; }`); + } + const activeLink = theme.getColor(textLinkActiveForeground); + if (activeLink) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a:hover, + .monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a:active { color: ${activeLink}; }`); + } + const focusColor = theme.getColor(focusBorder); + if (focusColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a:focus { outline-color: ${focusColor}; }`); + } + const border = theme.getColor(contrastBorder); + if (border) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button { border-color: ${border}; border: 1px solid; }`); + } + const activeBorder = theme.getColor(activeContrastBorder); + if (activeBorder) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:hover { outline-color: ${activeBorder}; }`); + } +}); diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started.ts new file mode 100644 index 000000000..dd6254527 --- /dev/null +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { escape } from 'vs/base/common/strings'; +import { localize } from 'vs/nls'; + +export default () => ` +
+
+
+
+
+

${escape(localize('gettingStarted.vscode', "Visual Studio Code"))}

+

${escape(localize({ key: 'gettingStarted.editingRedefined', comment: ['Shown as subtitle on the Welcome page.'] }, "Code editing. Redefined"))}

+
+
+
+
+
+ Back +
+
+
+
+
+
+
+
+
+ +
+
+`.replace(/\|/g, '`'); diff --git a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts index 6af6a4b7f..a405271ac 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts @@ -19,9 +19,8 @@ export default () => `

${escape(localize('welcomePage.start', "Start"))}

diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts index 396871dc3..1a59cbb17 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts @@ -13,6 +13,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import product from 'vs/platform/product/common/product'; Registry.as(ConfigurationExtensions.Configuration) .registerConfiguration({ @@ -21,13 +22,21 @@ Registry.as(ConfigurationExtensions.Configuration) 'workbench.startupEditor': { 'scope': ConfigurationScope.APPLICATION, // Make sure repositories cannot trigger opening a README for tracking. 'type': 'string', - 'enum': ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench'], - 'enumDescriptions': [ + 'enum': [ + ...['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench'], + ...(product.quality !== 'stable' + ? ['gettingStarted'] + : []) + ], + 'enumDescriptions': [...[ localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the Welcome page (default)."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file (only applies when opening an empty workspace)."), - localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."), + localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."),], + ...(product.quality !== 'stable' + ? [localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.gettingStarted' }, "Open the Getting Started page (experimental).")] + : []) ], 'default': 'welcomePage', 'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none are restored from the previous session.") diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 4a61a79fe..37a7512d4 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -15,7 +15,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { onUnexpectedError, isPromiseCanceledError } from 'vs/base/common/errors'; import { IWindowOpenable } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { localize } from 'vs/nls'; import { Action, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -29,7 +29,7 @@ import { ILifecycleService, StartupKind } from 'vs/workbench/services/lifecycle/ import { Disposable } from 'vs/base/common/lifecycle'; import { splitName } from 'vs/base/common/labels'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils'; import { IExtensionsViewPaneContainer, IExtensionsWorkbenchService, VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; @@ -46,6 +46,8 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { gettingStartedInputTypeId, GettingStartedPage } from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted'; +import { buttonBackground, buttonHoverBackground, welcomePageBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePageColors'; const configurationKey = 'workbench.startupEditor'; const oldConfigurationKey = 'workbench.welcome.enabled'; @@ -69,7 +71,8 @@ export class WelcomePageContribution implements IWorkbenchContribution { backupFileService.hasBackups().then(hasBackups => { // Open the welcome even if we opened a set of default editors if ((!editorService.activeEditor || layoutService.openedDefaultEditors) && !hasBackups) { - const openWithReadme = configurationService.getValue(configurationKey) === 'readme'; + const startupEditorSetting = configurationService.getValue(configurationKey) as string; + const openWithReadme = startupEditorSetting === 'readme'; if (openWithReadme) { return Promise.all(contextService.getWorkspace().folders.map(folder => { const folderUri = folder.uri; @@ -101,18 +104,23 @@ export class WelcomePageContribution implements IWorkbenchContribution { return undefined; }); } else { + const startupEditorTypeID = startupEditorSetting === 'gettingStarted' ? gettingStartedInputTypeId : welcomeInputTypeId; + const launchEditor = startupEditorSetting === 'gettingStarted' + ? instantiationService.createInstance(GettingStartedPage, {}) + : instantiationService.createInstance(WelcomePage); + let options: IEditorOptions; let editor = editorService.activeEditor; if (editor) { // Ensure that the welcome editor won't get opened more than once - if (editor.getTypeId() === welcomeInputTypeId || editorService.editors.some(e => e.getTypeId() === welcomeInputTypeId)) { + if (editor.getTypeId() === startupEditorTypeID || editorService.editors.some(e => e.getTypeId() === startupEditorTypeID)) { return undefined; } options = { pinned: false, index: 0 }; } else { options = { pinned: false }; } - return instantiationService.createInstance(WelcomePage).openEditor(options); + return launchEditor.openEditor(options); } } return undefined; @@ -129,7 +137,7 @@ function isWelcomePageEnabled(configurationService: IConfigurationService, conte return welcomeEnabled.value; } } - return startupEditor.value === 'welcomePage' || startupEditor.value === 'readme' || startupEditor.value === 'welcomePageInEmptyWorkbench' && contextService.getWorkbenchState() === WorkbenchState.EMPTY; + return startupEditor.value === 'welcomePage' || startupEditor.value === 'gettingStarted' || startupEditor.value === 'readme' || startupEditor.value === 'welcomePageInEmptyWorkbench' && contextService.getWorkbenchState() === WorkbenchState.EMPTY; } export class WelcomePageAction extends Action { @@ -323,7 +331,7 @@ class WelcomePage extends Disposable { showOnStartup.setAttribute('checked', 'checked'); } showOnStartup.addEventListener('click', e => { - this.configurationService.updateValue(configurationKey, showOnStartup.checked ? 'welcomePage' : 'newUntitledFile', ConfigurationTarget.USER); + this.configurationService.updateValue(configurationKey, showOnStartup.checked ? 'welcomePage' : 'newUntitledFile'); }); const prodName = container.querySelector('.welcomePage .title .caption') as HTMLElement; @@ -637,10 +645,6 @@ export class WelcomeInputFactory implements IEditorInputFactory { // theming -export const buttonBackground = registerColor('welcomePage.buttonBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonBackground', 'Background color for the buttons on the Welcome page.')); -export const buttonHoverBackground = registerColor('welcomePage.buttonHoverBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonHoverBackground', 'Hover background color for the buttons on the Welcome page.')); -export const welcomePageBackground = registerColor('welcomePage.background', { light: null, dark: null, hc: null }, localize('welcomePage.background', 'Background color for the Welcome page.')); - registerThemingParticipant((theme, collector) => { const backgroundColor = theme.getColor(welcomePageBackground); if (backgroundColor) { diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePageColors.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePageColors.ts new file mode 100644 index 000000000..e87483cf4 --- /dev/null +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePageColors.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { localize } from 'vs/nls'; + +// Seprate from main module to break dependency cycles between welcomePage and gettingStarted. +export const buttonBackground = registerColor('welcomePage.buttonBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonBackground', 'Background color for the buttons on the Welcome page.')); +export const buttonHoverBackground = registerColor('welcomePage.buttonHoverBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonHoverBackground', 'Hover background color for the buttons on the Welcome page.')); +export const welcomePageBackground = registerColor('welcomePage.background', { light: null, dark: null, hc: null }, localize('welcomePage.background', 'Background color for the Welcome page.')); diff --git a/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts index 6b2fa23f3..3cf7db6db 100644 --- a/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -19,7 +19,6 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution { @@ -28,7 +27,6 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution constructor( @IStorageService private readonly storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IOpenerService private readonly openerService: IOpenerService, @INotificationService private readonly notificationService: INotificationService, @IHostService private readonly hostService: IHostService, @@ -40,7 +38,6 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution @IEnvironmentService private readonly environmentService: IEnvironmentService, @IJSONEditingService private readonly jsonEditingService: IJSONEditingService ) { - storageKeysSyncRegistryService.registerStorageKey({ key: AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, version: 1 }); } protected async handleTelemetryOptOut(): Promise { @@ -53,7 +50,7 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution return; // return early if meanwhile another window opened (we only show the opt-out once) } - this.storageService.store(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL); + this.storageService.store(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL, StorageTarget.USER); this.privacyUrl = this.productService.privacyStatementUrl || this.productService.telemetryOptOutUrl; @@ -165,7 +162,6 @@ export class BrowserTelemetryOptOut extends AbstractTelemetryOptOut { constructor( @IStorageService storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IOpenerService openerService: IOpenerService, @INotificationService notificationService: INotificationService, @IHostService hostService: IHostService, @@ -177,7 +173,7 @@ export class BrowserTelemetryOptOut extends AbstractTelemetryOptOut { @IEnvironmentService environmentService: IEnvironmentService, @IJSONEditingService jsonEditingService: IJSONEditingService ) { - super(storageService, storageKeysSyncRegistryService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService); + super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService); this.handleTelemetryOptOut(); } diff --git a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts index 60c4fcfa3..81b28a108 100644 --- a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts @@ -16,13 +16,11 @@ import { AbstractTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryO import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; export class NativeTelemetryOptOut extends AbstractTelemetryOptOut { constructor( @IStorageService storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IOpenerService openerService: IOpenerService, @INotificationService notificationService: INotificationService, @IHostService hostService: IHostService, @@ -35,7 +33,7 @@ export class NativeTelemetryOptOut extends AbstractTelemetryOptOut { @IJSONEditingService jsonEditingService: IJSONEditingService, @INativeHostService private readonly nativeHostService: INativeHostService ) { - super(storageService, storageKeysSyncRegistryService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService); + super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService); this.handleTelemetryOptOut(); } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts index 04a605f1c..7db8ceec1 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts @@ -40,7 +40,7 @@ export class EditorWalkThroughAction extends Action { public run(): Promise { const input = this.instantiationService.createInstance(WalkThroughInput, inputOptions); - return this.editorService.openEditor(input, { pinned: true }) + return this.editorService.openEditor(input, { pinned: true, override: false }) .then(() => void (0)); } } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css index e2a46b379..0bdf1be5c 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css @@ -7,6 +7,7 @@ box-sizing: border-box; padding: 10px 20px; line-height: 22px; + height: inherit; user-select: initial; -webkit-user-select: initial; } diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index f98b00507..5f159a168 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -45,12 +45,16 @@ import { ConfigurationCache } from 'vs/workbench/services/configuration/electron import { SignService } from 'vs/platform/sign/node/signService'; import { ISignService } from 'vs/platform/sign/common/sign'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; -import { basename } from 'vs/base/common/resources'; +import { basename } from 'vs/base/common/path'; import { IProductService } from 'vs/platform/product/common/productService'; import product from 'vs/platform/product/common/product'; import { NativeLogService } from 'vs/workbench/services/log/electron-browser/logService'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { KeyboardLayoutService } from 'vs/workbench/services/keybinding/electron-sandbox/nativeKeyboardLayout'; +import { IKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayout'; class DesktopMain extends Disposable { @@ -232,8 +236,11 @@ class DesktopMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // User Data Provider - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(this.environmentService.appSettingsHome, this.configuration.backupPath ? URI.file(this.configuration.backupPath) : undefined, diskFileSystemProvider, this.environmentService, logService)); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, logService)); + // Uri Identity + const uriIdentityService = new UriIdentityService(fileService); + serviceCollection.set(IUriIdentityService, uriIdentityService); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // @@ -257,7 +264,7 @@ class DesktopMain extends Disposable { const payload = await this.resolveWorkspaceInitializationPayload(); const services = await Promise.all([ - this.createWorkspaceService(payload, fileService, remoteAgentService, logService).then(service => { + this.createWorkspaceService(payload, fileService, remoteAgentService, uriIdentityService, logService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -273,6 +280,14 @@ class DesktopMain extends Disposable { // Storage serviceCollection.set(IStorageService, service); + return service; + }), + + this.createKeyboardLayoutService(logService, mainProcessService).then(service => { + + // KeyboardLayout + serviceCollection.set(IKeyboardLayoutService, service); + return service; }) ]); @@ -310,8 +325,8 @@ class DesktopMain extends Disposable { // Fallback to empty workspace if we have no payload yet. if (!workspaceInitializationPayload) { let id: string; - if (this.environmentService.backupWorkspaceHome) { - id = basename(this.environmentService.backupWorkspaceHome); // we know the backupPath must be a unique path so we leverage its name as workspace ID + if (this.configuration.backupPath) { + id = basename(this.configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID } else if (this.environmentService.isExtensionDevelopment) { id = 'ext-dev'; // extension development window never stores backups and is a singleton } else { @@ -334,10 +349,12 @@ class DesktopMain extends Disposable { } catch (error) { onUnexpectedError(error); } + return; } private async createHash(resource: URI): Promise { + // Return early the folder is not local if (resource.scheme !== Schemas.file) { return createHash('md5').update(resource.toString()).digest('hex'); @@ -362,8 +379,8 @@ class DesktopMain extends Disposable { return createHash('md5').update(resource.fsPath).update(ctime ? String(ctime) : '').digest('hex'); } - private async createWorkspaceService(payload: IWorkspaceInitializationPayload, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { - const workspaceService = new WorkspaceService({ remoteAuthority: this.environmentService.remoteAuthority, configurationCache: new ConfigurationCache(this.environmentService) }, this.environmentService, fileService, remoteAgentService, logService); + private async createWorkspaceService(payload: IWorkspaceInitializationPayload, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { + const workspaceService = new WorkspaceService({ remoteAuthority: this.environmentService.remoteAuthority, configurationCache: new ConfigurationCache(this.environmentService) }, this.environmentService, fileService, remoteAgentService, uriIdentityService, logService); try { await workspaceService.initialize(payload); @@ -393,6 +410,20 @@ class DesktopMain extends Disposable { } } + private async createKeyboardLayoutService(logService: ILogService, mainProcessService: IMainProcessService): Promise { + const keyboardLayoutService = new KeyboardLayoutService(mainProcessService); + + try { + await keyboardLayoutService.initialize(); + + return keyboardLayoutService; + } catch (error) { + onUnexpectedError(error); + logService.error(error); + + return keyboardLayoutService; + } + } } export function main(configuration: INativeWorkbenchConfiguration): Promise { diff --git a/src/vs/workbench/electron-sandbox/actions/developerActions.ts b/src/vs/workbench/electron-sandbox/actions/developerActions.ts index 1753e42e6..8daa9ffde 100644 --- a/src/vs/workbench/electron-sandbox/actions/developerActions.ts +++ b/src/vs/workbench/electron-sandbox/actions/developerActions.ts @@ -42,6 +42,9 @@ export class ConfigureRuntimeArgumentsAction extends Action { } async run(): Promise { - await this.editorService.openEditor({ resource: this.environmentService.argvResource }); + await this.editorService.openEditor({ + resource: this.environmentService.argvResource, + options: { pinned: true } + }); } } diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 8e5d43e83..63bc327fa 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -211,16 +211,17 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; }, 'window.restoreWindows': { 'type': 'string', - 'enum': ['all', 'folders', 'one', 'none'], + 'enum': ['preserve', 'all', 'folders', 'one', 'none'], 'enumDescriptions': [ - nls.localize('window.reopenFolders.all', "Reopen all windows."), - nls.localize('window.reopenFolders.folders', "Reopen all folders. Empty workspaces will not be restored."), - nls.localize('window.reopenFolders.one', "Reopen the last active window."), - nls.localize('window.reopenFolders.none', "Never reopen a window. Always start with an empty one.") + nls.localize('window.reopenFolders.preserve', "Always reopen all windows. If a folder or workspace is opened (e.g. from the command line) it opens as a new window unless it was opened before. If files are opened they will open in one of the restored windows."), + nls.localize('window.reopenFolders.all', "Reopen all windows unless a folder, workspace or file is opened (e.g. from the command line)."), + nls.localize('window.reopenFolders.folders', "Reopen all windows that had folders or workspaces opened unless a folder, workspace or file is opened (e.g. from the command line)."), + nls.localize('window.reopenFolders.one', "Reopen the last active window unless a folder, workspace or file is opened (e.g. from the command line)."), + nls.localize('window.reopenFolders.none', "Never reopen a window. Unless a folder or workspace is opened (e.g. from the command line), an empty window will appear.") ], 'default': 'all', 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after a restart.") + 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after starting for the first time. This setting has no effect when the application is already running.") }, 'window.restoreFullscreen': { 'type': 'boolean', @@ -295,7 +296,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; }, 'window.enableExperimentalProxyLoginDialog': { 'type': 'boolean', - 'default': false, + 'default': true, 'scope': ConfigurationScope.APPLICATION, 'description': nls.localize('window.enableExperimentalProxyLoginDialog', "Enables a new login dialog for proxy authentication. Requires a restart to take effect."), } diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 891df1710..eddd5ab64 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -34,6 +34,8 @@ import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHos import { SimpleConfigurationService, simpleFileSystemProvider, SimpleLogService, SimpleRemoteAgentService, SimpleSignService, SimpleStorageService, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices'; import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-sandbox/remoteAuthorityResolverService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; class DesktopMain extends Disposable { @@ -205,7 +207,11 @@ class DesktopMain extends Disposable { fileService.registerProvider(Schemas.file, simpleFileSystemProvider); // User Data Provider - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(URI.file('user-home'), undefined, simpleFileSystemProvider, this.environmentService, logService)); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(Schemas.file, simpleFileSystemProvider, Schemas.userData, logService)); + + // Uri Identity + const uriIdentityService = new UriIdentityService(fileService); + serviceCollection.set(IUriIdentityService, uriIdentityService); const connection = remoteAgentService.getConnection(); if (connection) { diff --git a/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts b/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts new file mode 100644 index 000000000..5ed67ad69 --- /dev/null +++ b/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IDialogHandler, IDialogResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IDialogsModel, IDialogViewItem } from 'vs/workbench/common/dialogs'; +import { BrowserDialogHandler } from 'vs/workbench/browser/parts/dialogs/dialogHandler'; +import { NativeDialogHandler } from 'vs/workbench/electron-sandbox/parts/dialogs/dialogHandler'; +import { DialogService } from 'vs/workbench/services/dialogs/common/dialogService'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; + +export class DialogHandlerContribution extends Disposable implements IWorkbenchContribution { + private nativeImpl: IDialogHandler; + private browserImpl: IDialogHandler; + + private model: IDialogsModel; + private currentDialog: IDialogViewItem | undefined; + + constructor( + @IConfigurationService private configurationService: IConfigurationService, + @IDialogService private dialogService: IDialogService, + @ILogService logService: ILogService, + @ILayoutService layoutService: ILayoutService, + @IThemeService themeService: IThemeService, + @IKeybindingService keybindingService: IKeybindingService, + @IProductService productService: IProductService, + @IClipboardService clipboardService: IClipboardService, + @INativeHostService nativeHostService: INativeHostService + ) { + super(); + + this.browserImpl = new BrowserDialogHandler(logService, layoutService, themeService, keybindingService, productService, clipboardService); + this.nativeImpl = new NativeDialogHandler(logService, nativeHostService, productService, clipboardService); + + this.model = (this.dialogService as DialogService).model; + + this._register(this.model.onDidShowDialog(() => { + if (!this.currentDialog) { + this.processDialogs(); + } + })); + + this.processDialogs(); + } + + private async processDialogs(): Promise { + while (this.model.dialogs.length) { + this.currentDialog = this.model.dialogs[0]; + + let result: IDialogResult | undefined = undefined; + + // Confirm + if (this.currentDialog.args.confirmArgs) { + const args = this.currentDialog.args.confirmArgs; + result = this.useCustomDialog ? await this.browserImpl.confirm(args.confirmation) : await this.nativeImpl.confirm(args.confirmation); + } + + // Input (custom only) + else if (this.currentDialog.args.inputArgs) { + const args = this.currentDialog.args.inputArgs; + result = await this.browserImpl.input(args.severity, args.message, args.buttons, args.inputs, args.options); + } + + // Message + else if (this.currentDialog.args.showArgs) { + const args = this.currentDialog.args.showArgs; + result = this.useCustomDialog ? + await this.browserImpl.show(args.severity, args.message, args.buttons, args.options) : + await this.nativeImpl.show(args.severity, args.message, args.buttons, args.options); + } + + // About + else { + await this.nativeImpl.about(); + } + + this.currentDialog.close(result); + this.currentDialog = undefined; + } + } + + private get useCustomDialog(): boolean { + return this.configurationService.getValue('window.dialogStyle') === 'custom'; + } +} + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, LifecyclePhase.Starting); diff --git a/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts b/src/vs/workbench/electron-sandbox/parts/dialogs/dialogHandler.ts similarity index 74% rename from src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts rename to src/vs/workbench/electron-sandbox/parts/dialogs/dialogHandler.ts index c6e5fb62d..f56a97383 100644 --- a/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts +++ b/src/vs/workbench/electron-sandbox/parts/dialogs/dialogHandler.ts @@ -4,22 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import Severity from 'vs/base/common/severity'; -import { isLinux, isWindows } from 'vs/base/common/platform'; -import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions, IShowResult, IInputResult, IInput } from 'vs/platform/dialogs/common/dialogs'; -import { DialogService as HTMLDialogService } from 'vs/workbench/services/dialogs/browser/dialogService'; -import { ILogService } from 'vs/platform/log/common/log'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { MessageBoxOptions } from 'vs/base/parts/sandbox/common/electronTypes'; import { fromNow } from 'vs/base/common/date'; +import { mnemonicButtonLabel } from 'vs/base/common/labels'; +import { isLinux, isWindows } from 'vs/base/common/platform'; +import Severity from 'vs/base/common/severity'; +import { MessageBoxOptions } from 'vs/base/parts/sandbox/common/electronTypes'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IConfirmation, IConfirmationResult, IDialogHandler, IDialogOptions, IShowResult } from 'vs/platform/dialogs/common/dialogs'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { IProductService } from 'vs/platform/product/common/productService'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; interface IMassagedMessageBoxOptions { @@ -37,59 +31,7 @@ interface IMassagedMessageBoxOptions { buttonIndexMap: number[]; } -export class DialogService implements IDialogService { - - declare readonly _serviceBrand: undefined; - - private nativeImpl: IDialogService; - private customImpl: IDialogService; - - constructor( - @IConfigurationService private configurationService: IConfigurationService, - @ILogService logService: ILogService, - @ILayoutService layoutService: ILayoutService, - @IThemeService themeService: IThemeService, - @IKeybindingService keybindingService: IKeybindingService, - @IProductService productService: IProductService, - @IClipboardService clipboardService: IClipboardService, - @INativeHostService nativeHostService: INativeHostService - ) { - this.customImpl = new HTMLDialogService(logService, layoutService, themeService, keybindingService, productService, clipboardService); - this.nativeImpl = new NativeDialogService(logService, nativeHostService, productService, clipboardService); - } - - private get useCustomDialog(): boolean { - return this.configurationService.getValue('window.dialogStyle') === 'custom'; - } - - confirm(confirmation: IConfirmation): Promise { - if (this.useCustomDialog) { - return this.customImpl.confirm(confirmation); - } - - return this.nativeImpl.confirm(confirmation); - } - - show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { - if (this.useCustomDialog) { - return this.customImpl.show(severity, message, buttons, options); - } - - return this.nativeImpl.show(severity, message, buttons, options); - } - - input(severity: Severity, message: string, buttons: string[], inputs: IInput[], options?: IDialogOptions): Promise { - return this.customImpl.input(severity, message, buttons, inputs, options); - } - - about(): Promise { - return this.nativeImpl.about(); - } -} - -class NativeDialogService implements IDialogService { - - declare readonly _serviceBrand: undefined; +export class NativeDialogHandler implements IDialogHandler { constructor( @ILogService private readonly logService: ILogService, @@ -265,4 +207,3 @@ class NativeDialogService implements IDialogService { } } -registerSingleton(IDialogService, DialogService, true); diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 7002cb288..ea147a06b 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -9,7 +9,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { ILabelService } from 'vs/platform/label/common/label'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform'; import { IMenuService } from 'vs/platform/actions/common/actions'; @@ -32,11 +32,23 @@ export class TitlebarPart extends BrowserTitleBarPart { private dragRegion: HTMLElement | undefined; private resizer: HTMLElement | undefined; + private getMacTitlebarSize() { + const osVersion = this.environmentService.os.release; + if (parseFloat(osVersion) >= 20) { // Big Sur increases title bar height + return 28; + } + + return 22; + } + + get minimumHeight(): number { return isMacintosh ? this.getMacTitlebarSize() / getZoomFactor() : super.minimumHeight; } + get maximumHeight(): number { return this.minimumHeight; } + constructor( @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService protected readonly configurationService: IConfigurationService, @IEditorService editorService: IEditorService, - @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService, + @INativeWorkbenchEnvironmentService protected readonly environmentService: INativeWorkbenchEnvironmentService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, @@ -206,7 +218,7 @@ export class TitlebarPart extends BrowserTitleBarPart { updateLayout(dimension: DOM.Dimension): void { this.lastLayoutDimensions = dimension; - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if (getTitleBarStyle(this.configurationService) === 'custom') { // Only prevent zooming behavior on macOS or when the menubar is not visible if (isMacintosh || this.currentMenubarVisibility === 'hidden') { this.title.style.zoom = `${1 / getZoomFactor()}`; diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index ed4f26407..ada7a23fa 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -25,14 +25,8 @@ import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backu import { ITextSnapshot } from 'vs/editor/common/model'; import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ClassifiedEvent, GDPRClassification, StrictPropertyChecker } from 'vs/platform/telemetry/common/gdprTypings'; -import { IKeyboardLayoutInfo, IKeymapService, ILinuxKeyboardLayoutInfo, ILinuxKeyboardMapping, IMacKeyboardLayoutInfo, IMacKeyboardMapping, IWindowsKeyboardLayoutInfo, IWindowsKeyboardMapping } from 'vs/workbench/services/keybinding/common/keymapInfo'; -import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; -import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; -import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; -import { ChordKeybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; -import { ScanCodeBinding } from 'vs/base/common/scanCode'; -import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { isWindows, OS } from 'vs/base/common/platform'; +import { IKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayout'; +import { isWindows } from 'vs/base/common/platform'; import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewIcons, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; @@ -50,7 +44,7 @@ import { CustomTask, ContributedTask, InMemoryTask, TaskRunSource, ConfiguringTa import { TaskSystemInfo } from 'vs/workbench/contrib/tasks/common/taskSystem'; import { IExtensionTipsService, IConfigBasedExtensionTip, IExecutableBasedExtensionTip, IWorkspaceTips } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/workspaceTags'; -import { AsbtractOutputChannelModelService, IOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; +import { AbstractOutputChannelModelService, IOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModel'; import { joinPath } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; import { IIntegrityService, IntegrityTestResult } from 'vs/workbench/services/integrity/common/integrity'; @@ -59,7 +53,9 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; import type { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; import { Schemas } from 'vs/base/common/network'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { BrowserKeyboardLayoutService } from 'vs/workbench/services/keybinding/browser/keyboardLayoutService'; +import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminalInstanceService'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; //#region Environment @@ -87,12 +83,10 @@ export class SimpleNativeWorkbenchEnvironmentService implements INativeWorkbench get tmpDir(): URI { return joinPath(this.userRoamingDataHome, 'tmp'); } get logsPath(): string { return joinPath(this.userRoamingDataHome, 'logs').path; } - get backupWorkspaceHome(): URI { return joinPath(this.userRoamingDataHome, 'Backups', 'workspace'); } - updateBackupPath(newPath: string | undefined): void { } - sessionId = this.configuration.sessionId; machineId = this.configuration.machineId; remoteAuthority = this.configuration.remoteAuthority; + os = { release: 'unknown' }; options?: IWorkbenchConstructionOptions | undefined; logExtensionHostCommunication?: boolean | undefined; @@ -127,7 +121,7 @@ export class SimpleNativeWorkbenchEnvironmentService implements INativeWorkbench sharedIPCHandle: string = undefined!; - extensionsPath?: string | undefined; + extensionsPath: string = undefined!; extensionsDownloadPath: string = undefined!; builtinExtensionsPath: string = undefined!; @@ -433,6 +427,7 @@ export class SimpleRemoteAgentService implements IRemoteAgentService { async getRawEnvironment(): Promise { return null; } async scanExtensions(skipExtensions?: ExtensionIdentifier[]): Promise { return []; } async scanSingleExtension(extensionLocation: URI, isBuiltin: boolean): Promise { return null; } + async whenExtensionsReady(): Promise { } } //#endregion @@ -499,37 +494,11 @@ registerSingleton(ITelemetryService, SimpleTelemetryService); //#endregion -//#region Keymap Service +//#region Keymap Service (borrowed from browser for now to enable keyboard access) -class SimpleKeyboardMapper implements IKeyboardMapper { - dumpDebugInfo(): string { return ''; } - resolveKeybinding(keybinding: ChordKeybinding): ResolvedKeybinding[] { return []; } - resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding { - let keybinding = new SimpleKeybinding( - keyboardEvent.ctrlKey, - keyboardEvent.shiftKey, - keyboardEvent.altKey, - keyboardEvent.metaKey, - keyboardEvent.keyCode - ).toChord(); - return new USLayoutResolvedKeybinding(keybinding, OS); - } - resolveUserBinding(firstPart: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[] { return []; } -} +class SimpleKeyboardLayoutService extends BrowserKeyboardLayoutService { } -class SimpleKeymapService implements IKeymapService { - - declare readonly _serviceBrand: undefined; - - onDidChangeKeyboardMapper = Event.None; - getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { return new SimpleKeyboardMapper(); } - getCurrentKeyboardLayout(): (IWindowsKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | (ILinuxKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | (IMacKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | null { return null; } - getAllKeyboardLayouts(): IKeyboardLayoutInfo[] { return []; } - getRawKeyboardMapping(): IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping | null { return null; } - validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void { } -} - -registerSingleton(IKeymapService, SimpleKeymapService); +registerSingleton(IKeyboardLayoutService, SimpleKeyboardLayoutService); //#endregion @@ -683,7 +652,7 @@ registerSingleton(IUserDataAutoSyncService, SimpleUserDataAutoSyncAccountService //#region User Data Sync Store Management -class SimpleIUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { +class SimpleUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { declare readonly _serviceBrand: undefined; @@ -696,36 +665,10 @@ class SimpleIUserDataSyncStoreManagementService implements IUserDataSyncStoreMan async getPreviousUserDataSyncStore(): Promise { return undefined; } } -registerSingleton(IUserDataSyncStoreManagementService, SimpleIUserDataSyncStoreManagementService); +registerSingleton(IUserDataSyncStoreManagementService, SimpleUserDataSyncStoreManagementService); //#endregion -//#region IStorageKeysSyncRegistryService - -class SimpleIStorageKeysSyncRegistryService implements IStorageKeysSyncRegistryService { - - declare readonly _serviceBrand: undefined; - - onDidChangeStorageKeys = Event.None; - - storageKeys = []; - - registerStorageKey(): void { } - - onDidChangeExtensionStorageKeys = Event.None; - - extensionsStorageKeys = []; - - getExtensioStorageKeys() { return undefined; } - - registerExtensionStorageKeys(): void { } -} - -registerSingleton(IStorageKeysSyncRegistryService, SimpleIStorageKeysSyncRegistryService); - -//#endregion - - //#region Task class SimpleTaskService implements ITaskService { @@ -754,6 +697,7 @@ class SimpleTaskService implements ITaskService { tryResolveTask(configuringTask: ConfiguringTask): Promise { throw new Error('Method not implemented.'); } getTasksForGroup(group: string): Promise { throw new Error('Method not implemented.'); } getRecentlyUsedTasks(): LinkedMap { throw new Error('Method not implemented.'); } + removeRecentlyUsedTask(taskRecentlyUsedKey: string): void { throw new Error('Method not implemented.'); } migrateRecentTasks(tasks: Task[]): Promise { throw new Error('Method not implemented.'); } createSorter(): TaskSorter { throw new Error('Method not implemented.'); } getTaskDescription(task: CustomTask | ContributedTask | InMemoryTask | ConfiguringTask): string | undefined { throw new Error('Method not implemented.'); } @@ -798,7 +742,7 @@ class SimpleWorkspaceTagsService implements IWorkspaceTagsService { declare readonly _serviceBrand: undefined; async getTags(): Promise { return Object.create(null); } - getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { return undefined; } + async getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): Promise { return undefined; } async getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise { return []; } } @@ -809,7 +753,7 @@ registerSingleton(IWorkspaceTagsService, SimpleWorkspaceTagsService); //#region Output Channel -class SimpleOutputChannelModelService extends AsbtractOutputChannelModelService { +class SimpleOutputChannelModelService extends AbstractOutputChannelModelService { declare readonly _serviceBrand: undefined; } @@ -832,3 +776,9 @@ class SimpleIntegrityService implements IIntegrityService { registerSingleton(IIntegrityService, SimpleIntegrityService); //#endregion + +//#region Terminal Instance + +class SimpleTerminalInstanceService extends TerminalInstanceService { } + +registerSingleton(ITerminalInstanceService, SimpleTerminalInstanceService); diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 6f294f3c5..37afcaaf5 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -32,7 +32,7 @@ import { IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/wo import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; import { isWindows, isMacintosh } from 'vs/base/common/platform'; import { IProductService } from 'vs/platform/product/common/productService'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, IPromptChoice, NeverShowAgainScope, Severity } from 'vs/platform/notification/common/notification'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; @@ -43,7 +43,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { MenubarControl } from '../browser/parts/titlebar/menubarControl'; import { ILabelService } from 'vs/platform/label/common/label'; import { IUpdateService } from 'vs/platform/update/common/update'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IPreferencesService } from '../services/preferences/common/preferences'; import { IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/common/menubar'; import { IMenubarService } from 'vs/platform/menubar/electron-sandbox/menubar'; @@ -59,7 +59,6 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { Event } from 'vs/base/common/event'; -import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IAddressProvider, IAddress } from 'vs/platform/remote/common/remoteAgentConnection'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -185,14 +184,29 @@ export class NativeWindow extends Disposable { ipcRenderer.on('vscode:addFolders', (event: unknown, request: IAddFoldersRequest) => this.onAddFoldersRequest(request)); // Message support - ipcRenderer.on('vscode:showInfoMessage', (event: unknown, message: string) => { - this.notificationService.info(message); - }); + ipcRenderer.on('vscode:showInfoMessage', (event: unknown, message: string) => this.notificationService.info(message)); - // Display change events - ipcRenderer.on('vscode:displayChanged', () => { - clearAllFontInfos(); - }); + // Shell Environment Issue Notifications + const choices: IPromptChoice[] = [{ + label: nls.localize('learnMode', "Learn More"), + run: () => this.openerService.open('https://go.microsoft.com/fwlink/?linkid=2149667') + }]; + + ipcRenderer.on('vscode:showShellEnvSlowWarning', () => this.notificationService.prompt( + Severity.Warning, + nls.localize('shellEnvSlowWarning', "Resolving your shell environment is taking very long. Please review your shell configuration."), + choices, + { + sticky: true, + neverShowAgain: { id: 'ignoreShellEnvSlowWarning', scope: NeverShowAgainScope.GLOBAL } + } + )); + + ipcRenderer.on('vscode:showShellEnvTimeoutError', () => this.notificationService.prompt( + Severity.Error, + nls.localize('shellEnvTimeoutError', "Unable to resolve your shell environment in a reasonable time. Please review your shell configuration."), + choices + )); // Fullscreen Events ipcRenderer.on('vscode:enterFullScreen', async () => { @@ -237,7 +251,7 @@ export class NativeWindow extends Disposable { // Update state based on checkbox if (result.checkboxChecked) { - this.storageService.store(NativeWindow.REMEMBER_PROXY_CREDENTIALS_KEY, true, StorageScope.GLOBAL); + this.storageService.store(NativeWindow.REMEMBER_PROXY_CREDENTIALS_KEY, true, StorageScope.GLOBAL, StorageTarget.MACHINE); } else { this.storageService.remove(NativeWindow.REMEMBER_PROXY_CREDENTIALS_KEY, StorageScope.GLOBAL); } @@ -286,7 +300,7 @@ export class NativeWindow extends Disposable { } // Maximize/Restore on doubleclick (for macOS custom title) - if (isMacintosh && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if (isMacintosh && getTitleBarStyle(this.configurationService) === 'custom') { const titlePart = assertIsDefined(this.layoutService.getContainer(Parts.TITLEBAR_PART)); this._register(DOM.addDisposableListener(titlePart, DOM.EventType.DBLCLICK, e => { @@ -402,7 +416,7 @@ export class NativeWindow extends Disposable { this.customTitleContextMenuDisposable.clear(); // Provide new menu if a file is opened and we are on a custom title - if (!filePath || getTitleBarStyle(this.configurationService, this.environmentService) !== 'custom') { + if (!filePath || getTitleBarStyle(this.configurationService) !== 'custom') { return; } @@ -434,7 +448,7 @@ export class NativeWindow extends Disposable { private create(): void { // Native menu controller - if (isMacintosh || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + if (isMacintosh || getTitleBarStyle(this.configurationService) === 'native') { this._register(this.instantiationService.createInstance(NativeMenubarControl)); } diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 74fa84fa6..f28d3149c 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -14,7 +14,7 @@ import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IProductService } from 'vs/platform/product/common/productService'; import { isString } from 'vs/base/common/types'; @@ -375,11 +375,11 @@ export class AuthenticationService extends Disposable implements IAuthentication const allowList = readAllowedExtensions(storageService, providerId, session.account.label); if (!allowList.find(allowed => allowed.id === extensionId)) { allowList.push({ id: extensionId, name: extensionName }); - storageService.store(`${providerId}-${session.account.label}`, JSON.stringify(allowList), StorageScope.GLOBAL); + storageService.store(`${providerId}-${session.account.label}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER); } // And also set it as the preferred account for the extension - storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL); + storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL, StorageTarget.MACHINE); } }); diff --git a/src/vs/workbench/services/backup/browser/backupFileService.ts b/src/vs/workbench/services/backup/browser/backupFileService.ts new file mode 100644 index 000000000..afd98b6c7 --- /dev/null +++ b/src/vs/workbench/services/backup/browser/backupFileService.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IFileService } from 'vs/platform/files/common/files'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; +import { hash } from 'vs/base/common/hash'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { joinPath } from 'vs/base/common/resources'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +export class BrowserBackupFileService extends BackupFileService { + + declare readonly _serviceBrand: undefined; + + constructor( + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IFileService fileService: IFileService, + @ILogService logService: ILogService + ) { + super(joinPath(environmentService.userRoamingDataHome, 'Backups', contextService.getWorkspace().id), fileService, logService); + } + + protected hashPath(resource: URI): string { + const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); + + return hash(str).toString(16); + } +} + +registerSingleton(IBackupFileService, BrowserBackupFileService); diff --git a/src/vs/workbench/services/backup/common/backupFileService.ts b/src/vs/workbench/services/backup/common/backupFileService.ts index 5049d8f6d..16546d87a 100644 --- a/src/vs/workbench/services/backup/common/backupFileService.ts +++ b/src/vs/workbench/services/backup/common/backupFileService.ts @@ -6,7 +6,6 @@ import { join } from 'vs/base/common/path'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { hash } from 'vs/base/common/hash'; import { coalesce } from 'vs/base/common/arrays'; import { equals, deepClone } from 'vs/base/common/objects'; import { ResourceQueue } from 'vs/base/common/async'; @@ -15,8 +14,6 @@ import { IFileService, FileOperationError, FileOperationResult } from 'vs/platfo import { ITextSnapshot } from 'vs/editor/common/model'; import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { ResourceMap } from 'vs/base/common/map'; -import { Schemas } from 'vs/base/common/network'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { VSBuffer } from 'vs/base/common/buffer'; import { TextSnapshotReadable, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -108,42 +105,36 @@ export class BackupFilesModel implements IBackupFilesModel { } } -export class BackupFileService implements IBackupFileService { +export abstract class BackupFileService implements IBackupFileService { declare readonly _serviceBrand: undefined; private impl: BackupFileServiceImpl | InMemoryBackupFileService; constructor( - @IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService, + backupWorkspaceHome: URI | undefined, @IFileService protected fileService: IFileService, @ILogService private readonly logService: ILogService ) { - this.impl = this.initialize(); + this.impl = this.initialize(backupWorkspaceHome); } - protected hashPath(resource: URI): string { - const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); + protected abstract hashPath(resource: URI): string; - return hash(str).toString(16); - } - - private initialize(): BackupFileServiceImpl | InMemoryBackupFileService { - const backupWorkspaceResource = this.environmentService.backupWorkspaceHome; - if (backupWorkspaceResource) { - return new BackupFileServiceImpl(backupWorkspaceResource, this.hashPath, this.fileService, this.logService); + private initialize(backupWorkspaceHome: URI | undefined): BackupFileServiceImpl | InMemoryBackupFileService { + if (backupWorkspaceHome) { + return new BackupFileServiceImpl(backupWorkspaceHome, this.hashPath, this.fileService, this.logService); } return new InMemoryBackupFileService(this.hashPath); } - reinitialize(): void { + reinitialize(backupWorkspaceHome: URI | undefined): void { // Re-init implementation (unless we are running in-memory) if (this.impl instanceof BackupFileServiceImpl) { - const backupWorkspaceResource = this.environmentService.backupWorkspaceHome; - if (backupWorkspaceResource) { - this.impl.initialize(backupWorkspaceResource); + if (backupWorkspaceHome) { + this.impl.initialize(backupWorkspaceHome); } else { this.impl = new InMemoryBackupFileService(this.hashPath); } diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/electron-browser/backupFileService.ts similarity index 53% rename from src/vs/workbench/services/backup/node/backupFileService.ts rename to src/vs/workbench/services/backup/electron-browser/backupFileService.ts index 74beddef4..efbbeec9e 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/electron-browser/backupFileService.ts @@ -3,14 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BackupFileService as CommonBackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; +import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import * as crypto from 'crypto'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; -export class BackupFileService extends CommonBackupFileService { +export class NativeBackupFileService extends BackupFileService { + + constructor( + @INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, + @IFileService fileService: IFileService, + @ILogService logService: ILogService + ) { + super(environmentService.configuration.backupPath ? URI.file(environmentService.configuration.backupPath).with({ scheme: environmentService.userRoamingDataHome.scheme }) : undefined, fileService, logService); + } protected hashPath(resource: URI): string { return hashPath(resource); @@ -26,4 +37,4 @@ export function hashPath(resource: URI): string { return crypto.createHash('md5').update(str).digest('hex'); } -registerSingleton(IBackupFileService, BackupFileService); +registerSingleton(IBackupFileService, NativeBackupFileService); diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts index 0e00c5a49..1835f2775 100644 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts @@ -23,7 +23,7 @@ import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemPro import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService } from 'vs/platform/files/common/files'; -import { hashPath, BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; +import { hashPath, NativeBackupFileService } from 'vs/workbench/services/backup/electron-browser/backupFileService'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { VSBuffer } from 'vs/base/common/buffer'; import { TestWorkbenchConfiguration } from 'vs/workbench/test/electron-browser/workbenchTestServices'; @@ -53,7 +53,7 @@ class TestWorkbenchEnvironmentService extends NativeWorkbenchEnvironmentService } } -export class NodeTestBackupFileService extends BackupFileService { +export class NodeTestBackupFileService extends NativeBackupFileService { readonly fileService: IFileService; @@ -67,7 +67,7 @@ export class NodeTestBackupFileService extends BackupFileService { const fileService = new FileService(logService); const diskFileSystemProvider = new DiskFileSystemProvider(logService); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, URI.file(workspaceBackupPath), diskFileSystemProvider, environmentService, logService)); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, logService)); super(environmentService, fileService, logService); diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 717a91dd1..973ef52ff 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { Event, Emitter } from 'vs/base/common/event'; import * as errors from 'vs/base/common/errors'; -import { Disposable, IDisposable, dispose, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose, toDisposable, MutableDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; import { FileChangeType, FileChangesEvent, IFileService, whenProviderRegistered, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { ConfigurationModel, ConfigurationModelParser, UserSettings } from 'vs/platform/configuration/common/configurationModels'; @@ -92,6 +92,7 @@ class FileServiceBasedConfiguration extends Disposable { ) { super(); this.allResources = [...this.settingsResources, ...this.standAloneConfigurationResources.map(([, resource]) => resource)]; + this._register(combinedDisposable(...this.allResources.map(resource => this.fileService.watch(resources.dirname(resource))))); this._folderSettingsModelParser = new ConfigurationModelParser(name, this.scopes); this._standAloneConfigurations = []; this._cache = new ConfigurationModel(); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 2e23db80a..7a1b2cd45 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -32,6 +32,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; class Workspace extends BaseWorkspace { initialized: boolean = false; @@ -55,6 +56,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private readonly logService: ILogService; private readonly fileService: IFileService; + private readonly uriIdentityService: IUriIdentityService; protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; @@ -79,6 +81,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic environmentService: IWorkbenchEnvironmentService, fileService: IFileService, remoteAgentService: IRemoteAgentService, + uriIdentityService: IUriIdentityService, logService: ILogService, ) { super(); @@ -94,6 +97,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic this.defaultConfiguration = new DefaultConfigurationModel(); this.configurationCache = configurationCache; this.fileService = fileService; + this.uriIdentityService = uriIdentityService; this.logService = logService; this._configuration = new Configuration(this.defaultConfiguration, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); @@ -285,13 +289,38 @@ export class WorkspaceService extends Disposable implements IConfigurationServic }); } - reloadConfiguration(folder?: IWorkspaceFolder, key?: string): Promise { - if (folder) { - return this.reloadWorkspaceFolderConfiguration(folder, key); + async reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise { + if (target === undefined) { + const { local, remote } = await this.reloadUserConfiguration(); + await this.reloadWorkspaceConfiguration(); + await this.loadConfiguration(local, remote); + return; + } + + if (IWorkspaceFolder.isIWorkspaceFolder(target)) { + await this.reloadWorkspaceFolderConfiguration(target); + return; + } + + switch (target) { + case ConfigurationTarget.USER: + const { local, remote } = await this.reloadUserConfiguration(); + await this.loadConfiguration(local, remote); + return; + + case ConfigurationTarget.USER_LOCAL: + await this.reloadLocalUserConfiguration(); + return; + + case ConfigurationTarget.USER_REMOTE: + await this.reloadRemoteUserConfiguration(); + return; + + case ConfigurationTarget.WORKSPACE: + case ConfigurationTarget.WORKSPACE_FOLDER: + await this.reloadWorkspaceConfiguration(); + return; } - return this.reloadUserConfiguration() - .then(({ local, remote }) => this.reloadWorkspaceConfiguration() - .then(() => this.loadConfiguration(local, remote))); } inspect(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { @@ -346,20 +375,20 @@ export class WorkspaceService extends Disposable implements IConfigurationServic const workspaceConfigPath = workspaceIdentifier.configPath; const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), workspaceConfigPath); const workspaceId = workspaceIdentifier.id; - const workspace = new Workspace(workspaceId, workspaceFolders, workspaceConfigPath); + const workspace = new Workspace(workspaceId, workspaceFolders, workspaceConfigPath, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); workspace.initialized = this.workspaceConfiguration.initialized; return workspace; }); } private createSingleFolderWorkspace(singleFolder: ISingleFolderWorkspaceInitializationPayload): Promise { - const workspace = new Workspace(singleFolder.id, [toWorkspaceFolder(singleFolder.folder)]); + const workspace = new Workspace(singleFolder.id, [toWorkspaceFolder(singleFolder.folder)], null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); workspace.initialized = true; return Promise.resolve(workspace); } private createEmptyWorkspace(emptyWorkspace: IEmptyWorkspaceInitializationPayload): Promise { - const workspace = new Workspace(emptyWorkspace.id); + const workspace = new Workspace(emptyWorkspace.id, [], null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); workspace.initialized = true; return Promise.resolve(workspace); } @@ -465,10 +494,10 @@ export class WorkspaceService extends Disposable implements IConfigurationServic return new ConfigurationModel(); } - private reloadWorkspaceConfiguration(key?: string): Promise { + private reloadWorkspaceConfiguration(): Promise { const workbenchState = this.getWorkbenchState(); if (workbenchState === WorkbenchState.FOLDER) { - return this.onWorkspaceFolderConfigurationChanged(this.workspace.folders[0], key); + return this.onWorkspaceFolderConfigurationChanged(this.workspace.folders[0]); } if (workbenchState === WorkbenchState.WORKSPACE) { return this.workspaceConfiguration.reload().then(() => this.onWorkspaceConfigurationChanged()); @@ -476,8 +505,8 @@ export class WorkspaceService extends Disposable implements IConfigurationServic return Promise.resolve(undefined); } - private reloadWorkspaceFolderConfiguration(folder: IWorkspaceFolder, key?: string): Promise { - return this.onWorkspaceFolderConfigurationChanged(folder, key); + private reloadWorkspaceFolderConfiguration(folder: IWorkspaceFolder): Promise { + return this.onWorkspaceFolderConfigurationChanged(folder); } private loadConfiguration(userConfigurationModel: ConfigurationModel, remoteUserConfigurationModel: ConfigurationModel): Promise { @@ -592,7 +621,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } } - private onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder, key?: string): Promise { + private onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder): Promise { return this.loadFolderConfigurations([folder]) .then(([folderConfiguration]) => { const previous = { data: this._configuration.toData(), workspace: this.workspace }; @@ -700,7 +729,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic case EditableConfigurationTarget.WORKSPACE_FOLDER: const workspaceFolder = overrides && overrides.resource ? this.workspace.getFolder(overrides.resource) : null; if (workspaceFolder) { - return this.reloadWorkspaceFolderConfiguration(workspaceFolder, key); + return this.reloadWorkspaceFolderConfiguration(workspaceFolder); } } return Promise.resolve(); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index ee38ee006..71256640c 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -305,7 +305,7 @@ export class ConfigurationEditingService { } private openFile(resource: URI): void { - this.editorService.openEditor({ resource }); + this.editorService.openEditor({ resource, options: { pinned: true } }); } private reject(code: ConfigurationEditingErrorCode, target: EditableConfigurationTarget, operation: IConfigurationEditOperation): Promise { diff --git a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts index c0399d236..ef4976ce0 100644 --- a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts +++ b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts @@ -8,8 +8,9 @@ import { StandaloneConfigurationModelParser, Configuration } from 'vs/workbench/ import { ConfigurationModelParser, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { ResourceMap } from 'vs/base/common/map'; -import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; suite('FolderSettingsModelParser', () => { diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts index f9f3adb53..f33ff6088 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts @@ -41,6 +41,8 @@ import { ConfigurationCache } from 'vs/workbench/services/configuration/electron import { KeybindingsEditingService, IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { DisposableStore } from 'vs/base/common/lifecycle'; class TestWorkbenchEnvironmentService extends NativeWorkbenchEnvironmentService { @@ -61,6 +63,8 @@ suite('ConfigurationEditingService', () => { let globalTasksFile: string; let workspaceSettingsDir; + const disposables = new DisposableStore(); + suiteSetup(() => { const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ @@ -83,9 +87,9 @@ suite('ConfigurationEditingService', () => { }); }); - setup(() => { - return setUpWorkspace() - .then(() => setUpServices()); + setup(async () => { + await setUpWorkspace(); + await setUpServices(); }); async function setUpWorkspace(): Promise { @@ -99,50 +103,36 @@ suite('ConfigurationEditingService', () => { return await mkdirp(workspaceSettingsDir, 493); } - function setUpServices(noWorkspace: boolean = false): Promise { - // Clear services if they are already created - clearServices(); - + async function setUpServices(noWorkspace: boolean = false): Promise { instantiationService = workbenchInstantiationService(); const environmentService = new TestWorkbenchEnvironmentService(URI.file(workspaceDir)); instantiationService.stub(IEnvironmentService, environmentService); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService()); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); - return workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }).then(() => { - instantiationService.stub(IConfigurationService, workspaceService); - instantiationService.stub(IKeybindingEditingService, instantiationService.createInstance(KeybindingsEditingService)); - instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); - instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); - instantiationService.stub(ICommandService, CommandService); - testObject = instantiationService.createInstance(ConfigurationEditingService); - }); + await workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }); + instantiationService.stub(IConfigurationService, workspaceService); + instantiationService.stub(IKeybindingEditingService, instantiationService.createInstance(KeybindingsEditingService)); + instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + instantiationService.stub(ICommandService, CommandService); + testObject = instantiationService.createInstance(ConfigurationEditingService); } teardown(() => { - clearServices(); + disposables.clear(); if (workspaceDir) { return rimraf(workspaceDir, RimRafMode.MOVE); } return undefined; }); - function clearServices(): void { - if (instantiationService) { - const configuraitonService = instantiationService.get(IConfigurationService); - if (configuraitonService) { - configuraitonService.dispose(); - } - instantiationService = null!; - } - } - test('errors cases - invalid key', () => { return testObject.writeConfiguration(EditableConfigurationTarget.WORKSPACE, { key: 'unknown.key', value: 'value' }) .then(() => assert.fail('Should fail with ERROR_UNKNOWN_KEY'), diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index 57a0b0591..a7f455cd5 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -54,6 +54,7 @@ import product from 'vs/platform/product/common/product'; import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { Event } from 'vs/base/common/event'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; class TestWorkbenchEnvironmentService extends NativeWorkbenchEnvironmentService { @@ -105,6 +106,7 @@ function setUpWorkspace(folders: string[]): Promise<{ parentDir: string, configP suite('WorkspaceContextService - Folder', () => { let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceResource: string, workspaceContextService: IWorkspaceContextService; + const disposables = new DisposableStore(); setup(() => { return setUpFolderWorkspace(workspaceName) @@ -112,19 +114,17 @@ suite('WorkspaceContextService - Folder', () => { parentResource = parentDir; workspaceResource = folderDir; const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, new DiskFileSystemProvider(new NullLogService()), environmentService, new NullLogService())); - workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(environmentService, { _serviceBrand: undefined, ...product }, new RemoteAuthorityResolverService(), new SignService(undefined), new NullLogService()), new NullLogService()); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService()), Schemas.userData, new NullLogService()))); + workspaceContextService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(environmentService, { _serviceBrand: undefined, ...product }, new RemoteAuthorityResolverService(), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); return (workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir))); }); }); teardown(() => { - if (workspaceContextService) { - (workspaceContextService).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -167,6 +167,7 @@ suite('WorkspaceContextService - Folder', () => { suite('WorkspaceContextService - Workspace', () => { let parentResource: string, testObject: WorkspaceService, instantiationService: TestInstantiationService; + const disposables = new DisposableStore(); setup(() => { return setUpWorkspace(['a', 'b']) @@ -178,11 +179,11 @@ suite('WorkspaceContextService - Workspace', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); - const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService()); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -196,9 +197,7 @@ suite('WorkspaceContextService - Workspace', () => { }); teardown(() => { - if (testObject) { - (testObject).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -227,6 +226,7 @@ suite('WorkspaceContextService - Workspace', () => { suite('WorkspaceContextService - Workspace Editing', () => { let parentResource: string, testObject: WorkspaceService, instantiationService: TestInstantiationService; + const disposables = new DisposableStore(); setup(() => { return setUpWorkspace(['a', 'b']) @@ -238,11 +238,11 @@ suite('WorkspaceContextService - Workspace Editing', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); - const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService()); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -260,9 +260,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { }); teardown(() => { - if (testObject) { - (testObject).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -467,6 +465,7 @@ suite('WorkspaceService - Initialization', () => { let parentResource: string, workspaceConfigPath: URI, testObject: WorkspaceService, globalSettingsFile: string; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + const disposables = new DisposableStore(); suiteSetup(() => { configurationRegistry.registerConfiguration({ @@ -499,11 +498,11 @@ suite('WorkspaceService - Initialization', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); - const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService()); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); @@ -519,9 +518,7 @@ suite('WorkspaceService - Initialization', () => { }); teardown(() => { - if (testObject) { - (testObject).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -727,7 +724,7 @@ suite('WorkspaceConfigurationService - Folder', () => { let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceDir: string, testObject: IConfigurationService, globalSettingsFile: string, globalTasksFile: string, workspaceService: WorkspaceService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); let fileService: IFileService; - let disposableStore: DisposableStore = new DisposableStore(); + const disposables: DisposableStore = new DisposableStore(); suiteSetup(() => { configurationRegistry.registerConfiguration({ @@ -776,17 +773,17 @@ suite('WorkspaceConfigurationService - Folder', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); - workspaceService = disposableStore.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); + workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); // Watch workspace configuration directory - disposableStore.add(fileService.watch(joinPath(URI.file(workspaceDir), '.vscode'))); + disposables.add(fileService.watch(joinPath(URI.file(workspaceDir), '.vscode'))); return workspaceService.initialize(convertToWorkspacePayload(URI.file(folderDir))).then(() => { instantiationService.stub(IFileService, fileService); @@ -800,7 +797,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); teardown(() => { - disposableStore.clear(); + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -1232,6 +1229,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { let parentResource: string, workspaceContextService: IWorkspaceContextService, jsonEditingServce: IJSONEditingService, testObject: IConfigurationService, globalSettingsFile: string; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + const disposables = new DisposableStore(); suiteSetup(() => { configurationRegistry.registerConfiguration({ @@ -1282,11 +1280,11 @@ suite('WorkspaceConfigurationService-Multiroot', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); - const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService()); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -1308,9 +1306,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); teardown(() => { - if (testObject) { - (testObject).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -1841,7 +1837,8 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceDir: string, testObject: WorkspaceService, globalSettingsFile: string, remoteSettingsFile: string, remoteSettingsResource: URI, instantiationService: TestInstantiationService, resolveRemoteEnvironment: () => void; const remoteAuthority = 'configuraiton-tests'; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const disposables = new DisposableStore(); + let diskFileSystemProvider: DiskFileSystemProvider; suiteSetup(() => { configurationRegistry.registerConfiguration({ @@ -1886,11 +1883,12 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteEnvironmentPromise = new Promise>(c => resolveRemoteEnvironment = () => c({ settingsPath: remoteSettingsResource })); const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); - const fileService = new FileService(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve(), needsCaching: () => false }; - testObject = new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService, new NullLogService()); + testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); instantiationService.stub(IEnvironmentService, environmentService); @@ -1919,9 +1917,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { } teardown(() => { - if (testObject) { - (testObject).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -2096,7 +2092,7 @@ suite('ConfigurationService - Configuration Defaults', () => { const remoteAgentService = (workbenchInstantiationService()).createInstance(RemoteAgentService); const environmentService = new BrowserWorkbenchEnvironmentService({ logsPath: URI.file(''), workspaceId: '', configurationDefaults }, TestProductService); const fileService = new FileService(new NullLogService()); - return disposableStore.add(new WorkspaceService({ configurationCache: new BrowserConfigurationCache() }, environmentService, fileService, remoteAgentService, new NullLogService())); + return disposableStore.add(new WorkspaceService({ configurationCache: new BrowserConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); } }); diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index a40d034ec..5f22a895f 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -44,7 +44,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR getWorkspaceFolderCount: (): number => { return workspaceContextService.getWorkspace().folders.length; }, - getConfigurationValue: (folderUri: uri, suffix: string): string | undefined => { + getConfigurationValue: (folderUri: uri | undefined, suffix: string): string | undefined => { return configurationService.getValue(suffix, folderUri ? { resource: folderUri } : {}); }, getExecPath: (): string | undefined => { @@ -60,6 +60,20 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR } return this.labelService.getUriLabel(fileResource, { noPrefix: true }); }, + getWorkspaceFolderPathForFile: (): string | undefined => { + const fileResource = EditorResourceAccessor.getOriginalUri(editorService.activeEditor, { + supportSideBySide: SideBySideEditor.PRIMARY, + filterByScheme: [Schemas.file, Schemas.userData, Schemas.vscodeRemote] + }); + if (!fileResource) { + return undefined; + } + const wsFolder = workspaceContextService.getWorkspaceFolder(fileResource); + if (!wsFolder) { + return undefined; + } + return this.labelService.getUriLabel(wsFolder.uri, { noPrefix: true }); + }, getSelectedText: (): string | undefined => { const activeTextEditorControl = editorService.activeTextEditorControl; if (isCodeEditor(activeTextEditorControl)) { diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 4d632ea03..70459256a 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -19,15 +19,17 @@ import { ILabelService } from 'vs/platform/label/common/label'; export interface IVariableResolveContext { getFolderUri(folderName: string): uri | undefined; getWorkspaceFolderCount(): number; - getConfigurationValue(folderUri: uri, section: string): string | undefined; + getConfigurationValue(folderUri: uri | undefined, section: string): string | undefined; getExecPath(): string | undefined; getFilePath(): string | undefined; + getWorkspaceFolderPathForFile?(): string | undefined; getSelectedText(): string | undefined; getLineNumber(): string | undefined; } export class AbstractVariableResolverService implements IConfigurationResolverService { + static readonly VARIABLE_LHS = '${'; static readonly VARIABLE_REGEXP = /\$\{(.*?)\}/g; declare readonly _serviceBrand: undefined; @@ -38,7 +40,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe protected _contributedVariables: Map Promise> = new Map(); - constructor(_context: IVariableResolveContext, _labelService?: ILabelService, _envVariables?: IProcessEnvironment, private _ignoreEditorVariables = false) { + constructor(_context: IVariableResolveContext, _labelService?: ILabelService, _envVariables?: IProcessEnvironment) { this._context = _context; this._labelService = _labelService; if (_envVariables) { @@ -130,6 +132,10 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe // loop through all variables occurrences in 'value' const replaced = value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => { + // disallow attempted nesting, see #77289 + if (variable.includes(AbstractVariableResolverService.VARIABLE_LHS)) { + return match; + } let resolvedValue = this.evaluateSingleVariable(match, variable, folderUri, commandValueMapping); @@ -164,18 +170,31 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe if (filePath) { return filePath; } - throw new Error(localize('canNotResolveFile', "'{0}' can not be resolved. Please open an editor.", match)); + throw new Error(localize('canNotResolveFile', "Variable {0} can not be resolved. Please open an editor.", match)); + }; + + // common error handling for all variables that require an open editor + const getFolderPathForFile = (): string => { + + const filePath = getFilePath(); // throws error if no editor open + if (this._context.getWorkspaceFolderPathForFile) { + const folderPath = this._context.getWorkspaceFolderPathForFile(); + if (folderPath) { + return folderPath; + } + } + throw new Error(localize('canNotResolveFolderForFile', "Variable {0}: can not find workspace folder of '{1}'.", match, paths.basename(filePath))); }; // common error handling for all variables that require an open folder and accept a folder name argument - const getFolderUri = (withArg = true): uri => { + const getFolderUri = (): uri => { - if (withArg && argument) { + if (argument) { const folder = this._context.getFolderUri(argument); if (folder) { return folder; } - throw new Error(localize('canNotFindFolder', "'{0}' can not be resolved. No such folder '{1}'.", match, argument)); + throw new Error(localize('canNotFindFolder', "Variable {0} can not be resolved. No such folder '{1}'.", match, argument)); } if (folderUri) { @@ -183,9 +202,9 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe } if (this._context.getWorkspaceFolderCount() > 1) { - throw new Error(localize('canNotResolveWorkspaceFolderMultiRoot', "'{0}' can not be resolved in a multi folder workspace. Scope this variable using ':' and a workspace folder name.", match)); + throw new Error(localize('canNotResolveWorkspaceFolderMultiRoot', "Variable {0} can not be resolved in a multi folder workspace. Scope this variable using ':' and a workspace folder name.", match)); } - throw new Error(localize('canNotResolveWorkspaceFolder', "'{0}' can not be resolved. Please open a folder.", match)); + throw new Error(localize('canNotResolveWorkspaceFolder', "Variable {0} can not be resolved. Please open a folder.", match)); }; @@ -202,20 +221,20 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe // For `env` we should do the same as a normal shell does - evaluates undefined envs to an empty string #46436 return ''; } - throw new Error(localize('missingEnvVarName', "'{0}' can not be resolved because no environment variable name is given.", match)); + throw new Error(localize('missingEnvVarName', "Variable {0} can not be resolved because no environment variable name is given.", match)); case 'config': if (argument) { - const config = this._context.getConfigurationValue(getFolderUri(false), argument); + const config = this._context.getConfigurationValue(folderUri, argument); if (types.isUndefinedOrNull(config)) { - throw new Error(localize('configNotFound', "'{0}' can not be resolved because setting '{1}' not found.", match, argument)); + throw new Error(localize('configNotFound', "Variable {0} can not be resolved because setting '{1}' not found.", match, argument)); } if (types.isObject(config)) { - throw new Error(localize('configNoString', "'{0}' can not be resolved because '{1}' is a structured value.", match, argument)); + throw new Error(localize('configNoString', "Variable {0} can not be resolved because '{1}' is a structured value.", match, argument)); } return config; } - throw new Error(localize('missingConfigName', "'{0}' can not be resolved because no settings name is given.", match)); + throw new Error(localize('missingConfigName', "Variable {0} can not be resolved because no settings name is given.", match)); case 'command': return this.resolveFromMap(match, argument, commandValueMapping, 'command'); @@ -238,44 +257,32 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return paths.basename(this.fsPath(getFolderUri())); case 'lineNumber': - if (this._ignoreEditorVariables) { - return match; - } const lineNumber = this._context.getLineNumber(); if (lineNumber) { return lineNumber; } - throw new Error(localize('canNotResolveLineNumber', "'{0}' can not be resolved. Make sure to have a line selected in the active editor.", match)); + throw new Error(localize('canNotResolveLineNumber', "Variable {0} can not be resolved. Make sure to have a line selected in the active editor.", match)); case 'selectedText': - if (this._ignoreEditorVariables) { - return match; - } const selectedText = this._context.getSelectedText(); if (selectedText) { return selectedText; } - throw new Error(localize('canNotResolveSelectedText', "'{0}' can not be resolved. Make sure to have some text selected in the active editor.", match)); + throw new Error(localize('canNotResolveSelectedText', "Variable {0} can not be resolved. Make sure to have some text selected in the active editor.", match)); case 'file': - if (this._ignoreEditorVariables) { - return match; - } return getFilePath(); + case 'fileWorkspaceFolder': + return getFolderPathForFile(); + case 'relativeFile': - if (this._ignoreEditorVariables) { - return match; - } if (folderUri || argument) { return paths.relative(this.fsPath(getFolderUri()), getFilePath()); } return getFilePath(); case 'relativeFileDirname': - if (this._ignoreEditorVariables) { - return match; - } const dirname = paths.dirname(getFilePath()); if (folderUri || argument) { const relative = paths.relative(this.fsPath(getFolderUri()), dirname); @@ -284,30 +291,21 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return dirname; case 'fileDirname': - if (this._ignoreEditorVariables) { - return match; - } return paths.dirname(getFilePath()); case 'fileExtname': - if (this._ignoreEditorVariables) { - return match; - } return paths.extname(getFilePath()); case 'fileBasename': - if (this._ignoreEditorVariables) { - return match; - } return paths.basename(getFilePath()); case 'fileBasenameNoExtension': - if (this._ignoreEditorVariables) { - return match; - } const basename = paths.basename(getFilePath()); return (basename.slice(0, basename.length - paths.extname(basename).length)); + case 'fileDirnameBasename': + return paths.basename(paths.dirname(getFilePath())); + case 'execPath': const ep = this._context.getExecPath(); if (ep) { @@ -315,6 +313,9 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe } return match; + case 'pathSeparator': + return paths.sep; + default: try { return this.resolveFromMap(match, variable, commandValueMapping, undefined); @@ -332,7 +333,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe if (typeof v === 'string') { return v; } - throw new Error(localize('noValueForCommand', "'{0}' can not be resolved because the command has no value.", match)); + throw new Error(localize('noValueForCommand', "Variable {0} can not be resolved because the command has no value.", match)); } return match; } diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index 3e3de85fb..e1a333b06 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -175,6 +175,10 @@ suite('Configuration Resolver Service', () => { } }); + test('disallows nested keys (#77289)', () => { + assert.strictEqual(configurationResolverService!.resolve(workspace, '${env:key1} ${env:key1${env:key2}}'), 'Value for key1 ${env:key1${env:key2}}'); + }); + // test('substitute keys and values in object', () => { // const myObject = { // '${workspaceRootFolderName}': '${lineNumber}', @@ -211,6 +215,17 @@ suite('Configuration Resolver Service', () => { assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); }); + test('substitute configuration variable with undefined workspace folder', () => { + let configurationService: IConfigurationService = new TestConfigurationService({ + editor: { + fontFamily: 'foo' + } + }); + + let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService); + assert.strictEqual(service.resolve(undefined, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); + }); + test('substitute many configuration variables', () => { let configurationService: IConfigurationService; configurationService = new TestConfigurationService({ diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index a2005fe69..09a3e187b 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -44,7 +44,7 @@ export class ContextMenuService extends Disposable implements IContextMenuServic super(); // Custom context menu: Linux/Windows if custom title is enabled - if (!isMacintosh && getTitleBarStyle(configurationService, environmentService) === 'custom') { + if (!isMacintosh && getTitleBarStyle(configurationService) === 'custom') { this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, themeService); } diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index fda885169..7cfea5300 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -18,6 +18,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { hash } from 'vs/base/common/hash'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; class DecorationRule { @@ -106,9 +107,7 @@ class DecorationStyles { private readonly _decorationRules = new Map(); private readonly _dispoables = new DisposableStore(); - constructor( - private _themeService: IThemeService, - ) { + constructor(private readonly _themeService: IThemeService) { this._themeService.onDidColorThemeChange(this._onThemeChange, this, this._dispoables); } @@ -149,7 +148,7 @@ class DecorationStyles { badgeClassName, tooltip, dispose: () => { - if (rule && rule.release()) { + if (rule?.release()) { this._decorationRules.delete(key); rule.removeCSSRules(this._styleElement); rule = undefined; @@ -168,13 +167,13 @@ class DecorationStyles { class FileDecorationChangeEvent implements IResourceDecorationChangeEvent { - private readonly _data = TernarySearchTree.forUris(); + private readonly _data = TernarySearchTree.forUris(_uri => true); // events ignore all path casings affectsResource(uri: URI): boolean { - return this._data.get(uri) || this._data.findSuperstr(uri) !== undefined; + return this._data.get(uri) ?? this._data.findSuperstr(uri) !== undefined; } - static debouncer(last: FileDecorationChangeEvent | undefined, current: URI | URI[]) { + static debouncer(last: FileDecorationChangeEvent | undefined, current: URI | URI[]): FileDecorationChangeEvent { if (!last) { last = new FileDecorationChangeEvent(); } @@ -201,14 +200,18 @@ class DecorationDataRequest { class DecorationProviderWrapper { - readonly data = TernarySearchTree.forUris(); + readonly data: TernarySearchTree; private readonly _dispoable: IDisposable; constructor( readonly provider: IDecorationsProvider, + uriIdentityService: IUriIdentityService, private readonly _uriEmitter: Emitter, private readonly _flushEmitter: Emitter ) { + + this.data = TernarySearchTree.forUris(uri => uriIdentityService.extUri.ignorePathCasing(uri)); + this._dispoable = this.provider.onDidChange(uris => { if (!uris) { // flush event -> drop all data, can affect everything @@ -233,7 +236,7 @@ class DecorationProviderWrapper { } knowsAbout(uri: URI): boolean { - return Boolean(this.data.get(uri)) || Boolean(this.data.findSuperstr(uri)); + return this.data.has(uri) || Boolean(this.data.findSuperstr(uri)); } getOrRetrieve(uri: URI, includeChildren: boolean, callback: (data: IDecorationData, isChild: boolean) => void): void { @@ -254,9 +257,9 @@ class DecorationProviderWrapper { // (resolved) children const iter = this.data.findSuperstr(uri); if (iter) { - for (let item = iter.next(); !item.done; item = iter.next()) { - if (item.value && !(item.value instanceof DecorationDataRequest)) { - callback(item.value, true); + for (const [, value] of iter) { + if (value && !(value instanceof DecorationDataRequest)) { + callback(value, true); } } } @@ -326,6 +329,7 @@ export class DecorationsService implements IDecorationsService { constructor( @IThemeService themeService: IThemeService, + @IUriIdentityService private readonly _uriIdentityService: IUriIdentityService, ) { this._decorationStyles = new DecorationStyles(themeService); } @@ -340,6 +344,7 @@ export class DecorationsService implements IDecorationsService { const wrapper = new DecorationProviderWrapper( provider, + this._uriIdentityService, this._onDidChangeDecorationsDelayed, this._onDidChangeDecorations ); diff --git a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts index 9cbdbb2a6..5a4b76eb6 100644 --- a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts +++ b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts @@ -8,8 +8,11 @@ import { DecorationsService } from 'vs/workbench/services/decorations/browser/de import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; +import * as resources from 'vs/base/common/resources'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { mock } from 'vs/base/test/common/mock'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; suite('DecorationsService', function () { @@ -19,7 +22,12 @@ suite('DecorationsService', function () { if (service) { service.dispose(); } - service = new DecorationsService(new TestThemeService()); + service = new DecorationsService( + new TestThemeService(), + new class extends mock() { + extUri = resources.extUri; + } + ); }); test('Async provider, async/evented result', function () { diff --git a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts index 3d6973abe..222e877c6 100644 --- a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts @@ -24,6 +24,7 @@ import { trim } from 'vs/base/common/strings'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ILabelService } from 'vs/platform/label/common/label'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { Schemas } from 'vs/base/common/network'; export abstract class AbstractFileDialogService implements IFileDialogService { @@ -45,7 +46,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { @IPathService private readonly pathService: IPathService ) { } - defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined { + async defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): Promise { // Check for last active file first... let candidate = this.historyService.getLastActiveFile(schemeFilter); @@ -57,10 +58,14 @@ export abstract class AbstractFileDialogService implements IFileDialogService { candidate = candidate && resources.dirname(candidate); } - return candidate || undefined; + if (!candidate) { + candidate = await this.pathService.userHome({ preferLocal: schemeFilter === Schemas.file }); + } + + return candidate; } - defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined { + async defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): Promise { // Check for last active file root first... let candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter); @@ -70,10 +75,14 @@ export abstract class AbstractFileDialogService implements IFileDialogService { candidate = this.historyService.getLastActiveFile(schemeFilter); } - return candidate && resources.dirname(candidate) || undefined; + if (!candidate) { + return this.pathService.userHome({ preferLocal: schemeFilter === Schemas.file }); + } else { + return resources.dirname(candidate); + } } - defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow(), filename?: string): URI | undefined { + async defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow(), filename?: string): Promise { let defaultWorkspacePath: URI | undefined; // Check for current workspace config file first... if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { @@ -85,7 +94,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { // ...then fallback to default file path if (!defaultWorkspacePath) { - defaultWorkspacePath = this.defaultFilePath(schemeFilter); + defaultWorkspacePath = await this.defaultFilePath(schemeFilter); } if (defaultWorkspacePath && filename) { @@ -155,7 +164,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { if (stat.isDirectory || options.forceNewWindow || preferNewWindow) { return this.hostService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow }); } else { - return this.openerService.open(uri, { fromUserGesture: true }); + return this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } }); } } } @@ -172,7 +181,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { if (options.forceNewWindow || preferNewWindow) { return this.hostService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow }); } else { - return this.openerService.open(uri, { fromUserGesture: true }); + return this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } }); } } } diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index a25f8e001..a6eb25f5a 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -15,7 +15,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); + options.defaultUri = await this.defaultFilePath(schema); } return this.pickFileFolderAndOpenSimplified(schema, options, false); @@ -25,7 +25,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); + options.defaultUri = await this.defaultFilePath(schema); } return this.pickFileAndOpenSimplified(schema, options, false); @@ -35,7 +35,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFolderPath(schema); + options.defaultUri = await this.defaultFolderPath(schema); } return this.pickFolderAndOpenSimplified(schema, options); @@ -45,7 +45,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultWorkspacePath(schema); + options.defaultUri = await this.defaultWorkspacePath(schema); } return this.pickWorkspaceAndOpenSimplified(schema, options); diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 67885964d..e0eca9ebe 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -212,11 +212,16 @@ export class SimpleFileDialog { path = path.replace(/\\/g, '/'); } const uri: URI = this.scheme === Schemas.file ? URI.file(path) : URI.from({ scheme: this.scheme, path }); - return resources.toLocalResource(uri, uri.scheme === Schemas.file ? undefined : this.remoteAuthority, this.pathService.defaultUriScheme); + return resources.toLocalResource(uri, + // If the default scheme is file, then we don't care about the remote authority + uri.scheme === Schemas.file ? undefined : this.remoteAuthority, + // If there is a remote authority, then we should use the system's default URI as the local scheme. + // If there is *no* remote authority, then we should use the default scheme for this dialog as that is already local. + this.remoteAuthority ? this.pathService.defaultUriScheme : uri.scheme); } private getScheme(available: readonly string[] | undefined, defaultUri: URI | undefined): string { - if (available) { + if (available && available.length > 0) { if (defaultUri && (available.indexOf(defaultUri.scheme) >= 0)) { return defaultUri.scheme; } @@ -919,7 +924,8 @@ export class SimpleFileDialog { const ext = resources.extname(file); for (let i = 0; i < this.options.filters.length; i++) { for (let j = 0; j < this.options.filters[i].extensions.length; j++) { - if (ext === ('.' + this.options.filters[i].extensions[j])) { + const testExt = this.options.filters[i].extensions[j]; + if ((testExt === '*') || (ext === ('.' + testExt))) { return true; } } diff --git a/src/vs/workbench/services/dialogs/common/dialogService.ts b/src/vs/workbench/services/dialogs/common/dialogService.ts new file mode 100644 index 000000000..df9400859 --- /dev/null +++ b/src/vs/workbench/services/dialogs/common/dialogService.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Severity from 'vs/base/common/severity'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IInput, IInputResult, IShowResult } from 'vs/platform/dialogs/common/dialogs'; +import { DialogsModel, IDialogsModel } from 'vs/workbench/common/dialogs'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export class DialogService extends Disposable implements IDialogService { + _serviceBrand: undefined; + + readonly model: IDialogsModel = this._register(new DialogsModel()); + + async confirm(confirmation: IConfirmation): Promise { + const handle = this.model.show({ confirmArgs: { confirmation } }); + return await handle.result as IConfirmationResult; + } + + async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { + const handle = this.model.show({ showArgs: { severity, message, buttons, options } }); + return await handle.result as IShowResult; + } + + async input(severity: Severity, message: string, buttons: string[], inputs: IInput[], options?: IDialogOptions): Promise { + const handle = this.model.show({ inputArgs: { severity, message, buttons, inputs, options } }); + return await handle.result as IInputResult; + } + + async about(): Promise { + const handle = this.model.show({}); + await handle.result; + } +} + +registerSingleton(IDialogService, DialogService, true); diff --git a/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts index a6287ee77..3398150a5 100644 --- a/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts @@ -68,7 +68,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); + options.defaultUri = await this.defaultFilePath(schema); } const shouldUseSimplified = this.shouldUseSimplified(schema); @@ -82,7 +82,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); + options.defaultUri = await this.defaultFilePath(schema); } const shouldUseSimplified = this.shouldUseSimplified(schema); @@ -96,7 +96,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFolderPath(schema); + options.defaultUri = await this.defaultFolderPath(schema); } if (this.shouldUseSimplified(schema).useSimplified) { @@ -109,7 +109,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultWorkspacePath(schema); + options.defaultUri = await this.defaultWorkspacePath(schema); } if (this.shouldUseSimplified(schema).useSimplified) { diff --git a/src/vs/workbench/services/editor/browser/codeEditorService.ts b/src/vs/workbench/services/editor/browser/codeEditorService.ts index d0474b57d..89e432817 100644 --- a/src/vs/workbench/services/editor/browser/codeEditorService.ts +++ b/src/vs/workbench/services/editor/browser/codeEditorService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICodeEditor, isCodeEditor, isDiffEditor, isCompositeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor, isDiffEditor, isCompositeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; @@ -69,12 +69,34 @@ export class CodeEditorService extends CodeEditorServiceImpl { } private async doOpenCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise { + + // Special case: we want to detect the request to open an editor that + // is different from the current one to decide wether the current editor + // should be pinned or not. This ensures that the source of a navigation + // is not being replaced by the target. An example is "Goto definition" + // that otherwise would replace the editor everytime the user navigates. + if ( + source && // we need to know the origin of the navigation + !input.options?.pinned && // we only need to look at preview editors that open + !sideBySide && // we only need to care if editor opens in same group + !isEqual(source.getModel()?.uri, input.resource) // we only need to do this if the editor is about to change + ) { + for (const visiblePane of this.editorService.visibleEditorPanes) { + if (getCodeEditor(visiblePane.getControl()) === source) { + visiblePane.group.pinEditor(); + break; + } + } + } + + // Open as editor const control = await this.editorService.openEditor(input, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); if (control) { const widget = control.getControl(); if (isCodeEditor(widget)) { return widget; } + if (isCompositeEditor(widget) && isCodeEditor(widget.activeCodeEditor)) { return widget.activeCodeEditor; } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index a50d37b30..458e5e411 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -24,7 +24,6 @@ import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from import { coalesce, distinct, insert } from 'vs/base/common/arrays'; import { isCodeEditor, isDiffEditor, ICodeEditor, IDiffEditor, isCompositeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupView, IEditorOpeningEvent, EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; -import { ILabelService } from 'vs/platform/label/common/label'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; import { EditorsObserver } from 'vs/workbench/browser/parts/editor/editorsObserver'; @@ -73,7 +72,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @ILabelService private readonly labelService: ILabelService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @@ -817,11 +815,12 @@ export class EditorService extends Disposable implements EditorServiceImpl { const leftInput = this.createEditorInput({ resource: resourceDiffInput.leftResource, forceFile: resourceDiffInput.forceFile }); const rightInput = this.createEditorInput({ resource: resourceDiffInput.rightResource, forceFile: resourceDiffInput.forceFile }); - return new DiffEditorInput( - resourceDiffInput.label || this.toSideBySideLabel(leftInput, rightInput), + return this.instantiationService.createInstance(DiffEditorInput, + resourceDiffInput.label, resourceDiffInput.description, leftInput, - rightInput + rightInput, + undefined ); } @@ -881,7 +880,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // File if (resourceEditorInput.forceFile || this.fileService.canHandleResource(canonicalResource)) { - return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService); + return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.label, resourceEditorInput.description, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService); } // Resource @@ -897,6 +896,14 @@ export class EditorService extends Disposable implements EditorServiceImpl { else if (!(cachedInput instanceof ResourceEditorInput)) { cachedInput.setPreferredResource(preferredResource); + if (resourceEditorInput.label) { + cachedInput.setPreferredName(resourceEditorInput.label); + } + + if (resourceEditorInput.description) { + cachedInput.setPreferredDescription(resourceEditorInput.description); + } + if (resourceEditorInput.encoding) { cachedInput.setPreferredEncoding(resourceEditorInput.encoding); } @@ -968,19 +975,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { return input; } - private toSideBySideLabel(leftInput: EditorInput, rightInput: EditorInput): string | undefined { - - // If both editors are file inputs, we produce an optimized label - // by adding the relative path of both inputs to the label. This - // makes it easier to understand a file-based comparison. - if (this.fileEditorInputFactory.isFileEditorInput(leftInput) && this.fileEditorInputFactory.isFileEditorInput(rightInput)) { - return `${this.labelService.getUriLabel(leftInput.preferredResource, { relative: true })} ↔ ${this.labelService.getUriLabel(rightInput.preferredResource, { relative: true })}`; - } - - // Signal back that the label should be computed from within the editor - return undefined; - } - //#endregion //#region save/revert diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index 851734c7d..c54ec57ee 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -21,16 +21,6 @@ export const enum GroupDirection { RIGHT } -export function preferredSideBySideGroupDirection(configurationService: IConfigurationService): GroupDirection.DOWN | GroupDirection.RIGHT { - const openSideBySideDirection = configurationService.getValue<'right' | 'down'>('workbench.editor.openSideBySideDirection'); - - if (openSideBySideDirection === 'down') { - return GroupDirection.DOWN; - } - - return GroupDirection.RIGHT; -} - export const enum GroupOrientation { HORIZONTAL, VERTICAL @@ -595,3 +585,18 @@ export interface IEditorGroup { */ focus(): void; } + + +//#region Editor Group Helpers + +export function preferredSideBySideGroupDirection(configurationService: IConfigurationService): GroupDirection.DOWN | GroupDirection.RIGHT { + const openSideBySideDirection = configurationService.getValue<'right' | 'down'>('workbench.editor.openSideBySideDirection'); + + if (openSideBySideDirection === 'down') { + return GroupDirection.DOWN; + } + + return GroupDirection.RIGHT; +} + +//#endregion diff --git a/src/vs/workbench/services/editor/common/editorOpenWith.ts b/src/vs/workbench/services/editor/common/editorOpenWith.ts index f0e0503c3..425fa2aba 100644 --- a/src/vs/workbench/services/editor/common/editorOpenWith.ts +++ b/src/vs/workbench/services/editor/common/editorOpenWith.ts @@ -16,6 +16,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; import { extname, basename, isEqual } from 'vs/base/common/resources'; +import { Codicon } from 'vs/base/common/codicons'; /** * Id of the default editor for open with. @@ -71,7 +72,7 @@ export async function openEditorWith( description: entry.active ? nls.localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined, detail: entry.detail, buttons: resourceExt ? [{ - iconClass: 'codicon-settings-gear', + iconClass: Codicon.gear.classNames, tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) }] : undefined }; diff --git a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts index bd08a0b36..5f927c557 100644 --- a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts @@ -298,7 +298,7 @@ suite('EditorsObserver', function () { assert.equal(observer.hasEditor(input2.resource), true); assert.equal(observer.hasEditor(input3.resource), true); - storage._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); + storage.emitWillSaveState(WillSaveStateReason.SHUTDOWN); const restoredObserver = new EditorsObserver(part, storage); await part.whenRestored; @@ -350,7 +350,7 @@ suite('EditorsObserver', function () { assert.equal(observer.hasEditor(input2.resource), true); assert.equal(observer.hasEditor(input3.resource), true); - storage._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); + storage.emitWillSaveState(WillSaveStateReason.SHUTDOWN); const restoredObserver = new EditorsObserver(part, storage); await part.whenRestored; @@ -390,7 +390,7 @@ suite('EditorsObserver', function () { assert.equal(currentEditorsMRU[0].editor, input1); assert.equal(observer.hasEditor(input1.resource), true); - storage._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); + storage.emitWillSaveState(WillSaveStateReason.SHUTDOWN); const restoredObserver = new EditorsObserver(part, storage); await part.whenRestored; diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index a8d43045e..8f70949cd 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -15,6 +15,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { memoize } from 'vs/base/common/decorators'; import { onUnexpectedError } from 'vs/base/common/errors'; import { parseLineAndColumnAware } from 'vs/base/common/extpath'; +import { LogLevelToString } from 'vs/platform/log/common/log'; class BrowserWorkbenchConfiguration implements IWindowConfiguration { @@ -114,7 +115,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment @memoize get logsPath(): string { return this.options.logsPath.path; } - get logLevel(): string | undefined { return this.payload?.get('logLevel'); } + @memoize + get logLevel(): string | undefined { return this.payload?.get('logLevel') || (this.options.logLevel !== undefined ? LogLevelToString(this.options.logLevel) : undefined); } @memoize get logFile(): URI { return joinPath(this.options.logsPath, 'window.log'); } @@ -157,9 +159,6 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment @memoize get keyboardLayoutResource(): URI { return joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); } - @memoize - get backupWorkspaceHome(): URI { return joinPath(this.userRoamingDataHome, 'Backups', this.options.workspaceId); } - @memoize get untitledWorkspacesHome(): URI { return joinPath(this.userRoamingDataHome, 'Workspaces'); } diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index 54164056e..61725bf15 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -34,7 +34,6 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly sessionId: string; readonly logFile: URI; - readonly backupWorkspaceHome?: URI; readonly extHostLogsPath: URI; readonly logExtensionHostCommunication?: boolean; diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts index 576f2cc55..6746e75b0 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts @@ -10,6 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { join } from 'vs/base/common/path'; import { IProductService } from 'vs/platform/product/common/productService'; +import { IOSConfiguration } from 'vs/platform/windows/common/windows'; export class NativeWorkbenchEnvironmentService extends NativeEnvironmentService implements INativeWorkbenchEnvironmentService { @@ -30,20 +31,6 @@ export class NativeWorkbenchEnvironmentService extends NativeEnvironmentService @memoize get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.userData }); } - // Do NOT! memoize as `backupPath` can change in configuration - // via the `updateBackupPath` method below - get backupWorkspaceHome(): URI | undefined { - if (this.configuration.backupPath) { - return URI.file(this.configuration.backupPath).with({ scheme: this.userRoamingDataHome.scheme }); - } - - return undefined; - } - - updateBackupPath(newBackupPath: string | undefined): void { - this.configuration.backupPath = newBackupPath; - } - @memoize get logFile(): URI { return URI.file(join(this.logsPath, `renderer${this.configuration.windowId}.log`)); } @@ -82,6 +69,10 @@ export class NativeWorkbenchEnvironmentService extends NativeEnvironmentService return undefined; } + get os(): IOSConfiguration { + return this.configuration.os; + } + constructor( readonly configuration: INativeWorkbenchConfiguration, private readonly productService: IProductService diff --git a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts index e534169ba..4f1ef89cf 100644 --- a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchConfiguration, IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { INativeWindowConfiguration, IOSConfiguration } from 'vs/platform/windows/common/windows'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -27,8 +27,7 @@ export interface INativeWorkbenchEnvironmentService extends IWorkbenchEnvironmen readonly log?: string; - // TODO@ben this is a bit ugly - updateBackupPath(newPath: string | undefined): void; + readonly os: IOSConfiguration; /** * @deprecated this property will go away eventually as it diff --git a/src/vs/workbench/services/experiment/common/experimentService.ts b/src/vs/workbench/services/experiment/common/experimentService.ts index dc84569d6..0fa7fda89 100644 --- a/src/vs/workbench/services/experiment/common/experimentService.ts +++ b/src/vs/workbench/services/experiment/common/experimentService.ts @@ -6,18 +6,19 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import * as platform from 'vs/base/common/platform'; import type { IKeyValueStorage, IExperimentationTelemetry, IExperimentationFilterProvider, ExperimentationService as TASClient } from 'tas-client-umd'; import { MementoObject, Memento } from 'vs/workbench/common/memento'; -import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryData } from 'vs/base/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import product from 'vs/platform/product/common/product'; export const ITASExperimentService = createDecorator('TASExperimentService'); export interface ITASExperimentService { readonly _serviceBrand: undefined; getTreatment(name: string): Promise; + getCurrentExperiments(): Promise; } const storageKey = 'VSCode.ABExp.FeatureData'; @@ -37,11 +38,20 @@ class MementoKeyValueStorage implements IKeyValueStorage { } class ExperimentServiceTelemetry implements IExperimentationTelemetry { + private _lastAssignmentContext: string | undefined; constructor(private telemetryService: ITelemetryService) { } + get assignmentContext(): string[] | undefined { + return this._lastAssignmentContext?.split(';'); + } + // __GDPR__COMMON__ "VSCode.ABExp.Features" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } // __GDPR__COMMON__ "abexp.assignmentcontext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } setSharedProperty(name: string, value: string): void { + if (name === product.tasConfig?.assignmentContextTelemetryPropertyName) { + this._lastAssignmentContext = value; + } + this.telemetryService.setExperimentProperty(name, value); } @@ -165,6 +175,7 @@ enum TargetPopulation { export class ExperimentService implements ITASExperimentService { _serviceBrand: undefined; private tasClient: Promise | undefined; + private telemetry: ExperimentServiceTelemetry | undefined; private static MEMENTO_ID = 'experiment.service.memento'; private get experimentsEnabled(): boolean { @@ -172,13 +183,12 @@ export class ExperimentService implements ITASExperimentService { } constructor( - @IProductService private productService: IProductService, @ITelemetryService private telemetryService: ITelemetryService, @IStorageService private storageService: IStorageService, @IConfigurationService private configurationService: IConfigurationService, ) { - if (this.productService.tasConfig && this.experimentsEnabled && this.telemetryService.isOptedIn) { + if (product.tasConfig && this.experimentsEnabled && this.telemetryService.isOptedIn) { this.tasClient = this.setupTASClient(); } } @@ -195,26 +205,40 @@ export class ExperimentService implements ITASExperimentService { return (await this.tasClient).getTreatmentVariable('vscode', name); } + async getCurrentExperiments(): Promise { + if (!this.tasClient) { + return undefined; + } + + if (!this.experimentsEnabled) { + return undefined; + } + + await this.tasClient; + + return this.telemetry?.assignmentContext; + } + private async setupTASClient(): Promise { const telemetryInfo = await this.telemetryService.getTelemetryInfo(); - const targetPopulation = telemetryInfo.msftInternal ? TargetPopulation.Internal : (this.productService.quality === 'stable' ? TargetPopulation.Public : TargetPopulation.Insiders); + const targetPopulation = telemetryInfo.msftInternal ? TargetPopulation.Internal : (product.quality === 'stable' ? TargetPopulation.Public : TargetPopulation.Insiders); const machineId = telemetryInfo.machineId; const filterProvider = new ExperimentServiceFilterProvider( - this.productService.version, - this.productService.nameLong, + product.version, + product.nameLong, machineId, targetPopulation ); const memento = new Memento(ExperimentService.MEMENTO_ID, this.storageService); - const keyValueStorage = new MementoKeyValueStorage(memento.getMemento(StorageScope.GLOBAL)); + const keyValueStorage = new MementoKeyValueStorage(memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE)); - const telemetry = new ExperimentServiceTelemetry(this.telemetryService); + this.telemetry = new ExperimentServiceTelemetry(this.telemetryService); - const tasConfig = this.productService.tasConfig!; + const tasConfig = product.tasConfig!; const tasClient = new (await import('tas-client-umd')).ExperimentationService({ filterProviders: [filterProvider], - telemetry: telemetry, + telemetry: this.telemetry, storageKey: storageKey, keyValueStorage: keyValueStorage, featuresTelemetryPropertyName: tasConfig.featuresTelemetryPropertyName, diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts b/src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts new file mode 100644 index 000000000..7a93a5290 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts @@ -0,0 +1,337 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; +import { timeout } from 'vs/base/common/async'; + +// --- bisect service + +export const IExtensionBisectService = createDecorator('IExtensionBisectService'); + +export interface IExtensionBisectService { + + readonly _serviceBrand: undefined; + + isDisabledByBisect(extension: IExtension): boolean; + isActive: boolean; + disabledCount: number; + start(extensions: ILocalExtension[]): Promise; + next(seeingBad: boolean): Promise<{ id: string, bad: boolean } | undefined>; + reset(): Promise; +} + +class BisectState { + + static fromJSON(raw: string | undefined): BisectState | undefined { + if (!raw) { + return undefined; + } + try { + interface Raw extends BisectState { } + const data: Raw = JSON.parse(raw); + return new BisectState(data.extensions, data.low, data.high, data.mid); + } catch { + return undefined; + } + } + + constructor( + readonly extensions: string[], + readonly low: number, + readonly high: number, + readonly mid: number = ((low + high) / 2) | 0 + ) { } +} + +class ExtensionBisectService implements IExtensionBisectService { + + declare readonly _serviceBrand: undefined; + + private static readonly _storageKey = 'extensionBisectState'; + + private readonly _state: BisectState | undefined; + private readonly _disabled = new Map(); + + constructor( + @ILogService logService: ILogService, + @IStorageService private readonly _storageService: IStorageService, + ) { + const raw = _storageService.get(ExtensionBisectService._storageKey, StorageScope.GLOBAL); + this._state = BisectState.fromJSON(raw); + + if (this._state) { + const { mid, high } = this._state; + for (let i = 0; i < this._state.extensions.length; i++) { + const isDisabled = i >= mid && i < high; + this._disabled.set(this._state.extensions[i], isDisabled); + } + logService.warn('extension BISECT active', [...this._disabled]); + } + } + + get isActive() { + return !!this._state; + } + + get disabledCount() { + return this._state ? this._state.high - this._state.mid : -1; + } + + isDisabledByBisect(extension: IExtension): boolean { + if (!this._state) { + return false; + } + const disabled = this._disabled.get(extension.identifier.id); + return disabled ?? false; + } + + async start(extensions: ILocalExtension[]): Promise { + if (this._state) { + throw new Error('invalid state'); + } + const extensionIds = extensions.map(ext => ext.identifier.id); + const newState = new BisectState(extensionIds, 0, extensionIds.length, 0); + this._storageService.store(ExtensionBisectService._storageKey, JSON.stringify(newState), StorageScope.GLOBAL, StorageTarget.MACHINE); + await this._storageService.flush(); + } + + async next(seeingBad: boolean): Promise<{ id: string; bad: boolean; } | undefined> { + if (!this._state) { + throw new Error('invalid state'); + } + // check if bad when all extensions are disabled + if (seeingBad && this._state.mid === 0 && this._state.high === this._state.extensions.length) { + return { bad: true, id: '' }; + } + // check if there is only one left + if (this._state.low === this._state.high - 1) { + await this.reset(); + return { id: this._state.extensions[this._state.low], bad: seeingBad }; + } + // the second half is disabled so if there is still bad it must be + // in the first half + const nextState = new BisectState( + this._state.extensions, + seeingBad ? this._state.low : this._state.mid, + seeingBad ? this._state.mid : this._state.high, + ); + this._storageService.store(ExtensionBisectService._storageKey, JSON.stringify(nextState), StorageScope.GLOBAL, StorageTarget.MACHINE); + await this._storageService.flush(); + return undefined; + } + + async reset(): Promise { + this._storageService.remove(ExtensionBisectService._storageKey, StorageScope.GLOBAL); + await this._storageService.flush(); + } +} + +registerSingleton(IExtensionBisectService, ExtensionBisectService, true); + +// --- bisect UI + +class ExtensionBisectUi { + + static ctxIsBisectActive = new RawContextKey('isExtensionBisectActive', false); + + constructor( + @IContextKeyService contextKeyService: IContextKeyService, + @IExtensionBisectService private readonly _extensionBisectService: IExtensionBisectService, + @INotificationService private readonly _notificationService: INotificationService, + @ICommandService private readonly _commandService: ICommandService, + ) { + if (_extensionBisectService.isActive) { + ExtensionBisectUi.ctxIsBisectActive.bindTo(contextKeyService).set(true); + this._showBisectPrompt(); + } + } + + private _showBisectPrompt(): void { + + const goodPrompt: IPromptChoice = { + label: 'Good now', + run: () => this._commandService.executeCommand('extension.bisect.next', false) + }; + const badPrompt: IPromptChoice = { + label: 'This is bad', + run: () => this._commandService.executeCommand('extension.bisect.next', true) + }; + const stop: IPromptChoice = { + label: 'Stop Bisect', + run: () => this._commandService.executeCommand('extension.bisect.stop') + }; + + this._notificationService.prompt( + Severity.Info, + localize('bisect', "Extension Bisect is active and has disabled {0} extensions. Check if you can still reproduce the problem and proceed by selecting from these options.", this._extensionBisectService.disabledCount), + [goodPrompt, badPrompt, stop], + { sticky: true } + ); + } +} + +Registry.as(Extensions.Workbench).registerWorkbenchContribution( + ExtensionBisectUi, + LifecyclePhase.Restored +); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'extension.bisect.start', + title: localize('title.start', "Start Extension Bisect"), + category: localize('help', "Help"), + f1: true, + precondition: ExtensionBisectUi.ctxIsBisectActive.negate() + }); + } + + async run(accessor: ServicesAccessor): Promise { + const dialogService = accessor.get(IDialogService); + const hostService = accessor.get(IHostService); + const extensionManagement = accessor.get(IExtensionManagementService); + const extensionEnablementService = accessor.get(IGlobalExtensionEnablementService); + const extensionsBisect = accessor.get(IExtensionBisectService); + + const disabled = new Set(extensionEnablementService.getDisabledExtensions().map(id => id.id)); + const extensions = (await extensionManagement.getInstalled(ExtensionType.User)).filter(ext => !disabled.has(ext.identifier.id)); + + const res = await dialogService.confirm({ + message: localize('msg.start', "Extension Bisect"), + detail: localize('detail.start', "Extension Bisect will use binary search to find an extension that causes a problem. During the process the window reloads repeatedly (~{0} times). Each time you must confirm if you are still seeing problems.", 2 + Math.log2(extensions.length) | 0), + primaryButton: localize('msg2', "Start Extension Bisect") + }); + + if (res.confirmed) { + await extensionsBisect.start(extensions); + hostService.reload(); + } + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'extension.bisect.next', + title: localize('title.isBad', "Continue Extension Bisect"), + category: localize('help', "Help"), + f1: true, + precondition: ExtensionBisectUi.ctxIsBisectActive + }); + } + + async run(accessor: ServicesAccessor, seeingBad: boolean | undefined): Promise { + const dialogService = accessor.get(IDialogService); + const hostService = accessor.get(IHostService); + const bisectService = accessor.get(IExtensionBisectService); + const productService = accessor.get(IProductService); + const extensionEnablementService = accessor.get(IGlobalExtensionEnablementService); + const issueService = accessor.get(IWorkbenchIssueService); + + if (!bisectService.isActive) { + return; + } + if (seeingBad === undefined) { + const goodBadStopCancel = await this._checkForBad(dialogService, bisectService); + if (goodBadStopCancel === null) { + return; + } + seeingBad = goodBadStopCancel; + } + if (seeingBad === undefined) { + await bisectService.reset(); + hostService.reload(); + return; + } + const done = await bisectService.next(seeingBad); + if (!done) { + hostService.reload(); + return; + } + + if (done.bad) { + // DONE but nothing found + await dialogService.show(Severity.Info, localize('done.msg', "Extension Bisect"), [], { + detail: localize('done.detail2', "Extension Bisect is done but no extension has been identified. This might be a problem with {0}.", productService.nameShort) + }); + + } else { + // DONE and identified extension + const res = await dialogService.show(Severity.Info, localize('done.msg', "Extension Bisect"), + [localize('report', "Report Issue & Continue"), localize('done', "Continue")], + // [], + { + detail: localize('done.detail', "Extension Bisect is done and has identified {0} as the extension causing the problem.", done.id), + checkbox: { label: localize('done.disbale', "Keep this extension disabled"), checked: true }, + cancelId: 1 + } + ); + if (res.checkboxChecked) { + await extensionEnablementService.disableExtension({ id: done.id }, undefined); + } + if (res.choice === 0) { + issueService.openReporter({ extensionId: done.id }); + await timeout(750); // workaround for https://github.com/microsoft/vscode/issues/111871 + } + } + await bisectService.reset(); + hostService.reload(); + } + + private async _checkForBad(dialogService: IDialogService, bisectService: IExtensionBisectService): Promise { + const options = { + cancelId: 3, + detail: localize('bisect', "Extension Bisect is active and has disabled {0} extensions. Check if you can still reproduce the problem and proceed by selecting from these options.", bisectService.disabledCount), + }; + const res = await dialogService.show( + Severity.Info, + localize('msg.next', "Extension Bisect"), + [localize('next.good', "Good now"), localize('next.bad', "This is bad"), localize('next.stop', "Stop Bisect"), localize('next.cancel', "Cancel")], + options + ); + switch (res.choice) { + case 0: return false; //good now + case 1: return true; //bad + case 2: return undefined; //stop + } + return null; //cancel + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'extension.bisect.stop', + title: localize('title.stop', "Stop Extension Bisect"), + category: localize('help', "Help"), + f1: true, + precondition: ExtensionBisectUi.ctxIsBisectActive + }); + } + + async run(accessor: ServicesAccessor): Promise { + const extensionsBisect = accessor.get(IExtensionBisectService); + const hostService = accessor.get(IHostService); + await extensionsBisect.reset(); + hostService.reload(); + } +}); diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index 50d4d812b..94d47c9cd 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -24,6 +24,7 @@ import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/com import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect'; const SOURCE = 'IWorkbenchExtensionEnablementService'; @@ -50,6 +51,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench @ILifecycleService private readonly lifecycleService: ILifecycleService, @INotificationService private readonly notificationService: INotificationService, @IHostService hostService: IHostService, + @IExtensionBisectService private readonly extensionBisectService: IExtensionBisectService, ) { super(); this.storageManger = this._register(new StorageManager(storageService)); @@ -59,9 +61,9 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench // delay notification for extensions disabled until workbench restored if (this.allUserExtensionsDisabled) { this.lifecycleService.when(LifecyclePhase.Restored).then(() => { - this.notificationService.prompt(Severity.Info, localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{ - label: localize('Reload', "Reload"), - run: () => hostService.reload() + this.notificationService.prompt(Severity.Info, localize('extensionsDisabled', "All installed extensions are temporarily disabled."), [{ + label: localize('Reload', "Reload and Enable Extensions"), + run: () => hostService.reload({ disableExtensions: false }) }]); }); } @@ -76,6 +78,9 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench } getEnablementState(extension: IExtension): EnablementState { + if (this.extensionBisectService.isDisabledByBisect(extension)) { + return EnablementState.DisabledByEnvironemt; + } if (this._isDisabledInEnv(extension)) { return EnablementState.DisabledByEnvironemt; } @@ -210,14 +215,16 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench } } if (extensionKind === 'web') { - const enableLocalWebWorker = this.configurationService.getValue(webWorkerExtHostConfig); - if (enableLocalWebWorker) { - // Web extensions are enabled on all configurations - return false; - } - if (this.extensionManagementServerService.localExtensionManagementServer === null) { - // Web extensions run only in the web - return false; + if (this.extensionManagementServerService.webExtensionManagementServer) { + if (server === this.extensionManagementServerService.webExtensionManagementServer) { + return false; + } + } else if (server === this.extensionManagementServerService.localExtensionManagementServer) { + const enableLocalWebWorker = this.configurationService.getValue(webWorkerExtHostConfig); + if (enableLocalWebWorker) { + // Web extensions are enabled on all configurations + return false; + } } } } diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts new file mode 100644 index 000000000..7ade5f59a --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +class ExtensionUrlTrustService implements IExtensionUrlTrustService { + + declare readonly _serviceBrand: undefined; + + async isExtensionUrlTrusted(): Promise { + return false; + } +} + +registerSingleton(IExtensionUrlTrustService, ExtensionUrlTrustService); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index e51b22179..380898ba5 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IExtension, IScannedExtension, ExtensionType, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions'; +import { IExtension, IScannedExtension, ExtensionType, ITranslatedScannedExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier, ILocalExtension, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; @@ -29,6 +29,7 @@ export interface IWorkbenchExtensioManagementService extends IExtensionManagemen readonly _serviceBrand: undefined; installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise; updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension): Promise; + getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null } export const enum EnablementState { @@ -96,7 +97,7 @@ export interface IWebExtensionsScannerService { scanExtensions(type?: ExtensionType): Promise; scanAndTranslateExtensions(type?: ExtensionType): Promise; scanAndTranslateSingleExtension(extensionLocation: URI, extensionType: ExtensionType): Promise; - canAddExtension(galleryExtension: IGalleryExtension): Promise; + canAddExtension(galleryExtension: IGalleryExtension): boolean; addExtension(galleryExtension: IGalleryExtension): Promise; removeExtension(identifier: IExtensionIdentifier, version?: string): Promise; } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts index 410c10e04..bd730af53 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts @@ -43,12 +43,13 @@ export class ExtensionManagementServerService implements IExtensionManagementSer extensionManagementService, get label() { return labelService.getHostLabel(Schemas.vscodeRemote, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); } }; - } else if (isWeb) { + } + if (isWeb) { const extensionManagementService = instantiationService.createInstance(WebExtensionManagementService); this.webExtensionManagementServer = { id: 'web', extensionManagementService, - label: localize('web', "Web") + label: localize('browser', "Browser") }; } } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index de7e301d3..b1781e08b 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -5,7 +5,7 @@ import { Event, EventMultiplexer } from 'vs/base/common/event'; import { - ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions + ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServer, IExtensionManagementServerService, IWorkbenchExtensioManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionType, isLanguagePackExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; @@ -15,7 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { CancellationToken } from 'vs/base/common/cancellation'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localize } from 'vs/nls'; -import { prefersExecuteOnUI, canExecuteOnWorkspace, prefersExecuteOnWorkspace, canExecuteOnUI, prefersExecuteOnWeb, canExecuteOnWeb } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { prefersExecuteOnUI, getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { IDownloadService } from 'vs/platform/download/common/download'; @@ -23,7 +23,6 @@ import { flatten } from 'vs/base/common/arrays'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { canceled } from 'vs/base/common/errors'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; export class ExtensionManagementService extends Disposable implements IWorkbenchExtensioManagementService { @@ -45,7 +44,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench @IDownloadService protected readonly downloadService: IDownloadService, @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IDialogService private readonly dialogService: IDialogService, ) { super(); if (this.extensionManagementServerService.localExtensionManagementServer) { @@ -69,7 +68,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return flatten(result); } - async uninstall(extension: ILocalExtension): Promise { + async uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { const server = this.getServer(extension); if (!server) { return Promise.reject(`Invalid location ${extension.location.toString()}`); @@ -78,7 +77,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench if (isLanguagePackExtension(extension.manifest)) { return this.uninstallEverywhere(extension); } - return this.uninstallInServer(extension, server); + return this.uninstallInServer(extension, server, options); } return server.extensionManagementService.uninstall(extension); } @@ -102,7 +101,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return promise; } - private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, force?: boolean): Promise { + private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, options?: UninstallOptions): Promise { if (server === this.extensionManagementServerService.localExtensionManagementServer) { const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getInstalled(ExtensionType.User); const dependentNonUIExtensions = installedExtensions.filter(i => !prefersExecuteOnUI(i.manifest, this.productService, this.configurationService) @@ -111,7 +110,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return Promise.reject(new Error(this.getDependentsErrorMessage(extension, dependentNonUIExtensions))); } } - return server.extensionManagementService.uninstall(extension, force); + return server.extensionManagementService.uninstall(extension, options); } private getDependentsErrorMessage(extension: ILocalExtension, dependents: ILocalExtension[]): string { @@ -283,75 +282,56 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return Promise.reject(error); } - private getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | undefined { + getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null { // Only local server if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) { return this.extensionManagementServerService.localExtensionManagementServer; } - // 1. Install on preferred location - - // Install UI preferred extension on local server - if (prefersExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer; - } - // Install Workspace preferred extension on remote server - if (prefersExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { - return this.extensionManagementServerService.remoteExtensionManagementServer; - } - // Install Web preferred extension on web server - if (prefersExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) { - return this.extensionManagementServerService.webExtensionManagementServer; + const extensionKind = getExtensionKind(manifest, this.productService, this.configurationService); + for (const kind of extensionKind) { + if (kind === 'ui' && this.extensionManagementServerService.localExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer; + } + if (kind === 'workspace' && this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.extensionManagementServerService.remoteExtensionManagementServer; + } + if (kind === 'web' && this.extensionManagementServerService.webExtensionManagementServer) { + return this.extensionManagementServerService.webExtensionManagementServer; + } } - // 2. Install on supported location - - // Install UI supported extension on local server - if (canExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer; - } - // Install Workspace supported extension on remote server - if (canExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { - return this.extensionManagementServerService.remoteExtensionManagementServer; - } - // Install Web supported extension on web server - if (canExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) { - return this.extensionManagementServerService.webExtensionManagementServer; - } - - return undefined; + // Local server can accept any extension. So return local server if not compatible server found. + return this.extensionManagementServerService.localExtensionManagementServer; } private async hasToFlagExtensionsMachineScoped(extensions: IGalleryExtension[]): Promise { if (!this.userDataAutoSyncEnablementService.isEnabled() || !this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions)) { return false; } - return this.instantiationService.invokeFunction(async accessor => { - const dialogService = accessor.get(IDialogService); - const result = await dialogService.show( - Severity.Info, - extensions.length === 1 ? localize('install extension', "Install Extension") : localize('install extensions', "Install Extensions"), - [ - localize('install', "Install"), - localize('install and do no sync', "Install (Do not sync)"), - localize('cancel', "Cancel"), - ], - { - cancelId: 2, - detail: extensions.length === 1 - ? localize('install single extension', "Would you like to install and synchronize '{0}' extension across your devices?", extensions[0].displayName) - : localize('install multiple extensions', "Would you like to install and synchronize extensions across your devices?") - } - ); - switch (result.choice) { - case 0: - return false; - case 1: - return true; + const result = await this.dialogService.show( + Severity.Info, + extensions.length === 1 ? localize('install extension', "Install Extension") : localize('install extensions', "Install Extensions"), + [ + localize('install', "Install"), + localize('install and do no sync', "Install (Do not sync)"), + localize('cancel', "Cancel"), + ], + { + cancelId: 2, + detail: extensions.length === 1 + ? localize('install single extension', "Would you like to install and synchronize '{0}' extension across your devices?", extensions[0].displayName) + : localize('install multiple extensions', "Would you like to install and synchronize extensions across your devices?") } - throw canceled(); - }); + ); + switch (result.choice) { + case 0: + return false; + case 1: + return true; + } + throw canceled(); } getExtensionsReport(): Promise { diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts index 1bf8114d3..f0cc1bc86 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts @@ -25,6 +25,7 @@ import { Event } from 'vs/base/common/event'; import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; import { localize } from 'vs/nls'; import * as semver from 'vs/base/common/semver/semver'; +import { isArray } from 'vs/base/common/types'; interface IUserExtension { identifier: IExtensionIdentifier; @@ -125,25 +126,31 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten private async readDefaultUserWebExtensions(): Promise { const result: IStaticExtension[] = []; - const defaultUserWebExtensions = this.configurationService.getValue<{ location: string }[]>('_extensions.defaultUserWebExtensions') || []; - for (const webExtension of defaultUserWebExtensions) { - const extensionLocation = URI.parse(webExtension.location); - const manifestLocation = joinPath(extensionLocation, 'package.json'); - const context = await this.requestService.request({ type: 'GET', url: manifestLocation.toString(true) }, CancellationToken.None); - if (!isSuccess(context)) { - this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', manifestLocation); - continue; + const defaultUserWebExtensions = this.configurationService.getValue<{ location: string }[]>('_extensions.defaultUserWebExtensions'); + if (isArray(defaultUserWebExtensions)) { + for (const webExtension of defaultUserWebExtensions) { + try { + const extensionLocation = URI.parse(webExtension.location); + const manifestLocation = joinPath(extensionLocation, 'package.json'); + const context = await this.requestService.request({ type: 'GET', url: manifestLocation.toString(true) }, CancellationToken.None); + if (!isSuccess(context)) { + this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', manifestLocation); + continue; + } + const content = await asText(context); + if (!content) { + this.logService.warn('Skipped default user web extension as there is manifest is not found', manifestLocation); + continue; + } + const packageJSON = JSON.parse(content); + result.push({ + packageJSON, + extensionLocation, + }); + } catch (error) { + this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', webExtension); + } } - const content = await asText(context); - if (!content) { - this.logService.warn('Skipped default user web extension as there is manifest is not found', manifestLocation); - continue; - } - const packageJSON = JSON.parse(content); - result.push({ - packageJSON, - extensionLocation, - }); } return result; } @@ -231,12 +238,12 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten }; } - async canAddExtension(galleryExtension: IGalleryExtension): Promise { + canAddExtension(galleryExtension: IGalleryExtension): boolean { return !!galleryExtension.properties.webExtension && !!galleryExtension.webResource; } async addExtension(galleryExtension: IGalleryExtension): Promise { - if (!(await this.canAddExtension(galleryExtension))) { + if (!this.canAddExtension(galleryExtension)) { const error = new Error(localize('cannot be installed', "Cannot install '{0}' because this extension is not a web extension.", galleryExtension.displayName || galleryExtension.name)); error.name = INSTALL_ERROR_NOT_SUPPORTED; throw error; diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts index a58a136ac..e8a05aff2 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts @@ -15,8 +15,8 @@ import { IDownloadService } from 'vs/platform/download/common/download'; import { IProductService } from 'vs/platform/product/common/productService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { joinPath } from 'vs/base/common/resources'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; export class ExtensionManagementService extends BaseExtensionManagementService { @@ -29,9 +29,9 @@ export class ExtensionManagementService extends BaseExtensionManagementService { @IDownloadService downloadService: IDownloadService, @IUserDataAutoSyncEnablementService userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, - @IInstantiationService instantiationService: IInstantiationService, + @IDialogService dialogService: IDialogService, ) { - super(extensionManagementServerService, extensionGalleryService, configurationService, productService, downloadService, userDataAutoSyncEnablementService, userDataSyncResourceEnablementService, instantiationService); + super(extensionManagementServerService, extensionGalleryService, configurationService, productService, downloadService, userDataAutoSyncEnablementService, userDataSyncResourceEnablementService, dialogService); } protected async installVSIX(vsix: URI, server: IExtensionManagementServer): Promise { diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionUrlTrustService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionUrlTrustService.ts new file mode 100644 index 000000000..3cabb4951 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionUrlTrustService.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; + +class ExtensionUrlTrustService { + + declare readonly _serviceBrand: undefined; + + constructor(@IMainProcessService mainProcessService: IMainProcessService) { + return createChannelSender(mainProcessService.getChannel('extensionUrlTrust')); + } +} + +registerSingleton(IExtensionUrlTrustService, ExtensionUrlTrustService); diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index fe48d4f14..7259d0698 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -28,6 +28,8 @@ import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecy import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { mock } from 'vs/base/test/common/mock'; +import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect'; function createStorageService(instantiationService: TestInstantiationService): IStorageService { let service = instantiationService.get(IStorageService); @@ -61,7 +63,8 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.get(IUserDataSyncAccountService) || instantiationService.stub(IUserDataSyncAccountService, UserDataSyncAccountService), instantiationService.get(ILifecycleService) || instantiationService.stub(ILifecycleService, new TestLifecycleService()), instantiationService.get(INotificationService) || instantiationService.stub(INotificationService, new TestNotificationService()), - instantiationService.get(IHostService) + instantiationService.get(IHostService), + new class extends mock() { isDisabledByBisect() { return false; } } ); } @@ -542,36 +545,48 @@ suite('ExtensionEnablementService Test', () => { assert.equal(testObject.canChangeEnablement(localWorkspaceExtension), true); }); - test('test web extension on local server is disabled by kind', async () => { + test('test web extension on local server is disabled by kind when web worker is not enabled', async () => { instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService)); const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`) }); + (instantiationService.get(IConfigurationService)).setUserConfiguration('extensions', { webWorker: false }); testObject = new TestExtensionEnablementService(instantiationService); - assert.ok(!testObject.isEnabled(localWorkspaceExtension)); + assert.equal(testObject.isEnabled(localWorkspaceExtension), false); assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.DisabledByExtensionKind); }); - test('test web extension on remote server is not disabled by kind when there is no local server', async () => { - instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); - const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + test('test web extension on local server is not disabled by kind when web worker is enabled', async () => { + instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService)); + const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`) }); + (instantiationService.get(IConfigurationService)).setUserConfiguration('extensions', { webWorker: true }); testObject = new TestExtensionEnablementService(instantiationService); - assert.ok(testObject.isEnabled(localWorkspaceExtension)); + assert.equal(testObject.isEnabled(localWorkspaceExtension), true); assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally); }); - test('test web extension with no server is not disabled by kind when there is no local server', async () => { - instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); - const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.https }) }); + test('test web extension on remote server is disabled by kind when web worker is not enabled', async () => { + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(anExtensionManagementServer('vscode-local', instantiationService), anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); + const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: 'vscode-remote' }) }); + (instantiationService.get(IConfigurationService)).setUserConfiguration('extensions', { webWorker: false }); testObject = new TestExtensionEnablementService(instantiationService); - assert.ok(testObject.isEnabled(localWorkspaceExtension)); - assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally); + assert.equal(testObject.isEnabled(localWorkspaceExtension), false); + assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.DisabledByExtensionKind); }); - test('test web extension with no server is not disabled by kind when there is no local and remote server', async () => { - instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, null, anExtensionManagementServer('web', instantiationService))); - const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.https }) }); + test('test web extension on remote server is disabled by kind when web worker is enabled', async () => { + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(anExtensionManagementServer('vscode-local', instantiationService), anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); + const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: 'vscode-remote' }) }); + (instantiationService.get(IConfigurationService)).setUserConfiguration('extensions', { webWorker: true }); testObject = new TestExtensionEnablementService(instantiationService); - assert.ok(testObject.isEnabled(localWorkspaceExtension)); - assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally); + assert.equal(testObject.isEnabled(localWorkspaceExtension), false); + assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.DisabledByExtensionKind); + }); + + test('test web extension on web server is not disabled by kind', async () => { + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(anExtensionManagementServer('vscode-local', instantiationService), anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); + const webExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: 'web' }) }); + testObject = new TestExtensionEnablementService(instantiationService); + assert.equal(testObject.isEnabled(webExtension), true); + assert.deepEqual(testObject.getEnablementState(webExtension), EnablementState.EnabledGlobally); }); }); @@ -595,7 +610,7 @@ function anExtensionManagementServerService(localExtensionManagementServer: IExt _serviceBrand: undefined, localExtensionManagementServer, remoteExtensionManagementServer, - webExtensionManagementServer: null, + webExtensionManagementServer, getExtensionManagementServer: (extension: IExtension) => { if (extension.location.scheme === Schemas.file) { return localExtensionManagementServer; diff --git a/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts b/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts index ce9782b3a..d2a6f0012 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts @@ -7,8 +7,7 @@ import { distinct } from 'vs/base/common/arrays'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionIgnoredRecommendationsService, IgnoredRecommendationChangeNotification } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; @@ -35,12 +34,10 @@ export class ExtensionIgnoredRecommendationsService extends Disposable implement constructor( @IWorkpsaceExtensionsConfigService private readonly workpsaceExtensionsConfigService: IWorkpsaceExtensionsConfigService, @IStorageService private readonly storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { super(); - storageKeysSyncRegistryService.registerStorageKey({ key: ignoredRecommendationsStorageKey, version: 1 }); this._globalIgnoredRecommendations = this.getCachedIgnoredRecommendations(); - this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + this._register(this.storageService.onDidChangeValue(e => this.onDidStorageChange(e))); this.initIgnoredWorkspaceRecommendations(); } @@ -72,7 +69,7 @@ export class ExtensionIgnoredRecommendationsService extends Disposable implement return ignoredRecommendations.map(e => e.toLowerCase()); } - private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { + private onDidStorageChange(e: IStorageValueChangeEvent): void { if (e.key === ignoredRecommendationsStorageKey && e.scope === StorageScope.GLOBAL && this.ignoredRecommendationsValue !== this.getStoredIgnoredRecommendationsValue() /* This checks if current window changed the value or not */) { this._ignoredRecommendationsValue = undefined; @@ -106,7 +103,7 @@ export class ExtensionIgnoredRecommendationsService extends Disposable implement } private setStoredIgnoredRecommendationsValue(value: string): void { - this.storageService.store(ignoredRecommendationsStorageKey, value, StorageScope.GLOBAL); + this.storageService.store(ignoredRecommendationsStorageKey, value, StorageScope.GLOBAL, StorageTarget.USER); } } diff --git a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts index 21fb2dcf2..0d76094d0 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts @@ -7,11 +7,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IStringDictionary } from 'vs/base/common/collections'; import { Event } from 'vs/base/common/event'; -export interface IExtensionsConfigContent { - recommendations: string[]; - unwantedRecommendations: string[]; -} - export type DynamicRecommendation = 'dynamic'; export type ConfigRecommendation = 'config'; export type ExecutableRecommendation = 'executable'; diff --git a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts index dfcdaf501..dc16b3575 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts @@ -3,20 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesce, distinct, flatten } from 'vs/base/common/arrays'; +import { distinct, flatten } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; import { parse } from 'vs/base/common/json'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IFileService } from 'vs/platform/files/common/files'; +import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; +import { FileKind, IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { localize } from 'vs/nls'; +import { URI } from 'vs/base/common/uri'; +import { IJSONEditingService, IJSONValue } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { ResourceMap } from 'vs/base/common/map'; export const EXTENSIONS_CONFIG = '.vscode/extensions.json'; export interface IExtensionsConfigContent { - recommendations: string[]; - unwantedRecommendations: string[]; + recommendations?: string[]; + unwantedRecommendations?: string[]; } export const IWorkpsaceExtensionsConfigService = createDecorator('IWorkpsaceExtensionsConfigService'); @@ -26,8 +34,11 @@ export interface IWorkpsaceExtensionsConfigService { onDidChangeExtensionsConfigs: Event; getExtensionsConfigs(): Promise; + getRecommendations(): Promise; getUnwantedRecommendations(): Promise; + toggleRecommendation(extensionId: string): Promise; + toggleUnwantedRecommendation(extensionId: string): Promise; } export class WorkspaceExtensionsConfigService extends Disposable implements IWorkpsaceExtensionsConfigService { @@ -40,53 +51,217 @@ export class WorkspaceExtensionsConfigService extends Disposable implements IWor constructor( @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IFileService private readonly fileService: IFileService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IModelService private readonly modelService: IModelService, + @IModeService private readonly modeService: IModeService, + @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, ) { super(); - this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(e => this._onDidChangeExtensionsConfigs.fire())); + this._register(workspaceContextService.onDidChangeWorkspaceFolders(e => this._onDidChangeExtensionsConfigs.fire())); + this._register(fileService.onDidFilesChange(e => { + const workspace = workspaceContextService.getWorkspace(); + if ((workspace.configuration && e.affects(workspace.configuration)) + || workspace.folders.some(folder => e.affects(folder.toResource(EXTENSIONS_CONFIG))) + ) { + this._onDidChangeExtensionsConfigs.fire(); + } + })); } async getExtensionsConfigs(): Promise { const workspace = this.workspaceContextService.getWorkspace(); - const result = await Promise.all([ - this.resolveWorkspaceExtensionConfig(workspace), - ...workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderExtensionConfig(workspaceFolder)) - ]); - return coalesce(result); + const result: IExtensionsConfigContent[] = []; + const workspaceExtensionsConfigContent = workspace.configuration ? await this.resolveWorkspaceExtensionConfig(workspace.configuration) : undefined; + if (workspaceExtensionsConfigContent) { + result.push(workspaceExtensionsConfigContent); + } + result.push(...await Promise.all(workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderExtensionConfig(workspaceFolder)))); + return result; + } + + async getRecommendations(): Promise { + const configs = await this.getExtensionsConfigs(); + return distinct(flatten(configs.map(c => c.recommendations ? c.recommendations.map(c => c.toLowerCase()) : []))); } async getUnwantedRecommendations(): Promise { const configs = await this.getExtensionsConfigs(); - return distinct(flatten(configs.map(c => c.unwantedRecommendations.map(c => c.toLowerCase())))); + return distinct(flatten(configs.map(c => c.unwantedRecommendations ? c.unwantedRecommendations.map(c => c.toLowerCase()) : []))); } - private async resolveWorkspaceExtensionConfig(workspace: IWorkspace): Promise { - try { - if (workspace.configuration) { - const content = await this.fileService.readFile(workspace.configuration); - const extensionsConfigContent = parse(content.value.toString())['extensions']; - return this.parseExtensionConfig(extensionsConfigContent); + async toggleRecommendation(extensionId: string): Promise { + const workspace = this.workspaceContextService.getWorkspace(); + const workspaceExtensionsConfigContent = workspace.configuration ? await this.resolveWorkspaceExtensionConfig(workspace.configuration) : undefined; + const workspaceFolderExtensionsConfigContents = new ResourceMap(); + await Promise.all(workspace.folders.map(async workspaceFolder => { + const extensionsConfigContent = await this.resolveWorkspaceFolderExtensionConfig(workspaceFolder); + workspaceFolderExtensionsConfigContents.set(workspaceFolder.uri, extensionsConfigContent); + })); + + const isWorkspaceRecommended = workspaceExtensionsConfigContent && workspaceExtensionsConfigContent.recommendations?.some(r => r === extensionId); + const recommendedWorksapceFolders = workspace.folders.filter(workspaceFolder => workspaceFolderExtensionsConfigContents.get(workspaceFolder.uri)?.recommendations?.some(r => r === extensionId)); + const isRecommended = isWorkspaceRecommended || recommendedWorksapceFolders.length > 0; + + const workspaceOrFolders = isRecommended + ? await this.pickWorkspaceOrFolders(recommendedWorksapceFolders, isWorkspaceRecommended ? workspace : undefined, localize('select for remove', "Remove extension recommendation from")) + : await this.pickWorkspaceOrFolders(workspace.folders, workspace.configuration ? workspace : undefined, localize('select for add', "Add extension recommendation to")); + + for (const workspaceOrWorkspaceFolder of workspaceOrFolders) { + if (IWorkspace.isIWorkspace(workspaceOrWorkspaceFolder)) { + await this.addOrRemoveWorkspaceRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceExtensionsConfigContent, !isRecommended); + } else { + await this.addOrRemoveWorkspaceFolderRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceFolderExtensionsConfigContents.get(workspaceOrWorkspaceFolder.uri)!, !isRecommended); } - } catch (e) { /* Ignore */ } - return null; + } } - private async resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise { + async toggleUnwantedRecommendation(extensionId: string): Promise { + const workspace = this.workspaceContextService.getWorkspace(); + const workspaceExtensionsConfigContent = workspace.configuration ? await this.resolveWorkspaceExtensionConfig(workspace.configuration) : undefined; + const workspaceFolderExtensionsConfigContents = new ResourceMap(); + await Promise.all(workspace.folders.map(async workspaceFolder => { + const extensionsConfigContent = await this.resolveWorkspaceFolderExtensionConfig(workspaceFolder); + workspaceFolderExtensionsConfigContents.set(workspaceFolder.uri, extensionsConfigContent); + })); + + const isWorkspaceUnwanted = workspaceExtensionsConfigContent && workspaceExtensionsConfigContent.unwantedRecommendations?.some(r => r === extensionId); + const unWantedWorksapceFolders = workspace.folders.filter(workspaceFolder => workspaceFolderExtensionsConfigContents.get(workspaceFolder.uri)?.unwantedRecommendations?.some(r => r === extensionId)); + const isUnwanted = isWorkspaceUnwanted || unWantedWorksapceFolders.length > 0; + + const workspaceOrFolders = isUnwanted + ? await this.pickWorkspaceOrFolders(unWantedWorksapceFolders, isWorkspaceUnwanted ? workspace : undefined, localize('select for remove', "Remove extension recommendation from")) + : await this.pickWorkspaceOrFolders(workspace.folders, workspace.configuration ? workspace : undefined, localize('select for add', "Add extension recommendation to")); + + for (const workspaceOrWorkspaceFolder of workspaceOrFolders) { + if (IWorkspace.isIWorkspace(workspaceOrWorkspaceFolder)) { + await this.addOrRemoveWorkspaceUnwantedRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceExtensionsConfigContent, !isUnwanted); + } else { + await this.addOrRemoveWorkspaceFolderUnwantedRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceFolderExtensionsConfigContents.get(workspaceOrWorkspaceFolder.uri)!, !isUnwanted); + } + } + } + + private async addOrRemoveWorkspaceFolderRecommendation(extensionId: string, workspaceFolder: IWorkspaceFolder, extensionsConfigContent: IExtensionsConfigContent, add: boolean): Promise { + const values: IJSONValue[] = []; + if (add) { + values.push({ path: ['recommendations'], value: [...extensionsConfigContent.recommendations || [], extensionId] }); + if (extensionsConfigContent.unwantedRecommendations && extensionsConfigContent.unwantedRecommendations.some(e => e === extensionId)) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.recommendations) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + + if (values.length) { + return this.jsonEditingService.write(workspaceFolder.toResource(EXTENSIONS_CONFIG), values, true); + } + } + + private async addOrRemoveWorkspaceRecommendation(extensionId: string, workspace: IWorkspace, extensionsConfigContent: IExtensionsConfigContent | undefined, add: boolean): Promise { + const values: IJSONValue[] = []; + if (extensionsConfigContent) { + if (add) { + values.push({ path: ['recommendations'], value: [...extensionsConfigContent.recommendations || [], extensionId] }); + if (extensionsConfigContent.unwantedRecommendations && extensionsConfigContent.unwantedRecommendations.some(e => e === extensionId)) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.recommendations) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + } else if (add) { + values.push({ path: ['extensions'], value: { recommendations: [extensionId] } }); + } + + if (values.length) { + return this.jsonEditingService.write(workspace.configuration!, values, true); + } + } + + private async addOrRemoveWorkspaceFolderUnwantedRecommendation(extensionId: string, workspaceFolder: IWorkspaceFolder, extensionsConfigContent: IExtensionsConfigContent, add: boolean): Promise { + const values: IJSONValue[] = []; + if (add) { + values.push({ path: ['unwantedRecommendations'], value: [...extensionsConfigContent.unwantedRecommendations || [], extensionId] }); + if (extensionsConfigContent.recommendations && extensionsConfigContent.recommendations.some(e => e === extensionId)) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.unwantedRecommendations) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + if (values.length) { + return this.jsonEditingService.write(workspaceFolder.toResource(EXTENSIONS_CONFIG), values, true); + } + } + + private async addOrRemoveWorkspaceUnwantedRecommendation(extensionId: string, workspace: IWorkspace, extensionsConfigContent: IExtensionsConfigContent | undefined, add: boolean): Promise { + const values: IJSONValue[] = []; + if (extensionsConfigContent) { + if (add) { + values.push({ path: ['unwantedRecommendations'], value: [...extensionsConfigContent.unwantedRecommendations || [], extensionId] }); + if (extensionsConfigContent.recommendations && extensionsConfigContent.recommendations.some(e => e === extensionId)) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.unwantedRecommendations) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + } else if (add) { + values.push({ path: ['extensions'], value: { unwantedRecommendations: [extensionId] } }); + } + + if (values.length) { + return this.jsonEditingService.write(workspace.configuration!, values, true); + } + } + + private async pickWorkspaceOrFolders(workspaceFolders: IWorkspaceFolder[], workspace: IWorkspace | undefined, placeHolder: string): Promise<(IWorkspace | IWorkspaceFolder)[]> { + const workspaceOrFolders = workspace ? [...workspaceFolders, workspace] : [...workspaceFolders]; + if (workspaceOrFolders.length === 1) { + return workspaceOrFolders; + } + + const folderPicks: (IQuickPickItem & { workspaceOrFolder: IWorkspace | IWorkspaceFolder } | IQuickPickSeparator)[] = workspaceFolders.map(workspaceFolder => { + return { + label: workspaceFolder.name, + description: localize('workspace folder', "Workspace Folder"), + workspaceOrFolder: workspaceFolder, + iconClasses: getIconClasses(this.modelService, this.modeService, workspaceFolder.uri, FileKind.ROOT_FOLDER) + }; + }); + + if (workspace) { + folderPicks.push({ type: 'separator' }); + folderPicks.push({ + label: localize('workspace', "Workspace"), + workspaceOrFolder: workspace, + }); + } + + const result = await this.quickInputService.pick(folderPicks, { placeHolder, canPickMany: true }) || []; + return result.map(r => r.workspaceOrFolder!); + } + + private async resolveWorkspaceExtensionConfig(workspaceConfigurationResource: URI): Promise { + try { + const content = await this.fileService.readFile(workspaceConfigurationResource); + const extensionsConfigContent = parse(content.value.toString())['extensions']; + return extensionsConfigContent ? this.parseExtensionConfig(extensionsConfigContent) : undefined; + } catch (e) { /* Ignore */ } + return undefined; + } + + private async resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise { try { const content = await this.fileService.readFile(workspaceFolder.toResource(EXTENSIONS_CONFIG)); const extensionsConfigContent = parse(content.value.toString()); return this.parseExtensionConfig(extensionsConfigContent); } catch (e) { /* ignore */ } - return null; + return {}; } - private parseExtensionConfig(extensionsConfigContent: IExtensionsConfigContent | undefined): IExtensionsConfigContent | null { - if (extensionsConfigContent) { - return { - recommendations: distinct((extensionsConfigContent.recommendations || []).map(e => e.toLowerCase())), - unwantedRecommendations: distinct((extensionsConfigContent.unwantedRecommendations || []).map(e => e.toLowerCase())) - }; - } - return null; + private parseExtensionConfig(extensionsConfigContent: IExtensionsConfigContent): IExtensionsConfigContent { + return { + recommendations: distinct((extensionsConfigContent.recommendations || []).map(e => e.toLowerCase())), + unwantedRecommendations: distinct((extensionsConfigContent.unwantedRecommendations || []).map(e => e.toLowerCase())) + }; } } diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 1dff19bf1..c6eb78294 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -54,7 +54,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten new ExtensionRunningLocationClassifier( productService, configurationService, - (extensionKinds, isInstalledLocally, isInstalledRemotely) => this._pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely) + (extensionKinds, isInstalledLocally, isInstalledRemotely) => ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely) ), instantiationService, notificationService, @@ -137,11 +137,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten }; } - private _pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean): ExtensionRunningLocation { + public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean): ExtensionRunningLocation { + let canRunRemotely = false; for (const extensionKind of extensionKinds) { if (extensionKind === 'ui' && isInstalledRemotely) { - // ui extensions run remotely if possible - return ExtensionRunningLocation.Remote; + // ui extensions run remotely if possible (but only as a last resort) + canRunRemotely = true; } if (extensionKind === 'workspace' && isInstalledRemotely) { // workspace extensions run remotely if possible @@ -152,7 +153,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten return ExtensionRunningLocation.LocalWebWorker; } } - return ExtensionRunningLocation.None; + return (canRunRemotely ? ExtensionRunningLocation.Remote : ExtensionRunningLocation.None); } protected _createExtensionHosts(_isInitialStart: boolean): IExtensionHost[] { diff --git a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts index 207a43ec7..129ab322e 100644 --- a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts @@ -13,7 +13,7 @@ import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbe import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IURLHandler, IURLService, IOpenURLOptions } from 'vs/platform/url/common/url'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -26,24 +26,25 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; +import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; const FIVE_MINUTES = 5 * 60 * 1000; const THIRTY_SECONDS = 30 * 1000; const URL_TO_HANDLE = 'extensionUrlHandler.urlToHandle'; -const CONFIRMED_EXTENSIONS_CONFIGURATION_KEY = 'extensions.confirmedUriHandlerExtensionIds'; -const CONFIRMED_EXTENSIONS_STORAGE_KEY = 'extensionUrlHandler.confirmedExtensions'; +const USER_TRUSTED_EXTENSIONS_CONFIGURATION_KEY = 'extensions.confirmedUriHandlerExtensionIds'; +const USER_TRUSTED_EXTENSIONS_STORAGE_KEY = 'extensionUrlHandler.confirmedExtensions'; function isExtensionId(value: string): boolean { return /^[a-z0-9][a-z0-9\-]*\.[a-z0-9][a-z0-9\-]*$/i.test(value); } -class ConfirmedExtensionIdStorage { +class UserTrustedExtensionIdStorage { get extensions(): string[] { - const confirmedExtensionIdsJson = this.storageService.get(CONFIRMED_EXTENSIONS_STORAGE_KEY, StorageScope.GLOBAL, '[]'); + const userTrustedExtensionIdsJson = this.storageService.get(USER_TRUSTED_EXTENSIONS_STORAGE_KEY, StorageScope.GLOBAL, '[]'); try { - return JSON.parse(confirmedExtensionIdsJson); + return JSON.parse(userTrustedExtensionIdsJson); } catch { return []; } @@ -60,7 +61,7 @@ class ConfirmedExtensionIdStorage { } set(ids: string[]): void { - this.storageService.store(CONFIRMED_EXTENSIONS_STORAGE_KEY, JSON.stringify(ids), StorageScope.GLOBAL); + this.storageService.store(USER_TRUSTED_EXTENSIONS_STORAGE_KEY, JSON.stringify(ids), StorageScope.GLOBAL, StorageTarget.MACHINE); } } @@ -87,7 +88,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { private extensionHandlers = new Map(); private uriBuffer = new Map(); - private storage: ConfirmedExtensionIdStorage; + private userTrustedExtensionsStorage: UserTrustedExtensionIdStorage; private disposable: IDisposable; constructor( @@ -101,9 +102,10 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @IStorageService private readonly storageService: IStorageService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IProgressService private readonly progressService: IProgressService + @IProgressService private readonly progressService: IProgressService, + @IExtensionUrlTrustService private readonly extensionUrlTrustService: IExtensionUrlTrustService ) { - this.storage = new ConfirmedExtensionIdStorage(storageService); + this.userTrustedExtensionsStorage = new UserTrustedExtensionIdStorage(storageService); const interval = setInterval(() => this.garbageCollect(), THIRTY_SECONDS); const urlToHandleValue = this.storageService.get(URL_TO_HANDLE, StorageScope.WORKSPACE); @@ -118,7 +120,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { ); const cache = ExtensionUrlBootstrapHandler.cache; - setTimeout(() => cache.forEach(uri => this.handleURL(uri))); + setTimeout(() => cache.forEach(([uri, option]) => this.handleURL(uri, option))); } async handleURL(uri: URI, options?: IOpenURLOptions): Promise { @@ -135,14 +137,11 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { return true; } - let showConfirm: boolean; - if (options && options.trusted) { - showConfirm = false; - } else { - showConfirm = !this.isConfirmed(ExtensionIdentifier.toKey(extensionId)); - } + const trusted = options?.trusted + || (options?.originalUrl ? await this.extensionUrlTrustService.isExtensionUrlTrusted(extensionId, options.originalUrl) : false) + || this.didUserTrustExtension(ExtensionIdentifier.toKey(extensionId)); - if (showConfirm) { + if (!trusted) { let uriString = uri.toString(false); if (uriString.length > 40) { @@ -164,7 +163,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } if (result.checkboxChecked) { - this.storage.add(ExtensionIdentifier.toKey(extensionId)); + this.userTrustedExtensionsStorage.add(ExtensionIdentifier.toKey(extensionId)); } } @@ -293,7 +292,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } private async reloadAndHandle(url: URI): Promise { - this.storageService.store(URL_TO_HANDLE, JSON.stringify(url.toJSON()), StorageScope.WORKSPACE); + this.storageService.store(URL_TO_HANDLE, JSON.stringify(url.toJSON()), StorageScope.WORKSPACE, StorageTarget.MACHINE); await this.hostService.reload(); } @@ -313,22 +312,22 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { this.uriBuffer = uriBuffer; } - private isConfirmed(id: string): boolean { - if (this.storage.has(id)) { + private didUserTrustExtension(id: string): boolean { + if (this.userTrustedExtensionsStorage.has(id)) { return true; } - return this.getConfirmedExtensionIdsFromConfiguration().indexOf(id) > -1; + return this.getConfirmedTrustedExtensionIdsFromConfiguration().indexOf(id) > -1; } - private getConfirmedExtensionIdsFromConfiguration(): Array { - const confirmedExtensionIds = this.configurationService.getValue>(CONFIRMED_EXTENSIONS_CONFIGURATION_KEY); + private getConfirmedTrustedExtensionIdsFromConfiguration(): Array { + const trustedExtensionIds = this.configurationService.getValue>(USER_TRUSTED_EXTENSIONS_CONFIGURATION_KEY); - if (!Array.isArray(confirmedExtensionIds)) { + if (!Array.isArray(trustedExtensionIds)) { return []; } - return confirmedExtensionIds; + return trustedExtensionIds; } dispose(): void { @@ -346,10 +345,10 @@ registerSingleton(IExtensionUrlHandler, ExtensionUrlHandler); */ class ExtensionUrlBootstrapHandler implements IWorkbenchContribution, IURLHandler { - private static _cache: URI[] = []; + private static _cache: [URI, IOpenURLOptions | undefined][] = []; private static disposable: IDisposable; - static get cache(): URI[] { + static get cache(): [URI, IOpenURLOptions | undefined][] { ExtensionUrlBootstrapHandler.disposable.dispose(); const result = ExtensionUrlBootstrapHandler._cache; @@ -361,12 +360,12 @@ class ExtensionUrlBootstrapHandler implements IWorkbenchContribution, IURLHandle ExtensionUrlBootstrapHandler.disposable = urlService.registerHandler(this); } - async handleURL(uri: URI): Promise { + async handleURL(uri: URI, options?: IOpenURLOptions): Promise { if (!isExtensionId(uri.authority)) { return false; } - ExtensionUrlBootstrapHandler._cache.push(uri); + ExtensionUrlBootstrapHandler._cache.push([uri, options]); return true; } } @@ -391,7 +390,7 @@ class ManageAuthorizedExtensionURIsAction extends Action2 { async run(accessor: ServicesAccessor): Promise { const storageService = accessor.get(IStorageService); const quickInputService = accessor.get(IQuickInputService); - const storage = new ConfirmedExtensionIdStorage(storageService); + const storage = new UserTrustedExtensionIdStorage(storageService); const items = storage.extensions.map(label => ({ label, picked: true } as IQuickPickItem)); if (items.length === 0) { diff --git a/src/vs/workbench/services/extensions/common/extensionHostMain.ts b/src/vs/workbench/services/extensions/common/extensionHostMain.ts index e6b9d6fcb..b91ba18ad 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostMain.ts @@ -60,7 +60,6 @@ export class ExtensionHostMain { const instaService: IInstantiationService = new InstantiationService(services, true); - // todo@joh // ugly self - inject const terminalService = instaService.invokeFunction(accessor => accessor.get(IExtHostTerminalService)); this._disposables.add(terminalService); @@ -71,7 +70,6 @@ export class ExtensionHostMain { logService.info('extension host started'); logService.trace('initData', initData); - // todo@joh // ugly self - inject // must call initialize *after* creating the extension service // because `initialize` itself creates instances that depend on it diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index a68c61147..f974db68e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -273,18 +273,14 @@ export class CachedExtensionScanner { finalBuiltinExtensions = ExtensionScanner.mergeBuiltinExtensions(builtinExtensions, extraBuiltinExtensions); } - const userExtensions = ( - !environmentService.extensionsPath - ? Promise.resolve([]) - : this._scanExtensionsWithCache( - hostService, - notificationService, - environmentService, - USER_MANIFEST_CACHE_FILE, - new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations), - log - ) - ); + const userExtensions = (this._scanExtensionsWithCache( + hostService, + notificationService, + environmentService, + USER_MANIFEST_CACHE_FILE, + new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations), + log + )); // Always load developed extensions while extensions development let developedExtensions: Promise = Promise.resolve([]); diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 87fee781f..3050bc6e2 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -468,9 +468,12 @@ let _caCertificates: ReturnType | Promise; async function getCaCertificates(extHostLogService: ILogService) { if (!_caCertificates) { _caCertificates = readCaCertificates() - .then(res => res && res.certs.length ? res : undefined) + .then(res => { + extHostLogService.debug('ProxyResolver#getCaCertificates count', res && res.certs.length); + return res && res.certs.length ? res : undefined; + }) .catch(err => { - extHostLogService.error('ProxyResolver#getCertificates', toErrorMessage(err)); + extHostLogService.error('ProxyResolver#getCaCertificates error', toErrorMessage(err)); return undefined; }); } diff --git a/src/vs/workbench/services/extensions/test/electron-browser/extensionService.test.ts b/src/vs/workbench/services/extensions/test/electron-browser/extensionService.test.ts new file mode 100644 index 000000000..77a803dfa --- /dev/null +++ b/src/vs/workbench/services/extensions/test/electron-browser/extensionService.test.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ExtensionService as BrowserExtensionService } from 'vs/workbench/services/extensions/browser/extensionService'; +import { ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/abstractExtensionService'; + +suite('BrowserExtensionService', () => { + test('pickRunningLocation', () => { + assert.deepEqual(BrowserExtensionService.pickRunningLocation([], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation([], false, true), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation([], true, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation([], true, true), ExtensionRunningLocation.None); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, true), ExtensionRunningLocation.Remote); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, true), ExtensionRunningLocation.Remote); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], false, true), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], true, true), ExtensionRunningLocation.LocalWebWorker); + + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, true), ExtensionRunningLocation.Remote); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, true), ExtensionRunningLocation.Remote); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, true), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, true), ExtensionRunningLocation.LocalWebWorker); + + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, true), ExtensionRunningLocation.Remote); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, true), ExtensionRunningLocation.LocalWebWorker); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, true), ExtensionRunningLocation.Remote); + }); +}); diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts index b39a5cbb9..f5c555225 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts @@ -8,14 +8,20 @@ let MonacoEnvironment = (self).MonacoEnvironment; let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../../../'; + const trustedTypesPolicy = self.trustedTypes?.createPolicy('amdLoader', { createScriptURL: value => value }); + if (typeof (self).define !== 'function' || !(self).define.amd) { - importScripts(monacoBaseUrl + 'vs/loader.js'); + let loaderSrc: string | TrustedScriptURL = monacoBaseUrl + 'vs/loader.js'; + if (trustedTypesPolicy) { + loaderSrc = trustedTypesPolicy.createScriptURL(loaderSrc); + } + importScripts(loaderSrc as string); } require.config({ baseUrl: monacoBaseUrl, catchError: true, - createTrustedScriptURL: (value: string) => value + trustedTypesPolicy }); require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err)); diff --git a/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html b/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html index a99d3df29..392b4b4ae 100644 --- a/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html +++ b/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html @@ -1,7 +1,7 @@ - +