@@ -2,7 +2,7 @@ import { inject, injectable } from 'inversify';
2
2
import { app , BrowserWindow , BrowserWindowConstructorOptions , ipcMain , screen } from '@theia/core/electron-shared/electron' ;
3
3
import { fork } from 'child_process' ;
4
4
import { AddressInfo } from 'net' ;
5
- import { join } from 'path' ;
5
+ import { join , dirname } from 'path' ;
6
6
import * as fs from 'fs-extra' ;
7
7
import { initSplashScreen } from '../splash/splash-screen' ;
8
8
import { MaybePromise } from '@theia/core/lib/common/types' ;
@@ -16,6 +16,7 @@ import {
16
16
import { SplashServiceImpl } from '../splash/splash-service-impl' ;
17
17
import { URI } from '@theia/core/shared/vscode-uri' ;
18
18
import * as electronRemoteMain from '@theia/core/electron-shared/@electron/remote/main' ;
19
+ import { Deferred } from '@theia/core/lib/common/promise-util' ;
19
20
20
21
app . commandLine . appendSwitch ( 'disable-http-cache' ) ;
21
22
@@ -36,6 +37,7 @@ const WORKSPACES = 'workspaces';
36
37
export class ElectronMainApplication extends TheiaElectronMainApplication {
37
38
protected _windows : BrowserWindow [ ] = [ ] ;
38
39
protected startup = false ;
40
+ protected openFilePromise = new Deferred ( ) ;
39
41
40
42
@inject ( SplashServiceImpl )
41
43
protected readonly splashService : SplashServiceImpl ;
@@ -45,17 +47,53 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
45
47
// See: https://github.com/electron-userland/electron-builder/issues/2468
46
48
// Regression in Theia: https://github.com/eclipse-theia/theia/issues/8701
47
49
app . on ( 'ready' , ( ) => app . setName ( config . applicationName ) ) ;
50
+ this . attachFileAssociations ( ) ;
48
51
return super . start ( config ) ;
49
52
}
50
53
54
+ attachFileAssociations ( ) {
55
+ // OSX: register open-file event
56
+ if ( process . platform === 'darwin' ) {
57
+ app . on ( 'open-file' , async ( event , uri ) => {
58
+ event . preventDefault ( ) ;
59
+ if ( uri . endsWith ( '.ino' ) && await fs . pathExists ( uri ) ) {
60
+ this . openFilePromise . reject ( ) ;
61
+ await this . openSketch ( dirname ( uri ) ) ;
62
+ }
63
+ } ) ;
64
+ setTimeout ( ( ) => this . openFilePromise . resolve ( ) , 500 ) ;
65
+ } else {
66
+ this . openFilePromise . resolve ( ) ;
67
+ }
68
+ }
69
+
70
+ protected async isValidSketchPath ( uri : string ) : Promise < boolean | undefined > {
71
+ return typeof uri === 'string' && await fs . pathExists ( uri ) ;
72
+ }
73
+
51
74
protected async launch ( params : ElectronMainExecutionParams ) : Promise < void > {
52
75
this . startup = true ;
76
+
77
+ try {
78
+ // When running on MacOS, we either have to wait until
79
+ // 1. The `open-file` command has been received by the app, rejecting the promise
80
+ // 2. A short timeout resolves the promise automatically, falling back to the usual app launch
81
+ await this . openFilePromise . promise ;
82
+ } catch {
83
+ // Application has received the `open-file` event and will skip the default application launch
84
+ return ;
85
+ }
86
+
87
+ if ( process . platform === 'win32' && await this . launchWindowsOpen ( ) ) {
88
+ // Application has received a file in its arguments and will skip the default application launch
89
+ return ;
90
+ }
91
+
53
92
const workspaces : WorkspaceOptions [ ] | undefined = this . electronStore . get ( WORKSPACES ) ;
54
93
let useDefault = true ;
55
94
if ( workspaces && workspaces . length > 0 ) {
56
95
for ( const workspace of workspaces ) {
57
- const file = workspace . file ;
58
- if ( typeof file === 'string' && await fs . pathExists ( file ) ) {
96
+ if ( await this . isValidSketchPath ( workspace . file ) ) {
59
97
useDefault = false ;
60
98
await this . openSketch ( workspace ) ;
61
99
}
@@ -67,16 +105,46 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
67
105
}
68
106
}
69
107
70
- protected async openSketch ( workspace : WorkspaceOptions ) : Promise < BrowserWindow > {
108
+ protected async launchWindowsOpen ( ) : Promise < boolean > {
109
+ // Copy to prevent manipulation of original array
110
+ const argCopy = [ ...process . argv ] ;
111
+ if ( app . isPackaged ) {
112
+ // workaround for missing executable argument when app is packaged
113
+ argCopy . unshift ( 'packaged' ) ;
114
+ }
115
+ const possibleUris = argCopy . slice ( 2 ) || null ;
116
+ if ( possibleUris ) {
117
+ let uri : string | undefined ;
118
+ for ( const possibleUri of possibleUris ) {
119
+ if ( possibleUri . endsWith ( '.ino' ) && await this . isValidSketchPath ( possibleUri ) ) {
120
+ uri = possibleUri ;
121
+ break ;
122
+ }
123
+ }
124
+ if ( uri ) {
125
+ await this . openSketch ( dirname ( uri ) ) ;
126
+ return true ;
127
+ }
128
+ }
129
+ return false ;
130
+ }
131
+
132
+ protected async openSketch ( workspace : WorkspaceOptions | string ) : Promise < BrowserWindow > {
71
133
const options = await this . getLastWindowOptions ( ) ;
72
- options . x = workspace . x ;
73
- options . y = workspace . y ;
74
- options . width = workspace . width ;
75
- options . height = workspace . height ;
76
- options . isMaximized = workspace . isMaximized ;
77
- options . isFullScreen = workspace . isFullScreen ;
134
+ let file : string ;
135
+ if ( typeof workspace === 'object' ) {
136
+ options . x = workspace . x ;
137
+ options . y = workspace . y ;
138
+ options . width = workspace . width ;
139
+ options . height = workspace . height ;
140
+ options . isMaximized = workspace . isMaximized ;
141
+ options . isFullScreen = workspace . isFullScreen ;
142
+ file = workspace . file ;
143
+ } else {
144
+ file = workspace ;
145
+ }
78
146
const [ uri , electronWindow ] = await Promise . all ( [ this . createWindowUri ( ) , this . createWindow ( options ) ] ) ;
79
- electronWindow . loadURL ( uri . withFragment ( encodeURI ( workspace . file ) ) . toString ( true ) ) ;
147
+ electronWindow . loadURL ( uri . withFragment ( encodeURI ( file ) ) . toString ( true ) ) ;
80
148
return electronWindow ;
81
149
}
82
150
0 commit comments