forked from 1b-agent/opencadstudio-upstream
fix(script): support AutoCAD .scr scripts with multi-line, single-line, and prefix handling
This commit is contained in:
1 parent
ab0682153d
commit
f32d0422d2
6 files changed
+222
-34
No files matched your search
+137
-22
@@ -339,33 +339,55 @@ impl OpenCADStudio {
|
||||
// line is fed through the same command path the `--script` startup
|
||||
// flag uses. Blank rows submit Enter to the active command.
|
||||
"SCRIPT" | "SCR" => {
|
||||
use crate::command::ValuePromptCommand;
|
||||
let c = ValuePromptCommand::new("SCRIPT", "SCRIPT path to the .scr file:");
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
return Some(Task::perform(
|
||||
async move {
|
||||
crate::sys::file_dialog()
|
||||
.add_filter("Script files (*.scr)", &["scr"])
|
||||
.pick_file()
|
||||
.await
|
||||
.map(|h| crate::sys::handle_path(&h))
|
||||
},
|
||||
|path| match path {
|
||||
Some(p) => Message::Command(format!("SCRIPT \"{}\"", p.display())),
|
||||
None => Message::Noop,
|
||||
},
|
||||
));
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
use crate::command::ValuePromptCommand;
|
||||
let c = ValuePromptCommand::new("SCRIPT", "SCRIPT path to the .scr file:");
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
}
|
||||
}
|
||||
cmd if cmd.starts_with("SCRIPT ") || cmd.starts_with("SCR ") => {
|
||||
let path = cmd.split_once(' ').map(|(_, r)| r.trim().to_string());
|
||||
match path {
|
||||
Some(p) if !p.is_empty() => match std::fs::read_to_string(&p) {
|
||||
Ok(text) => {
|
||||
let cmds: Vec<Task<Message>> = text
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|l| !l.starts_with('#') && !l.starts_with(';'))
|
||||
.map(|l| Task::done(Message::ScriptLine(l.to_string())))
|
||||
.collect();
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"SCRIPT: running {} command(s) from {p}.",
|
||||
cmds.len()
|
||||
).as_ref());
|
||||
return Some(Task::batch(cmds));
|
||||
Some(p) if !p.is_empty() => {
|
||||
let clean_path = p.trim().trim_matches(|c| c == '"' || c == '\'');
|
||||
match std::fs::read_to_string(clean_path) {
|
||||
Ok(text) => {
|
||||
let cmds: Vec<Task<Message>> = text
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|l| !l.starts_with('#') && !l.starts_with(';'))
|
||||
.map(|l| Task::done(Message::ScriptLine(l.to_string())))
|
||||
.collect();
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"SCRIPT: running {} command(s) from {clean_path}.",
|
||||
cmds.len()
|
||||
).as_ref());
|
||||
return Some(Task::batch(cmds));
|
||||
}
|
||||
Err(e) => {
|
||||
self.command_line
|
||||
.push_error(crate::tf!("SCRIPT: cannot read {clean_path}: {e}").as_ref());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
self.command_line
|
||||
.push_error(crate::tf!("SCRIPT: cannot read {p}: {e}").as_ref());
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
self.command_line
|
||||
.push_info(crate::t!("Usage: SCRIPT <path to .scr file>").as_ref());
|
||||
@@ -418,3 +440,96 @@ fn parse_background_color(args: &[&str]) -> Option<[f32; 4]> {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::app::{Message, OpenCADStudio};
|
||||
|
||||
fn fresh_app() -> OpenCADStudio {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
app
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_script_draws_line() {
|
||||
let mut app = fresh_app();
|
||||
let i = app.active_tab;
|
||||
let before_count = app.tabs[i].scene.document.entities().count();
|
||||
|
||||
// Feed standard multi-line CAD script sequence
|
||||
let _ = app.update(Message::ScriptLine("LINE".into()));
|
||||
assert!(app.tabs[i].active_cmd.is_some(), "LINE command should be active");
|
||||
|
||||
let _ = app.update(Message::ScriptLine("0,0".into()));
|
||||
assert!(app.tabs[i].active_cmd.is_some(), "LINE command should stay active after first point");
|
||||
|
||||
let _ = app.update(Message::ScriptLine("10,10".into()));
|
||||
let _ = app.update(Message::ScriptLine("".into())); // Enter to finish
|
||||
|
||||
let after_count = app.tabs[i].scene.document.entities().count();
|
||||
assert_eq!(after_count, before_count + 1, "Line entity should have been created");
|
||||
assert!(app.tabs[i].active_cmd.is_none(), "LINE command should be finished");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_line_script_still_works() {
|
||||
let mut app = fresh_app();
|
||||
let i = app.active_tab;
|
||||
let before_count = app.tabs[i].scene.document.entities().count();
|
||||
|
||||
let _ = app.update(Message::ScriptLine("CIRCLE 5,5 2".into()));
|
||||
|
||||
let after_count = app.tabs[i].scene.document.entities().count();
|
||||
assert_eq!(after_count, before_count + 1, "Circle entity should have been created");
|
||||
assert!(app.tabs[i].active_cmd.is_none(), "CIRCLE command should be finished");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn autodesk_command_prefixes_strip_underscore_and_dot() {
|
||||
let mut app = fresh_app();
|
||||
let i = app.active_tab;
|
||||
let before_count = app.tabs[i].scene.document.entities().count();
|
||||
|
||||
let _ = app.update(Message::ScriptLine("_CIRCLE 10,10 3".into()));
|
||||
|
||||
let after_count = app.tabs[i].scene.document.entities().count();
|
||||
assert_eq!(after_count, before_count + 1, "_CIRCLE should resolve to CIRCLE and create entity");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_script_scr_draws_all_entities() {
|
||||
let mut app = fresh_app();
|
||||
let i = app.active_tab;
|
||||
const SCRIPT: &str = r#"
|
||||
_LINE
|
||||
0,0
|
||||
100,0
|
||||
100,100
|
||||
0,100
|
||||
C
|
||||
|
||||
_LINE
|
||||
0,0
|
||||
100,100
|
||||
|
||||
_LINE
|
||||
0,100
|
||||
100,0
|
||||
|
||||
CIRCLE
|
||||
50,50
|
||||
35
|
||||
|
||||
CIRCLE 50,50 15
|
||||
|
||||
ZOOM EXTENTS
|
||||
"#;
|
||||
for line in SCRIPT.lines().map(str::trim).filter(|l| !l.starts_with('#') && !l.starts_with(';')) {
|
||||
let _ = app.update(Message::ScriptLine(line.to_string()));
|
||||
}
|
||||
// 4 square edges + 2 diagonal lines + 2 circles = 8 entities
|
||||
assert_eq!(app.tabs[i].scene.document.entities().count(), 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,11 @@ impl OpenCADStudio {
|
||||
Some(rest) => (rest.trim(), true),
|
||||
None => (cmd, false),
|
||||
};
|
||||
let cmd = cmd.trim();
|
||||
// AutoCAD command prefixes: '_' (international name), '.' (built-in name bypass)
|
||||
let cmd = cmd.strip_prefix('.').unwrap_or(cmd);
|
||||
let cmd = cmd.strip_prefix('_').unwrap_or(cmd);
|
||||
let cmd = cmd.strip_prefix('.').unwrap_or(cmd);
|
||||
let resolved = self.resolve_alias(cmd);
|
||||
let cmd = resolved.as_deref().unwrap_or(cmd);
|
||||
if is_spacemouse_command(cmd) {
|
||||
|
||||
Reference in new issue
Block a user