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

Multi crate workaround dialog #118

Merged
merged 2 commits into from
Dec 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 24 additions & 34 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,12 @@ async function rustcSysroot(toolchain) {
try {
let { stdout } = await exec(`rustup run ${toolchain} rustc --print sysroot`)
return stdout.trim()
}
catch (e) {
} catch (e) {
// make an attempt to use system rustc
try {
let { stdout } = await exec(`rustc --print sysroot`)
return stdout.trim()
}
catch (sys_e) {
} catch (sys_e) {
throw e
}
}
Expand Down Expand Up @@ -146,8 +144,7 @@ async function serverEnv(toolchain) {
try {
let sysroot = await rustcSysroot(toolchain)
env.RUST_SRC_PATH = path.join(sysroot, "/lib/rustlib/src/rust/src/")
}
catch (e) {
} catch (e) {
console.warn("Failed to find sysroot: " + e)
}
}
Expand All @@ -161,8 +158,7 @@ async function serverEnv(toolchain) {
async function installRlsComponents(toolchain) {
try {
await exec(`rustup component add rls-preview --toolchain ${toolchain}`)
}
catch (e) {
} catch (e) {
let suggestedVersion
if (toolchain.startsWith('nightly')) {
// 'rls-preview' not available search for a decent suggestion
Expand Down Expand Up @@ -190,8 +186,7 @@ async function installRlsComponents(toolchain) {
try {
await exec(`rustup component add rust-src --toolchain ${toolchain}`)
await exec(`rustup component add rust-analysis --toolchain ${toolchain}`)
}
catch (e) {
} catch (e) {
atom.notifications.addError(`\`rust-src\`/\`rust-analysis\` not found on \`${toolchain}\``, {
dismissable: true
})
Expand All @@ -210,7 +205,7 @@ function logSuspiciousStdout(process) {
chunk.toString('utf8')
.split('\n')
.filter(l => l.trim() &&
l.length < 10000 && // ignore long chunks, these are much more likely to be false positives
l.length < 10000 && // ignore long chunks, these are much more likely to be false positives
!l.startsWith("Content-Length:") &&
!l.includes('"jsonrpc":"2.0"'))
.forEach(line => console.error("Rust (RLS) suspicious stdout:", line))
Expand Down Expand Up @@ -253,8 +248,7 @@ async function checkRls(busySignalService) {

try {
return await _checkingRls
}
finally {
} finally {
_checkingRls = null
}
}
Expand Down Expand Up @@ -371,8 +365,7 @@ class RustLanguageClient extends AutoLanguageClient {
try {
await exec(`rustup run ${toolchain} rustc --version`)
clearIdeRustInfos()
}
catch (e) {
} catch (e) {
this._handleMissingToolchain(toolchain)
throw e
}
Expand All @@ -385,8 +378,7 @@ class RustLanguageClient extends AutoLanguageClient {
async _handleMissingToolchain(toolchain) {
if (!await exec('rustup --version').catch(() => false)) {
this._handleMissingRustup()
}
else if (await checkHasRls(toolchain)) {
} else if (await checkHasRls(toolchain)) {
let clicked = await atomPrompt(`\`rustup\` missing ${toolchain} toolchain`, {
detail: `rustup toolchain install ${toolchain}`,
}, ['Install'])
Expand Down Expand Up @@ -414,8 +406,7 @@ class RustLanguageClient extends AutoLanguageClient {
)
}
}
}
else {
} else {
this._handleMissingToolchainMissingRls(toolchain)
}
}
Expand All @@ -436,8 +427,7 @@ class RustLanguageClient extends AutoLanguageClient {

if (toolchain === 'nightly') {
note.description += ' Try using a previous _dated_ nightly.'
}
else if (toolchain.startsWith('nightly')) {
} else if (toolchain.startsWith('nightly')) {
note.description += ' Try using another nightly version.'
}

Expand All @@ -457,8 +447,7 @@ class RustLanguageClient extends AutoLanguageClient {
}
})
}
}
catch (e) {
} catch (e) {
console.warn(e)
}

Expand Down Expand Up @@ -491,8 +480,7 @@ class RustLanguageClient extends AutoLanguageClient {
.then(() => this._restartLanguageServers())
.catch(logErr)
}
}
catch (e) {
} catch (e) {
e && console.warn(e)
}
}
Expand All @@ -518,17 +506,20 @@ class RustLanguageClient extends AutoLanguageClient {

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

return this._checkToolchain()
.then(() => checkRls(this.busySignalService))
.then(() => this._restartLanguageServers(`Switched Rls toolchain to \`${newValue}\``))
.then(() => this._promptToUpdateToolchain())
.catch(e => logErr(e, console.info))
try {
await this._checkToolchain()
await checkRls(this.busySignalService)
await this._restartLanguageServers(`Switched Rls toolchain to \`${newValue}\``)
return this._promptToUpdateToolchain()
} catch (e) {
return logErr(e, console.info)
}
}, 1000)
))

Expand Down Expand Up @@ -649,8 +640,7 @@ class RustLanguageClient extends AutoLanguageClient {
env: await serverEnv(toolchain),
cwd: projectPath
}))
}
catch (e) {
} catch (e) {
throw new Error("failed to start server: " + e)
}
}
Expand Down Expand Up @@ -699,7 +689,7 @@ if (process.platform === "win32") {
RustLanguageClient.prototype._handleMissingRustup = () => {
atomPrompt("`rustup` is not available", {
description: "`rustup` is required for ide-rust functionality. " +
"**Install from https://www.rustup.rs and restart atom**."
"**Install from https://www.rustup.rs and restart atom**."
})
}
}
Expand Down
132 changes: 121 additions & 11 deletions lib/rls-project.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,37 @@ class RlsProject {
/** @type {?BusyMessage} */
this._rustDocBusyMessage = null

this._disposable = atom.notifications.onDidAddNotification(async note => {
if (this._disposable &&
(!this.server ||
!this.server.connection ||
!this.server.connection.isConnected)) {
this._disposable.dispose()
return
}

await handleMultiCrateProjectErrors(this.server.projectPath, note)
})

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

let { id, title, message, percentage, done } = params
let {
id,
title,
message,
percentage,
done
} = params
let busyMessage = this._progress.get(id)

if (done) {
if (busyMessage) busyMessage.dispose()
this._progress.delete(id)
}
else {
} else {
let busyText = `${path.basename(this.server.projectPath)} RLS ${title.toLowerCase()}`
if (busyMessage) {
// use previous percentages/messages according to the spec
Expand All @@ -46,8 +62,7 @@ class RlsProject {

if (busyMessage) {
busyMessage.setTitle(busyText)
}
else {
} else {
busyMessage = busySignal.reportBusy(busyText)
this._progress.set(id, busyMessage)
}
Expand All @@ -68,8 +83,7 @@ class RlsProject {
server.connection.onCustom('rustDocument/beginBuild', () => {
if (this._rustDocBusyMessage) {
this._rustDocBusyMessage.count += 1
}
else {
} else {
let busySignal = this.getBusySignalService()
if (busySignal) {
this._rustDocBusyMessage = busySignal
Expand All @@ -83,8 +97,8 @@ class RlsProject {
this._rustDocBusyMessage.count -= 1

if (this._rustDocBusyMessage.count === 0) {
this._rustDocBusyMessage.dispose()
this._rustDocBusyMessage = null
this._rustDocBusyMessage.dispose()
this._rustDocBusyMessage = null
}
}
})
Expand Down Expand Up @@ -114,15 +128,21 @@ class RlsProject {
if (_.isEqual(config, this._lastSentConfig)) return

this.server.connection.didChangeConfiguration({
settings: { rust: config }
settings: {
rust: config
}
})
this._lastSentConfig = config
})
}

// Default Rls config according to package settings & Rls defaults
defaultConfig() {
const { allTargets, clippyPreference } = atom.config.get("ide-rust.rlsDefaultConfig")
const {
allTargets,
clippyPreference
} = atom.config.get("ide-rust.rlsDefaultConfig")

const rlsConfig = {}
if (allTargets === "On" || allTargets === "Off") {
rlsConfig.all_targets = allTargets === "On"
Expand All @@ -134,4 +154,94 @@ class RlsProject {
}
}

/**
* Converts fs async callback functions to use promises
* @param {function} functionWithCallback
* @return {function} async function
*/
function callbackAsync(functionWithCallback) {
return async (...args) => {
return new Promise((resolve, reject) => {
functionWithCallback(...args, (err, ...out) => {
if (err) {
reject(err)
} else {
resolve(...out)
}
})
})
}
}

const asyncLstat = callbackAsync(fs.lstat)

/**
* Check error notifications to see if the cause is a multi-crate project & offer help.
*
* See https://github.com/rust-lang/atom-ide-rust#multi-crate-projects
*
* @param {string} projectPath
* @param {Notification} errorNote
*/
async function handleMultiCrateProjectErrors(projectPath, errorNote) {
const options = errorNote.options || {}
const detail = options.detail || ''

if (options._src !== 'ide-rust' &&
errorNote.getType() === 'error' &&
(errorNote.getMessage() || '').startsWith('could not find `Cargo.toml`') &&
detail.endsWith(projectPath)) {

let root_manifest = await (asyncLstat(path.join(projectPath, 'Cargo.toml')).catch(() => false))
if (root_manifest) {
return
}

try {
const ls = await callbackAsync(fs.readdir)(projectPath)
const childProjects = []
for (const f of ls) {
let file = path.join(projectPath, f)
let stat = await asyncLstat(file)
if (stat.isDirectory()) {
let has_manifest = await (asyncLstat(path.join(file, 'Cargo.toml')).catch(() => false))
if (has_manifest) {
childProjects.push(f)
}
}
}

if (childProjects.length) {
let newNote
const projects = childProjects.map(p => `"${p}"`).join(', ')
const workspaceManifest = `[workspace]\nmembers = [${projects}]`
let options = {
_src: 'ide-rust',
dismissable: true,
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)`,
buttons: [{
text: 'Add workspace Cargo.toml',
onDidClick: async () => {
await callbackAsync(fs.writeFile)(path.join(projectPath, 'Cargo.toml'), workspaceManifest)
newNote.dismiss()
errorNote.dismiss()
}
}, {
text: 'Ignore project',
onDidClick: () => {
const ignoredPaths = atom.config.get('ide-rust.ignoredProjectPaths')
atom.config.set('ide-rust.ignoredProjectPaths', [ignoredPaths, projectPath].join(', '))
newNote.dismiss()
errorNote.dismiss()
}
}]
}
newNote = atom.notifications.addInfo('Multi-crate project detected', options)
}
} catch (e) {
console.warn(e)
}
}
}

module.exports = RlsProject