1
1
import { inject , injectable } from 'inversify' ;
2
- import { app , BrowserWindow , BrowserWindowConstructorOptions , ipcMain , screen } from '@theia/core/electron-shared/electron' ;
2
+ import { app , BrowserWindow , BrowserWindowConstructorOptions , ipcMain , screen , Event as ElectronEvent } 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,8 @@ 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' ;
20
+ import * as os from '@theia/core/lib/common/os' ;
19
21
20
22
app . commandLine . appendSwitch ( 'disable-http-cache' ) ;
21
23
@@ -36,6 +38,7 @@ const WORKSPACES = 'workspaces';
36
38
export class ElectronMainApplication extends TheiaElectronMainApplication {
37
39
protected _windows : BrowserWindow [ ] = [ ] ;
38
40
protected startup = false ;
41
+ protected openFilePromise = new Deferred ( ) ;
39
42
40
43
@inject ( SplashServiceImpl )
41
44
protected readonly splashService : SplashServiceImpl ;
@@ -45,17 +48,52 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
45
48
// See: https://github.com/electron-userland/electron-builder/issues/2468
46
49
// Regression in Theia: https://github.com/eclipse-theia/theia/issues/8701
47
50
app . on ( 'ready' , ( ) => app . setName ( config . applicationName ) ) ;
51
+ this . attachFileAssociations ( ) ;
48
52
return super . start ( config ) ;
49
53
}
50
54
55
+ attachFileAssociations ( ) {
56
+ // OSX: register open-file event
57
+ if ( os . isOSX ) {
58
+ app . on ( 'open-file' , async ( event , uri ) => {
59
+ event . preventDefault ( ) ;
60
+ if ( uri . endsWith ( '.ino' ) && await fs . pathExists ( uri ) ) {
61
+ this . openFilePromise . reject ( ) ;
62
+ await this . openSketch ( dirname ( uri ) ) ;
63
+ }
64
+ } ) ;
65
+ setTimeout ( ( ) => this . openFilePromise . resolve ( ) , 500 ) ;
66
+ } else {
67
+ this . openFilePromise . resolve ( ) ;
68
+ }
69
+ }
70
+
71
+ protected async isValidSketchPath ( uri : string ) : Promise < boolean | undefined > {
72
+ return typeof uri === 'string' && await fs . pathExists ( uri ) ;
73
+ }
74
+
51
75
protected async launch ( params : ElectronMainExecutionParams ) : Promise < void > {
76
+ try {
77
+ // When running on MacOS, we either have to wait until
78
+ // 1. The `open-file` command has been received by the app, rejecting the promise
79
+ // 2. A short timeout resolves the promise automatically, falling back to the usual app launch
80
+ await this . openFilePromise . promise ;
81
+ } catch {
82
+ // Application has received the `open-file` event and will skip the default application launch
83
+ return ;
84
+ }
85
+
86
+ if ( ! os . isOSX && await this . launchFromArgs ( params ) ) {
87
+ // Application has received a file in its arguments and will skip the default application launch
88
+ return ;
89
+ }
90
+
52
91
this . startup = true ;
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,39 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
67
105
}
68
106
}
69
107
70
- protected async openSketch ( workspace : WorkspaceOptions ) : Promise < BrowserWindow > {
108
+ protected async launchFromArgs ( params : ElectronMainExecutionParams ) : Promise < boolean > {
109
+ // Copy to prevent manipulation of original array
110
+ const argCopy = [ ...params . argv ] ;
111
+ let uri : string | undefined ;
112
+ for ( const possibleUri of argCopy ) {
113
+ if ( possibleUri . endsWith ( '.ino' ) && await this . isValidSketchPath ( possibleUri ) ) {
114
+ uri = possibleUri ;
115
+ break ;
116
+ }
117
+ }
118
+ if ( uri ) {
119
+ await this . openSketch ( dirname ( uri ) ) ;
120
+ return true ;
121
+ }
122
+ return false ;
123
+ }
124
+
125
+ protected async openSketch ( workspace : WorkspaceOptions | string ) : Promise < BrowserWindow > {
71
126
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 ;
127
+ let file : string ;
128
+ if ( typeof workspace === 'object' ) {
129
+ options . x = workspace . x ;
130
+ options . y = workspace . y ;
131
+ options . width = workspace . width ;
132
+ options . height = workspace . height ;
133
+ options . isMaximized = workspace . isMaximized ;
134
+ options . isFullScreen = workspace . isFullScreen ;
135
+ file = workspace . file ;
136
+ } else {
137
+ file = workspace ;
138
+ }
78
139
const [ uri , electronWindow ] = await Promise . all ( [ this . createWindowUri ( ) , this . createWindow ( options ) ] ) ;
79
- electronWindow . loadURL ( uri . withFragment ( encodeURI ( workspace . file ) ) . toString ( true ) ) ;
140
+ electronWindow . loadURL ( uri . withFragment ( encodeURI ( file ) ) . toString ( true ) ) ;
80
141
return electronWindow ;
81
142
}
82
143
@@ -101,6 +162,14 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
101
162
} ) ;
102
163
}
103
164
165
+ protected async onSecondInstance ( event : ElectronEvent , argv : string [ ] , cwd : string ) : Promise < void > {
166
+ if ( ! os . isOSX && await this . launchFromArgs ( { cwd, argv, secondInstance : true } ) ) {
167
+ // Application has received a file in its arguments
168
+ return ;
169
+ }
170
+ super . onSecondInstance ( event , argv , cwd ) ;
171
+ }
172
+
104
173
/**
105
174
* Use this rather than creating `BrowserWindow` instances from scratch, since some security parameters need to be set, this method will do it.
106
175
*
0 commit comments