mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
Implement ra_proc_macro client logic
This commit is contained in:
parent
fa3c7742af
commit
503cbd3f4b
11 changed files with 827 additions and 23 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -1075,7 +1075,12 @@ dependencies = [
|
||||||
name = "ra_proc_macro"
|
name = "ra_proc_macro"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"log",
|
||||||
"ra_tt",
|
"ra_tt",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -9,6 +9,15 @@ pub struct ProcMacroExpander {
|
||||||
proc_macro_id: ProcMacroId,
|
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 {
|
impl ProcMacroExpander {
|
||||||
pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander {
|
pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander {
|
||||||
ProcMacroExpander { krate, proc_macro_id }
|
ProcMacroExpander { krate, proc_macro_id }
|
||||||
|
@ -25,8 +34,24 @@ impl ProcMacroExpander {
|
||||||
.proc_macro
|
.proc_macro
|
||||||
.get(self.proc_macro_id.0 as usize)
|
.get(self.proc_macro_id.0 as usize)
|
||||||
.clone()
|
.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)
|
proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_derive_atr(tt: &tt::Subtree, _name: &str) -> Option<tt::Subtree> {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
|
@ -10,3 +10,8 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ra_tt = { path = "../ra_tt" }
|
ra_tt = { path = "../ra_tt" }
|
||||||
|
serde_derive = "1.0.104"
|
||||||
|
serde = "1.0.104"
|
||||||
|
serde_json = "1.0.48"
|
||||||
|
log = "0.4.8"
|
||||||
|
crossbeam-channel = "0.4.0"
|
||||||
|
|
|
@ -5,55 +5,102 @@
|
||||||
//! is used to provide basic infrastructure for communication between two
|
//! is used to provide basic infrastructure for communication between two
|
||||||
//! processes: Client (RA itself), Server (the external program)
|
//! processes: Client (RA itself), Server (the external program)
|
||||||
|
|
||||||
|
mod rpc;
|
||||||
|
mod process;
|
||||||
|
pub mod msg;
|
||||||
|
|
||||||
|
use process::ProcMacroProcessSrv;
|
||||||
use ra_tt::{SmolStr, Subtree};
|
use ra_tt::{SmolStr, Subtree};
|
||||||
|
use rpc::ProcMacroKind;
|
||||||
use std::{
|
use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
pub use rpc::{ExpansionResult, ExpansionTask};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct ProcMacroProcessExpander {
|
pub struct ProcMacroProcessExpander {
|
||||||
process: Arc<ProcMacroProcessSrv>,
|
process: Arc<ProcMacroProcessSrv>,
|
||||||
|
dylib_path: PathBuf,
|
||||||
name: SmolStr,
|
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 {
|
impl ra_tt::TokenExpander for ProcMacroProcessExpander {
|
||||||
fn expand(
|
fn expand(
|
||||||
&self,
|
&self,
|
||||||
_subtree: &Subtree,
|
subtree: &Subtree,
|
||||||
_attr: Option<&Subtree>,
|
_attr: Option<&Subtree>,
|
||||||
) -> Result<Subtree, ra_tt::ExpansionError> {
|
) -> Result<Subtree, ra_tt::ExpansionError> {
|
||||||
// FIXME: do nothing for now
|
self.process.custom_derive(&self.dylib_path, subtree, &self.name)
|
||||||
Ok(Subtree::default())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ProcMacroProcessSrv {
|
enum ProcMacroClientKind {
|
||||||
path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub enum ProcMacroClient {
|
|
||||||
Process { process: Arc<ProcMacroProcessSrv> },
|
Process { process: Arc<ProcMacroProcessSrv> },
|
||||||
Dummy,
|
Dummy,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ProcMacroClient {
|
||||||
|
kind: ProcMacroClientKind,
|
||||||
|
}
|
||||||
|
|
||||||
impl ProcMacroClient {
|
impl ProcMacroClient {
|
||||||
pub fn extern_process(process_path: &Path) -> ProcMacroClient {
|
pub fn extern_process(process_path: &Path) -> Result<ProcMacroClient, std::io::Error> {
|
||||||
let process = ProcMacroProcessSrv { path: process_path.into() };
|
let process = ProcMacroProcessSrv::run(process_path)?;
|
||||||
ProcMacroClient::Process { process: Arc::new(process) }
|
Ok(ProcMacroClient { kind: ProcMacroClientKind::Process { process: Arc::new(process) } })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dummy() -> ProcMacroClient {
|
pub fn dummy() -> ProcMacroClient {
|
||||||
ProcMacroClient::Dummy
|
ProcMacroClient { kind: ProcMacroClientKind::Dummy }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn by_dylib_path(
|
pub fn by_dylib_path(
|
||||||
&self,
|
&self,
|
||||||
_dylib_path: &Path,
|
dylib_path: &Path,
|
||||||
) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> {
|
) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> {
|
||||||
// FIXME: return empty for now
|
match &self.kind {
|
||||||
vec![]
|
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<dyn ra_tt::TokenExpander> =
|
||||||
|
Arc::new(ProcMacroProcessExpander {
|
||||||
|
process: process.clone(),
|
||||||
|
name: name.clone(),
|
||||||
|
dylib_path: dylib_path.into(),
|
||||||
|
});
|
||||||
|
Some((name, expander))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
218
crates/ra_proc_macro/src/msg.rs
Normal file
218
crates/ra_proc_macro/src/msg.rs
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
//! A simplified version of lsp base protocol for rpc
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
fmt,
|
||||||
|
io::{self, BufRead, Write},
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Message {
|
||||||
|
Request(Request),
|
||||||
|
Response(Response),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Request> for Message {
|
||||||
|
fn from(request: Request) -> Message {
|
||||||
|
Message::Request(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Response> for Message {
|
||||||
|
fn from(response: Response) -> Message {
|
||||||
|
Message::Response(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct RequestId(IdRepr);
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum IdRepr {
|
||||||
|
U64(u64),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for RequestId {
|
||||||
|
fn from(id: u64) -> RequestId {
|
||||||
|
RequestId(IdRepr::U64(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for RequestId {
|
||||||
|
fn from(id: String) -> RequestId {
|
||||||
|
RequestId(IdRepr::String(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RequestId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match &self.0 {
|
||||||
|
IdRepr::U64(it) => fmt::Display::fmt(it, f),
|
||||||
|
IdRepr::String(it) => fmt::Display::fmt(it, f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Request {
|
||||||
|
pub id: RequestId,
|
||||||
|
pub method: String,
|
||||||
|
pub params: serde_json::Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Response {
|
||||||
|
// JSON RPC allows this to be null if it was impossible
|
||||||
|
// to decode the request's id. Ignore this special case
|
||||||
|
// and just die horribly.
|
||||||
|
pub id: RequestId,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub result: Option<serde_json::Value>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub error: Option<ResponseError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct ResponseError {
|
||||||
|
pub code: i32,
|
||||||
|
pub message: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub data: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub enum ErrorCode {
|
||||||
|
// Defined by JSON RPC
|
||||||
|
ParseError = -32700,
|
||||||
|
InvalidRequest = -32600,
|
||||||
|
MethodNotFound = -32601,
|
||||||
|
InvalidParams = -32602,
|
||||||
|
InternalError = -32603,
|
||||||
|
ServerErrorStart = -32099,
|
||||||
|
ServerErrorEnd = -32000,
|
||||||
|
ServerNotInitialized = -32002,
|
||||||
|
UnknownErrorCode = -32001,
|
||||||
|
|
||||||
|
// Defined by protocol
|
||||||
|
ExpansionError = -32900,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Notification {
|
||||||
|
pub method: String,
|
||||||
|
pub params: serde_json::Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message {
|
||||||
|
pub fn read(r: &mut impl BufRead) -> io::Result<Option<Message>> {
|
||||||
|
let text = match read_msg_text(r)? {
|
||||||
|
None => return Ok(None),
|
||||||
|
Some(text) => text,
|
||||||
|
};
|
||||||
|
let msg = serde_json::from_str(&text)?;
|
||||||
|
Ok(Some(msg))
|
||||||
|
}
|
||||||
|
pub fn write(self, w: &mut impl Write) -> io::Result<()> {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct JsonRpc {
|
||||||
|
jsonrpc: &'static str,
|
||||||
|
#[serde(flatten)]
|
||||||
|
msg: Message,
|
||||||
|
}
|
||||||
|
let text = serde_json::to_string(&JsonRpc { jsonrpc: "2.0", msg: self })?;
|
||||||
|
write_msg_text(w, &text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
pub fn new_ok<R: Serialize>(id: RequestId, result: R) -> Response {
|
||||||
|
Response { id, result: Some(serde_json::to_value(result).unwrap()), error: None }
|
||||||
|
}
|
||||||
|
pub fn new_err(id: RequestId, code: i32, message: String) -> Response {
|
||||||
|
let error = ResponseError { code, message, data: None };
|
||||||
|
Response { id, result: None, error: Some(error) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request {
|
||||||
|
pub fn new<P: Serialize>(id: RequestId, method: String, params: P) -> Request {
|
||||||
|
Request { id, method, params: serde_json::to_value(params).unwrap() }
|
||||||
|
}
|
||||||
|
pub fn extract<P: DeserializeOwned>(self, method: &str) -> Result<(RequestId, P), Request> {
|
||||||
|
if self.method == method {
|
||||||
|
let params = serde_json::from_value(self.params).unwrap_or_else(|err| {
|
||||||
|
panic!("Invalid request\nMethod: {}\n error: {}", method, err)
|
||||||
|
});
|
||||||
|
Ok((self.id, params))
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Notification {
|
||||||
|
pub fn new(method: String, params: impl Serialize) -> Notification {
|
||||||
|
Notification { method, params: serde_json::to_value(params).unwrap() }
|
||||||
|
}
|
||||||
|
pub fn extract<P: DeserializeOwned>(self, method: &str) -> Result<P, Notification> {
|
||||||
|
if self.method == method {
|
||||||
|
let params = serde_json::from_value(self.params).unwrap();
|
||||||
|
Ok(params)
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_msg_text(inp: &mut impl BufRead) -> io::Result<Option<String>> {
|
||||||
|
fn invalid_data(error: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
|
||||||
|
io::Error::new(io::ErrorKind::InvalidData, error)
|
||||||
|
}
|
||||||
|
macro_rules! invalid_data {
|
||||||
|
($($tt:tt)*) => (invalid_data(format!($($tt)*)))
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut size = None;
|
||||||
|
let mut buf = String::new();
|
||||||
|
loop {
|
||||||
|
buf.clear();
|
||||||
|
if inp.read_line(&mut buf)? == 0 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
if !buf.ends_with("\r\n") {
|
||||||
|
return Err(invalid_data!("malformed header: {:?}", buf));
|
||||||
|
}
|
||||||
|
let buf = &buf[..buf.len() - 2];
|
||||||
|
if buf.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let mut parts = buf.splitn(2, ": ");
|
||||||
|
let header_name = parts.next().unwrap();
|
||||||
|
let header_value =
|
||||||
|
parts.next().ok_or_else(|| invalid_data!("malformed header: {:?}", buf))?;
|
||||||
|
if header_name == "Content-Length" {
|
||||||
|
size = Some(header_value.parse::<usize>().map_err(invalid_data)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let size: usize = size.ok_or_else(|| invalid_data!("no Content-Length"))?;
|
||||||
|
let mut buf = buf.into_bytes();
|
||||||
|
buf.resize(size, 0);
|
||||||
|
inp.read_exact(&mut buf)?;
|
||||||
|
let buf = String::from_utf8(buf).map_err(invalid_data)?;
|
||||||
|
log::debug!("< {}", buf);
|
||||||
|
Ok(Some(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_msg_text(out: &mut impl Write, msg: &str) -> io::Result<()> {
|
||||||
|
log::debug!("> {}", msg);
|
||||||
|
write!(out, "Content-Length: {}\r\n\r\n", msg.len())?;
|
||||||
|
out.write_all(msg.as_bytes())?;
|
||||||
|
out.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
202
crates/ra_proc_macro/src/process.rs
Normal file
202
crates/ra_proc_macro/src/process.rs
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
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::{
|
||||||
|
io::{self, Write},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::{Child, Command, Stdio},
|
||||||
|
thread::spawn,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub(crate) struct ProcMacroProcessSrv {
|
||||||
|
inner: Option<Handle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Task {
|
||||||
|
req: Message,
|
||||||
|
result_tx: Sender<Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Handle {
|
||||||
|
sender: Sender<Task>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Process {
|
||||||
|
path: PathBuf,
|
||||||
|
child: Child,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Process {
|
||||||
|
fn run(process_path: &Path) -> Result<Process, io::Error> {
|
||||||
|
let child = Command::new(process_path.clone())
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.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()).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<ProcMacroProcessSrv, io::Error> {
|
||||||
|
let process = Process::run(process_path)?;
|
||||||
|
|
||||||
|
let (task_tx, task_rx) = bounded(0);
|
||||||
|
|
||||||
|
let _ = spawn(move || {
|
||||||
|
client_loop(task_rx, process);
|
||||||
|
});
|
||||||
|
Ok(ProcMacroProcessSrv { inner: Some(Handle { sender: task_tx }) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_proc_macros(
|
||||||
|
&self,
|
||||||
|
dylib_path: &Path,
|
||||||
|
) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> {
|
||||||
|
let task = ListMacrosTask { lib: dylib_path.to_path_buf() };
|
||||||
|
|
||||||
|
let result: ListMacrosResult = self.send_task("list_macros", task)?;
|
||||||
|
Ok(result.macros)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn custom_derive(
|
||||||
|
&self,
|
||||||
|
dylib_path: &Path,
|
||||||
|
subtree: &Subtree,
|
||||||
|
derive_name: &str,
|
||||||
|
) -> Result<Subtree, ra_tt::ExpansionError> {
|
||||||
|
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("custom_derive", task)?;
|
||||||
|
Ok(result.expansion)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_task<'a, T, R>(&self, method: &str, task: T) -> Result<R, ra_tt::ExpansionError>
|
||||||
|
where
|
||||||
|
T: serde::Serialize,
|
||||||
|
R: serde::de::DeserializeOwned + Default,
|
||||||
|
{
|
||||||
|
let handle = match &self.inner {
|
||||||
|
None => return Err(ra_tt::ExpansionError::Unknown("No handle is found.".to_string())),
|
||||||
|
Some(it) => it,
|
||||||
|
};
|
||||||
|
|
||||||
|
let msg = serde_json::to_value(task).unwrap();
|
||||||
|
|
||||||
|
// FIXME: use a proper request id
|
||||||
|
let id = 0;
|
||||||
|
let req = Request { id: id.into(), method: method.into(), params: msg };
|
||||||
|
|
||||||
|
let (result_tx, result_rx) = bounded(0);
|
||||||
|
|
||||||
|
handle.sender.send(Task { req: req.into(), result_tx }).unwrap();
|
||||||
|
let response = result_rx.recv().unwrap();
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Message::Request(_) => {
|
||||||
|
return Err(ra_tt::ExpansionError::Unknown(
|
||||||
|
"Return request from ra_proc_srv".into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Message::Response(res) => {
|
||||||
|
if let Some(err) = res.error {
|
||||||
|
return Err(ra_tt::ExpansionError::ExpansionError(err.message));
|
||||||
|
}
|
||||||
|
match res.result {
|
||||||
|
None => Ok(R::default()),
|
||||||
|
Some(res) => {
|
||||||
|
let result: R = serde_json::from_value(res)
|
||||||
|
.map_err(|err| ra_tt::ExpansionError::JsonError(err.to_string()))?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
|
||||||
|
let (mut stdin, mut stdout) = match process.stdio() {
|
||||||
|
None => return,
|
||||||
|
Some(it) => it,
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let task = match task_rx.recv() {
|
||||||
|
Ok(task) => task,
|
||||||
|
Err(_) => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = match send_message(&mut stdin, &mut stdout, task.req) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(_err) => {
|
||||||
|
let res = Response {
|
||||||
|
id: 0.into(),
|
||||||
|
result: None,
|
||||||
|
error: Some(ResponseError {
|
||||||
|
code: ErrorCode::ServerErrorEnd as i32,
|
||||||
|
message: "Server closed".into(),
|
||||||
|
data: None,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
if task.result_tx.send(res.into()).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(res) = res {
|
||||||
|
if task.result_tx.send(res).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = process.child.kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_message(
|
||||||
|
mut writer: &mut impl Write,
|
||||||
|
mut reader: &mut impl BufRead,
|
||||||
|
msg: Message,
|
||||||
|
) -> Result<Option<Message>, io::Error> {
|
||||||
|
msg.write(&mut writer)?;
|
||||||
|
Ok(Message::read(&mut reader)?)
|
||||||
|
}
|
260
crates/ra_proc_macro/src/rpc.rs
Normal file
260
crates/ra_proc_macro/src/rpc.rs
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
//! Data struture serialization related stuffs for RPC
|
||||||
|
|
||||||
|
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<Subtree>,
|
||||||
|
|
||||||
|
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<Delimiter>,
|
||||||
|
#[serde(with = "vec_token_tree")]
|
||||||
|
pub token_trees: Vec<TokenTree>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<S>(value: &Option<Delimiter>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
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<Option<Delimiter>, 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<S>(value: &Option<Subtree>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
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<Option<Subtree>, 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<S>(value: &Vec<TokenTree>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
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<Vec<TokenTree>, 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(&task).unwrap();
|
||||||
|
let back: ExpansionResult = serde_json::from_str(&json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result, back);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
ops,
|
ops,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
@ -299,7 +300,10 @@ pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures)
|
||||||
Message::CompilerArtifact(message) => {
|
Message::CompilerArtifact(message) => {
|
||||||
if message.target.kind.contains(&"proc-macro".to_string()) {
|
if message.target.kind.contains(&"proc-macro".to_string()) {
|
||||||
let package_id = message.package_id;
|
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());
|
acc.proc_dylib_paths.insert(package_id, filename.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,3 +320,19 @@ pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures)
|
||||||
|
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: File a better way to know if it is a dylib
|
||||||
|
fn is_dylib(path: &Path) -> bool {
|
||||||
|
let ext = match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase())
|
||||||
|
{
|
||||||
|
None => return false,
|
||||||
|
Some(ext) => ext,
|
||||||
|
};
|
||||||
|
|
||||||
|
match ext.as_str() {
|
||||||
|
"dll" => true,
|
||||||
|
"dylib" => true,
|
||||||
|
"so" => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -189,7 +189,12 @@ impl Subtree {
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[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 {
|
pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe {
|
||||||
fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
|
fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
|
||||||
|
|
|
@ -109,6 +109,7 @@ fn get_config(
|
||||||
},
|
},
|
||||||
rustfmt_args: config.rustfmt_args.clone(),
|
rustfmt_args: config.rustfmt_args.clone(),
|
||||||
vscode_lldb: config.vscode_lldb,
|
vscode_lldb: config.vscode_lldb,
|
||||||
|
proc_macro_srv: None, // FIXME: get this from config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ pub struct Config {
|
||||||
pub rustfmt_args: Vec<String>,
|
pub rustfmt_args: Vec<String>,
|
||||||
pub check: CheckConfig,
|
pub check: CheckConfig,
|
||||||
pub vscode_lldb: bool,
|
pub vscode_lldb: bool,
|
||||||
|
pub proc_macro_srv: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `WorldState` is the primary mutable state of the language server
|
/// `WorldState` is the primary mutable state of the language server
|
||||||
|
@ -167,8 +168,23 @@ impl WorldState {
|
||||||
vfs_file.map(|f| FileId(f.0))
|
vfs_file.map(|f| FileId(f.0))
|
||||||
};
|
};
|
||||||
|
|
||||||
let proc_macro_client =
|
let proc_macro_client = match &options.proc_macro_srv {
|
||||||
ProcMacroClient::extern_process(std::path::Path::new("ra_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
|
workspaces
|
||||||
.iter()
|
.iter()
|
||||||
|
|
Loading…
Reference in a new issue