@@ -3,12 +3,14 @@ import {
3
3
DisposableCollection ,
4
4
} from '@theia/core/lib/common/disposable' ;
5
5
import { MenuModelRegistry } from '@theia/core/lib/common/menu/menu-model-registry' ;
6
+ import type { MenuPath } from '@theia/core/lib/common/menu/menu-types' ;
6
7
import { nls } from '@theia/core/lib/common/nls' ;
7
8
import { Deferred } from '@theia/core/lib/common/promise-util' ;
8
9
import { inject , injectable } from '@theia/core/shared/inversify' ;
9
10
import { MainMenuManager } from '../../common/main-menu-manager' ;
10
11
import {
11
12
BoardsService ,
13
+ BoardWithPackage ,
12
14
createPlatformIdentifier ,
13
15
getBoardInfo ,
14
16
InstalledBoardWithPackage ,
@@ -176,54 +178,104 @@ SN: ${SN}
176
178
? createPlatformIdentifier ( selectedBoard )
177
179
: undefined ;
178
180
179
- // Installed boards
180
- installedBoards . forEach ( ( board , index ) => {
181
- const { packageId, packageName, fqbn, name, manuallyInstalled } = board ;
182
-
183
- let packageLabel =
184
- packageName +
185
- `${
186
- manuallyInstalled
187
- ? nls . localize ( 'arduino/board/inSketchbook' , ' (in Sketchbook)' )
188
- : ''
189
- } `;
190
- if (
191
- selectedBoardPlatformId &&
192
- platformIdentifierEquals ( packageId , selectedBoardPlatformId )
193
- ) {
194
- packageLabel = `✓ ${ packageLabel } ` ;
181
+ // Keys are the vendor IDs
182
+ type BoardsPerVendor = Record < string , BoardWithPackage [ ] > ;
183
+ // Group boards by their platform names. The keys are the platform names as menu labels.
184
+ // If there is a platform name (menu label) collision, refine the menu label with the vendor ID.
185
+ const groupedBoards = new Map < string , BoardsPerVendor > ( ) ;
186
+ for ( const board of installedBoards ) {
187
+ const { packageId, packageName } = board ;
188
+ const { vendorId } = packageId ;
189
+ let boardsPerPackageName = groupedBoards . get ( packageName ) ;
190
+ if ( ! boardsPerPackageName ) {
191
+ boardsPerPackageName = { } as BoardsPerVendor ;
192
+ groupedBoards . set ( packageName , boardsPerPackageName ) ;
195
193
}
196
- // Platform submenu
197
- const platformMenuPath = [
198
- ...boardsPackagesGroup ,
199
- serializePlatformIdentifier ( packageId ) ,
200
- ] ;
201
- // Note: Registering the same submenu twice is a noop. No need to group the boards per platform.
202
- this . menuModelRegistry . registerSubmenu ( platformMenuPath , packageLabel , {
203
- order : packageName . toLowerCase ( ) ,
204
- } ) ;
194
+ let boardPerVendor : BoardWithPackage [ ] | undefined =
195
+ boardsPerPackageName [ vendorId ] ;
196
+ if ( ! boardPerVendor ) {
197
+ boardPerVendor = [ ] ;
198
+ boardsPerPackageName [ vendorId ] = boardPerVendor ;
199
+ }
200
+ boardPerVendor . push ( board ) ;
201
+ }
205
202
206
- const id = `arduino-select-board--${ fqbn } ` ;
207
- const command = { id } ;
208
- const handler = {
209
- execute : ( ) =>
210
- this . boardsServiceProvider . updateBoard ( { name : name , fqbn : fqbn } ) ,
211
- isToggled : ( ) => fqbn === selectedBoard ?. fqbn ,
212
- } ;
203
+ // Installed boards
204
+ Array . from ( groupedBoards . entries ( ) ) . forEach (
205
+ ( [ packageName , boardsPerPackage ] ) => {
206
+ const useVendorSuffix = Object . keys ( boardsPerPackage ) . length > 1 ;
207
+ Object . entries ( boardsPerPackage ) . forEach ( ( [ vendorId , boards ] ) => {
208
+ let platformMenuPath : MenuPath | undefined = undefined ;
209
+ boards . forEach ( ( board , index ) => {
210
+ const { packageId, fqbn, name, manuallyInstalled } = board ;
211
+ // create the platform submenu once.
212
+ // creating and registering the same submenu twice in Theia is a noop, though.
213
+ if ( ! platformMenuPath ) {
214
+ let packageLabel =
215
+ packageName +
216
+ `${
217
+ manuallyInstalled
218
+ ? nls . localize (
219
+ 'arduino/board/inSketchbook' ,
220
+ ' (in Sketchbook)'
221
+ )
222
+ : ''
223
+ } `;
224
+ if (
225
+ selectedBoardPlatformId &&
226
+ platformIdentifierEquals ( packageId , selectedBoardPlatformId )
227
+ ) {
228
+ packageLabel = `✓ ${ packageLabel } ` ;
229
+ }
230
+ if ( useVendorSuffix ) {
231
+ packageLabel += ` (${ vendorId } )` ;
232
+ }
233
+ // Platform submenu
234
+ platformMenuPath = [
235
+ ...boardsPackagesGroup ,
236
+ serializePlatformIdentifier ( packageId ) ,
237
+ ] ;
238
+ this . menuModelRegistry . registerSubmenu (
239
+ platformMenuPath ,
240
+ packageLabel ,
241
+ {
242
+ order : packageName . toLowerCase ( ) ,
243
+ }
244
+ ) ;
245
+ }
213
246
214
- // Board menu
215
- const menuAction = {
216
- commandId : id ,
217
- label : name ,
218
- order : String ( index ) . padStart ( 4 ) , // pads with leading zeros for alphanumeric sort where order is 1, 2, 11, and NOT 1, 11, 2
219
- } ;
220
- this . commandRegistry . registerCommand ( command , handler ) ;
221
- this . toDisposeBeforeMenuRebuild . push (
222
- Disposable . create ( ( ) => this . commandRegistry . unregisterCommand ( command ) )
223
- ) ;
224
- this . menuModelRegistry . registerMenuAction ( platformMenuPath , menuAction ) ;
225
- // Note: we do not dispose the menu actions individually. Calling `unregisterSubmenu` on the parent will wipe the children menu nodes recursively.
226
- } ) ;
247
+ const id = `arduino-select-board--${ fqbn } ` ;
248
+ const command = { id } ;
249
+ const handler = {
250
+ execute : ( ) =>
251
+ this . boardsServiceProvider . updateBoard ( {
252
+ name : name ,
253
+ fqbn : fqbn ,
254
+ } ) ,
255
+ isToggled : ( ) => fqbn === selectedBoard ?. fqbn ,
256
+ } ;
257
+
258
+ // Board menu
259
+ const menuAction = {
260
+ commandId : id ,
261
+ label : name ,
262
+ order : String ( index ) . padStart ( 4 ) , // pads with leading zeros for alphanumeric sort where order is 1, 2, 11, and NOT 1, 11, 2
263
+ } ;
264
+ this . commandRegistry . registerCommand ( command , handler ) ;
265
+ this . toDisposeBeforeMenuRebuild . push (
266
+ Disposable . create ( ( ) =>
267
+ this . commandRegistry . unregisterCommand ( command )
268
+ )
269
+ ) ;
270
+ this . menuModelRegistry . registerMenuAction (
271
+ platformMenuPath ,
272
+ menuAction
273
+ ) ;
274
+ // Note: we do not dispose the menu actions individually. Calling `unregisterSubmenu` on the parent will wipe the children menu nodes recursively.
275
+ } ) ;
276
+ } ) ;
277
+ }
278
+ ) ;
227
279
228
280
// Detected ports
229
281
const registerPorts = (
0 commit comments