Character Styles: New Character Style keeps a language tag the selected text holds

charStyle.new now validates attrs against the target text's attributes, so a
language tag that is not in the list but that the selected run already holds
(a native file can store any tag) is kept. Any other unlisted tag is still an
error.
This commit is contained in:
dotCooCoo
2026-11-02 12:04:16 +07:00
committed by bflatastic
parent 6e7945773e
commit 7a527fce64
2 changed files with 18 additions and 4 deletions
+6 -4
View File
@@ -475,8 +475,9 @@ fn name_param<'a>(p: &'a Value, kind: Kind, verb: &str) -> Result<&'a str> {
str_param(p, "name").ok_or_else(|| bad(&kind.cmd(verb), "missing `name`"))
}
/// The `attrs` of `p`, validated. `kept` holds the style's current attributes (`charStyle.edit`):
/// a language tag that is not in the list, which a file can give a style, stays when `attrs` keeps it.
/// The `attrs` of `p`, validated. `kept` holds the style's current attributes (`charStyle.edit`) or
/// the target text's attributes (`charStyle.new`): a language tag that is not in the list, which a
/// file can give a style or a run, stays when `attrs` keeps it.
fn attrs_param(p: &Value, kind: Kind, verb: &str, kept: Option<&Map<String, Value>>) -> Result<Option<Map<String, Value>>> {
match p.get("attrs") {
None | Some(Value::Null) => Ok(None),
@@ -536,9 +537,10 @@ fn list(s: &mut Session, kind: Kind) -> Result<Value> {
fn new(s: &mut Session, p: &Value, kind: Kind) -> Result<Value> {
let c = kind.cmd("new");
let attrs = match attrs_param(p, kind, "new", None)? {
let target = selection_attrs(s, p, kind)?;
let attrs = match attrs_param(p, kind, "new", target.as_ref())? {
Some(a) => a,
None => match selection_attrs(s, p, kind)? {
None => match target {
Some(a) => a,
None => {
let a = new_style_attrs(s, kind);
+12
View File
@@ -245,6 +245,18 @@ fn character_style_options_edit_keeps_a_language_tag_from_a_file() {
assert!(error.contains("unknown language"), "{error}");
}
#[test]
fn new_character_style_keeps_a_language_tag_from_the_text() {
let (mut s, id) = session_with_text("Text");
if let Some(NodeKind::Text(t)) = std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc).node_mut(NodeId(id)).map(|n| &mut n.kind) {
t.runs[0].style.language = Some("x-file".into());
}
s.execute("charStyle.new", &json!({"name":"Body","id":id,"attrs":{"size":18.0,"language":"x-file"}})).unwrap();
assert_eq!(s.doc().unwrap().doc.char_styles.iter().find(|d| d.name == "Body").unwrap().attrs["language"], json!("x-file"));
let error = s.execute("charStyle.new", &json!({"name":"Other","id":id,"attrs":{"language":"x-other"}})).unwrap_err().to_string();
assert!(error.contains("unknown language"), "{error}");
}
#[test]
fn editing_character_style_clears_removed_size_without_losing_local_override() {
let (mut s, id) = session_with_text("One two");