Skip to content
This repository was archived by the owner on Mar 19, 2024. It is now read-only.

Commit 03b9326

Browse files
authored
Multi crate workaround dialog (#118)
* Format code * Add multi-crate project detection with workarounds
1 parent 1bf6880 commit 03b9326

File tree

2 files changed

+145
-45
lines changed

2 files changed

+145
-45
lines changed

lib/index.js

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,12 @@ async function rustcSysroot(toolchain) {
107107
try {
108108
let { stdout } = await exec(`rustup run ${toolchain} rustc --print sysroot`)
109109
return stdout.trim()
110-
}
111-
catch (e) {
110+
} catch (e) {
112111
// make an attempt to use system rustc
113112
try {
114113
let { stdout } = await exec(`rustc --print sysroot`)
115114
return stdout.trim()
116-
}
117-
catch (sys_e) {
115+
} catch (sys_e) {
118116
throw e
119117
}
120118
}
@@ -146,8 +144,7 @@ async function serverEnv(toolchain) {
146144
try {
147145
let sysroot = await rustcSysroot(toolchain)
148146
env.RUST_SRC_PATH = path.join(sysroot, "/lib/rustlib/src/rust/src/")
149-
}
150-
catch (e) {
147+
} catch (e) {
151148
console.warn("Failed to find sysroot: " + e)
152149
}
153150
}
@@ -161,8 +158,7 @@ async function serverEnv(toolchain) {
161158
async function installRlsComponents(toolchain) {
162159
try {
163160
await exec(`rustup component add rls-preview --toolchain ${toolchain}`)
164-
}
165-
catch (e) {
161+
} catch (e) {
166162
let suggestedVersion
167163
if (toolchain.startsWith('nightly')) {
168164
// 'rls-preview' not available search for a decent suggestion
@@ -190,8 +186,7 @@ async function installRlsComponents(toolchain) {
190186
try {
191187
await exec(`rustup component add rust-src --toolchain ${toolchain}`)
192188
await exec(`rustup component add rust-analysis --toolchain ${toolchain}`)
193-
}
194-
catch (e) {
189+
} catch (e) {
195190
atom.notifications.addError(`\`rust-src\`/\`rust-analysis\` not found on \`${toolchain}\``, {
196191
dismissable: true
197192
})
@@ -210,7 +205,7 @@ function logSuspiciousStdout(process) {
210205
chunk.toString('utf8')
211206
.split('\n')
212207
.filter(l => l.trim() &&
213-
l.length < 10000 && // ignore long chunks, these are much more likely to be false positives
208+
l.length < 10000 && // ignore long chunks, these are much more likely to be false positives
214209
!l.startsWith("Content-Length:") &&
215210
!l.includes('"jsonrpc":"2.0"'))
216211
.forEach(line => console.error("Rust (RLS) suspicious stdout:", line))
@@ -253,8 +248,7 @@ async function checkRls(busySignalService) {
253248

254249
try {
255250
return await _checkingRls
256-
}
257-
finally {
251+
} finally {
258252
_checkingRls = null
259253
}
260254
}
@@ -371,8 +365,7 @@ class RustLanguageClient extends AutoLanguageClient {
371365
try {
372366
await exec(`rustup run ${toolchain} rustc --version`)
373367
clearIdeRustInfos()
374-
}
375-
catch (e) {
368+
} catch (e) {
376369
this._handleMissingToolchain(toolchain)
377370
throw e
378371
}
@@ -385,8 +378,7 @@ class RustLanguageClient extends AutoLanguageClient {
385378
async _handleMissingToolchain(toolchain) {
386379
if (!await exec('rustup --version').catch(() => false)) {
387380
this._handleMissingRustup()
388-
}
389-
else if (await checkHasRls(toolchain)) {
381+
} else if (await checkHasRls(toolchain)) {
390382
let clicked = await atomPrompt(`\`rustup\` missing ${toolchain} toolchain`, {
391383
detail: `rustup toolchain install ${toolchain}`,
392384
}, ['Install'])
@@ -414,8 +406,7 @@ class RustLanguageClient extends AutoLanguageClient {
414406
)
415407
}
416408
}
417-
}
418-
else {
409+
} else {
419410
this._handleMissingToolchainMissingRls(toolchain)
420411
}
421412
}
@@ -436,8 +427,7 @@ class RustLanguageClient extends AutoLanguageClient {
436427

437428
if (toolchain === 'nightly') {
438429
note.description += ' Try using a previous _dated_ nightly.'
439-
}
440-
else if (toolchain.startsWith('nightly')) {
430+
} else if (toolchain.startsWith('nightly')) {
441431
note.description += ' Try using another nightly version.'
442432
}
443433

@@ -457,8 +447,7 @@ class RustLanguageClient extends AutoLanguageClient {
457447
}
458448
})
459449
}
460-
}
461-
catch (e) {
450+
} catch (e) {
462451
console.warn(e)
463452
}
464453

@@ -491,8 +480,7 @@ class RustLanguageClient extends AutoLanguageClient {
491480
.then(() => this._restartLanguageServers())
492481
.catch(logErr)
493482
}
494-
}
495-
catch (e) {
483+
} catch (e) {
496484
e && console.warn(e)
497485
}
498486
}
@@ -518,17 +506,20 @@ class RustLanguageClient extends AutoLanguageClient {
518506

519507
// Watch config toolchain changes -> switch, install & update toolchains, restart servers
520508
this.disposables.add(atom.config.onDidChange('ide-rust.rlsToolchain',
521-
_.debounce(({ newValue }) => {
509+
_.debounce(async ({ newValue }) => {
522510
if (rlsCommandOverride()) {
523511
// don't bother checking toolchain if an override is being used
524512
return
525513
}
526514

527-
return this._checkToolchain()
528-
.then(() => checkRls(this.busySignalService))
529-
.then(() => this._restartLanguageServers(`Switched Rls toolchain to \`${newValue}\``))
530-
.then(() => this._promptToUpdateToolchain())
531-
.catch(e => logErr(e, console.info))
515+
try {
516+
await this._checkToolchain()
517+
await checkRls(this.busySignalService)
518+
await this._restartLanguageServers(`Switched Rls toolchain to \`${newValue}\``)
519+
return this._promptToUpdateToolchain()
520+
} catch (e) {
521+
return logErr(e, console.info)
522+
}
532523
}, 1000)
533524
))
534525

@@ -649,8 +640,7 @@ class RustLanguageClient extends AutoLanguageClient {
649640
env: await serverEnv(toolchain),
650641
cwd: projectPath
651642
}))
652-
}
653-
catch (e) {
643+
} catch (e) {
654644
throw new Error("failed to start server: " + e)
655645
}
656646
}
@@ -699,7 +689,7 @@ if (process.platform === "win32") {
699689
RustLanguageClient.prototype._handleMissingRustup = () => {
700690
atomPrompt("`rustup` is not available", {
701691
description: "`rustup` is required for ide-rust functionality. " +
702-
"**Install from https://www.rustup.rs and restart atom**."
692+
"**Install from https://www.rustup.rs and restart atom**."
703693
})
704694
}
705695
}

lib/rls-project.js

Lines changed: 121 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,37 @@ class RlsProject {
2020
/** @type {?BusyMessage} */
2121
this._rustDocBusyMessage = null
2222

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+
})
2334

2435
// Rls (>= 2018-02-24) sends `window/progress` notifications
2536
// see https://github.com/Microsoft/language-server-protocol/pull/245/files
2637
server.connection.onCustom('window/progress', params => {
2738
const busySignal = this.getBusySignalService()
2839
if (!busySignal) return
2940

30-
let { id, title, message, percentage, done } = params
41+
let {
42+
id,
43+
title,
44+
message,
45+
percentage,
46+
done
47+
} = params
3148
let busyMessage = this._progress.get(id)
3249

3350
if (done) {
3451
if (busyMessage) busyMessage.dispose()
3552
this._progress.delete(id)
36-
}
37-
else {
53+
} else {
3854
let busyText = `${path.basename(this.server.projectPath)} RLS ${title.toLowerCase()}`
3955
if (busyMessage) {
4056
// use previous percentages/messages according to the spec
@@ -46,8 +62,7 @@ class RlsProject {
4662

4763
if (busyMessage) {
4864
busyMessage.setTitle(busyText)
49-
}
50-
else {
65+
} else {
5166
busyMessage = busySignal.reportBusy(busyText)
5267
this._progress.set(id, busyMessage)
5368
}
@@ -68,8 +83,7 @@ class RlsProject {
6883
server.connection.onCustom('rustDocument/beginBuild', () => {
6984
if (this._rustDocBusyMessage) {
7085
this._rustDocBusyMessage.count += 1
71-
}
72-
else {
86+
} else {
7387
let busySignal = this.getBusySignalService()
7488
if (busySignal) {
7589
this._rustDocBusyMessage = busySignal
@@ -83,8 +97,8 @@ class RlsProject {
8397
this._rustDocBusyMessage.count -= 1
8498

8599
if (this._rustDocBusyMessage.count === 0) {
86-
this._rustDocBusyMessage.dispose()
87-
this._rustDocBusyMessage = null
100+
this._rustDocBusyMessage.dispose()
101+
this._rustDocBusyMessage = null
88102
}
89103
}
90104
})
@@ -114,15 +128,21 @@ class RlsProject {
114128
if (_.isEqual(config, this._lastSentConfig)) return
115129

116130
this.server.connection.didChangeConfiguration({
117-
settings: { rust: config }
131+
settings: {
132+
rust: config
133+
}
118134
})
119135
this._lastSentConfig = config
120136
})
121137
}
122138

123139
// Default Rls config according to package settings & Rls defaults
124140
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+
126146
const rlsConfig = {}
127147
if (allTargets === "On" || allTargets === "Off") {
128148
rlsConfig.all_targets = allTargets === "On"
@@ -134,4 +154,94 @@ class RlsProject {
134154
}
135155
}
136156

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+
137247
module.exports = RlsProject

0 commit comments

Comments
 (0)