Add runnable for rust main function (#13087)
Release Notes: - N/A https://github.com/zed-industries/zed/assets/21101490/7a57805c-1d31-48b2-bc2c-3a6f0b730d72
This commit is contained in:
parent
5dc26c261d
commit
3701e190ce
2 changed files with 157 additions and 11 deletions
|
@ -346,10 +346,17 @@ pub(crate) struct RustContextProvider;
|
||||||
const RUST_PACKAGE_TASK_VARIABLE: VariableName =
|
const RUST_PACKAGE_TASK_VARIABLE: VariableName =
|
||||||
VariableName::Custom(Cow::Borrowed("RUST_PACKAGE"));
|
VariableName::Custom(Cow::Borrowed("RUST_PACKAGE"));
|
||||||
|
|
||||||
|
/// The bin name corresponding to the current file in Cargo.toml
|
||||||
|
const RUST_BIN_NAME_TASK_VARIABLE: VariableName =
|
||||||
|
VariableName::Custom(Cow::Borrowed("RUST_BIN_NAME"));
|
||||||
|
|
||||||
|
const RUST_MAIN_FUNCTION_TASK_VARIABLE: VariableName =
|
||||||
|
VariableName::Custom(Cow::Borrowed("_rust_main_function_end"));
|
||||||
|
|
||||||
impl ContextProvider for RustContextProvider {
|
impl ContextProvider for RustContextProvider {
|
||||||
fn build_context(
|
fn build_context(
|
||||||
&self,
|
&self,
|
||||||
_: &TaskVariables,
|
task_variables: &TaskVariables,
|
||||||
location: &Location,
|
location: &Location,
|
||||||
cx: &mut gpui::AppContext,
|
cx: &mut gpui::AppContext,
|
||||||
) -> Result<TaskVariables> {
|
) -> Result<TaskVariables> {
|
||||||
|
@ -358,17 +365,35 @@ impl ContextProvider for RustContextProvider {
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.file()
|
.file()
|
||||||
.and_then(|file| Some(file.as_local()?.abs_path(cx)));
|
.and_then(|file| Some(file.as_local()?.abs_path(cx)));
|
||||||
Ok(
|
|
||||||
|
let local_abs_path = local_abs_path.as_deref();
|
||||||
|
|
||||||
|
let is_main_function = task_variables
|
||||||
|
.get(&RUST_MAIN_FUNCTION_TASK_VARIABLE)
|
||||||
|
.is_some();
|
||||||
|
|
||||||
|
if is_main_function {
|
||||||
|
if let Some((package_name, bin_name)) = local_abs_path
|
||||||
|
.and_then(|local_abs_path| package_name_and_bin_name_from_abs_path(local_abs_path))
|
||||||
|
{
|
||||||
|
return Ok(TaskVariables::from_iter([
|
||||||
|
(RUST_PACKAGE_TASK_VARIABLE.clone(), package_name),
|
||||||
|
(RUST_BIN_NAME_TASK_VARIABLE.clone(), bin_name),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(package_name) = local_abs_path
|
if let Some(package_name) = local_abs_path
|
||||||
.as_deref()
|
|
||||||
.and_then(|local_abs_path| local_abs_path.parent())
|
.and_then(|local_abs_path| local_abs_path.parent())
|
||||||
.and_then(human_readable_package_name)
|
.and_then(human_readable_package_name)
|
||||||
{
|
{
|
||||||
TaskVariables::from_iter(Some((RUST_PACKAGE_TASK_VARIABLE.clone(), package_name)))
|
return Ok(TaskVariables::from_iter([(
|
||||||
} else {
|
RUST_PACKAGE_TASK_VARIABLE.clone(),
|
||||||
TaskVariables::default()
|
package_name,
|
||||||
},
|
)]));
|
||||||
)
|
}
|
||||||
|
|
||||||
|
Ok(TaskVariables::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn associated_tasks(&self) -> Option<TaskTemplates> {
|
fn associated_tasks(&self) -> Option<TaskTemplates> {
|
||||||
|
@ -426,6 +451,23 @@ impl ContextProvider for RustContextProvider {
|
||||||
tags: vec!["rust-mod-test".to_owned()],
|
tags: vec!["rust-mod-test".to_owned()],
|
||||||
..TaskTemplate::default()
|
..TaskTemplate::default()
|
||||||
},
|
},
|
||||||
|
TaskTemplate {
|
||||||
|
label: format!(
|
||||||
|
"cargo run -p {} --bin {}",
|
||||||
|
RUST_PACKAGE_TASK_VARIABLE.template_value(),
|
||||||
|
RUST_BIN_NAME_TASK_VARIABLE.template_value(),
|
||||||
|
),
|
||||||
|
command: "cargo".into(),
|
||||||
|
args: vec![
|
||||||
|
"run".into(),
|
||||||
|
"-p".into(),
|
||||||
|
RUST_PACKAGE_TASK_VARIABLE.template_value(),
|
||||||
|
"--bin".into(),
|
||||||
|
RUST_BIN_NAME_TASK_VARIABLE.template_value(),
|
||||||
|
],
|
||||||
|
tags: vec!["rust-main".to_owned()],
|
||||||
|
..TaskTemplate::default()
|
||||||
|
},
|
||||||
TaskTemplate {
|
TaskTemplate {
|
||||||
label: format!(
|
label: format!(
|
||||||
"cargo test -p {}",
|
"cargo test -p {}",
|
||||||
|
@ -455,6 +497,65 @@ impl ContextProvider for RustContextProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Part of the data structure of Cargo metadata
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct CargoMetadata {
|
||||||
|
packages: Vec<CargoPackage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct CargoPackage {
|
||||||
|
id: String,
|
||||||
|
targets: Vec<CargoTarget>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct CargoTarget {
|
||||||
|
name: String,
|
||||||
|
kind: Vec<String>,
|
||||||
|
src_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn package_name_and_bin_name_from_abs_path(abs_path: &Path) -> Option<(String, String)> {
|
||||||
|
let output = std::process::Command::new("cargo")
|
||||||
|
.current_dir(abs_path.parent()?)
|
||||||
|
.arg("metadata")
|
||||||
|
.arg("--no-deps")
|
||||||
|
.arg("--format-version")
|
||||||
|
.arg("1")
|
||||||
|
.output()
|
||||||
|
.log_err()?
|
||||||
|
.stdout;
|
||||||
|
|
||||||
|
let metadata: CargoMetadata = serde_json::from_slice(&output).log_err()?;
|
||||||
|
|
||||||
|
retrieve_package_id_and_bin_name_from_metadata(metadata, abs_path).and_then(
|
||||||
|
|(package_id, bin_name)| {
|
||||||
|
let package_name = package_name_from_pkgid(&package_id);
|
||||||
|
|
||||||
|
package_name.map(|package_name| (package_name.to_owned(), bin_name))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retrieve_package_id_and_bin_name_from_metadata(
|
||||||
|
metadata: CargoMetadata,
|
||||||
|
abs_path: &Path,
|
||||||
|
) -> Option<(String, String)> {
|
||||||
|
let abs_path = abs_path.to_str()?;
|
||||||
|
|
||||||
|
for package in metadata.packages {
|
||||||
|
for target in package.targets {
|
||||||
|
let is_bin = target.kind.iter().any(|kind| kind == "bin");
|
||||||
|
if target.src_path == abs_path && is_bin {
|
||||||
|
return Some((package.id, target.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn human_readable_package_name(package_directory: &Path) -> Option<String> {
|
fn human_readable_package_name(package_directory: &Path) -> Option<String> {
|
||||||
let pkgid = String::from_utf8(
|
let pkgid = String::from_utf8(
|
||||||
std::process::Command::new("cargo")
|
std::process::Command::new("cargo")
|
||||||
|
@ -815,4 +916,37 @@ mod tests {
|
||||||
assert_eq!(package_name_from_pkgid(input), Some(expected));
|
assert_eq!(package_name_from_pkgid(input), Some(expected));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_retrieve_package_id_and_bin_name_from_metadata() {
|
||||||
|
for (input, absolute_path, expected) in [
|
||||||
|
(
|
||||||
|
r#"{"packages":[{"id":"path+file:///path/to/zed/crates/zed#0.131.0","targets":[{"name":"zed","kind":["bin"],"src_path":"/path/to/zed/src/main.rs"}]}]}"#,
|
||||||
|
"/path/to/zed/src/main.rs",
|
||||||
|
Some(("path+file:///path/to/zed/crates/zed#0.131.0", "zed")),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r#"{"packages":[{"id":"path+file:///path/to/custom-package#my-custom-package@0.1.0","targets":[{"name":"my-custom-bin","kind":["bin"],"src_path":"/path/to/custom-package/src/main.rs"}]}]}"#,
|
||||||
|
"/path/to/custom-package/src/main.rs",
|
||||||
|
Some((
|
||||||
|
"path+file:///path/to/custom-package#my-custom-package@0.1.0",
|
||||||
|
"my-custom-bin",
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r#"{"packages":[{"id":"path+file:///path/to/custom-package#my-custom-package@0.1.0","targets":[{"name":"my-custom-package","kind":["lib"],"src_path":"/path/to/custom-package/src/main.rs"}]}]}"#,
|
||||||
|
"/path/to/custom-package/src/main.rs",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let metadata: CargoMetadata = serde_json::from_str(input).unwrap();
|
||||||
|
|
||||||
|
let absolute_path = Path::new(absolute_path);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
retrieve_package_id_and_bin_name_from_metadata(metadata, absolute_path),
|
||||||
|
expected.map(|(pkgid, bin)| (pkgid.to_owned(), bin.to_owned()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,3 +25,15 @@
|
||||||
)
|
)
|
||||||
(#set! tag rust-test)
|
(#set! tag rust-test)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
; Rust main function
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(function_item
|
||||||
|
name: (_) @run
|
||||||
|
body: _
|
||||||
|
) @_rust_main_function_end
|
||||||
|
(#eq? @run "main")
|
||||||
|
)
|
||||||
|
(#set! tag rust-main)
|
||||||
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue