1
- import { exec } from "child_process" ;
2
- import { promisify } from "util" ;
3
1
import { field , logger , time , Time } from "@coder/logger" ;
4
- import { escapePath } from "@coder/protocol" ;
5
- import { retry } from "./retry" ;
2
+ import { InitData } from "@coder/protocol" ;
3
+ import { retry , Retry } from "./retry" ;
4
+ import { client } from "./fill/client" ;
5
+ import { Clipboard , clipboard } from "./fill/clipboard" ;
6
+
7
+ export interface IURI {
8
+
9
+ readonly path : string ;
10
+ readonly fsPath : string ;
11
+ readonly scheme : string ;
12
+
13
+ }
14
+
15
+ export interface IURIFactory {
16
+
17
+ /**
18
+ * Convert the object to an instance of a real URI.
19
+ */
20
+ create < T extends IURI > ( uri : IURI ) : T ;
21
+ file ( path : string ) : IURI ;
22
+ parse ( raw : string ) : IURI ;
6
23
7
- export interface IClientOptions {
8
- mkDirs ?: string [ ] ;
9
24
}
10
25
11
26
/**
@@ -14,48 +29,96 @@ export interface IClientOptions {
14
29
* Everything the client provides is asynchronous so you can wait on what
15
30
* you need from it without blocking anything else.
16
31
*
17
- * It also provides task management to help asynchronously load and time
18
- * external code.
32
+ * It also provides task management to help asynchronously load and time code.
19
33
*/
20
- export class Client {
34
+ export abstract class Client {
21
35
22
- public readonly mkDirs : Promise < void > ;
36
+ public readonly retry : Retry = retry ;
37
+ public readonly clipboard : Clipboard = clipboard ;
38
+ public readonly uriFactory : IURIFactory ;
23
39
private start : Time | undefined ;
24
40
private readonly progressElement : HTMLElement | undefined ;
25
- private tasks : string [ ] ;
26
- private finishedTaskCount : number ;
41
+ private tasks : string [ ] = [ ] ;
42
+ private finishedTaskCount = 0 ;
43
+ private readonly loadTime : Time ;
44
+
45
+ public constructor ( ) {
46
+ logger . info ( "Loading IDE" ) ;
47
+
48
+ this . loadTime = time ( 2500 ) ;
49
+
50
+ const overlay = document . getElementById ( "overlay" ) ;
51
+ const logo = document . getElementById ( "logo" ) ;
52
+ const msgElement = overlay
53
+ ? overlay . querySelector ( ".message" ) as HTMLElement
54
+ : undefined ;
55
+
56
+ if ( overlay && logo ) {
57
+ overlay . addEventListener ( "mousemove" , ( event ) => {
58
+ const xPos = ( ( event . clientX - logo . offsetLeft ) / 24 ) . toFixed ( 2 ) ;
59
+ const yPos = ( ( logo . offsetTop - event . clientY ) / 24 ) . toFixed ( 2 ) ;
60
+
61
+ logo . style . transform = `perspective(200px) rotateX(${ yPos } deg) rotateY(${ xPos } deg)` ;
62
+ } ) ;
63
+ }
27
64
28
- public constructor ( options : IClientOptions ) {
29
- this . tasks = [ ] ;
30
- this . finishedTaskCount = 0 ;
31
65
this . progressElement = typeof document !== "undefined"
32
66
? document . querySelector ( "#fill" ) as HTMLElement
33
67
: undefined ;
34
68
35
- this . mkDirs = this . wrapTask ( "Creating directories" , 100 , async ( ) => {
36
- if ( options . mkDirs && options . mkDirs . length > 0 ) {
37
- await promisify ( exec ) ( `mkdir -p ${ options . mkDirs . map ( escapePath ) . join ( " " ) } ` ) ;
38
- }
69
+ require ( "path" ) . posix = require ( "path" ) ;
70
+
71
+ window . addEventListener ( "contextmenu" , ( event ) => {
72
+ event . preventDefault ( ) ;
39
73
} ) ;
40
74
41
75
// Prevent Firefox from trying to reconnect when the page unloads.
42
76
window . addEventListener ( "unload" , ( ) => {
43
- retry . block ( ) ;
77
+ this . retry . block ( ) ;
78
+ logger . info ( "Unloaded" ) ;
79
+ } ) ;
80
+
81
+ this . uriFactory = this . createUriFactory ( ) ;
82
+
83
+ this . initialize ( ) . then ( ( ) => {
84
+ if ( overlay ) {
85
+ overlay . style . opacity = "0" ;
86
+ overlay . addEventListener ( "transitionend" , ( ) => {
87
+ overlay . remove ( ) ;
88
+ } ) ;
89
+ }
90
+ logger . info ( "Load completed" , field ( "duration" , this . loadTime ) ) ;
91
+ } ) . catch ( ( error ) => {
92
+ logger . error ( error . message ) ;
93
+ if ( overlay ) {
94
+ overlay . classList . add ( "error" ) ;
95
+ }
96
+ if ( msgElement ) {
97
+ const button = document . createElement ( "div" ) ;
98
+ button . className = "reload-button" ;
99
+ button . innerText = "Reload" ;
100
+ button . addEventListener ( "click" , ( ) => {
101
+ location . reload ( ) ;
102
+ } ) ;
103
+ msgElement . innerText = `Failed to load: ${ error . message } .` ;
104
+ msgElement . parentElement ! . appendChild ( button ) ;
105
+ }
106
+ logger . warn ( "Load completed with errors" , field ( "duration" , this . loadTime ) ) ;
44
107
} ) ;
45
108
}
46
109
47
110
/**
48
111
* Wrap a task in some logging, timing, and progress updates. Can optionally
49
112
* wait on other tasks which won't count towards this task's time.
50
113
*/
51
- public async wrapTask < T > ( description : string , duration : number , task : ( ) => Promise < T > ) : Promise < T > ;
52
- public async wrapTask < T , V > ( description : string , duration : number , task : ( v : V ) => Promise < T > , t : Promise < V > ) : Promise < T > ;
53
- public async wrapTask < T , V1 , V2 > ( description : string , duration : number , task : ( v1 : V1 , v2 : V2 ) => Promise < T > , t1 : Promise < V1 > , t2 : Promise < V2 > ) : Promise < T > ;
54
- public async wrapTask < T , V1 , V2 , V3 > ( description : string , duration : number , task : ( v1 : V1 , v2 : V2 , v3 : V3 ) => Promise < T > , t1 : Promise < V1 > , t2 : Promise < V2 > , t3 : Promise < V3 > ) : Promise < T > ;
55
- public async wrapTask < T , V1 , V2 , V3 , V4 > ( description : string , duration : number , task : ( v1 : V1 , v2 : V2 , v3 : V3 , v4 : V4 ) => Promise < T > , t1 : Promise < V1 > , t2 : Promise < V2 > , t3 : Promise < V3 > , t4 : Promise < V4 > ) : Promise < T > ;
56
- public async wrapTask < T , V1 , V2 , V3 , V4 , V5 > ( description : string , duration : number , task : ( v1 : V1 , v2 : V2 , v3 : V3 , v4 : V4 , v5 : V5 ) => Promise < T > , t1 : Promise < V1 > , t2 : Promise < V2 > , t3 : Promise < V3 > , t4 : Promise < V4 > , t5 : Promise < V5 > ) : Promise < T > ;
57
- public async wrapTask < T , V1 , V2 , V3 , V4 , V5 , V6 > ( description : string , duration : number , task : ( v1 : V1 , v2 : V2 , v3 : V3 , v4 : V4 , v5 : V5 , v6 : V6 ) => Promise < T > , t1 : Promise < V1 > , t2 : Promise < V2 > , t3 : Promise < V3 > , t4 : Promise < V4 > , t5 : Promise < V5 > , t6 : Promise < V6 > ) : Promise < T > ;
58
- public async wrapTask < T > (
114
+ public async task < T > ( description : string , duration : number , task : ( ) => Promise < T > ) : Promise < T > ;
115
+ public async task < T , V > ( description : string , duration : number , task : ( v : V ) => Promise < T > , t : Promise < V > ) : Promise < T > ;
116
+ public async task < T , V1 , V2 > ( description : string , duration : number , task : ( v1 : V1 , v2 : V2 ) => Promise < T > , t1 : Promise < V1 > , t2 : Promise < V2 > ) : Promise < T > ;
117
+ public async task < T , V1 , V2 , V3 > ( description : string , duration : number , task : ( v1 : V1 , v2 : V2 , v3 : V3 ) => Promise < T > , t1 : Promise < V1 > , t2 : Promise < V2 > , t3 : Promise < V3 > ) : Promise < T > ;
118
+ public async task < T , V1 , V2 , V3 , V4 > ( description : string , duration : number , task : ( v1 : V1 , v2 : V2 , v3 : V3 , v4 : V4 ) => Promise < T > , t1 : Promise < V1 > , t2 : Promise < V2 > , t3 : Promise < V3 > , t4 : Promise < V4 > ) : Promise < T > ;
119
+ public async task < T , V1 , V2 , V3 , V4 , V5 > ( description : string , duration : number , task : ( v1 : V1 , v2 : V2 , v3 : V3 , v4 : V4 , v5 : V5 ) => Promise < T > , t1 : Promise < V1 > , t2 : Promise < V2 > , t3 : Promise < V3 > , t4 : Promise < V4 > , t5 : Promise < V5 > ) : Promise < T > ;
120
+ public async task < T , V1 , V2 , V3 , V4 , V5 , V6 > ( description : string , duration : number , task : ( v1 : V1 , v2 : V2 , v3 : V3 , v4 : V4 , v5 : V5 , v6 : V6 ) => Promise < T > , t1 : Promise < V1 > , t2 : Promise < V2 > , t3 : Promise < V3 > , t4 : Promise < V4 > , t5 : Promise < V5 > , t6 : Promise < V6 > ) : Promise < T > ;
121
+ public async task < T > (
59
122
description : string , duration : number = 100 , task : ( ...args : any [ ] ) => Promise < T > , ...after : Array < Promise < any > > // tslint:disable-line no-any
60
123
) : Promise < T > {
61
124
this . tasks . push ( description ) ;
@@ -97,4 +160,21 @@ export class Client {
97
160
}
98
161
}
99
162
163
+ /**
164
+ * A promise that resolves with initialization data.
165
+ */
166
+ public get initData ( ) : Promise < InitData > {
167
+ return client . initData ;
168
+ }
169
+
170
+ /**
171
+ * Initialize the IDE.
172
+ */
173
+ protected abstract initialize ( ) : Promise < void > ;
174
+
175
+ /**
176
+ * Create URI factory.
177
+ */
178
+ protected abstract createUriFactory ( ) : IURIFactory ;
179
+
100
180
}
0 commit comments