Greek keyboard: keep the alias-persistence helpers the merge of main dropped

The merge of main into this branch (e3b3a2e0) settled its two conflicts, in
crates/engine/src/cmd/file.rs and crates/engine/src/cmd/file/pgp.rs, by keeping
only this branch's side. Main had added `alias_profile`, `restore_aliases` and
`keepable_path` at the same place, with the exports of the first two and of
`MAX_ALIAS_PROFILE_BYTES`. They were removed, although the UI's profile code and
the tests in pgp.rs call them, so the UI crate and the engine's tests no longer
compiled.

Both sides stay now: main's three functions and their exports, and this
branch's `greek_alias`. The two files are main's version plus this branch's own
changes again, and nothing else differs from an automatic merge.
This commit is contained in:
V1dalTheAbidal
2026-11-11 21:27:35 +02:00
parent e3b3a2e0ee
commit e8a79d191c
2 changed files with 44 additions and 1 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ use super::{CommandSpec, EngineError, Interactive, always, bad, bool_or, find_co
use crate::{Accept, Input, Prompt, Result, Session, Step};
/// The rules the Command Aliases dialog checks before it sends an alias to ALIASEDIT.
pub use pgp::{MAX_ALIASES, alias_name, command_id};
pub use pgp::{MAX_ALIAS_PROFILE_BYTES, MAX_ALIASES, alias_name, alias_profile, command_id, restore_aliases};
pub(crate) use pgp::{greek_alias, specs as alias_specs, user_alias};
pub use template::{qnew_template, set_qnew_template};
+43
View File
@@ -64,6 +64,49 @@ pub(crate) fn greek_alias(word: &str) -> Option<String> {
store().map.keys().find(|alias| crate::keyboard::same_greek_word(alias, word)).cloned()
}
/// The user aliases and the file REINIT reads, as the host's user profile keeps them:
/// `{"aliases": {alias: command}, "file": path or null}`. Not drawing data.
pub fn alias_profile() -> Value {
let st = store();
// A path [`restore_aliases`] would refuse is left out, so it can't grow the profile either.
json!({ "aliases": st.map, "file": st.file.as_deref().filter(|f| keepable_path(f)) })
}
/// Whether the profile keeps an alias file path: short enough, and no control characters.
fn keepable_path(f: &str) -> bool {
f.chars().count() <= MAX_ALIAS_FILE_CHARS && !f.chars().any(char::is_control)
}
/// Restore aliases saved by [`alias_profile`], replacing the user aliases and the file.
///
/// The saved set is untrusted (a hand-edited or older profile): entries that are not aliases of
/// real commands are skipped, command names are never aliased, at most [`MAX_ALIASES`] are kept,
/// and a malformed section changes nothing.
pub fn restore_aliases(saved: &Value) {
let Some(obj) = saved.as_object() else { return };
let file = match obj.get("file") {
None | Some(Value::Null) => None,
Some(Value::String(f)) if keepable_path(f) => Some(f.clone()),
Some(Value::String(_)) => None,
// A file entry that is not a path makes the section malformed: keep the aliases as they are.
Some(_) => return,
};
let Some(map) = obj.get("aliases").and_then(Value::as_object) else { return };
let mut aliases = BTreeMap::new();
// Entries that are skipped don't use up the cap.
for (alias, target) in map {
if aliases.len() >= MAX_ALIASES {
break;
}
let Ok(name) = alias_name(alias) else { continue };
let Some(id) = target.as_str().and_then(command_id) else { continue };
aliases.insert(name, id);
}
let mut st = store();
st.map = aliases;
st.file = file;
}
/// The command id `target` names (`*LINE`, `_.line`, `-LAYER`…), if there is such a command.
pub fn command_id(target: &str) -> Option<String> {
let t = target.trim().trim_start_matches('*').trim().trim_start_matches(['_', '.']).to_ascii_lowercase();