mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 02:25:55 +00:00
doc, ui: a feature that lost a reference asks for it to be picked again
DocError::Reselect names the feature and the stored reference (a face or edge name) that no longer finds what was picked: "<feature>: the reference `<name>` no longer exists as it was picked; reselect it". Evaluation fills in the feature's name; reselect_parts reads both back from a feature's error text, and the UI shows the message translated (ar, de, en, he, ja, ko, zh in assets/locales/pending/reference-reselect.json). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"ar": {
|
||||
"diagnostic.reselect_reference": "{feature}: المرجع `{reference}` لم يعد موجودًا كما اختير؛ أعد اختياره"
|
||||
},
|
||||
"de": {
|
||||
"diagnostic.reselect_reference": "{feature}: Die Referenz `{reference}` existiert nicht mehr so, wie sie ausgewählt wurde; wählen Sie sie erneut aus"
|
||||
},
|
||||
"en": {
|
||||
"diagnostic.reselect_reference": "{feature}: the reference `{reference}` no longer exists as it was picked; reselect it"
|
||||
},
|
||||
"he": {
|
||||
"diagnostic.reselect_reference": "{feature}: ההפניה `{reference}` כבר אינה קיימת כפי שנבחרה; יש לבחור אותה מחדש"
|
||||
},
|
||||
"ja": {
|
||||
"diagnostic.reselect_reference": "{feature}: 参照 `{reference}` は選択したときのままでは存在しません。選択し直してください"
|
||||
},
|
||||
"ko": {
|
||||
"diagnostic.reselect_reference": "{feature}: 참조 `{reference}`이(가) 선택했을 때의 모습으로 더 이상 존재하지 않습니다. 다시 선택하세요"
|
||||
},
|
||||
"zh": {
|
||||
"diagnostic.reselect_reference": "{feature}:引用 `{reference}` 已不再是选择时的样子;请重新选择"
|
||||
}
|
||||
}
|
||||
@@ -2063,7 +2063,7 @@ impl Model {
|
||||
}
|
||||
Ok(None) => f,
|
||||
Err(e) => {
|
||||
frame_error = Some(e.to_string());
|
||||
frame_error = Some(e.in_feature(&f.name).to_string());
|
||||
f
|
||||
}
|
||||
};
|
||||
@@ -2074,7 +2074,7 @@ impl Model {
|
||||
None => kernel::guard(&f.name, || {
|
||||
let (r, deferred) =
|
||||
kernel::validating(validation, || eval_feature(doc, &vals, f, &mut next, &mut warning, &mut import_report, &mut repair));
|
||||
r.map_err(|e| kernel::KernelError::Failed(e.to_string()))?;
|
||||
r.map_err(|e| kernel::KernelError::Failed(e.in_feature(&f.name).to_string()))?;
|
||||
// The checks the feature's steps left to its end: the bodies it keeps.
|
||||
if deferred { check_kept(&state, &next) } else { Ok(()) }
|
||||
}),
|
||||
|
||||
@@ -103,6 +103,41 @@ pub enum DocError {
|
||||
Sketch(#[from] solidcraft_sketch::SketchError),
|
||||
#[error(transparent)]
|
||||
Kernel(#[from] solidcraft_kernel::KernelError),
|
||||
/// A stored reference (a face or edge name, a piece of a split face) no longer finds exactly
|
||||
/// what it named: the feature using it fails, and the user picks it again. `feature` is
|
||||
/// empty until the evaluation of the feature fills it in ([`DocError::in_feature`]).
|
||||
#[error("{}", reselect_message(feature, reference))]
|
||||
Reselect { feature: String, reference: String },
|
||||
}
|
||||
|
||||
impl DocError {
|
||||
/// A feature's `reference` that no longer finds what it named ([`DocError::Reselect`]), for
|
||||
/// the feature being evaluated.
|
||||
pub fn reselect(reference: impl Into<String>) -> Self {
|
||||
DocError::Reselect { feature: String::new(), reference: reference.into() }
|
||||
}
|
||||
|
||||
/// This error, raised while feature `feature` was evaluated: a [`DocError::Reselect`] names it.
|
||||
pub fn in_feature(self, feature: &str) -> Self {
|
||||
match self {
|
||||
DocError::Reselect { feature: f, reference } if f.is_empty() => DocError::Reselect { feature: feature.into(), reference },
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// What a feature that lost a reference says ([`DocError::Reselect`]).
|
||||
fn reselect_message(feature: &str, reference: &str) -> String {
|
||||
let feature = if feature.is_empty() { "this feature" } else { feature };
|
||||
format!("{feature}: the reference `{reference}` no longer exists as it was picked; reselect it")
|
||||
}
|
||||
|
||||
/// The feature and the reference of a [`DocError::Reselect`] message, read back from its text
|
||||
/// (a feature's error is kept as text): `None` for any other message.
|
||||
pub fn reselect_parts(text: &str) -> Option<(&str, &str)> {
|
||||
let (feature, rest) = text.split_once(": the reference `")?;
|
||||
let reference = rest.strip_suffix("` no longer exists as it was picked; reselect it")?;
|
||||
Some((feature, reference))
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, DocError>;
|
||||
|
||||
@@ -202,8 +202,12 @@ impl Locale {
|
||||
}
|
||||
|
||||
/// Evaluation errors stay English for scripts; the empty Cut/Intersect hint that names
|
||||
/// another component is translated where it is shown. Other text is returned unchanged.
|
||||
/// another component, and a feature's request to reselect a reference it lost, are translated
|
||||
/// where they are shown. Other text is returned unchanged.
|
||||
pub fn diagnostic(self, text: &str) -> String {
|
||||
if let Some((feature, reference)) = solidcraft_engine::doc::reselect_parts(text) {
|
||||
return self.format("diagnostic.reselect_reference", &[("feature", &self.given_name(feature)), ("reference", reference)]);
|
||||
}
|
||||
const PREFIX: &str = "there is no body to cut or intersect in component '";
|
||||
if let Some((before, detail)) = text.split_once(PREFIX)
|
||||
&& let Some((component, detail)) = detail.split_once("'; the tool may overlap '")
|
||||
@@ -379,6 +383,18 @@ mod tests {
|
||||
assert_eq!(Locale::for_language("ja").diagnostic(plain), plain);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reselect_reference_diagnostic_translates_with_the_feature_and_reference_as_given() {
|
||||
let message = solidcraft_engine::doc::DocError::reselect("F4:side:c1#1|F4:top").in_feature("Fillet {2}").to_string();
|
||||
assert_eq!(Locale::for_language("en").diagnostic(&message), message);
|
||||
for language in ["ar", "de", "he", "ja", "ko", "zh"] {
|
||||
let translated = Locale::for_language(language).diagnostic(&message);
|
||||
assert_ne!(translated, message, "{language}");
|
||||
// Right-to-left text comes back in display order, so only what it holds is checked.
|
||||
assert!(translated.contains("Fillet {2}") && translated.contains("`F4:side:c1#1|F4:top`"), "{language}: {translated}");
|
||||
}
|
||||
}
|
||||
|
||||
/// English first, then by id, so adding a locale edits only its own line.
|
||||
#[test]
|
||||
fn locales_are_listed_english_first_then_by_id() {
|
||||
|
||||
Reference in New Issue
Block a user