From 8168ec2a2801369863937cd797450520bb65e6cc Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Wed, 22 May 2024 07:18:49 +0200 Subject: [PATCH] go: Add runnables (#12110) This adds support for runnables to Go. It adds the following tasks: - `go test $ZED_GO_PACKAGE -run $ZED_SYMBOL` - `go test $ZED_GO_PACKAGE` - `go test ./...` - `go run $ZED_GO_PACKAGE` if it has a `main` function Release Notes: - Added built-in Go runnables and tasks that allow users to run Go test functions, test packages, or run `main` functions. Demo: https://github.com/zed-industries/zed/assets/1185253/a6271d80-faf4-466a-bf63-efbec8fe6c35 https://github.com/zed-industries/zed/assets/1185253/92f2b616-7501-463d-b613-1ec1084ae0cd --- crates/languages/src/go.rs | 91 ++++++++++++++++++++++++++- crates/languages/src/go/runnables.scm | 9 +++ crates/languages/src/lib.rs | 7 ++- 3 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 crates/languages/src/go/runnables.scm diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index d3a562b4694022c22f5d29bf2c962a923a73ec47..85c92323c4db9efacf563244fb16bf9449c24879 100644 --- a/crates/languages/src/go.rs +++ b/crates/languages/src/go.rs @@ -13,15 +13,17 @@ use settings::Settings; use smol::{fs, process}; use std::{ any::Any, + borrow::Cow, ffi::{OsStr, OsString}, ops::Range, - path::PathBuf, + path::{Path, PathBuf}, str, sync::{ atomic::{AtomicBool, Ordering::SeqCst}, Arc, }, }; +use task::{TaskTemplate, TaskTemplates, TaskVariables, VariableName}; use util::{fs::remove_matching, maybe, ResultExt}; fn server_binary_arguments() -> Vec { @@ -438,6 +440,93 @@ fn adjust_runs( runs } +pub(crate) struct GoContextProvider; + +const GO_PACKAGE_TASK_VARIABLE: VariableName = VariableName::Custom(Cow::Borrowed("GO_PACKAGE")); + +impl ContextProvider for GoContextProvider { + fn build_context( + &self, + worktree_abs_path: Option<&Path>, + location: &Location, + cx: &mut gpui::AppContext, + ) -> Result { + let local_abs_path = location + .buffer + .read(cx) + .file() + .and_then(|file| Some(file.as_local()?.abs_path(cx))); + + Ok( + if let Some(buffer_dir) = local_abs_path + .as_deref() + .and_then(|local_abs_path| local_abs_path.parent()) + { + // Prefer the relative form `./my-nested-package/is-here` over + // absolute path, because it's more readable in the modal, but + // the absolute path also works. + let package_name = worktree_abs_path + .and_then(|worktree_abs_path| buffer_dir.strip_prefix(worktree_abs_path).ok()) + .map(|relative_pkg_dir| { + if relative_pkg_dir.as_os_str().is_empty() { + ".".into() + } else { + format!("./{}", relative_pkg_dir.to_string_lossy()) + } + }) + .unwrap_or_else(|| format!("{}", buffer_dir.to_string_lossy())); + + TaskVariables::from_iter(Some(( + GO_PACKAGE_TASK_VARIABLE.clone(), + package_name.to_string(), + ))) + } else { + TaskVariables::default() + }, + ) + } + + fn associated_tasks(&self) -> Option { + Some(TaskTemplates(vec![ + TaskTemplate { + label: format!( + "go test {} -run {}", + GO_PACKAGE_TASK_VARIABLE.template_value(), + VariableName::Symbol.template_value(), + ), + command: "go".into(), + args: vec![ + "test".into(), + GO_PACKAGE_TASK_VARIABLE.template_value(), + "-run".into(), + VariableName::Symbol.template_value(), + ], + tags: vec!["go-test".to_owned()], + ..TaskTemplate::default() + }, + TaskTemplate { + label: format!("go test {}", GO_PACKAGE_TASK_VARIABLE.template_value()), + command: "go".into(), + args: vec!["test".into(), GO_PACKAGE_TASK_VARIABLE.template_value()], + ..TaskTemplate::default() + }, + TaskTemplate { + label: "go test ./...".into(), + command: "go".into(), + args: vec!["test".into(), "./...".into()], + ..TaskTemplate::default() + }, + TaskTemplate { + label: format!("go run {}", GO_PACKAGE_TASK_VARIABLE.template_value(),), + command: "go".into(), + args: vec!["run".into(), GO_PACKAGE_TASK_VARIABLE.template_value()], + tags: vec!["go-main".to_owned()], + ..TaskTemplate::default() + }, + ])) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/languages/src/go/runnables.scm b/crates/languages/src/go/runnables.scm new file mode 100644 index 0000000000000000000000000000000000000000..a412ecd87cacb9404fbb9e3e62d5a4288c23bd1c --- /dev/null +++ b/crates/languages/src/go/runnables.scm @@ -0,0 +1,9 @@ +( + (function_declaration name: (_) @run + (#match? @run "^Test.*")) +) @go-test + +( + (function_declaration name: (_) @run + (#eq? @run "main")) +) @go-main diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index 6927ced42e760a14ffc1f34a8e762facc1e7b06d..726d130035d1bb3627eab1c9f0c2b5e91802a91b 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -8,7 +8,10 @@ use smol::stream::StreamExt; use std::{str, sync::Arc}; use util::{asset_str, ResultExt}; -use crate::{bash::bash_task_context, python::python_task_context, rust::RustContextProvider}; +use crate::{ + bash::bash_task_context, go::GoContextProvider, python::python_task_context, + rust::RustContextProvider, +}; mod bash; mod c; @@ -103,7 +106,7 @@ pub fn init( "css", vec![Arc::new(css::CssLspAdapter::new(node_runtime.clone())),] ); - language!("go", vec![Arc::new(go::GoLspAdapter)]); + language!("go", vec![Arc::new(go::GoLspAdapter)], GoContextProvider); language!("gomod"); language!("gowork"); language!(