@@ -20,21 +20,37 @@ class RlsProject {
20
20
/** @type {?BusyMessage } */
21
21
this . _rustDocBusyMessage = null
22
22
23
+ this . _disposable = atom . notifications . onDidAddNotification ( async note => {
24
+ if ( this . _disposable &&
25
+ ( ! this . server ||
26
+ ! this . server . connection ||
27
+ ! this . server . connection . isConnected ) ) {
28
+ this . _disposable . dispose ( )
29
+ return
30
+ }
31
+
32
+ await handleMultiCrateProjectErrors ( this . server . projectPath , note )
33
+ } )
23
34
24
35
// Rls (>= 2018-02-24) sends `window/progress` notifications
25
36
// see https://github.com/Microsoft/language-server-protocol/pull/245/files
26
37
server . connection . onCustom ( 'window/progress' , params => {
27
38
const busySignal = this . getBusySignalService ( )
28
39
if ( ! busySignal ) return
29
40
30
- let { id, title, message, percentage, done } = params
41
+ let {
42
+ id,
43
+ title,
44
+ message,
45
+ percentage,
46
+ done
47
+ } = params
31
48
let busyMessage = this . _progress . get ( id )
32
49
33
50
if ( done ) {
34
51
if ( busyMessage ) busyMessage . dispose ( )
35
52
this . _progress . delete ( id )
36
- }
37
- else {
53
+ } else {
38
54
let busyText = `${ path . basename ( this . server . projectPath ) } RLS ${ title . toLowerCase ( ) } `
39
55
if ( busyMessage ) {
40
56
// use previous percentages/messages according to the spec
@@ -46,8 +62,7 @@ class RlsProject {
46
62
47
63
if ( busyMessage ) {
48
64
busyMessage . setTitle ( busyText )
49
- }
50
- else {
65
+ } else {
51
66
busyMessage = busySignal . reportBusy ( busyText )
52
67
this . _progress . set ( id , busyMessage )
53
68
}
@@ -68,8 +83,7 @@ class RlsProject {
68
83
server . connection . onCustom ( 'rustDocument/beginBuild' , ( ) => {
69
84
if ( this . _rustDocBusyMessage ) {
70
85
this . _rustDocBusyMessage . count += 1
71
- }
72
- else {
86
+ } else {
73
87
let busySignal = this . getBusySignalService ( )
74
88
if ( busySignal ) {
75
89
this . _rustDocBusyMessage = busySignal
@@ -83,8 +97,8 @@ class RlsProject {
83
97
this . _rustDocBusyMessage . count -= 1
84
98
85
99
if ( this . _rustDocBusyMessage . count === 0 ) {
86
- this . _rustDocBusyMessage . dispose ( )
87
- this . _rustDocBusyMessage = null
100
+ this . _rustDocBusyMessage . dispose ( )
101
+ this . _rustDocBusyMessage = null
88
102
}
89
103
}
90
104
} )
@@ -114,15 +128,21 @@ class RlsProject {
114
128
if ( _ . isEqual ( config , this . _lastSentConfig ) ) return
115
129
116
130
this . server . connection . didChangeConfiguration ( {
117
- settings : { rust : config }
131
+ settings : {
132
+ rust : config
133
+ }
118
134
} )
119
135
this . _lastSentConfig = config
120
136
} )
121
137
}
122
138
123
139
// Default Rls config according to package settings & Rls defaults
124
140
defaultConfig ( ) {
125
- const { allTargets, clippyPreference } = atom . config . get ( "ide-rust.rlsDefaultConfig" )
141
+ const {
142
+ allTargets,
143
+ clippyPreference
144
+ } = atom . config . get ( "ide-rust.rlsDefaultConfig" )
145
+
126
146
const rlsConfig = { }
127
147
if ( allTargets === "On" || allTargets === "Off" ) {
128
148
rlsConfig . all_targets = allTargets === "On"
@@ -134,4 +154,94 @@ class RlsProject {
134
154
}
135
155
}
136
156
157
+ /**
158
+ * Converts fs async callback functions to use promises
159
+ * @param {function } functionWithCallback
160
+ * @return {function } async function
161
+ */
162
+ function callbackAsync ( functionWithCallback ) {
163
+ return async ( ...args ) => {
164
+ return new Promise ( ( resolve , reject ) => {
165
+ functionWithCallback ( ...args , ( err , ...out ) => {
166
+ if ( err ) {
167
+ reject ( err )
168
+ } else {
169
+ resolve ( ...out )
170
+ }
171
+ } )
172
+ } )
173
+ }
174
+ }
175
+
176
+ const asyncLstat = callbackAsync ( fs . lstat )
177
+
178
+ /**
179
+ * Check error notifications to see if the cause is a multi-crate project & offer help.
180
+ *
181
+ * See https://github.com/rust-lang/atom-ide-rust#multi-crate-projects
182
+ *
183
+ * @param {string } projectPath
184
+ * @param {Notification } errorNote
185
+ */
186
+ async function handleMultiCrateProjectErrors ( projectPath , errorNote ) {
187
+ const options = errorNote . options || { }
188
+ const detail = options . detail || ''
189
+
190
+ if ( options . _src !== 'ide-rust' &&
191
+ errorNote . getType ( ) === 'error' &&
192
+ ( errorNote . getMessage ( ) || '' ) . startsWith ( 'could not find `Cargo.toml`' ) &&
193
+ detail . endsWith ( projectPath ) ) {
194
+
195
+ let root_manifest = await ( asyncLstat ( path . join ( projectPath , 'Cargo.toml' ) ) . catch ( ( ) => false ) )
196
+ if ( root_manifest ) {
197
+ return
198
+ }
199
+
200
+ try {
201
+ const ls = await callbackAsync ( fs . readdir ) ( projectPath )
202
+ const childProjects = [ ]
203
+ for ( const f of ls ) {
204
+ let file = path . join ( projectPath , f )
205
+ let stat = await asyncLstat ( file )
206
+ if ( stat . isDirectory ( ) ) {
207
+ let has_manifest = await ( asyncLstat ( path . join ( file , 'Cargo.toml' ) ) . catch ( ( ) => false ) )
208
+ if ( has_manifest ) {
209
+ childProjects . push ( f )
210
+ }
211
+ }
212
+ }
213
+
214
+ if ( childProjects . length ) {
215
+ let newNote
216
+ const projects = childProjects . map ( p => `"${ p } "` ) . join ( ', ' )
217
+ const workspaceManifest = `[workspace]\nmembers = [${ projects } ]`
218
+ let options = {
219
+ _src : 'ide-rust' ,
220
+ dismissable : true ,
221
+ description : `Child projects without a root (or higher) workspace are not supported. A root manifest at _${ path . join ( projectPath , 'Cargo.toml' ) } _ could allow RLS to build the projects as a workspace.\n\nSee [atom-ide-rust#multi-crate-projects](https://github.com/rust-lang/atom-ide-rust#multi-crate-projects)` ,
222
+ buttons : [ {
223
+ text : 'Add workspace Cargo.toml' ,
224
+ onDidClick : async ( ) => {
225
+ await callbackAsync ( fs . writeFile ) ( path . join ( projectPath , 'Cargo.toml' ) , workspaceManifest )
226
+ newNote . dismiss ( )
227
+ errorNote . dismiss ( )
228
+ }
229
+ } , {
230
+ text : 'Ignore project' ,
231
+ onDidClick : ( ) => {
232
+ const ignoredPaths = atom . config . get ( 'ide-rust.ignoredProjectPaths' )
233
+ atom . config . set ( 'ide-rust.ignoredProjectPaths' , [ ignoredPaths , projectPath ] . join ( ', ' ) )
234
+ newNote . dismiss ( )
235
+ errorNote . dismiss ( )
236
+ }
237
+ } ]
238
+ }
239
+ newNote = atom . notifications . addInfo ( 'Multi-crate project detected' , options )
240
+ }
241
+ } catch ( e ) {
242
+ console . warn ( e )
243
+ }
244
+ }
245
+ }
246
+
137
247
module . exports = RlsProject
0 commit comments