From b4dc64b575d72a85c34d28f9e3de8beebbf7b2cd Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Fri, 10 Aug 2018 01:40:28 +0200 Subject: [PATCH 01/18] Add Rust language service via nuclide-rust --- pkg/nuclide-rust/lib/RustLanguage.js | 106 +++++++++++++++++++++++++++ pkg/nuclide-rust/lib/main.js | 27 +++++++ pkg/nuclide-rust/package.json | 14 ++++ 3 files changed, 147 insertions(+) create mode 100644 pkg/nuclide-rust/lib/RustLanguage.js create mode 100644 pkg/nuclide-rust/lib/main.js create mode 100644 pkg/nuclide-rust/package.json diff --git a/pkg/nuclide-rust/lib/RustLanguage.js b/pkg/nuclide-rust/lib/RustLanguage.js new file mode 100644 index 0000000000..12f6796c3b --- /dev/null +++ b/pkg/nuclide-rust/lib/RustLanguage.js @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the license found in the LICENSE file in + * the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import type {ServerConnection} from '../../nuclide-remote-connection'; +import type {AtomLanguageServiceConfig} from '../../nuclide-language-service/lib/AtomLanguageService'; +import type {LanguageService} from '../../nuclide-language-service/lib/LanguageService'; + +import { + AtomLanguageService, + getHostServices, +} from '../../nuclide-language-service'; +import {NullLanguageService} from '../../nuclide-language-service-rpc'; +import {getNotifierByConnection} from '../../nuclide-open-files'; +import {getVSCodeLanguageServiceByConnection} from '../../nuclide-remote-connection'; + +async function connectionToRustService( + connection: ?ServerConnection, +): Promise { + const [fileNotifier, host] = await Promise.all([ + getNotifierByConnection(connection), + getHostServices(), + ]); + const cmd = 'rls'; + const lspService = await getVSCodeLanguageServiceByConnection( + connection, + ).createMultiLspLanguageService( + 'rust', + cmd, + [], + { + fileNotifier, + host, + projectFileNames: ['Cargo.toml'], + fileExtensions: ['.rs'], + logCategory: 'nuclide-rust', + logLevel: 'TRACE', + useOriginalEnvironment: true, + additionalLogFilesRetentionPeriod: 5 * 60 * 1000, // 5 minutes + waitForDiagnostics: true, + }, + ); + + return lspService || new NullLanguageService(); +} + +async function createLanguageService(): Promise< + AtomLanguageService, +> { + // TODO: Fill in more, already supported features + const diagnosticsConfig = { + version: '0.2.0', + analyticsEventName: 'rust.observe-diagnostics', + }; + + const definitionConfig = { + version: '0.1.0', + priority: 1, + definitionEventName: 'rust.definition', + }; + + const typeHint = { + version: '0.0.0', + priority: 1, + analyticsEventName: 'ocaml.typeHint', + }; + + const autocompleteConfig = { + inclusionPriority: 1, + suggestionPriority: 3, + excludeLowerPriority: false, + analytics: { + eventName: 'nuclide-rust', + shouldLogInsertedSuggestion: false, + }, + disableForSelector: null, + autocompleteCacherConfig: null, + supportsResolve: false, + }; + + const atomConfig: AtomLanguageServiceConfig = { + name: 'Rust', + grammars: ['source.rust'], + diagnostics: diagnosticsConfig, + definition: definitionConfig, + autocomplete: autocompleteConfig, + typeHint: typeHint, + }; + return new AtomLanguageService(connectionToRustService, atomConfig); +} + +export let rustLanguageService: Promise< + AtomLanguageService, +> = createLanguageService(); + +export function resetRustLanguageService(): void { + rustLanguageService.then(value => value.dispose()); + rustLanguageService = createLanguageService(); +} diff --git a/pkg/nuclide-rust/lib/main.js b/pkg/nuclide-rust/lib/main.js new file mode 100644 index 0000000000..48e4b22fe7 --- /dev/null +++ b/pkg/nuclide-rust/lib/main.js @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the license found in the LICENSE file in + * the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import { + rustLanguageService, + resetRustLanguageService, +} from './RustLanguage'; + +export function activate() { + if (process.platform !== 'win32') { + rustLanguageService.then(value => value.activate()); + } +} + +export function deactivate(): void { + if (process.platform !== 'win32') { + resetRustLanguageService(); + } +} diff --git a/pkg/nuclide-rust/package.json b/pkg/nuclide-rust/package.json new file mode 100644 index 0000000000..d8ffd2da0b --- /dev/null +++ b/pkg/nuclide-rust/package.json @@ -0,0 +1,14 @@ +{ + "name": "nuclide-rust", + "main": "./lib/main.js", + "version": "0.0.0", + "description": "Provides Rust language support in Nuclide", + "author": "NEEDS OWNER", + "atomTestRunner": "../../lib/test-runner-entry.js", + "nuclide": { + "packageType": "AtomPackage" + }, + "activationHooks": [ + "language-rust:grammar-used" + ] +} From 2cd3106a40c6a14d11c7fd37fe9010cd7913d960 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Fri, 10 Aug 2018 01:59:04 +0200 Subject: [PATCH 02/18] Run eslint --- pkg/nuclide-rust/lib/RustLanguage.js | 27 +++++++++++---------------- pkg/nuclide-rust/lib/main.js | 5 +---- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/pkg/nuclide-rust/lib/RustLanguage.js b/pkg/nuclide-rust/lib/RustLanguage.js index 12f6796c3b..863765047f 100644 --- a/pkg/nuclide-rust/lib/RustLanguage.js +++ b/pkg/nuclide-rust/lib/RustLanguage.js @@ -31,22 +31,17 @@ async function connectionToRustService( const cmd = 'rls'; const lspService = await getVSCodeLanguageServiceByConnection( connection, - ).createMultiLspLanguageService( - 'rust', - cmd, - [], - { - fileNotifier, - host, - projectFileNames: ['Cargo.toml'], - fileExtensions: ['.rs'], - logCategory: 'nuclide-rust', - logLevel: 'TRACE', - useOriginalEnvironment: true, - additionalLogFilesRetentionPeriod: 5 * 60 * 1000, // 5 minutes + ).createMultiLspLanguageService('rust', cmd, [], { + fileNotifier, + host, + projectFileNames: ['Cargo.toml'], + fileExtensions: ['.rs'], + logCategory: 'nuclide-rust', + logLevel: 'TRACE', + useOriginalEnvironment: true, + additionalLogFilesRetentionPeriod: 5 * 60 * 1000, // 5 minutes waitForDiagnostics: true, - }, - ); + }); return lspService || new NullLanguageService(); } @@ -91,7 +86,7 @@ async function createLanguageService(): Promise< diagnostics: diagnosticsConfig, definition: definitionConfig, autocomplete: autocompleteConfig, - typeHint: typeHint, + typeHint, }; return new AtomLanguageService(connectionToRustService, atomConfig); } diff --git a/pkg/nuclide-rust/lib/main.js b/pkg/nuclide-rust/lib/main.js index 48e4b22fe7..b3a42e2f50 100644 --- a/pkg/nuclide-rust/lib/main.js +++ b/pkg/nuclide-rust/lib/main.js @@ -9,10 +9,7 @@ * @format */ -import { - rustLanguageService, - resetRustLanguageService, -} from './RustLanguage'; +import {rustLanguageService, resetRustLanguageService} from './RustLanguage'; export function activate() { if (process.platform !== 'win32') { From 0809729b15fc67e2df2a0d1b5f326c39a08962c7 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Fri, 10 Aug 2018 16:58:38 +0200 Subject: [PATCH 03/18] Clean up and add more LSP capabilities --- pkg/nuclide-rust/lib/RustLanguage.js | 109 +++++++++++++++------------ 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/pkg/nuclide-rust/lib/RustLanguage.js b/pkg/nuclide-rust/lib/RustLanguage.js index 863765047f..0b55d8513b 100644 --- a/pkg/nuclide-rust/lib/RustLanguage.js +++ b/pkg/nuclide-rust/lib/RustLanguage.js @@ -28,20 +28,25 @@ async function connectionToRustService( getNotifierByConnection(connection), getHostServices(), ]); + const service = getVSCodeLanguageServiceByConnection(connection); + const cmd = 'rls'; - const lspService = await getVSCodeLanguageServiceByConnection( - connection, - ).createMultiLspLanguageService('rust', cmd, [], { - fileNotifier, - host, - projectFileNames: ['Cargo.toml'], - fileExtensions: ['.rs'], - logCategory: 'nuclide-rust', - logLevel: 'TRACE', - useOriginalEnvironment: true, - additionalLogFilesRetentionPeriod: 5 * 60 * 1000, // 5 minutes - waitForDiagnostics: true, - }); + const lspService = await service.createMultiLspLanguageService( + 'rust', + cmd, + [], + { + fileNotifier, + host, + projectFileNames: ['Cargo.toml', '.buckconfig'], + fileExtensions: ['.rs'], + logCategory: 'nuclide-rust', + logLevel: 'TRACE', + useOriginalEnvironment: true, + additionalLogFilesRetentionPeriod: 5 * 60 * 1000, // 5 minutes + waitForDiagnostics: true, + }, + ); return lspService || new NullLanguageService(); } @@ -49,45 +54,53 @@ async function connectionToRustService( async function createLanguageService(): Promise< AtomLanguageService, > { - // TODO: Fill in more, already supported features - const diagnosticsConfig = { - version: '0.2.0', - analyticsEventName: 'rust.observe-diagnostics', - }; - - const definitionConfig = { - version: '0.1.0', - priority: 1, - definitionEventName: 'rust.definition', - }; - - const typeHint = { - version: '0.0.0', - priority: 1, - analyticsEventName: 'ocaml.typeHint', - }; - - const autocompleteConfig = { - inclusionPriority: 1, - suggestionPriority: 3, - excludeLowerPriority: false, - analytics: { - eventName: 'nuclide-rust', - shouldLogInsertedSuggestion: false, - }, - disableForSelector: null, - autocompleteCacherConfig: null, - supportsResolve: false, - }; - const atomConfig: AtomLanguageServiceConfig = { name: 'Rust', grammars: ['source.rust'], - diagnostics: diagnosticsConfig, - definition: definitionConfig, - autocomplete: autocompleteConfig, - typeHint, + diagnostics: { + version: '0.2.0', + analyticsEventName: 'rust.observe-diagnostics', + }, + definition: { + version: '0.1.0', + priority: 1, + definitionEventName: 'rust.definition', + }, + codeFormat: { + version: '0.1.0', + priority: 1, + analyticsEventName: 'rust.formatCode', + canFormatRanges: true, + canFormatAtPosition: true, + }, + findReferences: { + version: '0.1.0', + analyticsEventName: 'rust.get-references', + }, + rename: { + version: '0.0.0', + priority: 1, + analyticsEventName: 'rust:rename', + }, + autocomplete: { + inclusionPriority: 1, + suggestionPriority: 3, + excludeLowerPriority: false, + analytics: { + eventName: 'nuclide-rust', + shouldLogInsertedSuggestion: false, + }, + disableForSelector: '.source.rust .comment, .source.rust .string', + autocompleteCacherConfig: null, + supportsResolve: false, + }, + typeHint: { + version: '0.0.0', + priority: 1, + analyticsEventName: 'rust.typeHint', + }, }; + return new AtomLanguageService(connectionToRustService, atomConfig); } From a446661d385c954521eb515fadc02175271636c9 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Sat, 11 Aug 2018 15:43:51 +0200 Subject: [PATCH 04/18] Clean up and use createPackage format Also consume Buck task service to later rebuild index on save --- pkg/nuclide-rust/lib/RustLanguage.js | 105 ++++++++++++--------------- pkg/nuclide-rust/lib/main.js | 40 +++++++--- pkg/nuclide-rust/package.json | 17 ++++- 3 files changed, 94 insertions(+), 68 deletions(-) diff --git a/pkg/nuclide-rust/lib/RustLanguage.js b/pkg/nuclide-rust/lib/RustLanguage.js index 0b55d8513b..60d4944daf 100644 --- a/pkg/nuclide-rust/lib/RustLanguage.js +++ b/pkg/nuclide-rust/lib/RustLanguage.js @@ -51,64 +51,55 @@ async function connectionToRustService( return lspService || new NullLanguageService(); } -async function createLanguageService(): Promise< - AtomLanguageService, -> { - const atomConfig: AtomLanguageServiceConfig = { - name: 'Rust', - grammars: ['source.rust'], - diagnostics: { - version: '0.2.0', - analyticsEventName: 'rust.observe-diagnostics', - }, - definition: { - version: '0.1.0', - priority: 1, - definitionEventName: 'rust.definition', - }, - codeFormat: { - version: '0.1.0', - priority: 1, - analyticsEventName: 'rust.formatCode', - canFormatRanges: true, - canFormatAtPosition: true, - }, - findReferences: { - version: '0.1.0', - analyticsEventName: 'rust.get-references', - }, - rename: { - version: '0.0.0', - priority: 1, - analyticsEventName: 'rust:rename', - }, - autocomplete: { - inclusionPriority: 1, - suggestionPriority: 3, - excludeLowerPriority: false, - analytics: { - eventName: 'nuclide-rust', - shouldLogInsertedSuggestion: false, - }, - disableForSelector: '.source.rust .comment, .source.rust .string', - autocompleteCacherConfig: null, - supportsResolve: false, +export const atomConfig: AtomLanguageServiceConfig = { + name: 'Rust', + grammars: ['source.rust'], + diagnostics: { + version: '0.2.0', + analyticsEventName: 'rust.observe-diagnostics', + }, + definition: { + version: '0.1.0', + priority: 1, + definitionEventName: 'rust.definition', + }, + codeFormat: { + version: '0.1.0', + priority: 1, + analyticsEventName: 'rust.formatCode', + canFormatRanges: true, + canFormatAtPosition: true, + }, + findReferences: { + version: '0.1.0', + analyticsEventName: 'rust.get-references', + }, + rename: { + version: '0.0.0', + priority: 1, + analyticsEventName: 'rust:rename', + }, + autocomplete: { + inclusionPriority: 1, + suggestionPriority: 3, + excludeLowerPriority: false, + analytics: { + eventName: 'nuclide-rust', + shouldLogInsertedSuggestion: false, }, - typeHint: { - version: '0.0.0', - priority: 1, - analyticsEventName: 'rust.typeHint', - }, - }; + disableForSelector: '.source.rust .comment, .source.rust .string', + autocompleteCacherConfig: null, + supportsResolve: false, + }, + typeHint: { + version: '0.0.0', + priority: 1, + analyticsEventName: 'rust.typeHint', + }, +}; +export function createRustLanguageService(): AtomLanguageService< + LanguageService, +> { return new AtomLanguageService(connectionToRustService, atomConfig); } - -export let rustLanguageService: Promise< - AtomLanguageService, -> = createLanguageService(); - -export function resetRustLanguageService(): void { - rustLanguageService.then(value => value.dispose()); - rustLanguageService = createLanguageService(); -} diff --git a/pkg/nuclide-rust/lib/main.js b/pkg/nuclide-rust/lib/main.js index b3a42e2f50..a853cfb607 100644 --- a/pkg/nuclide-rust/lib/main.js +++ b/pkg/nuclide-rust/lib/main.js @@ -5,20 +5,42 @@ * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. * - * @flow strict-local + * @flow * @format */ -import {rustLanguageService, resetRustLanguageService} from './RustLanguage'; +import type {BuckTaskRunnerService} from '../../nuclide-buck/lib/types'; +import type { + AtomLanguageService, + LanguageService, +} from '../../nuclide-language-service'; -export function activate() { - if (process.platform !== 'win32') { - rustLanguageService.then(value => value.activate()); +import createPackage from 'nuclide-commons-atom/createPackage'; +import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; +import {createRustLanguageService} from './RustLanguage'; + +class Activation { + _rustLanguageService: AtomLanguageService; + _buckTaskRunnerService: ?BuckTaskRunnerService; + _subscriptions: UniversalDisposable; + + constructor(rawState: ?Object) { + this._rustLanguageService = createRustLanguageService(); + this._rustLanguageService.activate(); + + this._subscriptions = new UniversalDisposable(this._rustLanguageService); } -} -export function deactivate(): void { - if (process.platform !== 'win32') { - resetRustLanguageService(); + consumeBuckTaskRunner(service: BuckTaskRunnerService): IDisposable { + this._buckTaskRunnerService = service; + return new UniversalDisposable(() => { + this._buckTaskRunnerService = null; + }); + } + + dispose(): void { + this._subscriptions.dispose(); } } + +createPackage(module.exports, Activation); diff --git a/pkg/nuclide-rust/package.json b/pkg/nuclide-rust/package.json index d8ffd2da0b..49554cb1c1 100644 --- a/pkg/nuclide-rust/package.json +++ b/pkg/nuclide-rust/package.json @@ -6,9 +6,22 @@ "author": "NEEDS OWNER", "atomTestRunner": "../../lib/test-runner-entry.js", "nuclide": { - "packageType": "AtomPackage" + "packageType": "AtomPackage", + "configMetadata": { + "pathComponents": [ + "Language", + "Rust" + ] + } }, "activationHooks": [ "language-rust:grammar-used" - ] + ], + "consumedServices": { + "nuclide.buck-task-runner": { + "versions": { + "0.0.0": "consumeBuckTaskRunner" + } + } + } } From 672273810a6d1efa85bec6104a22561284521f39 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Sun, 12 Aug 2018 01:36:08 +0200 Subject: [PATCH 05/18] Allow to override RLS path --- pkg/nuclide-rust/lib/RustLanguage.js | 10 +++++++--- pkg/nuclide-rust/package.json | 8 ++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/pkg/nuclide-rust/lib/RustLanguage.js b/pkg/nuclide-rust/lib/RustLanguage.js index 60d4944daf..abdc42f0ce 100644 --- a/pkg/nuclide-rust/lib/RustLanguage.js +++ b/pkg/nuclide-rust/lib/RustLanguage.js @@ -5,7 +5,7 @@ * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. * - * @flow strict-local + * @flow * @format */ @@ -13,6 +13,7 @@ import type {ServerConnection} from '../../nuclide-remote-connection'; import type {AtomLanguageServiceConfig} from '../../nuclide-language-service/lib/AtomLanguageService'; import type {LanguageService} from '../../nuclide-language-service/lib/LanguageService'; +import featureConfig from 'nuclide-commons-atom/feature-config'; import { AtomLanguageService, getHostServices, @@ -21,6 +22,10 @@ import {NullLanguageService} from '../../nuclide-language-service-rpc'; import {getNotifierByConnection} from '../../nuclide-open-files'; import {getVSCodeLanguageServiceByConnection} from '../../nuclide-remote-connection'; +export function getRlsPath(): string { + return (featureConfig.get('nuclide-rust.rlsPath'): any); +} + async function connectionToRustService( connection: ?ServerConnection, ): Promise { @@ -30,10 +35,9 @@ async function connectionToRustService( ]); const service = getVSCodeLanguageServiceByConnection(connection); - const cmd = 'rls'; const lspService = await service.createMultiLspLanguageService( 'rust', - cmd, + getRlsPath(), [], { fileNotifier, diff --git a/pkg/nuclide-rust/package.json b/pkg/nuclide-rust/package.json index 49554cb1c1..6ea7ce0923 100644 --- a/pkg/nuclide-rust/package.json +++ b/pkg/nuclide-rust/package.json @@ -12,6 +12,14 @@ "Language", "Rust" ] + }, + "config": { + "rlsPath": { + "title": "Rust Language Server path", + "type": "string", + "default": "rls", + "description": "Path to the RLS executable. By default uses rustup-managed 'rls' executable." + } } }, "activationHooks": [ From b2f7f11b5e490cf192c84a351963b0e4630dd93b Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Sun, 12 Aug 2018 01:47:46 +0200 Subject: [PATCH 06/18] Update RLS index after Buck build After hitting Buck build button, the relevant save-analysis Buck build is performed and RLS is supplied with a dummy build command to retrieve necessary save-analysis indexing files. Things left to do/worth noting: * We should detect if Buck is an appropriate build system for a given LSP service and if not, not override RLS' external build command * Until path remapping lands in rustc (https://github.com/rust-lang/rust/pull/53110) the index-based features (find refs, goto def etc.) only work for source files in the buck-out/**/$TARGET#save-analysis-container source files. * RLS needs some bugfixin' since currently external build command does not properly refresh file dirty state, leading to an endless build loop. --- pkg/nuclide-rust/lib/BuckIntegration.js | 85 +++++++++++++++++++++++++ pkg/nuclide-rust/lib/BuckUtils.js | 50 +++++++++++++++ pkg/nuclide-rust/lib/RustLanguage.js | 5 ++ pkg/nuclide-rust/lib/main.js | 4 ++ 4 files changed, 144 insertions(+) create mode 100644 pkg/nuclide-rust/lib/BuckIntegration.js create mode 100644 pkg/nuclide-rust/lib/BuckUtils.js diff --git a/pkg/nuclide-rust/lib/BuckIntegration.js b/pkg/nuclide-rust/lib/BuckIntegration.js new file mode 100644 index 0000000000..6c91bb892c --- /dev/null +++ b/pkg/nuclide-rust/lib/BuckIntegration.js @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the license found in the LICENSE file in + * the root directory of this source tree. + * + * @flow + * @format + */ + +import type {TaskInfo} from '../../nuclide-buck/lib/types'; +import type { + AtomLanguageService, + LanguageService, +} from '../../nuclide-language-service'; + +import invariant from 'invariant'; +import {getLogger} from 'log4js'; +import fsPromise from 'nuclide-commons/fsPromise'; +import nuclideUri from 'nuclide-commons/nuclideUri'; +import {getRustInputs, getSaveAnalysisTargets, normalizeNameForBuckQuery} from './BuckUtils'; + +import * as BuckService from '../../nuclide-buck-rpc'; + +const logger = getLogger('nuclide-rust'); + +export async function updateRlsBuildForTask( + task: TaskInfo, + service: AtomLanguageService, +) { + const buildTarget = normalizeNameForBuckQuery(task.buildTarget); + + const files = await getRustInputs(task.buckRoot, buildTarget); + // Probably not a Rust build target, ignore + if (files.length == 0) + return; + // We need only to pick a representative file to get a related lang service + const fileUri = task.buckRoot + '/' + files[0]; + atom.notifications.addInfo(`fileUri: ${fileUri}`); + + const langService = await service.getLanguageServiceForUri(fileUri); + invariant(langService != null); + + // Since `buck` execution is not trivial - the command may be overriden, needs + // to inherit the environment, passes internal FB USER to env etc. the RLS + // can't just invoke that. + // Instead, we build now, copy paths to resulting .json analysis artifacts to + // a temp file and just use `cat $TMPFILE` as a dummy build command. + const analysisTargets = await getSaveAnalysisTargets(task.buckRoot, buildTarget); + logger.debug(`analysisTargets: ${analysisTargets.join('\n')}`); + let artifacts: Array = []; + + const buildReport = await BuckService.build(task.buckRoot, analysisTargets); + if (buildReport.success === false) { + atom.notifications.addError("save-analysis build failed"); + return; + } + + Object.values(buildReport.results) + // TODO: https://buckbuild.com/command/build.html specifies that for + // FETCHED_FROM_CACHE we might not get an output file - can we force it + // somehow? Or we always locally produce a save-analysis .json file? + .forEach((targetReport: any) => artifacts.push(targetReport.output)); + + const tempfile = await fsPromise.tempfile(); + await fsPromise.writeFile(tempfile, artifacts.join('\n')); + + // TODO: Windows? + const buildCommand = `cat ${tempfile}`; + + logger.debug(`Built SA artifacts: ${artifacts.join('\n')}`); + logger.debug(`buildCommand: ${buildCommand}`); + + langService.sendLspNotification(fileUri, 'workspace/didChangeConfiguration', + { + settings: { + rust: { + unstable_features: true, // Required for build_command + build_on_save: true, + build_command: buildCommand, // TODO: Only in RLS branch: https://github.com/Xanewok/rls/tree/external-build + } + } + }); +} diff --git a/pkg/nuclide-rust/lib/BuckUtils.js b/pkg/nuclide-rust/lib/BuckUtils.js new file mode 100644 index 0000000000..c0440c125a --- /dev/null +++ b/pkg/nuclide-rust/lib/BuckUtils.js @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the license found in the LICENSE file in + * the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import type {TaskInfo} from '../../nuclide-buck/lib/types'; + +import * as BuckService from '../../nuclide-buck-rpc'; + +export function getRustInputs(buckRoot: string, buildTarget: BuildTarget): Promise> { + return BuckService. + query(buckRoot, `filter('.*\\.rs$', inputs('${buildTarget}'))`, []); +} + +export function getSaveAnalysisTargets(buckRoot: string, buildTarget: BuildTarget): Promise> { + // Save-analysis build flavor is only supported by rust_{binary, library} + // kinds (so exclude prebuilt_rust_library kind) + const query: string = `kind('^rust_.*', deps(${buildTarget}))`; + + return BuckService.query(buckRoot, query, []).then(deps => + deps.map(dep => dep + '#save-analysis'), + ); +} + +export type BuildTarget = string; + +// FIXME: Copied from nuclide-buck-rpc +// Buck query doesn't allow omitting // or adding # for flavors, this needs to be fixed in buck. +export function normalizeNameForBuckQuery(aliasOrTarget: string): BuildTarget { + let canonicalName = aliasOrTarget; + // Don't prepend // for aliases (aliases will not have colons or .) + if ( + (canonicalName.indexOf(':') !== -1 || canonicalName.indexOf('.') !== -1) && + canonicalName.indexOf('//') === -1 + ) { + canonicalName = '//' + canonicalName; + } + // Strip flavor string + const flavorIndex = canonicalName.indexOf('#'); + if (flavorIndex !== -1) { + canonicalName = canonicalName.substr(0, flavorIndex); + } + return canonicalName; +} diff --git a/pkg/nuclide-rust/lib/RustLanguage.js b/pkg/nuclide-rust/lib/RustLanguage.js index abdc42f0ce..05978ecbd6 100644 --- a/pkg/nuclide-rust/lib/RustLanguage.js +++ b/pkg/nuclide-rust/lib/RustLanguage.js @@ -49,6 +49,11 @@ async function connectionToRustService( useOriginalEnvironment: true, additionalLogFilesRetentionPeriod: 5 * 60 * 1000, // 5 minutes waitForDiagnostics: true, + initializationOptions: { + // Don't let RLS eagerly build (and fail crashing while finding a + // Cargo.toml if the project uses Buck) for now. + omitInitBuild: true, + }, }, ); diff --git a/pkg/nuclide-rust/lib/main.js b/pkg/nuclide-rust/lib/main.js index a853cfb607..441b2a051c 100644 --- a/pkg/nuclide-rust/lib/main.js +++ b/pkg/nuclide-rust/lib/main.js @@ -19,6 +19,8 @@ import createPackage from 'nuclide-commons-atom/createPackage'; import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; import {createRustLanguageService} from './RustLanguage'; +import {updateRlsBuildForTask} from './BuckIntegration'; + class Activation { _rustLanguageService: AtomLanguageService; _buckTaskRunnerService: ?BuckTaskRunnerService; @@ -32,6 +34,8 @@ class Activation { } consumeBuckTaskRunner(service: BuckTaskRunnerService): IDisposable { + service.onDidCompleteTask(task => updateRlsBuildForTask(task, this._rustLanguageService)); + this._buckTaskRunnerService = service; return new UniversalDisposable(() => { this._buckTaskRunnerService = null; From 15d4b3cc0adbbd6b2dad6e72d96d4abfb8d10317 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Fri, 31 Aug 2018 22:35:43 +0200 Subject: [PATCH 07/18] Adjust after API change and prep for PR --- pkg/nuclide-rust/lib/BuckIntegration.js | 14 ++++++++------ pkg/nuclide-rust/lib/RustLanguage.js | 4 ++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pkg/nuclide-rust/lib/BuckIntegration.js b/pkg/nuclide-rust/lib/BuckIntegration.js index 6c91bb892c..7f3a81da8e 100644 --- a/pkg/nuclide-rust/lib/BuckIntegration.js +++ b/pkg/nuclide-rust/lib/BuckIntegration.js @@ -31,13 +31,14 @@ export async function updateRlsBuildForTask( ) { const buildTarget = normalizeNameForBuckQuery(task.buildTarget); + // TODO: Filter by known Rust build targets const files = await getRustInputs(task.buckRoot, buildTarget); // Probably not a Rust build target, ignore if (files.length == 0) return; // We need only to pick a representative file to get a related lang service const fileUri = task.buckRoot + '/' + files[0]; - atom.notifications.addInfo(`fileUri: ${fileUri}`); + logger.debug(`fileUri: ${fileUri}`); const langService = await service.getLanguageServiceForUri(fileUri); invariant(langService != null); @@ -52,15 +53,16 @@ export async function updateRlsBuildForTask( let artifacts: Array = []; const buildReport = await BuckService.build(task.buckRoot, analysisTargets); - if (buildReport.success === false) { - atom.notifications.addError("save-analysis build failed"); + if (!buildReport.success) { + atom.notifications.addError("[nuclide-rust] save-analysis build failed"); return; } Object.values(buildReport.results) // TODO: https://buckbuild.com/command/build.html specifies that for // FETCHED_FROM_CACHE we might not get an output file - can we force it - // somehow? Or we always locally produce a save-analysis .json file? + // somehow? Or we always locally produce a save-analysis .json file for + // #save-analysis flavor? .forEach((targetReport: any) => artifacts.push(targetReport.output)); const tempfile = await fsPromise.tempfile(); @@ -72,13 +74,13 @@ export async function updateRlsBuildForTask( logger.debug(`Built SA artifacts: ${artifacts.join('\n')}`); logger.debug(`buildCommand: ${buildCommand}`); - langService.sendLspNotification(fileUri, 'workspace/didChangeConfiguration', + await langService.sendLspNotification('workspace/didChangeConfiguration', { settings: { rust: { unstable_features: true, // Required for build_command build_on_save: true, - build_command: buildCommand, // TODO: Only in RLS branch: https://github.com/Xanewok/rls/tree/external-build + build_command: buildCommand, } } }); diff --git a/pkg/nuclide-rust/lib/RustLanguage.js b/pkg/nuclide-rust/lib/RustLanguage.js index 05978ecbd6..33dd104167 100644 --- a/pkg/nuclide-rust/lib/RustLanguage.js +++ b/pkg/nuclide-rust/lib/RustLanguage.js @@ -52,6 +52,10 @@ async function connectionToRustService( initializationOptions: { // Don't let RLS eagerly build (and fail crashing while finding a // Cargo.toml if the project uses Buck) for now. + // TODO: Pass initial config (at least the `build_command`). + // https://github.com/rust-lang-nursery/rls/issues/1026 + // Without this the RLS can still can crash when the user starts + // modifying .rs files. omitInitBuild: true, }, }, From 9d6297d273c609596d580e6f7c0da5ef3ea8c797 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Fri, 31 Aug 2018 23:07:35 +0200 Subject: [PATCH 08/18] Fix issues reported by ESLint --- pkg/nuclide-rust/lib/BuckIntegration.js | 42 ++++++++++++++----------- pkg/nuclide-rust/lib/BuckUtils.js | 19 +++++++---- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/pkg/nuclide-rust/lib/BuckIntegration.js b/pkg/nuclide-rust/lib/BuckIntegration.js index 7f3a81da8e..7135cd37da 100644 --- a/pkg/nuclide-rust/lib/BuckIntegration.js +++ b/pkg/nuclide-rust/lib/BuckIntegration.js @@ -15,11 +15,14 @@ import type { LanguageService, } from '../../nuclide-language-service'; -import invariant from 'invariant'; +import assert from 'assert'; import {getLogger} from 'log4js'; import fsPromise from 'nuclide-commons/fsPromise'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {getRustInputs, getSaveAnalysisTargets, normalizeNameForBuckQuery} from './BuckUtils'; +import { + getRustInputs, + getSaveAnalysisTargets, + normalizeNameForBuckQuery, +} from './BuckUtils'; import * as BuckService from '../../nuclide-buck-rpc'; @@ -34,27 +37,31 @@ export async function updateRlsBuildForTask( // TODO: Filter by known Rust build targets const files = await getRustInputs(task.buckRoot, buildTarget); // Probably not a Rust build target, ignore - if (files.length == 0) + if (files.length === 0) { return; + } // We need only to pick a representative file to get a related lang service const fileUri = task.buckRoot + '/' + files[0]; logger.debug(`fileUri: ${fileUri}`); const langService = await service.getLanguageServiceForUri(fileUri); - invariant(langService != null); + assert(langService != null); // Since `buck` execution is not trivial - the command may be overriden, needs // to inherit the environment, passes internal FB USER to env etc. the RLS // can't just invoke that. // Instead, we build now, copy paths to resulting .json analysis artifacts to // a temp file and just use `cat $TMPFILE` as a dummy build command. - const analysisTargets = await getSaveAnalysisTargets(task.buckRoot, buildTarget); + const analysisTargets = await getSaveAnalysisTargets( + task.buckRoot, + buildTarget, + ); logger.debug(`analysisTargets: ${analysisTargets.join('\n')}`); - let artifacts: Array = []; + const artifacts: Array = []; const buildReport = await BuckService.build(task.buckRoot, analysisTargets); if (!buildReport.success) { - atom.notifications.addError("[nuclide-rust] save-analysis build failed"); + atom.notifications.addError('[nuclide-rust] save-analysis build failed'); return; } @@ -74,14 +81,13 @@ export async function updateRlsBuildForTask( logger.debug(`Built SA artifacts: ${artifacts.join('\n')}`); logger.debug(`buildCommand: ${buildCommand}`); - await langService.sendLspNotification('workspace/didChangeConfiguration', - { - settings: { - rust: { - unstable_features: true, // Required for build_command - build_on_save: true, - build_command: buildCommand, - } - } - }); + await langService.sendLspNotification('workspace/didChangeConfiguration', { + settings: { + rust: { + unstable_features: true, // Required for build_command + build_on_save: true, + build_command: buildCommand, + }, + }, + }); } diff --git a/pkg/nuclide-rust/lib/BuckUtils.js b/pkg/nuclide-rust/lib/BuckUtils.js index c0440c125a..eca1ebd570 100644 --- a/pkg/nuclide-rust/lib/BuckUtils.js +++ b/pkg/nuclide-rust/lib/BuckUtils.js @@ -9,16 +9,23 @@ * @format */ -import type {TaskInfo} from '../../nuclide-buck/lib/types'; - import * as BuckService from '../../nuclide-buck-rpc'; -export function getRustInputs(buckRoot: string, buildTarget: BuildTarget): Promise> { - return BuckService. - query(buckRoot, `filter('.*\\.rs$', inputs('${buildTarget}'))`, []); +export function getRustInputs( + buckRoot: string, + buildTarget: BuildTarget, +): Promise> { + return BuckService.query( + buckRoot, + `filter('.*\\.rs$', inputs('${buildTarget}'))`, + [], + ); } -export function getSaveAnalysisTargets(buckRoot: string, buildTarget: BuildTarget): Promise> { +export function getSaveAnalysisTargets( + buckRoot: string, + buildTarget: BuildTarget, +): Promise> { // Save-analysis build flavor is only supported by rust_{binary, library} // kinds (so exclude prebuilt_rust_library kind) const query: string = `kind('^rust_.*', deps(${buildTarget}))`; From 44355787405859b37286ab1edc47b13459cfeed5 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Fri, 31 Aug 2018 23:20:42 +0200 Subject: [PATCH 09/18] ...and for main.js as well --- pkg/nuclide-rust/lib/main.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/nuclide-rust/lib/main.js b/pkg/nuclide-rust/lib/main.js index 441b2a051c..274e305c39 100644 --- a/pkg/nuclide-rust/lib/main.js +++ b/pkg/nuclide-rust/lib/main.js @@ -34,7 +34,9 @@ class Activation { } consumeBuckTaskRunner(service: BuckTaskRunnerService): IDisposable { - service.onDidCompleteTask(task => updateRlsBuildForTask(task, this._rustLanguageService)); + service.onDidCompleteTask(task => + updateRlsBuildForTask(task, this._rustLanguageService), + ); this._buckTaskRunnerService = service; return new UniversalDisposable(() => { From 798fed2b35b7cda2a35014cc6c0f2165970b4306 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 3 Sep 2018 15:43:35 +0200 Subject: [PATCH 10/18] Don't use invariant nor assert --- pkg/nuclide-rust/lib/BuckIntegration.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/nuclide-rust/lib/BuckIntegration.js b/pkg/nuclide-rust/lib/BuckIntegration.js index 7135cd37da..f586bd2221 100644 --- a/pkg/nuclide-rust/lib/BuckIntegration.js +++ b/pkg/nuclide-rust/lib/BuckIntegration.js @@ -15,7 +15,6 @@ import type { LanguageService, } from '../../nuclide-language-service'; -import assert from 'assert'; import {getLogger} from 'log4js'; import fsPromise from 'nuclide-commons/fsPromise'; import { @@ -45,7 +44,11 @@ export async function updateRlsBuildForTask( logger.debug(`fileUri: ${fileUri}`); const langService = await service.getLanguageServiceForUri(fileUri); - assert(langService != null); + if (langService == null) { + atom.notifications.addError(`[nuclide-rust] couldn't find language service + for target ${buildTarget}`); + return; + } // Since `buck` execution is not trivial - the command may be overriden, needs // to inherit the environment, passes internal FB USER to env etc. the RLS From ab2bc8902392b83299dfe431a2494b258b800b86 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 3 Sep 2018 16:08:13 +0200 Subject: [PATCH 11/18] Ignore Buck non-Rust build targets --- pkg/nuclide-rust/lib/BuckUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/nuclide-rust/lib/BuckUtils.js b/pkg/nuclide-rust/lib/BuckUtils.js index eca1ebd570..11c4410a4f 100644 --- a/pkg/nuclide-rust/lib/BuckUtils.js +++ b/pkg/nuclide-rust/lib/BuckUtils.js @@ -17,7 +17,7 @@ export function getRustInputs( ): Promise> { return BuckService.query( buckRoot, - `filter('.*\\.rs$', inputs('${buildTarget}'))`, + `filter('.*\\.rs$', inputs(kind('^rust_.*', ${buildTarget})))`, [], ); } From ca76f6cdb17310313e3dbda400d9ba14697e1bb5 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 3 Sep 2018 16:59:47 +0200 Subject: [PATCH 12/18] Add startup disclaimer --- pkg/nuclide-rust/lib/main.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/nuclide-rust/lib/main.js b/pkg/nuclide-rust/lib/main.js index 274e305c39..7496d9d6a2 100644 --- a/pkg/nuclide-rust/lib/main.js +++ b/pkg/nuclide-rust/lib/main.js @@ -21,12 +21,18 @@ import {createRustLanguageService} from './RustLanguage'; import {updateRlsBuildForTask} from './BuckIntegration'; +const DISCLAIMER = `[nuclide-rust] Support for Buck-managed Rust +projects is currently experimental. For it to work correctly, please build +the target you plan on working using Buck toolbar.`; + class Activation { _rustLanguageService: AtomLanguageService; _buckTaskRunnerService: ?BuckTaskRunnerService; _subscriptions: UniversalDisposable; constructor(rawState: ?Object) { + atom.notifications.addInfo(DISCLAIMER); + this._rustLanguageService = createRustLanguageService(); this._rustLanguageService.activate(); From 9ce74be10de54f6823592a2c3206d758fb7a9623 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Thu, 6 Sep 2018 12:42:42 +0200 Subject: [PATCH 13/18] Address feedback --- pkg/nuclide-rust/lib/BuckIntegration.js | 1 - pkg/nuclide-rust/lib/BuckUtils.js | 7 +++---- pkg/nuclide-rust/lib/main.js | 8 +------- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/pkg/nuclide-rust/lib/BuckIntegration.js b/pkg/nuclide-rust/lib/BuckIntegration.js index f586bd2221..9dda276f7c 100644 --- a/pkg/nuclide-rust/lib/BuckIntegration.js +++ b/pkg/nuclide-rust/lib/BuckIntegration.js @@ -33,7 +33,6 @@ export async function updateRlsBuildForTask( ) { const buildTarget = normalizeNameForBuckQuery(task.buildTarget); - // TODO: Filter by known Rust build targets const files = await getRustInputs(task.buckRoot, buildTarget); // Probably not a Rust build target, ignore if (files.length === 0) { diff --git a/pkg/nuclide-rust/lib/BuckUtils.js b/pkg/nuclide-rust/lib/BuckUtils.js index 11c4410a4f..925bc7fa78 100644 --- a/pkg/nuclide-rust/lib/BuckUtils.js +++ b/pkg/nuclide-rust/lib/BuckUtils.js @@ -22,7 +22,7 @@ export function getRustInputs( ); } -export function getSaveAnalysisTargets( +export async function getSaveAnalysisTargets( buckRoot: string, buildTarget: BuildTarget, ): Promise> { @@ -30,9 +30,8 @@ export function getSaveAnalysisTargets( // kinds (so exclude prebuilt_rust_library kind) const query: string = `kind('^rust_.*', deps(${buildTarget}))`; - return BuckService.query(buckRoot, query, []).then(deps => - deps.map(dep => dep + '#save-analysis'), - ); + const deps = await BuckService.query(buckRoot, query, []); + return deps.map(dep => dep + '#save-analysis'); } export type BuildTarget = string; diff --git a/pkg/nuclide-rust/lib/main.js b/pkg/nuclide-rust/lib/main.js index 7496d9d6a2..b016e254c2 100644 --- a/pkg/nuclide-rust/lib/main.js +++ b/pkg/nuclide-rust/lib/main.js @@ -27,7 +27,6 @@ the target you plan on working using Buck toolbar.`; class Activation { _rustLanguageService: AtomLanguageService; - _buckTaskRunnerService: ?BuckTaskRunnerService; _subscriptions: UniversalDisposable; constructor(rawState: ?Object) { @@ -40,14 +39,9 @@ class Activation { } consumeBuckTaskRunner(service: BuckTaskRunnerService): IDisposable { - service.onDidCompleteTask(task => + return service.onDidCompleteTask(task => updateRlsBuildForTask(task, this._rustLanguageService), ); - - this._buckTaskRunnerService = service; - return new UniversalDisposable(() => { - this._buckTaskRunnerService = null; - }); } dispose(): void { From 9f64faf40f56e04178c0af7feef7a298a3ae98bf Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Fri, 14 Sep 2018 20:50:45 +0200 Subject: [PATCH 14/18] Work around Buck bug with outputs relative to Buck cell For example, running `buck build cell//:target` will create output with paths relative to the cell, and not the current Buck root. --- pkg/nuclide-rust/lib/BuckIntegration.js | 33 +++++++++++++++++++------ pkg/nuclide-rust/lib/BuckUtils.js | 15 +++++++++-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/pkg/nuclide-rust/lib/BuckIntegration.js b/pkg/nuclide-rust/lib/BuckIntegration.js index 9dda276f7c..f0713a293d 100644 --- a/pkg/nuclide-rust/lib/BuckIntegration.js +++ b/pkg/nuclide-rust/lib/BuckIntegration.js @@ -21,6 +21,7 @@ import { getRustInputs, getSaveAnalysisTargets, normalizeNameForBuckQuery, + getRustBuildFile, } from './BuckUtils'; import * as BuckService from '../../nuclide-buck-rpc'; @@ -33,14 +34,30 @@ export async function updateRlsBuildForTask( ) { const buildTarget = normalizeNameForBuckQuery(task.buildTarget); - const files = await getRustInputs(task.buckRoot, buildTarget); - // Probably not a Rust build target, ignore - if (files.length === 0) { + // Output is relative to Buck root but the built target may be managed by a + // Buck cell (nested Buck root). + // Here, Buck returns input paths relative to the possible cell, but the build + // file always relative to the current Buck root. Because of that, we use the + // build file path to determine the possible Buck cell root to which the + // inputs are relative to. + // FIXME: This is a bug in Buck, only query for files when the output is fixed. + const [buildFile, files] = await Promise.all([ + getRustBuildFile(task.buckRoot, buildTarget), + getRustInputs(task.buckRoot, buildTarget), + ]); + // Not a Rust build target, ignore + if (buildFile == null || files.length === 0) { return; } + const buckRoot = await BuckService.getRootForPath(buildFile); + if (buckRoot == null) { + logger.error(`Couldn't find Buck root for ${buildFile}`); + return; + } + + logger.debug(`Detected Buck root: ${buckRoot}`); // We need only to pick a representative file to get a related lang service - const fileUri = task.buckRoot + '/' + files[0]; - logger.debug(`fileUri: ${fileUri}`); + const fileUri = buckRoot + '/' + files[0]; const langService = await service.getLanguageServiceForUri(fileUri); if (langService == null) { @@ -59,7 +76,6 @@ export async function updateRlsBuildForTask( buildTarget, ); logger.debug(`analysisTargets: ${analysisTargets.join('\n')}`); - const artifacts: Array = []; const buildReport = await BuckService.build(task.buckRoot, analysisTargets); if (!buildReport.success) { @@ -67,12 +83,15 @@ export async function updateRlsBuildForTask( return; } + const artifacts: Array = []; Object.values(buildReport.results) // TODO: https://buckbuild.com/command/build.html specifies that for // FETCHED_FROM_CACHE we might not get an output file - can we force it // somehow? Or we always locally produce a save-analysis .json file for // #save-analysis flavor? - .forEach((targetReport: any) => artifacts.push(targetReport.output)); + .forEach((targetReport: any) => + artifacts.push(`${buckRoot}/${targetReport.output}`), + ); const tempfile = await fsPromise.tempfile(); await fsPromise.writeFile(tempfile, artifacts.join('\n')); diff --git a/pkg/nuclide-rust/lib/BuckUtils.js b/pkg/nuclide-rust/lib/BuckUtils.js index 925bc7fa78..38fd1f77a5 100644 --- a/pkg/nuclide-rust/lib/BuckUtils.js +++ b/pkg/nuclide-rust/lib/BuckUtils.js @@ -11,6 +11,19 @@ import * as BuckService from '../../nuclide-buck-rpc'; +export type BuildTarget = string; + +export async function getRustBuildFile( + buckRoot: string, + buildTarget: BuildTarget, +): Promise { + return BuckService.query( + buckRoot, + `buildfile(kind('^rust_.*', ${buildTarget}))`, + [], + ).then(buildfiles => buildfiles[0] || null); +} + export function getRustInputs( buckRoot: string, buildTarget: BuildTarget, @@ -34,8 +47,6 @@ export async function getSaveAnalysisTargets( return deps.map(dep => dep + '#save-analysis'); } -export type BuildTarget = string; - // FIXME: Copied from nuclide-buck-rpc // Buck query doesn't allow omitting // or adding # for flavors, this needs to be fixed in buck. export function normalizeNameForBuckQuery(aliasOrTarget: string): BuildTarget { From 8797da05b6440fff7e9b03f4d0b1e63d6a8f88f3 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Fri, 14 Sep 2018 21:45:35 +0200 Subject: [PATCH 15/18] Fix fetching Buck root for a build target I could've sworn it worked with relative directories - it boiled down to ```js const buildFile = ...; // relative return fsPromise.findNearestFile('.buckconfig', buildFile); ``` which *should* work for relative files, I believe? --- pkg/nuclide-rust/lib/BuckIntegration.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/nuclide-rust/lib/BuckIntegration.js b/pkg/nuclide-rust/lib/BuckIntegration.js index f0713a293d..aa3018774c 100644 --- a/pkg/nuclide-rust/lib/BuckIntegration.js +++ b/pkg/nuclide-rust/lib/BuckIntegration.js @@ -41,14 +41,16 @@ export async function updateRlsBuildForTask( // build file path to determine the possible Buck cell root to which the // inputs are relative to. // FIXME: This is a bug in Buck, only query for files when the output is fixed. - const [buildFile, files] = await Promise.all([ + const [relativeBuildFile, files] = await Promise.all([ getRustBuildFile(task.buckRoot, buildTarget), getRustInputs(task.buckRoot, buildTarget), ]); // Not a Rust build target, ignore - if (buildFile == null || files.length === 0) { + if (relativeBuildFile == null || files.length === 0) { return; } + + const buildFile = `${task.buckRoot}/${relativeBuildFile}`; const buckRoot = await BuckService.getRootForPath(buildFile); if (buckRoot == null) { logger.error(`Couldn't find Buck root for ${buildFile}`); From 384c616eb6e96f6ce8effe603475ae6d34c700b2 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Fri, 14 Sep 2018 23:36:09 +0200 Subject: [PATCH 16/18] Display busy spinner when running the indexing build --- pkg/nuclide-rust/lib/BuckIntegration.js | 31 +++++++++++++++++++++---- pkg/nuclide-rust/lib/main.js | 15 +++++++++++- pkg/nuclide-rust/package.json | 5 ++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/pkg/nuclide-rust/lib/BuckIntegration.js b/pkg/nuclide-rust/lib/BuckIntegration.js index aa3018774c..32ccf1da00 100644 --- a/pkg/nuclide-rust/lib/BuckIntegration.js +++ b/pkg/nuclide-rust/lib/BuckIntegration.js @@ -9,6 +9,7 @@ * @format */ +import type {BusySignalService, BusySignalOptions} from 'atom-ide-ui'; import type {TaskInfo} from '../../nuclide-buck/lib/types'; import type { AtomLanguageService, @@ -31,6 +32,7 @@ const logger = getLogger('nuclide-rust'); export async function updateRlsBuildForTask( task: TaskInfo, service: AtomLanguageService, + busySignalService: ?BusySignalService, ) { const buildTarget = normalizeNameForBuckQuery(task.buildTarget); @@ -73,13 +75,19 @@ export async function updateRlsBuildForTask( // can't just invoke that. // Instead, we build now, copy paths to resulting .json analysis artifacts to // a temp file and just use `cat $TMPFILE` as a dummy build command. - const analysisTargets = await getSaveAnalysisTargets( - task.buckRoot, - buildTarget, + const doSaveAnalysisBuild = () => + getSaveAnalysisTargets(task.buckRoot, buildTarget).then(analysisTargets => { + logger.debug(`analysisTargets: ${analysisTargets.join('\n')}`); + + return BuckService.build(task.buckRoot, analysisTargets); + }); + + const buildReport = await reportBusyWhile( + busySignalService, + '[nuclide-rust] Indexing...', + doSaveAnalysisBuild, ); - logger.debug(`analysisTargets: ${analysisTargets.join('\n')}`); - const buildReport = await BuckService.build(task.buckRoot, analysisTargets); if (!buildReport.success) { atom.notifications.addError('[nuclide-rust] save-analysis build failed'); return; @@ -114,3 +122,16 @@ export async function updateRlsBuildForTask( }, }); } + +function reportBusyWhile( + busySignalService: ?BusySignalService, + title: string, + f: () => Promise, + options?: BusySignalOptions, +): Promise { + if (busySignalService) { + return busySignalService.reportBusyWhile(title, f, options); + } else { + return f(); + } +} diff --git a/pkg/nuclide-rust/lib/main.js b/pkg/nuclide-rust/lib/main.js index b016e254c2..4719e2cdd1 100644 --- a/pkg/nuclide-rust/lib/main.js +++ b/pkg/nuclide-rust/lib/main.js @@ -10,6 +10,7 @@ */ import type {BuckTaskRunnerService} from '../../nuclide-buck/lib/types'; +import type {BusySignalService} from 'atom-ide-ui'; import type { AtomLanguageService, LanguageService, @@ -28,6 +29,7 @@ the target you plan on working using Buck toolbar.`; class Activation { _rustLanguageService: AtomLanguageService; _subscriptions: UniversalDisposable; + _busySignalService: ?BusySignalService; constructor(rawState: ?Object) { atom.notifications.addInfo(DISCLAIMER); @@ -40,10 +42,21 @@ class Activation { consumeBuckTaskRunner(service: BuckTaskRunnerService): IDisposable { return service.onDidCompleteTask(task => - updateRlsBuildForTask(task, this._rustLanguageService), + updateRlsBuildForTask( + task, + this._rustLanguageService, + this._busySignalService, + ), ); } + consumeBusySignal(busySignalService: BusySignalService): IDisposable { + this._busySignalService = busySignalService; + return new UniversalDisposable(() => { + this._busySignalService = null; + }); + } + dispose(): void { this._subscriptions.dispose(); } diff --git a/pkg/nuclide-rust/package.json b/pkg/nuclide-rust/package.json index 6ea7ce0923..cafd505452 100644 --- a/pkg/nuclide-rust/package.json +++ b/pkg/nuclide-rust/package.json @@ -26,6 +26,11 @@ "language-rust:grammar-used" ], "consumedServices": { + "atom-ide-busy-signal": { + "versions": { + "0.1.0": "consumeBusySignal" + } + }, "nuclide.buck-task-runner": { "versions": { "0.0.0": "consumeBuckTaskRunner" From 2779b3043adcf8b9225413b82e976f359cc884e1 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 3 Oct 2018 20:48:41 +0200 Subject: [PATCH 17/18] Add code action capabilities --- pkg/nuclide-rust/lib/RustLanguage.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/nuclide-rust/lib/RustLanguage.js b/pkg/nuclide-rust/lib/RustLanguage.js index 33dd104167..670f1c2b57 100644 --- a/pkg/nuclide-rust/lib/RustLanguage.js +++ b/pkg/nuclide-rust/lib/RustLanguage.js @@ -76,21 +76,27 @@ export const atomConfig: AtomLanguageServiceConfig = { priority: 1, definitionEventName: 'rust.definition', }, + codeAction: { + version: '0.1.0', + priority: 1, + analyticsEventName: 'rust.codeAction', + applyAnalyticsEventName: 'rust.applyCodeAction', + }, codeFormat: { version: '0.1.0', priority: 1, - analyticsEventName: 'rust.formatCode', + analyticsEventName: 'rust.codeFormat', canFormatRanges: true, canFormatAtPosition: true, }, findReferences: { version: '0.1.0', - analyticsEventName: 'rust.get-references', + analyticsEventName: 'rust.findReferences', }, rename: { version: '0.0.0', priority: 1, - analyticsEventName: 'rust:rename', + analyticsEventName: 'rust.rename', }, autocomplete: { inclusionPriority: 1, From 7b9c326d1646262b6270e2e5c94353b96c83d152 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 3 Oct 2018 20:50:43 +0200 Subject: [PATCH 18/18] Check for Rust rule before doing Buck query --- pkg/nuclide-rust/lib/BuckIntegration.js | 6 +++++- pkg/nuclide-rust/lib/BuckUtils.js | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/nuclide-rust/lib/BuckIntegration.js b/pkg/nuclide-rust/lib/BuckIntegration.js index 32ccf1da00..ff15611790 100644 --- a/pkg/nuclide-rust/lib/BuckIntegration.js +++ b/pkg/nuclide-rust/lib/BuckIntegration.js @@ -19,10 +19,11 @@ import type { import {getLogger} from 'log4js'; import fsPromise from 'nuclide-commons/fsPromise'; import { + getRustBuildFile, getRustInputs, getSaveAnalysisTargets, + isRustBuildRuleType, normalizeNameForBuckQuery, - getRustBuildFile, } from './BuckUtils'; import * as BuckService from '../../nuclide-buck-rpc'; @@ -34,6 +35,9 @@ export async function updateRlsBuildForTask( service: AtomLanguageService, busySignalService: ?BusySignalService, ) { + if (!isRustBuildRuleType(task.buildRuleType.type)) { + return; + } const buildTarget = normalizeNameForBuckQuery(task.buildTarget); // Output is relative to Buck root but the built target may be managed by a diff --git a/pkg/nuclide-rust/lib/BuckUtils.js b/pkg/nuclide-rust/lib/BuckUtils.js index 38fd1f77a5..39e51915e9 100644 --- a/pkg/nuclide-rust/lib/BuckUtils.js +++ b/pkg/nuclide-rust/lib/BuckUtils.js @@ -13,6 +13,10 @@ import * as BuckService from '../../nuclide-buck-rpc'; export type BuildTarget = string; +export function isRustBuildRuleType(type_: string): boolean { + return type_.startsWith('rust_'); +} + export async function getRustBuildFile( buckRoot: string, buildTarget: BuildTarget,