diff --git a/Cargo.lock b/Cargo.lock index 196a37743988..f618fcc3db13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1075,7 +1075,12 @@ dependencies = [ name = "ra_proc_macro" version = "0.1.0" dependencies = [ + "crossbeam-channel", + "jod-thread", + "log", "ra_tt", + "serde", + "serde_json", ] [[package]] diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs index 4d270e0def6c..97d1208ec6b7 100644 --- a/crates/ra_hir_expand/src/proc_macro.rs +++ b/crates/ra_hir_expand/src/proc_macro.rs @@ -9,6 +9,15 @@ pub struct ProcMacroExpander { proc_macro_id: ProcMacroId, } +macro_rules! err { + ($fmt:literal, $($tt:tt),*) => { + mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown(format!($fmt, $($tt),*))) + }; + ($fmt:literal) => { + mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown($fmt.to_string())) + } +} + impl ProcMacroExpander { pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander { ProcMacroExpander { krate, proc_macro_id } @@ -25,8 +34,24 @@ impl ProcMacroExpander { .proc_macro .get(self.proc_macro_id.0 as usize) .clone() - .ok_or_else(|| mbe::ExpandError::ConversionError)?; + .ok_or_else(|| err!("No derive macro found."))?; + + let tt = remove_derive_atr(tt, &proc_macro.name) + .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) } } + +fn remove_derive_atr(tt: &tt::Subtree, _name: &str) -> Option { + // FIXME: proper handle the remove derive + // We assume the first 2 tokens are #[derive(name)] + if tt.token_trees.len() > 2 { + let mut tt = tt.clone(); + tt.token_trees.remove(0); + tt.token_trees.remove(0); + return Some(tt); + } + + None +} diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml index bc2c37296d16..d009ceb82e53 100644 --- a/crates/ra_proc_macro/Cargo.toml +++ b/crates/ra_proc_macro/Cargo.toml @@ -10,3 +10,8 @@ doctest = false [dependencies] ra_tt = { path = "../ra_tt" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +log = "0.4.8" +crossbeam-channel = "0.4.0" +jod-thread = "0.1.1" diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs index 5e21dd487257..51fbb046a973 100644 --- a/crates/ra_proc_macro/src/lib.rs +++ b/crates/ra_proc_macro/src/lib.rs @@ -5,55 +5,104 @@ //! is used to provide basic infrastructure for communication between two //! processes: Client (RA itself), Server (the external program) +mod rpc; +mod process; +pub mod msg; + +use process::{ProcMacroProcessSrv, ProcMacroProcessThread}; use ra_tt::{SmolStr, Subtree}; +use rpc::ProcMacroKind; use std::{ path::{Path, PathBuf}, sync::Arc, }; -#[derive(Debug, Clone, PartialEq, Eq)] +pub use rpc::{ExpansionResult, ExpansionTask}; + +#[derive(Debug, Clone)] pub struct ProcMacroProcessExpander { process: Arc, + dylib_path: PathBuf, name: SmolStr, } +impl Eq for ProcMacroProcessExpander {} +impl PartialEq for ProcMacroProcessExpander { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + && self.dylib_path == other.dylib_path + && Arc::ptr_eq(&self.process, &other.process) + } +} + impl ra_tt::TokenExpander for ProcMacroProcessExpander { fn expand( &self, - _subtree: &Subtree, + subtree: &Subtree, _attr: Option<&Subtree>, ) -> Result { - // FIXME: do nothing for now - Ok(Subtree::default()) + self.process.custom_derive(&self.dylib_path, subtree, &self.name) } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ProcMacroProcessSrv { - path: PathBuf, +#[derive(Debug)] +enum ProcMacroClientKind { + Process { process: Arc, thread: ProcMacroProcessThread }, + Dummy, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ProcMacroClient { - Process { process: Arc }, - Dummy, +#[derive(Debug)] +pub struct ProcMacroClient { + kind: ProcMacroClientKind, } impl ProcMacroClient { - pub fn extern_process(process_path: &Path) -> ProcMacroClient { - let process = ProcMacroProcessSrv { path: process_path.into() }; - ProcMacroClient::Process { process: Arc::new(process) } + pub fn extern_process(process_path: &Path) -> Result { + let (thread, process) = ProcMacroProcessSrv::run(process_path)?; + Ok(ProcMacroClient { + kind: ProcMacroClientKind::Process { process: Arc::new(process), thread }, + }) } pub fn dummy() -> ProcMacroClient { - ProcMacroClient::Dummy + ProcMacroClient { kind: ProcMacroClientKind::Dummy } } pub fn by_dylib_path( &self, - _dylib_path: &Path, + dylib_path: &Path, ) -> Vec<(SmolStr, Arc)> { - // FIXME: return empty for now - vec![] + match &self.kind { + ProcMacroClientKind::Dummy => vec![], + ProcMacroClientKind::Process { process, .. } => { + let macros = match process.find_proc_macros(dylib_path) { + Err(err) => { + eprintln!("Fail to find proc macro. Error: {:#?}", err); + return vec![]; + } + Ok(macros) => macros, + }; + + macros + .into_iter() + .filter_map(|(name, kind)| { + // FIXME: Support custom derive only for now. + match kind { + ProcMacroKind::CustomDerive => { + let name = SmolStr::new(&name); + let expander: Arc = + Arc::new(ProcMacroProcessExpander { + process: process.clone(), + name: name.clone(), + dylib_path: dylib_path.into(), + }); + Some((name, expander)) + } + _ => None, + } + }) + .collect() + } + } } } diff --git a/crates/ra_proc_macro/src/msg.rs b/crates/ra_proc_macro/src/msg.rs new file mode 100644 index 000000000000..aa95bcc8f7bc --- /dev/null +++ b/crates/ra_proc_macro/src/msg.rs @@ -0,0 +1,93 @@ +//! Defines messages for cross-process message based on `ndjson` wire protocol + +use std::{ + convert::TryFrom, + io::{self, BufRead, Write}, +}; + +use crate::{ + rpc::{ListMacrosResult, ListMacrosTask}, + ExpansionResult, ExpansionTask, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum Request { + ListMacro(ListMacrosTask), + ExpansionMacro(ExpansionTask), +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum Response { + Error(ResponseError), + ListMacro(ListMacrosResult), + ExpansionMacro(ExpansionResult), +} + +macro_rules! impl_try_from_response { + ($ty:ty, $tag:ident) => { + impl TryFrom for $ty { + type Error = &'static str; + fn try_from(value: Response) -> Result { + match value { + Response::$tag(res) => Ok(res), + _ => Err("Fail to convert from response"), + } + } + } + }; +} + +impl_try_from_response!(ListMacrosResult, ListMacro); +impl_try_from_response!(ExpansionResult, ExpansionMacro); + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct ResponseError { + pub code: ErrorCode, + pub message: String, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum ErrorCode { + ServerErrorEnd, + ExpansionError, +} + +pub trait Message: Sized + Serialize + DeserializeOwned { + fn read(r: &mut impl BufRead) -> io::Result> { + let text = match read_json(r)? { + None => return Ok(None), + Some(text) => text, + }; + let msg = serde_json::from_str(&text)?; + Ok(Some(msg)) + } + fn write(self, w: &mut impl Write) -> io::Result<()> { + let text = serde_json::to_string(&self)?; + write_json(w, &text) + } +} + +impl Message for Request {} +impl Message for Response {} + +fn read_json(inp: &mut impl BufRead) -> io::Result> { + let mut buf = String::new(); + if inp.read_line(&mut buf)? == 0 { + return Ok(None); + } + // Remove ending '\n' + let buf = &buf[..buf.len() - 1]; + if buf.is_empty() { + return Ok(None); + } + Ok(Some(buf.to_string())) +} + +fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> { + log::debug!("> {}", msg); + out.write_all(msg.as_bytes())?; + out.write_all(b"\n")?; + out.flush()?; + Ok(()) +} diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs new file mode 100644 index 000000000000..e8c85be38ae7 --- /dev/null +++ b/crates/ra_proc_macro/src/process.rs @@ -0,0 +1,196 @@ +//! Handle process life-time and message passing for proc-macro client + +use crossbeam_channel::{bounded, Receiver, Sender}; +use ra_tt::Subtree; + +use crate::msg::{ErrorCode, Message, Request, Response, ResponseError}; +use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; + +use io::{BufRead, BufReader}; +use std::{ + convert::{TryFrom, TryInto}, + io::{self, Write}, + path::{Path, PathBuf}, + process::{Child, Command, Stdio}, + sync::{Arc, Weak}, +}; + +#[derive(Debug, Default)] +pub(crate) struct ProcMacroProcessSrv { + inner: Option>>, +} + +#[derive(Debug)] +pub(crate) struct ProcMacroProcessThread { + // XXX: drop order is significant + sender: Arc>, + handle: jod_thread::JoinHandle<()>, +} + +struct Task { + req: Request, + result_tx: Sender>, +} + +struct Process { + path: PathBuf, + child: Child, +} + +impl Drop for Process { + fn drop(&mut self) { + let _ = self.child.kill(); + } +} + +impl Process { + fn run(process_path: &Path) -> Result { + let child = Command::new(process_path.clone()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn()?; + + Ok(Process { path: process_path.into(), child }) + } + + fn restart(&mut self) -> Result<(), io::Error> { + let _ = self.child.kill(); + self.child = Command::new(self.path.clone()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn()?; + Ok(()) + } + + fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> { + let stdin = self.child.stdin.take()?; + let stdout = self.child.stdout.take()?; + let read = BufReader::new(stdout); + + Some((stdin, read)) + } +} + +impl ProcMacroProcessSrv { + pub fn run( + process_path: &Path, + ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error> { + let process = Process::run(process_path)?; + + let (task_tx, task_rx) = bounded(0); + let handle = jod_thread::spawn(move || { + client_loop(task_rx, process); + }); + + let task_tx = Arc::new(task_tx); + let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) }; + let thread = ProcMacroProcessThread { handle, sender: task_tx }; + + Ok((thread, srv)) + } + + pub fn find_proc_macros( + &self, + dylib_path: &Path, + ) -> Result, ra_tt::ExpansionError> { + let task = ListMacrosTask { lib: dylib_path.to_path_buf() }; + + let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?; + Ok(result.macros) + } + + pub fn custom_derive( + &self, + dylib_path: &Path, + subtree: &Subtree, + derive_name: &str, + ) -> Result { + let task = ExpansionTask { + macro_body: subtree.clone(), + macro_name: derive_name.to_string(), + attributes: None, + lib: dylib_path.to_path_buf(), + }; + + let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?; + Ok(result.expansion) + } + + pub fn send_task(&self, req: Request) -> Result + where + R: TryFrom, + { + let sender = match &self.inner { + None => return Err(ra_tt::ExpansionError::Unknown("No sender is found.".to_string())), + Some(it) => it, + }; + + let (result_tx, result_rx) = bounded(0); + let sender = match sender.upgrade() { + None => { + return Err(ra_tt::ExpansionError::Unknown("Proc macro process is closed.".into())) + } + Some(it) => it, + }; + sender.send(Task { req: req.into(), result_tx }).unwrap(); + let res = result_rx + .recv() + .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?; + + match res { + Some(Response::Error(err)) => { + return Err(ra_tt::ExpansionError::ExpansionError(err.message)); + } + Some(res) => Ok(res.try_into().map_err(|err| { + ra_tt::ExpansionError::Unknown(format!( + "Fail to get response, reason : {:#?} ", + err + )) + })?), + None => Err(ra_tt::ExpansionError::Unknown("Empty result".into())), + } + } +} + +fn client_loop(task_rx: Receiver, mut process: Process) { + let (mut stdin, mut stdout) = match process.stdio() { + None => return, + Some(it) => it, + }; + + for task in task_rx { + let Task { req, result_tx } = task; + + match send_request(&mut stdin, &mut stdout, req) { + Ok(res) => result_tx.send(res).unwrap(), + Err(_err) => { + let res = Response::Error(ResponseError { + code: ErrorCode::ServerErrorEnd, + message: "Server closed".into(), + }); + result_tx.send(res.into()).unwrap(); + // Restart the process + if process.restart().is_err() { + break; + } + let stdio = match process.stdio() { + None => break, + Some(it) => it, + }; + stdin = stdio.0; + stdout = stdio.1; + } + } + } +} + +fn send_request( + mut writer: &mut impl Write, + mut reader: &mut impl BufRead, + req: Request, +) -> Result, io::Error> { + req.write(&mut writer)?; + Ok(Response::read(&mut reader)?) +} diff --git a/crates/ra_proc_macro/src/rpc.rs b/crates/ra_proc_macro/src/rpc.rs new file mode 100644 index 000000000000..66b3f55db370 --- /dev/null +++ b/crates/ra_proc_macro/src/rpc.rs @@ -0,0 +1,266 @@ +//! Data struture serialization related stuffs for RPC +//! +//! Define all necessary rpc serialization data structure, +//! which include ra_tt related data and some task messages. +//! Although adding Serialize and Deserialize trait to ra_tt directly seem to be much easier, +//! we deliberately duplicate the ra_tt struct with #[serde(with = "XXDef")] +//! for separation of code responsibility. + +use ra_tt::{ + Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, SmolStr, Spacing, Subtree, TokenId, + TokenTree, +}; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] +pub struct ListMacrosTask { + pub lib: PathBuf, +} + +#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] +pub enum ProcMacroKind { + CustomDerive, + FuncLike, + Attr, +} + +#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] +pub struct ListMacrosResult { + pub macros: Vec<(String, ProcMacroKind)>, +} + +#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] +pub struct ExpansionTask { + /// Argument of macro call. + /// + /// In custom derive that would be a struct or enum; in attribute-like macro - underlying + /// item; in function-like macro - the macro body. + #[serde(with = "SubtreeDef")] + pub macro_body: Subtree, + + /// Names of macros to expand. + /// + /// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In + /// attribute-like and functiona-like macros - single name of macro itself (`show_streams`). + pub macro_name: String, + + /// Possible attributes for the attribute-like macros. + #[serde(with = "opt_subtree_def")] + pub attributes: Option, + + pub lib: PathBuf, +} + +#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] +pub struct ExpansionResult { + #[serde(with = "SubtreeDef")] + pub expansion: Subtree, +} + +#[derive(Serialize, Deserialize)] +#[serde(remote = "DelimiterKind")] +enum DelimiterKindDef { + Parenthesis, + Brace, + Bracket, +} + +#[derive(Serialize, Deserialize)] +#[serde(remote = "TokenId")] +struct TokenIdDef(u32); + +#[derive(Serialize, Deserialize)] +#[serde(remote = "Delimiter")] +struct DelimiterDef { + #[serde(with = "TokenIdDef")] + pub id: TokenId, + #[serde(with = "DelimiterKindDef")] + pub kind: DelimiterKind, +} + +#[derive(Serialize, Deserialize)] +#[serde(remote = "Subtree")] +struct SubtreeDef { + #[serde(default, with = "opt_delimiter_def")] + pub delimiter: Option, + #[serde(with = "vec_token_tree")] + pub token_trees: Vec, +} + +#[derive(Serialize, Deserialize)] +#[serde(remote = "TokenTree")] +enum TokenTreeDef { + #[serde(with = "LeafDef")] + Leaf(Leaf), + #[serde(with = "SubtreeDef")] + Subtree(Subtree), +} + +#[derive(Serialize, Deserialize)] +#[serde(remote = "Leaf")] +enum LeafDef { + #[serde(with = "LiteralDef")] + Literal(Literal), + #[serde(with = "PunctDef")] + Punct(Punct), + #[serde(with = "IdentDef")] + Ident(Ident), +} + +#[derive(Serialize, Deserialize)] +#[serde(remote = "Literal")] +struct LiteralDef { + pub text: SmolStr, + #[serde(with = "TokenIdDef")] + pub id: TokenId, +} + +#[derive(Serialize, Deserialize)] +#[serde(remote = "Punct")] +struct PunctDef { + pub char: char, + #[serde(with = "SpacingDef")] + pub spacing: Spacing, + #[serde(with = "TokenIdDef")] + pub id: TokenId, +} + +#[derive(Serialize, Deserialize)] +#[serde(remote = "Spacing")] +enum SpacingDef { + Alone, + Joint, +} + +#[derive(Serialize, Deserialize)] +#[serde(remote = "Ident")] +struct IdentDef { + pub text: SmolStr, + #[serde(with = "TokenIdDef")] + pub id: TokenId, +} + +mod opt_delimiter_def { + use super::{Delimiter, DelimiterDef}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + pub fn serialize(value: &Option, serializer: S) -> Result + where + S: Serializer, + { + #[derive(Serialize)] + struct Helper<'a>(#[serde(with = "DelimiterDef")] &'a Delimiter); + value.as_ref().map(Helper).serialize(serializer) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper(#[serde(with = "DelimiterDef")] Delimiter); + let helper = Option::deserialize(deserializer)?; + Ok(helper.map(|Helper(external)| external)) + } +} + +mod opt_subtree_def { + use super::{Subtree, SubtreeDef}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + pub fn serialize(value: &Option, serializer: S) -> Result + where + S: Serializer, + { + #[derive(Serialize)] + struct Helper<'a>(#[serde(with = "SubtreeDef")] &'a Subtree); + value.as_ref().map(Helper).serialize(serializer) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper(#[serde(with = "SubtreeDef")] Subtree); + let helper = Option::deserialize(deserializer)?; + Ok(helper.map(|Helper(external)| external)) + } +} + +mod vec_token_tree { + use super::{TokenTree, TokenTreeDef}; + use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}; + + pub fn serialize(value: &Vec, serializer: S) -> Result + where + S: Serializer, + { + #[derive(Serialize)] + struct Helper<'a>(#[serde(with = "TokenTreeDef")] &'a TokenTree); + + let items: Vec<_> = value.iter().map(Helper).collect(); + let mut seq = serializer.serialize_seq(Some(items.len()))?; + for element in items { + seq.serialize_element(&element)?; + } + seq.end() + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper(#[serde(with = "TokenTreeDef")] TokenTree); + + let helper = Vec::deserialize(deserializer)?; + Ok(helper.into_iter().map(|Helper(external)| external).collect()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn fixture_token_tree() -> Subtree { + let mut subtree = Subtree::default(); + subtree + .token_trees + .push(TokenTree::Leaf(Ident { text: "struct".into(), id: TokenId(0) }.into())); + subtree + .token_trees + .push(TokenTree::Leaf(Ident { text: "Foo".into(), id: TokenId(1) }.into())); + subtree.token_trees.push(TokenTree::Subtree( + Subtree { + delimiter: Some(Delimiter { id: TokenId(2), kind: DelimiterKind::Brace }), + token_trees: vec![], + } + .into(), + )); + subtree + } + + #[test] + fn test_proc_macro_rpc_works() { + let tt = fixture_token_tree(); + let task = ExpansionTask { + macro_body: tt.clone(), + macro_name: Default::default(), + attributes: None, + lib: Default::default(), + }; + + let json = serde_json::to_string(&task).unwrap(); + let back: ExpansionTask = serde_json::from_str(&json).unwrap(); + + assert_eq!(task.macro_body, back.macro_body); + + let result = ExpansionResult { expansion: tt.clone() }; + let json = serde_json::to_string(&result).unwrap(); + let back: ExpansionResult = serde_json::from_str(&json).unwrap(); + + assert_eq!(result, back); + } +} diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 738fd6f610cb..0aea01d8331e 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -1,6 +1,7 @@ //! FIXME: write short doc here use std::{ + ffi::OsStr, ops, path::{Path, PathBuf}, }; @@ -299,7 +300,10 @@ pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) Message::CompilerArtifact(message) => { if message.target.kind.contains(&"proc-macro".to_string()) { let package_id = message.package_id; - if let Some(filename) = message.filenames.get(0) { + // Skip rmeta file + if let Some(filename) = + message.filenames.iter().filter(|name| is_dylib(name)).next() + { acc.proc_dylib_paths.insert(package_id, filename.clone()); } } @@ -316,3 +320,11 @@ pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) acc } + +// FIXME: File a better way to know if it is a dylib +fn is_dylib(path: &Path) -> bool { + match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) { + None => false, + Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"), + } +} diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs index 1015ce0a60d9..bd484aa30a04 100644 --- a/crates/ra_tt/src/lib.rs +++ b/crates/ra_tt/src/lib.rs @@ -189,7 +189,12 @@ impl Subtree { pub mod buffer; #[derive(Debug, PartialEq, Eq)] -pub enum ExpansionError {} +pub enum ExpansionError { + IOError(String), + JsonError(String), + Unknown(String), + ExpansionError(String), +} pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index c233f72ffe6b..9fd568601646 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -109,6 +109,7 @@ fn get_config( }, rustfmt_args: config.rustfmt_args.clone(), vscode_lldb: config.vscode_lldb, + proc_macro_srv: None, // FIXME: get this from config } } diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index a15a7085f280..4e2ddcb8bb47 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs @@ -58,6 +58,7 @@ pub struct Config { pub rustfmt_args: Vec, pub check: CheckConfig, pub vscode_lldb: bool, + pub proc_macro_srv: Option, } /// `WorldState` is the primary mutable state of the language server @@ -167,8 +168,23 @@ impl WorldState { vfs_file.map(|f| FileId(f.0)) }; - let proc_macro_client = - ProcMacroClient::extern_process(std::path::Path::new("ra_proc_macro_srv")); + let proc_macro_client = match &config.proc_macro_srv { + None => ProcMacroClient::dummy(), + Some(srv) => { + let path = Path::new(&srv); + match ProcMacroClient::extern_process(path) { + Ok(it) => it, + Err(err) => { + log::error!( + "Fail to run ra_proc_macro_srv from path {}, error : {}", + path.to_string_lossy(), + err + ); + ProcMacroClient::dummy() + } + } + } + }; workspaces .iter()