mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
fix(core/history): classify sh -lc reads for explore cells
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
use crate::bash::try_parse_bash;
|
||||
use crate::bash::try_parse_word_only_commands_sequence;
|
||||
use crate::util::is_shell_like_executable;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use shlex::split as shlex_split;
|
||||
use shlex::try_join as shlex_try_join;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum ParsedCommand {
|
||||
Read {
|
||||
@@ -49,16 +48,8 @@ fn shlex_join(tokens: &[String]) -> String {
|
||||
.unwrap_or_else(|_| "<command included NUL byte>".to_string())
|
||||
}
|
||||
|
||||
fn is_bash_executable(cmd: &str) -> bool {
|
||||
let trimmed = cmd.trim_matches('"').trim_matches('\'');
|
||||
if trimmed.eq_ignore_ascii_case("bash") || trimmed.eq_ignore_ascii_case("bash.exe") {
|
||||
return true;
|
||||
}
|
||||
Path::new(trimmed)
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.map(|name| name.eq_ignore_ascii_case("bash") || name.eq_ignore_ascii_case("bash.exe"))
|
||||
.unwrap_or(false)
|
||||
fn is_shell_executable(cmd: &str) -> bool {
|
||||
is_shell_like_executable(cmd)
|
||||
}
|
||||
|
||||
/// DO NOT REVIEW THIS CODE BY HAND
|
||||
@@ -124,6 +115,18 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sh_lc_sed_is_read_command() {
|
||||
let inner = "sed -n '10,20p' code-rs/tui/src/chatwidget.rs";
|
||||
assert_parsed(
|
||||
&vec_str(&["sh", "-lc", inner]),
|
||||
vec![ParsedCommand::Read {
|
||||
cmd: inner.to_string(),
|
||||
name: "chatwidget.rs".to_string(),
|
||||
}],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_diff_staged_is_read_command() {
|
||||
assert_parsed(
|
||||
@@ -1119,7 +1122,7 @@ fn normalize_tokens(cmd: &[String]) -> Vec<String> {
|
||||
// Do not re-shlex already-tokenized input; just drop the prefix.
|
||||
rest.to_vec()
|
||||
}
|
||||
[bash, flag, script] if is_bash_executable(bash) && (flag == "-c" || flag == "-lc") => {
|
||||
[bash, flag, script] if is_shell_executable(bash) && (flag == "-c" || flag == "-lc") => {
|
||||
shlex_split(script)
|
||||
.unwrap_or_else(|| vec!["bash".to_string(), flag.clone(), script.clone()])
|
||||
}
|
||||
@@ -1400,7 +1403,7 @@ fn parse_bash_lc_commands(original: &[String]) -> Option<Vec<ParsedCommand>> {
|
||||
let [bash, flag, script] = original else {
|
||||
return None;
|
||||
};
|
||||
if !is_bash_executable(bash) || flag != "-lc" {
|
||||
if !is_shell_executable(bash) || flag != "-lc" {
|
||||
return None;
|
||||
}
|
||||
if let Some(tree) = try_parse_bash(script) {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use shlex;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::util::is_shell_like_executable;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct ZshShell {
|
||||
pub(crate) shell_path: String,
|
||||
@@ -137,15 +138,7 @@ fn strip_bash_lc(command: &[String]) -> Option<String> {
|
||||
}
|
||||
|
||||
fn is_bash_like(cmd: &str) -> bool {
|
||||
let trimmed = cmd.trim_matches('"').trim_matches('\'');
|
||||
if trimmed.eq_ignore_ascii_case("bash") || trimmed.eq_ignore_ascii_case("bash.exe") {
|
||||
return true;
|
||||
}
|
||||
Path::new(trimmed)
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.map(|name| name.eq_ignore_ascii_case("bash") || name.eq_ignore_ascii_case("bash.exe"))
|
||||
.unwrap_or(false)
|
||||
is_shell_like_executable(cmd)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
|
||||
use std::sync::Arc;
|
||||
@@ -25,11 +26,39 @@ pub fn escape_command(command: &[String]) -> String {
|
||||
|
||||
pub fn strip_bash_lc_and_escape(command: &[String]) -> String {
|
||||
match command {
|
||||
[first, second, third] if first == "bash" && second == "-lc" => third.clone(),
|
||||
[first, second, third]
|
||||
if is_shell_like_executable(first)
|
||||
&& (second == "-lc" || second == "-c") =>
|
||||
{
|
||||
third.clone()
|
||||
}
|
||||
_ => escape_command(command),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_shell_like_executable(token: &str) -> bool {
|
||||
let trimmed = token.trim_matches('"').trim_matches('\'');
|
||||
let name = Path::new(trimmed)
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or(trimmed)
|
||||
.to_ascii_lowercase();
|
||||
matches!(
|
||||
name.as_str(),
|
||||
"bash"
|
||||
| "bash.exe"
|
||||
| "sh"
|
||||
| "sh.exe"
|
||||
| "zsh"
|
||||
| "zsh.exe"
|
||||
| "dash"
|
||||
| "dash.exe"
|
||||
| "ksh"
|
||||
| "ksh.exe"
|
||||
| "busybox"
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn notify_on_sigint() -> Arc<Notify> {
|
||||
let notify = Arc::new(Notify::new());
|
||||
|
||||
Reference in New Issue
Block a user