1717/* eslint-disable @typescript-eslint/no-explicit-any */
1818
1919import { inject , injectable , optional } from '@theia/core/shared/inversify' ;
20- import { CommandRegistry , Disposable , DisposableCollection , nls , CommandMenu , AcceleratorSource , ContextExpressionMatcher } from '@theia/core' ;
20+ import { MenuPath , CommandRegistry , Disposable , DisposableCollection , nls , CommandMenu , AcceleratorSource , ContextExpressionMatcher } from '@theia/core' ;
2121import { MenuModelRegistry } from '@theia/core/lib/common' ;
2222import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar' ;
2323import { DeployedPlugin , IconUrl , Menu } from '../../../common' ;
2424import { ScmWidget } from '@theia/scm/lib/browser/scm-widget' ;
2525import { KeybindingRegistry , QuickCommandService } from '@theia/core/lib/browser' ;
2626import {
27- CodeEditorWidgetUtil , PLUGIN_EDITOR_TITLE_MENU , PLUGIN_EDITOR_TITLE_RUN_MENU , PLUGIN_SCM_TITLE_MENU , PLUGIN_VIEW_TITLE_MENU
27+ CodeEditorWidgetUtil , codeToTheiaMappings , PLUGIN_EDITOR_TITLE_MENU , PLUGIN_EDITOR_TITLE_RUN_MENU , PLUGIN_SCM_TITLE_MENU , PLUGIN_VIEW_TITLE_MENU
2828} from './vscode-theia-menu-mappings' ;
2929import { PluginMenuCommandAdapter } from './plugin-menu-command-adapter' ;
3030import { ContextKeyService } from '@theia/core/lib/browser/context-key-service' ;
3131import { PluginSharedStyle } from '../plugin-shared-style' ;
3232import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables' ;
3333
34+ function identity ( ...args : unknown [ ] ) : unknown [ ] {
35+ return args ;
36+ }
37+
3438@injectable ( )
3539export class MenusContributionPointHandler {
3640
@@ -63,6 +67,9 @@ export class MenusContributionPointHandler {
6367 this . tabBarToolbar . registerMenuDelegate ( PLUGIN_VIEW_TITLE_MENU , widget => ! CodeEditorWidgetUtil . is ( widget ) ) ;
6468 }
6569
70+ private getMatchingTheiaMenuPaths ( contributionPoint : string ) : MenuPath [ ] | undefined {
71+ return ( codeToTheiaMappings as Map < string , MenuPath [ ] > ) . get ( contributionPoint ) ;
72+ }
6673
6774 handle ( plugin : DeployedPlugin ) : Disposable {
6875 const allMenus = plugin . contributes ?. menus ;
@@ -85,61 +92,66 @@ export class MenusContributionPointHandler {
8592 if ( contributionPoint === 'commandPalette' ) {
8693 toDispose . push ( this . registerCommandPaletteAction ( item ) ) ;
8794 } else {
88- const target = [ this . pluginMenuCommandAdapter . toProbablyUniquePath ( contributionPoint ) ] ;
95+ const targets = this . getMatchingTheiaMenuPaths ( contributionPoint ) ?? [ [ contributionPoint ] ] ;
8996 const { group, order } = this . parseGroup ( item . group ) ;
9097 const { submenu, command } = item ;
9198 if ( submenu && command ) {
9299 console . warn (
93100 `Menu item ${ command } from plugin ${ plugin . metadata . model . id } contributed both submenu and command. Only command will be registered.`
94101 ) ;
95102 }
103+ const contributionPointAdapter = this . pluginMenuCommandAdapter . getArgumentAdapter ( contributionPoint ) ;
96104 if ( command ) {
97- const menuPath = group ? [ ...target , group ] : target ;
98-
99- const cmd = this . commandRegistry . getCommand ( command ) ;
100- if ( ! cmd ) {
101- console . debug ( `No label for action menu node: No command "${ command } " exists.` ) ;
102- continue ;
103- }
104- const label = cmd . label || cmd . id ;
105- const icon = cmd . iconClass ;
106- const action : CommandMenu & AcceleratorSource = {
107- id : command ,
108- sortString : order || '' ,
109- isVisible : < T > ( contextMatcher : ContextExpressionMatcher < T > , context : T | undefined , ...args : any [ ] ) : boolean => {
110- if ( item . when && ! contextMatcher . match ( item . when , context ) ) {
111- return false ;
112- }
105+ const actionAdapter = contributionPointAdapter ?? identity ;
106+ targets . forEach ( target => {
107+ const menuPath = group ? [ ...target , group ] : target ;
113108
114- return this . commandRegistry . isVisible ( command , ...args ) ;
115- } ,
116- icon : icon ,
117- label : label ,
118- isEnabled : ( ...args : any [ ] ) : boolean =>
119- this . commandRegistry . isEnabled ( command , ...args ) ,
120- run : ( ...args : any [ ] ) : Promise < void > =>
121- this . commandRegistry . executeCommand ( command , ...args ) ,
122- isToggled : ( ) => false ,
123- getAccelerator : ( context : HTMLElement | undefined ) : string [ ] => {
124- const bindings = this . keybindingRegistry . getKeybindingsForCommand ( command ) ;
125- // Only consider the first active keybinding.
126- if ( bindings . length ) {
127- const binding = bindings . find ( b => this . keybindingRegistry . isEnabledInScope ( b , context ) ) ;
128- if ( binding ) {
129- return this . keybindingRegistry . acceleratorFor ( binding , '+' , true ) ;
109+ const cmd = this . commandRegistry . getCommand ( command ) ;
110+ if ( ! cmd ) {
111+ console . debug ( `No label for action menu node: No command "${ command } " exists.` ) ;
112+ return ;
113+ }
114+ const label = cmd . label || cmd . id ;
115+ const icon = cmd . iconClass ;
116+ const action : CommandMenu & AcceleratorSource = {
117+ id : command ,
118+ sortString : order || '' ,
119+ isVisible : < T > ( contextMatcher : ContextExpressionMatcher < T > , context : T | undefined , ...args : any [ ] ) : boolean => {
120+ if ( item . when && ! contextMatcher . match ( item . when , context ) ) {
121+ return false ;
122+ }
123+
124+ return this . commandRegistry . isVisible ( command , ...actionAdapter ( ...args ) ) ;
125+ } ,
126+ icon : icon ,
127+ label : label ,
128+ isEnabled : ( ...args : any [ ] ) : boolean =>
129+ this . commandRegistry . isEnabled ( command , ...actionAdapter ( ...args ) ) ,
130+ run : ( ...args : any [ ] ) : Promise < void > =>
131+ this . commandRegistry . executeCommand ( command , ...actionAdapter ( ...args ) ) ,
132+ isToggled : ( ) => false ,
133+ getAccelerator : ( context : HTMLElement | undefined ) : string [ ] => {
134+ const bindings = this . keybindingRegistry . getKeybindingsForCommand ( command ) ;
135+ // Only consider the first active keybinding.
136+ if ( bindings . length ) {
137+ const binding = bindings . find ( b => this . keybindingRegistry . isEnabledInScope ( b , context ) ) ;
138+ if ( binding ) {
139+ return this . keybindingRegistry . acceleratorFor ( binding , '+' , true ) ;
140+ }
130141 }
142+ return [ ] ;
131143 }
132- return [ ] ;
133- }
134- } ;
135- toDispose . push ( this . menuRegistry . registerCommandMenu ( menuPath , action ) ) ;
144+ } ;
145+ toDispose . push ( this . menuRegistry . registerCommandMenu ( menuPath , action ) ) ;
146+ } ) ;
136147 } else if ( submenu ) {
137- toDispose . push ( this . menuRegistry . linkCompoundMenuNode ( {
148+ targets . forEach ( target => toDispose . push ( this . menuRegistry . linkCompoundMenuNode ( {
138149 newParentPath : group ? [ ...target , group ] : target ,
139- submenuPath : [ submenu ] ,
150+ submenuPath : [ submenu ! ] ,
140151 order : order ,
141152 when : item . when ,
142- } ) ) ;
153+ argumentAdapter : contributionPointAdapter
154+ } ) ) ) ;
143155 }
144156 }
145157 } catch ( error ) {
0 commit comments