From 8d9d6c3a9dc80c105fc548a6b8cdc870c11f7b3b Mon Sep 17 00:00:00 2001 From: Kai Schmidt Date: Thu, 20 Jun 2024 10:18:46 -0700 Subject: [PATCH] allow commit specification in git imports --- site/src/backend.rs | 14 ++++++++++---- src/compile/mod.rs | 24 +++++++++++++++++------- src/compile/modifier.rs | 2 +- src/primitive/mod.rs | 4 ++-- src/sys.rs | 14 +++++++++++++- src/sys_native.rs | 22 +++++++++++++++++++--- todo.md | 1 - 7 files changed, 62 insertions(+), 19 deletions(-) diff --git a/site/src/backend.rs b/site/src/backend.rs index dbd0af62..3551b62a 100644 --- a/site/src/backend.rs +++ b/site/src/backend.rs @@ -9,7 +9,7 @@ use std::{ use crate::{editor::get_ast_time, weewuh}; use leptos::*; -use uiua::{Handle, Report, SysBackend, EXAMPLE_TXT, EXAMPLE_UA}; +use uiua::{GitTarget, Handle, Report, SysBackend, EXAMPLE_TXT, EXAMPLE_UA}; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response}; @@ -350,9 +350,15 @@ impl SysBackend for WebBackend { while (instant::now() - start) / 1000.0 < seconds {} Ok(()) } - fn load_git_module(&self, url: &str, branch: Option<&str>) -> Result { - if branch.is_some() { - return Err("Git branch specification is not supported in the web backend".into()); + fn load_git_module(&self, url: &str, target: GitTarget) -> Result { + match target { + GitTarget::Default => {} + GitTarget::Branch(_) => { + return Err("Git branch specification is not supported in the web backend".into()) + } + GitTarget::Commit(_) => { + return Err("Git commit specification is not supported in the web backend".into()) + } } let mut parts = url.rsplitn(3, '/'); let repo_name = parts.next().ok_or("Invalid git url")?; diff --git a/src/compile/mod.rs b/src/compile/mod.rs index bf35fa2f..4a78f928 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -30,7 +30,7 @@ use crate::{ lsp::{CodeMeta, SigDecl}, optimize::{optimize_instrs, optimize_instrs_mut}, parse::{count_placeholders, parse, split_words, unsplit_words}, - Array, Assembly, BindingKind, Boxed, Diagnostic, DiagnosticKind, DocComment, Ident, + Array, Assembly, BindingKind, Boxed, Diagnostic, DiagnosticKind, DocComment, GitTarget, Ident, ImplPrimitive, InputSrc, IntoInputSrc, IntoSysBackend, Primitive, RunMode, SemanticComment, SysBackend, Uiua, UiuaError, UiuaErrorKind, UiuaResult, Value, CONSTANTS, EXAMPLE_UA, VERSION, }; @@ -712,12 +712,22 @@ code: /// Import a module pub(crate) fn import_module(&mut self, path_str: &str, span: &CodeSpan) -> UiuaResult { // Resolve path - let path = if let Some(mut url) = path_str.strip_prefix("git:") { - let mut branch = None; - if let Some((a, b)) = url.split_once("branch:") { - url = a; - branch = Some(b.trim()); + let path = if let Some(mut url) = path_str.trim().strip_prefix("git:") { + if url.contains("branch:") && url.contains("commit:") { + return Err(self.fatal_error( + span.clone(), + "Cannot specify both branch and commit in git import", + )); } + let target = if let Some((a, b)) = url.split_once("branch:") { + url = a; + GitTarget::Branch(b.trim().into()) + } else if let Some((a, b)) = url.split_once("commit:") { + url = a; + GitTarget::Commit(b.trim().into()) + } else { + GitTarget::Default + }; // Git import let mut url = url.trim().trim_end_matches(".git").to_string(); if ![".com", ".net", ".org", ".io", ".dev"] @@ -733,7 +743,7 @@ code: url = format!("https://{url}"); } self.backend() - .load_git_module(&url, branch) + .load_git_module(&url, target) .map_err(|e| self.fatal_error(span.clone(), e))? } else { // Normal import diff --git a/src/compile/modifier.rs b/src/compile/modifier.rs index 719301b5..74a3eeff 100644 --- a/src/compile/modifier.rs +++ b/src/compile/modifier.rs @@ -624,7 +624,7 @@ impl Compiler { self.push_instr(Instr::PushFunc(func)); } } - Flop => { + Rear => { let operand = modified.code_operands().next().unwrap().clone(); let (mut instrs, sig) = self.compile_operand_word(operand)?; if sig.args != 2 { diff --git a/src/primitive/mod.rs b/src/primitive/mod.rs index cb55d685..10fc80d7 100644 --- a/src/primitive/mod.rs +++ b/src/primitive/mod.rs @@ -395,7 +395,7 @@ impl Primitive { use SysOp::*; matches!( self, - (But | Flop) + (But | Rear) | (Orient | Coordinate | Astar | Fft | Triangle | Case) | Sys(Ffi | MemCopy | MemFree | TlsListen) | (Stringify | Quote | Sig) @@ -851,7 +851,7 @@ impl Primitive { | Primitive::By | Primitive::Gap | Primitive::But - | Primitive::Flop + | Primitive::Rear | Primitive::Un | Primitive::Under | Primitive::Content diff --git a/src/sys.rs b/src/sys.rs index 3ca1a242..c4e103e4 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1021,11 +1021,23 @@ pub trait SysBackend: Any + Send + Sync + 'static { /// Load a git repo as a module /// /// The returned path should be loadable via [`SysBackend::file_read_all`] - fn load_git_module(&self, url: &str, branch: Option<&str>) -> Result { + fn load_git_module(&self, url: &str, target: GitTarget) -> Result { Err("Loading git modules is not supported in this environment".into()) } } +/// A target for a git repository +#[derive(Debug, Clone, Default)] +pub enum GitTarget { + /// The latest commit on the default branch + #[default] + Default, + /// The latest commit on a specific branch + Branch(String), + /// A specific commit + Commit(String), +} + impl fmt::Debug for dyn SysBackend { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "") diff --git a/src/sys_native.rs b/src/sys_native.rs index 7136b421..81f9ae0d 100644 --- a/src/sys_native.rs +++ b/src/sys_native.rs @@ -15,7 +15,7 @@ use std::{ time::Duration, }; -use crate::{Handle, SysBackend}; +use crate::{GitTarget, Handle, SysBackend}; use dashmap::DashMap; use once_cell::sync::Lazy; @@ -1009,7 +1009,7 @@ impl SysBackend for NativeSys { crate::ffi_free(ptr); Ok(()) } - fn load_git_module(&self, url: &str, branch: Option<&str>) -> Result { + fn load_git_module(&self, url: &str, target: GitTarget) -> Result { if let Some(path) = NATIVE_SYS.git_paths.get(url) { if path.is_err() || path.as_ref().unwrap().exists() { return path.clone(); @@ -1052,7 +1052,7 @@ impl SysBackend for NativeSys { if !submodule_path.exists() { let submod_path = submodule_path.to_string_lossy(); let mut args = vec!["submodule", "add", "--force"]; - if let Some(branch) = branch { + if let GitTarget::Branch(branch) = &target { args.push("-b"); args.push(branch); } @@ -1071,6 +1071,22 @@ impl SysBackend for NativeSys { stderr.read_to_string(&mut err).map_err(|e| e.to_string())?; return Err(format!("Failed to add submodule: {err}")); } + if let GitTarget::Commit(hash) = &target { + std::env::set_current_dir(&*submod_path).map_err(|e| e.to_string())?; + let mut child = Command::new("git") + .args(["checkout", hash]) + .stderr(Stdio::piped()) + .stdout(Stdio::null()) + .spawn() + .map_err(|e| e.to_string())?; + let status = child.wait().map_err(|e| e.to_string())?; + if !status.success() { + let stderr = child.stderr.as_mut().unwrap(); + let mut err = String::new(); + stderr.read_to_string(&mut err).map_err(|e| e.to_string())?; + return Err(format!("Failed to checkout commit: {err}")); + } + } } Ok(path) })(); diff --git a/todo.md b/todo.md index d4fe6808..5d07494a 100644 --- a/todo.md +++ b/todo.md @@ -2,7 +2,6 @@ - 0.12 - Stabilize labels - - `branch:` and `commit:` specifiers for imports - Goto definition/Ctrl+click on git imports to go to the URL - `take` `&fras`/`&frab` - Stack-source locality tutorial