TEXTEDIT prompt, Undo and Mode; MTEXT takes lines until an empty one (#234) (#239)

TEXTEDIT's prompt no longer reads "or or". Undo reverts the last edit made
in this TEXTEDIT, and Mode switches between Multiple (keep selecting) and
Single (end after one edit). At the command line and in scripts, MTEXT
takes one paragraph per input line until an empty line, instead of ending
after the first line and running the next one as a command. The Text
Editor window sends its contents and then Enter, so it still ends MTEXT.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Valentin Klein
2026-10-10 14:37:52 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent a6b728b0f7
commit ca882b38e2
5 changed files with 126 additions and 10 deletions
+11 -3
View File
@@ -1315,6 +1315,9 @@ impl Interactive for TextM {
struct MTextM {
first: Option<Vec2>,
second: Option<Vec2>,
/// Paragraphs typed so far: one per input line, an empty line ends the text (as on
/// AutoCAD's command line and in scripts).
lines: Vec<String>,
}
impl Interactive for MTextM {
@@ -1327,14 +1330,19 @@ impl Interactive for MTextM {
(Some(a), None) => Prompt::new("Specify opposite corner", Accept::POINT)
.kw(&["Height", "Justify", "Line spacing", "Rotation", "Style", "Width", "Columns"])
.base(a),
_ => Prompt::new("Enter text (use \\P for new paragraphs)", Accept::TEXT),
_ if self.lines.is_empty() => Prompt::new("Enter text (use \\P for new paragraphs)", Accept::TEXT),
_ => Prompt::new("Enter next line of text (empty line to finish)", Accept::TEXT),
}
}
fn input(&mut self, s: &mut Session, i: Input) -> Result<Step> {
match (self.first, self.second, i) {
(None, _, Input::Point(p)) => self.first = Some(p),
(Some(_), None, Input::Point(p)) => self.second = Some(p),
(Some(a), Some(b), Input::Text(t)) => {
(Some(_), Some(_), Input::Text(t)) if !t.is_empty() => self.lines.push(t),
(Some(a), Some(b), Input::Text(_) | Input::Enter) => {
if self.lines.is_empty() {
return Ok(Step::Done);
}
let d = s.doc()?;
let h = d.header.f64("TEXTSIZE", 0.2);
let style = d.header.str("TEXTSTYLE", "Standard");
@@ -1346,7 +1354,7 @@ impl Interactive for MTextM {
attach: 1,
rotation: 0.0,
style,
contents: t,
contents: self.lines.join("\\P"),
line_spacing: 1.0,
}))?;
return Ok(Step::Done);
+38 -5
View File
@@ -2269,6 +2269,12 @@ fn run_textedit(s: &mut Session, p: &Value) -> Result<Value> {
#[derive(Default)]
struct TextEditM {
h: Option<(Handle, String)>,
/// Edits made in this run, newest last: each object as it was before (the Undo option).
edits: Vec<(Handle, EntityKind)>,
/// Single mode: end after one edit (the Mode option; Multiple by default).
single: bool,
/// Asking for the edit mode.
mode: bool,
}
impl Interactive for TextEditM {
@@ -2276,12 +2282,26 @@ impl Interactive for TextEditM {
"TEXTEDIT"
}
fn prompt(&self, _s: &Session) -> Prompt {
if self.mode {
let current = if self.single { "Single" } else { "Multiple" };
return Prompt::new("Enter a text edit mode option", curves::KW).kw(&["Single", "Multiple"]).default(current);
}
match &self.h {
None => Prompt::new("Select an annotation object or", Accept::POINT).kw(&["Undo", "Mode"]),
None => Prompt::new("Select an annotation object", Accept::POINT).kw(&["Undo", "Mode"]),
Some((_, old)) => Prompt::new("Enter new text", Accept::TEXT).default(old.chars().take(40).collect::<String>()),
}
}
fn input(&mut self, s: &mut Session, i: Input) -> Result<Step> {
if self.mode {
match i {
Input::Keyword(k) if k == "Single" => self.single = true,
Input::Keyword(k) if k == "Multiple" => self.single = false,
Input::Enter => {}
_ => return Err(other("Enter Single or Multiple.")),
}
self.mode = false;
return Ok(Step::Continue);
}
match (&self.h, i) {
(None, Input::Point(p)) => {
let h = curves::pick_at(s, p).ok_or_else(|| other("*Invalid selection*"))?;
@@ -2290,16 +2310,29 @@ impl Interactive for TextEditM {
Ok(Step::Continue)
}
(Some((h, _)), Input::Text(t) | Input::Keyword(t)) => {
set_text(s, *h, &t)?;
let h = *h;
let before = curves::entity(s, h)?.kind;
set_text(s, h, &t)?;
self.edits.push((h, before));
self.h = None;
Ok(Step::Continue)
Ok(if self.single { Step::Done } else { Step::Continue })
}
(Some(_), Input::Enter) => {
self.h = None;
Ok(Step::Continue)
Ok(if self.single { Step::Done } else { Step::Continue })
}
(None, Input::Enter) => Ok(Step::Done),
(None, Input::Keyword(_)) => Ok(Step::Continue),
(None, Input::Keyword(k)) if k == "Undo" => {
match self.edits.pop() {
Some((h, before)) => set_kind(s, h, before)?,
None => s.echo("Everything has been undone"),
}
Ok(Step::Continue)
}
(None, Input::Keyword(k)) if k == "Mode" => {
self.mode = true;
Ok(Step::Continue)
}
_ => Ok(Step::Continue),
}
}