This commit add Cargo-style project discovery for Buck and Bazel users.

This feature requires the user to add a command that generates a
`rust-project.json` from a set of files. Project discovery can be invoked
in two ways:

1. At extension activation time, which includes the generated
   `rust-project.json` as part of the linkedProjects argument in
    InitializeParams
2. Through a new command titled "Add current file to workspace", which
   makes use of a new, rust-analyzer specific LSP request that adds
   the workspace without erasing any existing workspaces.

I think that the command-running functionality _could_ merit being
placed into its own extension (and expose it via extension contribution
points), if only provide build-system idiomatic progress reporting and
status handling, but I haven't (yet) made an extension that does this.
This commit is contained in:
David Barsky 2023-03-09 15:06:26 -05:00
parent 9549753352
commit 8af3d6367e
14 changed files with 258 additions and 25 deletions

View file

@ -4,6 +4,7 @@
use std::{fmt, str::FromStr};
use cfg::CfgOptions;
use serde::Serialize;
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum CfgFlag {
@ -38,6 +39,18 @@ impl<'de> serde::Deserialize<'de> for CfgFlag {
}
}
impl Serialize for CfgFlag {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
CfgFlag::Atom(s) => serializer.serialize_str(s),
CfgFlag::KeyValue { .. } => serializer.serialize_str(&format!("{}", &self)),
}
}
}
impl Extend<CfgFlag> for CfgOptions {
fn extend<T: IntoIterator<Item = CfgFlag>>(&mut self, iter: T) {
for cfg_flag in iter {

View file

@ -54,7 +54,7 @@ use std::path::PathBuf;
use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
use serde::{de, Deserialize};
use serde::{de, ser, Deserialize, Serialize};
use crate::cfg_flag::CfgFlag;
@ -171,14 +171,14 @@ impl ProjectJson {
}
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProjectJsonData {
sysroot: Option<PathBuf>,
sysroot_src: Option<PathBuf>,
crates: Vec<CrateData>,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
struct CrateData {
display_name: Option<String>,
root_module: PathBuf,
@ -200,7 +200,7 @@ struct CrateData {
repository: Option<String>,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename = "edition")]
enum EditionData {
#[serde(rename = "2015")]
@ -221,16 +221,16 @@ impl From<EditionData> for Edition {
}
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
struct DepData {
/// Identifies a crate by position in the crates array.
#[serde(rename = "crate")]
krate: usize,
#[serde(deserialize_with = "deserialize_crate_name")]
#[serde(deserialize_with = "deserialize_crate_name", serialize_with = "serialize_crate_name")]
name: CrateName,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
struct CrateSource {
include_dirs: Vec<PathBuf>,
exclude_dirs: Vec<PathBuf>,
@ -243,3 +243,10 @@ where
let name = String::deserialize(de)?;
CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {err:?}")))
}
fn serialize_crate_name<S>(crate_name: &CrateName, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
crate_name.serialize(serializer)
}

View file

@ -272,7 +272,6 @@ config_data! {
/// The warnings will be indicated by a blue squiggly underline in code
/// and a blue icon in the `Problems Panel`.
diagnostics_warningsAsInfo: Vec<String> = "[]",
/// These directories will be ignored by rust-analyzer. They are
/// relative to the workspace root, and globs are not supported. You may
/// also need to add the folders to Code's `files.watcherExclude`.
@ -895,6 +894,15 @@ impl Config {
}
}
pub fn add_linked_projects(&mut self, linked_projects: Vec<ProjectJsonData>) {
let mut linked_projects = linked_projects
.into_iter()
.map(ManifestOrProjectJson::ProjectJson)
.collect::<Vec<ManifestOrProjectJson>>();
self.data.linkedProjects.append(&mut linked_projects);
}
pub fn did_save_text_document_dynamic_registration(&self) -> bool {
let caps = try_or_def!(self.caps.text_document.as_ref()?.synchronization.clone()?);
caps.did_save == Some(true) && caps.dynamic_registration == Some(true)

View file

@ -5,6 +5,7 @@
use std::{
io::Write as _,
process::{self, Stdio},
sync::Arc,
};
use anyhow::Context;
@ -46,6 +47,22 @@ use crate::{
pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> {
state.proc_macro_clients.clear();
state.proc_macro_changed = false;
state.fetch_workspaces_queue.request_op("reload workspace request".to_string());
state.fetch_build_data_queue.request_op("reload workspace request".to_string());
Ok(())
}
pub(crate) fn handle_add_project(
state: &mut GlobalState,
params: lsp_ext::AddProjectParams,
) -> Result<()> {
state.proc_macro_clients.clear();
state.proc_macro_changed = false;
let config = Arc::make_mut(&mut state.config);
config.add_linked_projects(params.project);
state.fetch_workspaces_queue.request_op("reload workspace request".to_string());
state.fetch_build_data_queue.request_op("reload workspace request".to_string());
Ok(())

View file

@ -9,6 +9,7 @@ use lsp_types::{
notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams,
PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams,
};
use project_model::ProjectJsonData;
use serde::{Deserialize, Serialize};
use crate::line_index::PositionEncoding;
@ -51,6 +52,20 @@ impl Request for ReloadWorkspace {
const METHOD: &'static str = "rust-analyzer/reloadWorkspace";
}
pub enum AddProject {}
impl Request for AddProject {
type Params = AddProjectParams;
type Result = ();
const METHOD: &'static str = "rust-analyzer/addProject";
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct AddProjectParams {
pub project: Vec<ProjectJsonData>,
}
pub enum SyntaxTree {}
impl Request for SyntaxTree {

View file

@ -625,6 +625,7 @@ impl GlobalState {
.on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)
.on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)
.on_sync_mut::<lsp_ext::ShuffleCrateGraph>(handlers::handle_shuffle_crate_graph)
.on_sync_mut::<lsp_ext::AddProject>(handlers::handle_add_project)
.on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
.on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
.on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)