mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
feat: Attempt to format expand_macro output with rustfmt if possible
This commit is contained in:
parent
53afd2a707
commit
e2f1a9a558
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -611,6 +611,7 @@ dependencies = [
|
|||||||
"syntax",
|
"syntax",
|
||||||
"test_utils",
|
"test_utils",
|
||||||
"text_edit",
|
"text_edit",
|
||||||
|
"toolchain",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
@ -32,6 +32,7 @@ ide_assists = { path = "../ide_assists", version = "0.0.0" }
|
|||||||
ide_diagnostics = { path = "../ide_diagnostics", version = "0.0.0" }
|
ide_diagnostics = { path = "../ide_diagnostics", version = "0.0.0" }
|
||||||
ide_ssr = { path = "../ide_ssr", version = "0.0.0" }
|
ide_ssr = { path = "../ide_ssr", version = "0.0.0" }
|
||||||
ide_completion = { path = "../ide_completion", version = "0.0.0" }
|
ide_completion = { path = "../ide_completion", version = "0.0.0" }
|
||||||
|
toolchain = { path = "../toolchain", version = "0.0.0" }
|
||||||
|
|
||||||
# ide should depend only on the top-level `hir` package. if you need
|
# ide should depend only on the top-level `hir` package. if you need
|
||||||
# something from some `hir_xxx` subpackage, reexport the API via `hir`.
|
# something from some `hir_xxx` subpackage, reexport the API via `hir`.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use hir::Semantics;
|
use hir::Semantics;
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
helpers::pick_best_token, syntax_helpers::insert_whitespace_into_node::insert_ws_into,
|
base_db::FileId, helpers::pick_best_token,
|
||||||
RootDatabase,
|
syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase,
|
||||||
};
|
};
|
||||||
use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T};
|
use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T};
|
||||||
|
|
||||||
@ -58,10 +58,9 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
|
|||||||
.take_while(|it| it != &token)
|
.take_while(|it| it != &token)
|
||||||
.filter(|it| it.kind() == T![,])
|
.filter(|it| it.kind() == T![,])
|
||||||
.count();
|
.count();
|
||||||
Some(ExpandedMacro {
|
let expansion =
|
||||||
name,
|
format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?);
|
||||||
expansion: expansions.get(idx).cloned().map(insert_ws_into)?.to_string(),
|
Some(ExpandedMacro { name, expansion })
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if derive.is_some() {
|
if derive.is_some() {
|
||||||
@ -72,17 +71,20 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
|
|||||||
// currently we only recursively expand one of the two types
|
// currently we only recursively expand one of the two types
|
||||||
let mut expanded = None;
|
let mut expanded = None;
|
||||||
let mut name = None;
|
let mut name = None;
|
||||||
|
let mut kind = SyntaxKind::ERROR;
|
||||||
for node in tok.ancestors() {
|
for node in tok.ancestors() {
|
||||||
if let Some(item) = ast::Item::cast(node.clone()) {
|
if let Some(item) = ast::Item::cast(node.clone()) {
|
||||||
if let Some(def) = sema.resolve_attr_macro_call(&item) {
|
if let Some(def) = sema.resolve_attr_macro_call(&item) {
|
||||||
name = Some(def.name(db).to_string());
|
name = Some(def.name(db).to_string());
|
||||||
expanded = expand_attr_macro_recur(&sema, &item);
|
expanded = expand_attr_macro_recur(&sema, &item);
|
||||||
|
kind = SyntaxKind::MACRO_ITEMS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(mac) = ast::MacroCall::cast(node) {
|
if let Some(mac) = ast::MacroCall::cast(node) {
|
||||||
name = Some(mac.path()?.segment()?.name_ref()?.to_string());
|
name = Some(mac.path()?.segment()?.name_ref()?.to_string());
|
||||||
expanded = expand_macro_recur(&sema, &mac);
|
expanded = expand_macro_recur(&sema, &mac);
|
||||||
|
kind = mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,7 +92,8 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
|
|||||||
// FIXME:
|
// FIXME:
|
||||||
// macro expansion may lose all white space information
|
// macro expansion may lose all white space information
|
||||||
// But we hope someday we can use ra_fmt for that
|
// But we hope someday we can use ra_fmt for that
|
||||||
let expansion = insert_ws_into(expanded?).to_string();
|
let expansion = format(db, kind, position.file_id, expanded?);
|
||||||
|
|
||||||
Some(ExpandedMacro { name: name.unwrap_or_else(|| "???".to_owned()), expansion })
|
Some(ExpandedMacro { name: name.unwrap_or_else(|| "???".to_owned()), expansion })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +133,70 @@ fn expand<T: AstNode>(
|
|||||||
Some(expanded)
|
Some(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format(db: &RootDatabase, kind: SyntaxKind, file_id: FileId, expanded: SyntaxNode) -> String {
|
||||||
|
let expansion = insert_ws_into(expanded).to_string();
|
||||||
|
|
||||||
|
_format(db, kind, file_id, &expansion).unwrap_or(expansion)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn _format(
|
||||||
|
_db: &RootDatabase,
|
||||||
|
_kind: SyntaxKind,
|
||||||
|
_file_id: FileId,
|
||||||
|
_expansion: &str,
|
||||||
|
) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
fn _format(
|
||||||
|
db: &RootDatabase,
|
||||||
|
kind: SyntaxKind,
|
||||||
|
file_id: FileId,
|
||||||
|
expansion: &str,
|
||||||
|
) -> Option<String> {
|
||||||
|
use ide_db::base_db::{FileLoader, SourceDatabase};
|
||||||
|
// hack until we get hygiene working (same character amount to preserve formatting as much as possible)
|
||||||
|
const DOLLAR_CRATE_REPLACE: &str = &"__r_a_";
|
||||||
|
let expansion = expansion.replace("$crate", DOLLAR_CRATE_REPLACE);
|
||||||
|
let (prefix, suffix) = match kind {
|
||||||
|
SyntaxKind::MACRO_PAT => ("fn __(", ": u32);"),
|
||||||
|
SyntaxKind::MACRO_EXPR | SyntaxKind::MACRO_STMTS => ("fn __() {", "}"),
|
||||||
|
SyntaxKind::MACRO_TYPE => ("type __ =", ";"),
|
||||||
|
_ => ("", ""),
|
||||||
|
};
|
||||||
|
let expansion = format!("{prefix}{expansion}{suffix}");
|
||||||
|
|
||||||
|
let &crate_id = db.relevant_crates(file_id).iter().next()?;
|
||||||
|
let edition = db.crate_graph()[crate_id].edition;
|
||||||
|
|
||||||
|
let mut cmd = std::process::Command::new(toolchain::rustfmt());
|
||||||
|
cmd.arg("--edition");
|
||||||
|
cmd.arg(edition.to_string());
|
||||||
|
|
||||||
|
let mut rustfmt = cmd
|
||||||
|
.stdin(std::process::Stdio::piped())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
std::io::Write::write_all(&mut rustfmt.stdin.as_mut()?, expansion.as_bytes()).ok()?;
|
||||||
|
|
||||||
|
let output = rustfmt.wait_with_output().ok()?;
|
||||||
|
let captured_stdout = String::from_utf8(output.stdout).ok()?;
|
||||||
|
|
||||||
|
if output.status.success() && !captured_stdout.trim().is_empty() {
|
||||||
|
let foo = captured_stdout.replace(DOLLAR_CRATE_REPLACE, "$crate");
|
||||||
|
let trim_indent = stdx::trim_indent(foo.trim().strip_prefix(prefix)?.strip_suffix(suffix)?);
|
||||||
|
tracing::debug!("expand_macro: formatting succeeded");
|
||||||
|
Some(trim_indent)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
|
@ -340,7 +340,6 @@ impl TryToNav for hir::Macro {
|
|||||||
Either::Left(it) => it,
|
Either::Left(it) => it,
|
||||||
Either::Right(it) => it,
|
Either::Right(it) => it,
|
||||||
};
|
};
|
||||||
tracing::debug!("nav target {:#?}", name_owner.syntax());
|
|
||||||
let mut res = NavigationTarget::from_named(
|
let mut res = NavigationTarget::from_named(
|
||||||
db,
|
db,
|
||||||
src.as_ref().with_value(name_owner),
|
src.as_ref().with_value(name_owner),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user