Merge pull request #6562 from dotCooCoo/fix/script-console-report-text

Script Console: report lines match script.console, counts of one are singular, every run ends
This commit is contained in:
BFlat
2026-11-11 20:36:11 +07:00
committed by GitHub
35 changed files with 390 additions and 78 deletions
+2 -1
View File
@@ -106,7 +106,8 @@ impl Entry {
Entry::End(e) => format!("ended: {}", e.id()),
Entry::Report { ms, host_calls, slowest } => {
let time = ms.map_or_else(|| "time unavailable".to_string(), |ms| format!("{ms} ms"));
let mut s = format!("{time}, {host_calls} host calls");
let calls = if *host_calls == 1 { "host call" } else { "host calls" };
let mut s = format!("{time}, {host_calls} {calls}");
if !slowest.is_empty() {
let list: Vec<String> = slowest.iter().map(|c| format!("{} ×{} {} ms", c.id, c.calls, c.total_ms)).collect();
s.push_str("; slowest: ");
+37 -13
View File
@@ -166,21 +166,27 @@ thread_local! {
static NEXT_JOB: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
/// A session's hold on its running job: while one exists the lease is held. Dropping it ends the job.
/// It keeps the run's number and its name as console lines give it, so the run's console entry can
/// end when the job is gone.
#[derive(Debug)]
pub struct JobKey(u64);
pub struct JobKey {
id: u64,
run: u64,
name: Arc<str>,
}
impl Drop for JobKey {
fn drop(&mut self) {
let _ = JOBS.try_with(|jobs| {
if let Ok(mut jobs) = jobs.try_borrow_mut() {
jobs.remove(&self.0);
jobs.remove(&self.id);
}
});
}
}
fn with_job<T>(key: &JobKey, f: impl FnOnce(&mut Job) -> T) -> Option<T> {
JOBS.with(|jobs| jobs.borrow_mut().get_mut(&key.0).map(f))
JOBS.with(|jobs| jobs.borrow_mut().get_mut(&key.id).map(f))
}
/// The state a script changes that a rollback puts back.
@@ -1235,12 +1241,8 @@ impl Session {
/// ([`Self::script_report`]). False with none running.
pub fn script_abandon(&mut self) -> bool {
let Some(key) = self.script_key.take() else { return false };
let Some(job) = JOBS.with(|jobs| jobs.borrow_mut().remove(&key.0)) else {
drop(key);
raise_ids(self);
self.script_stop.clear();
self.reset_tool();
cmd::compositefonts::sync(self);
let Some(job) = JOBS.with(|jobs| jobs.borrow_mut().remove(&key.id)) else {
self.script_gone(key, console::Ending::Stopped);
return true;
};
drop(key);
@@ -1250,6 +1252,19 @@ impl Session {
true
}
/// Ends the run whose job is no longer held: its console entry ends with `ending`, with no
/// report line, since its time and host calls went with the job, and the session's state is
/// put back as at the end of any run.
fn script_gone(&mut self, key: JobKey, ending: console::Ending) {
let line = console::ConsoleLine { run: key.run, name: key.name.clone(), entry: console::Entry::End(ending) };
self.script_console.push(line, false);
drop(key);
raise_ids(self);
self.script_stop.clear();
self.reset_tool();
cmd::compositefonts::sync(self);
}
/// A script can't start while a document holds a drag in progress or an undo group, which its
/// one undo step could not take in.
fn settle_for_script(&mut self) -> Result<()> {
@@ -1342,10 +1357,11 @@ impl Session {
return Err(EngineError::Other(message));
}
let scope = crate::file_access::ScopeHandle::capture();
let (run, line_name) = (host.run, host.name.clone());
let job = Job { realm, host, world: World::working(self), start_uids: open, name: name.clone(), scope };
let id = NEXT_JOB.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
JOBS.with(|jobs| jobs.borrow_mut().insert(id, job));
self.script_key = Some(JobKey(id));
self.script_key = Some(JobKey { id, run, name: line_name });
Ok(name)
}
@@ -1353,14 +1369,15 @@ impl Session {
/// run has ended: its report, or the error that ended it, with the documents put back.
pub fn script_pump(&mut self) -> Option<Result<Value>> {
let key = self.script_key.take()?;
let Some(mut job) = JOBS.with(|jobs| jobs.borrow_mut().remove(&key.0)) else {
let Some(mut job) = JOBS.with(|jobs| jobs.borrow_mut().remove(&key.id)) else {
self.script_gone(key, console::Ending::Internal);
return Some(Err(EngineError::Other(JOB_GONE.to_string())));
};
if let Some(e) = job.host.dialogs.take_failure() {
return Some(self.script_failed(job, &e));
}
if (job.host.waiting.get() || job.host.dialogs.is_waiting()) && !self.script_stop.is_set() {
JOBS.with(|jobs| jobs.borrow_mut().insert(key.0, job));
JOBS.with(|jobs| jobs.borrow_mut().insert(key.id, job));
self.script_key = Some(key);
return None;
}
@@ -1382,7 +1399,7 @@ impl Session {
}
Ok(Err(e)) => Some(self.script_failed(job, &e)),
Ok(Ok(Progress::Paused)) => {
JOBS.with(|jobs| jobs.borrow_mut().insert(key.0, job));
JOBS.with(|jobs| jobs.borrow_mut().insert(key.id, job));
self.script_key = Some(key);
None
}
@@ -1653,6 +1670,13 @@ fn run_slice(s: &mut Session, job: &mut Job) -> std::result::Result<std::result:
r
}
/// Tests: removes the session's running job while the session keeps its hold on it. → whether a
/// job was removed.
#[cfg(test)]
pub(crate) fn forget_job(s: &Session) -> bool {
s.script_key.as_ref().is_some_and(|k| JOBS.with(|jobs| jobs.borrow_mut().remove(&k.id)).is_some())
}
/// The objects a run may add before it is asked to continue or stop.
fn growth_objects() -> u64 {
#[cfg(test)]
+49
View File
@@ -170,6 +170,55 @@ fn a_stopped_run_reports_its_diagnostics_and_its_unended_line() {
assert_eq!(texts(&mut s, "end"), ["ended: stopped"]);
}
/// A report line counts one host call in the singular, and gives a run without a clock as "time
/// unavailable".
#[test]
fn a_report_line_counts_one_host_call_in_the_singular_and_says_when_time_is_unavailable() {
use crate::script::console::{Entry, Slow};
let report = |ms: Option<u64>, host_calls: u64| Entry::Report { ms, host_calls, slowest: vec![] }.text();
assert_eq!(report(Some(3), 0), "3 ms, 0 host calls");
assert_eq!(report(Some(3), 1), "3 ms, 1 host call");
assert_eq!(report(Some(0), 2), "0 ms, 2 host calls");
assert_eq!(report(None, 0), "time unavailable, 0 host calls");
assert_eq!(report(None, 1), "time unavailable, 1 host call");
assert_eq!(report(None, 2), "time unavailable, 2 host calls");
let slowest = vec![Slow { id: "a.b".into(), calls: 1, total_ms: 4, max_ms: 4 }];
assert_eq!(Entry::Report { ms: None, host_calls: 1, slowest }.text(), "time unavailable, 1 host call; slowest: a.b ×1 4 ms");
}
/// A run whose job is gone still ends its console entry, once, with its own number and name and no
/// report line: as stopped when it is abandoned, as an internal fault when a slice finds it gone.
/// The session can start the next run.
#[test]
fn a_run_whose_job_is_gone_still_ends_its_console_entry() {
for abandon in [true, false] {
let mut s = Session::new();
s.script_frames = true;
run(&mut s, "$.writeln('one'); while (true) {}").unwrap();
assert!(s.script_pump().is_none());
assert!(script::forget_job(&s));
if abandon {
assert!(s.script_abandon());
} else {
let e = s.script_pump().unwrap().unwrap_err();
assert_eq!(e.to_string(), "the script's run is no longer available");
}
assert!(!s.script_running());
let all = lines(&mut s);
let kinds: Vec<&str> = all.iter().map(|(_, k, _)| k.as_str()).collect();
assert_eq!(kinds, ["start", "output", "end"], "{all:?}");
assert_eq!(all[2].2, if abandon { "ended: stopped" } else { "ended: internal" });
let c = s.execute("script.console", &json!({})).unwrap();
assert_eq!((c["lines"][2]["run"].as_u64(), c["lines"][2]["name"].as_str()), (Some(1), Some("t.js")));
assert!(!s.script_abandon() && s.script_pump().is_none(), "nothing is left to end");
assert_eq!(lines(&mut s).len(), 3, "the entry ends once");
s.script_frames = false;
run(&mut s, "$.writeln('two')").unwrap();
assert_eq!(texts(&mut s, "end").last().map(String::as_str), Some("ended: finished"), "the next run starts");
assert_eq!(lines(&mut s).last().map(|l| l.0), Some(2));
}
}
#[test]
fn runs_are_numbered_a_busy_run_adds_nothing_and_clear_keeps_the_report() {
let mut s = Session::new();
+8 -2
View File
@@ -6521,8 +6521,14 @@ breakText Glyphs Глифи
Ended at a limit Прекратен при лимит
Stopped Спрян
Ended by an internal fault Прекратен поради вътрешна грешка
{ms} ms, {calls} host calls {ms} ms, {calls} извиквания към хоста
{dropped} earlier lines dropped, {cut} lines cut {dropped} по-ранни реда премахнати, {cut} реда съкратени
time unavailable времето не е налично
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} най-бавни: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} извикване към хоста|{n} извиквания към хоста
@plural {n} earlier line dropped|{n} earlier lines dropped {n} по-ранен ред премахнат|{n} по-ранни реда премахнати
@plural {n} line cut|{n} lines cut {n} ред съкратен|{n} реда съкратени
# Deferred plug-in effects
Refresh Deferred Effects Обновяване на отложените ефекти
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6572,8 +6572,14 @@ shape Open Otvoren
Ended at a limit Završeno na ograničenju
Stopped Zaustavljeno
Ended by an internal fault Završeno zbog interne greške
{ms} ms, {calls} host calls {ms} ms, {calls} poziva hosta
{dropped} earlier lines dropped, {cut} lines cut {dropped} ranijih redova uklonjeno, {cut} redova skraćeno
time unavailable vrijeme nije dostupno
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} najsporije: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} poziv hosta|{n} poziva hosta|{n} poziva hosta
@plural {n} earlier line dropped|{n} earlier lines dropped {n} raniji red uklonjen|{n} ranija reda uklonjena|{n} ranijih redova uklonjeno
@plural {n} line cut|{n} lines cut {n} red skraćen|{n} reda skraćena|{n} redova skraćeno
# Deferred plug-in effects
Refresh Deferred Effects Osvježi odgođene efekte
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6776,8 +6776,14 @@ breakText Glyphs Glifs
Ended at a limit Acabat en un límit
Stopped Aturat
Ended by an internal fault Acabat per un error intern
{ms} ms, {calls} host calls {ms} ms, {calls} crides a l’amfitrió
{dropped} earlier lines dropped, {cut} lines cut {dropped} línies anteriors descartades, {cut} línies tallades
time unavailable temps no disponible
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} més lentes: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} crida a l’amfitrió|{n} crides a l’amfitrió
@plural {n} earlier line dropped|{n} earlier lines dropped {n} línia anterior descartada|{n} línies anteriors descartades
@plural {n} line cut|{n} lines cut {n} línia tallada|{n} línies tallades
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` i `last` han de ser números de línies que la consola conserva, `first` abans de `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6795,8 +6795,14 @@ breakText Glyphs Glyfy
Ended at a limit Ukončeno na limitu
Stopped Zastaveno
Ended by an internal fault Ukončeno vnitřní chybou
{ms} ms, {calls} host calls {ms} ms, {calls} volání hostitele
{dropped} earlier lines dropped, {cut} lines cut {dropped} starších řádků zahozeno, {cut} řádků zkráceno
time unavailable čas není k dispozici
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} nejpomalejší: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} volání hostitele|{n} volání hostitele|{n} volání hostitele
@plural {n} earlier line dropped|{n} earlier lines dropped {n} starší řádek zahozen|{n} starší řádky zahozeny|{n} starších řádků zahozeno
@plural {n} line cut|{n} lines cut {n} řádek zkrácen|{n} řádky zkráceny|{n} řádků zkráceno
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` a `last` musí být čísla řádků, které konzole uchovává, `first` před `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6535,8 +6535,14 @@ breakText Glyphs Glyffer
Ended at a limit Afsluttet ved en grænse
Stopped Stoppet
Ended by an internal fault Afsluttet af en intern fejl
{ms} ms, {calls} host calls {ms} ms, {calls} værtskald
{dropped} earlier lines dropped, {cut} lines cut {dropped} tidligere linjer fjernet, {cut} linjer afkortet
time unavailable tid ikke tilgængelig
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} langsomste: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} værtskald|{n} værtskald
@plural {n} earlier line dropped|{n} earlier lines dropped {n} tidligere linje fjernet|{n} tidligere linjer fjernet
@plural {n} line cut|{n} lines cut {n} linje afkortet|{n} linjer afkortet
# Deferred plug-in effects
Refresh Deferred Effects Opdatér udskudte effekter
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6382,8 +6382,14 @@ breakText Glyphs Glyphen
Ended at a limit An einem Limit beendet
Stopped Gestoppt
Ended by an internal fault Durch einen internen Fehler beendet
{ms} ms, {calls} host calls {ms} ms, {calls} Host-Aufrufe
{dropped} earlier lines dropped, {cut} lines cut {dropped} ältere Zeilen verworfen, {cut} Zeilen gekürzt
time unavailable Zeit nicht verfügbar
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} langsamste: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} Host-Aufruf|{n} Host-Aufrufe
@plural {n} earlier line dropped|{n} earlier lines dropped {n} ältere Zeile verworfen|{n} ältere Zeilen verworfen
@plural {n} line cut|{n} lines cut {n} Zeile gekürzt|{n} Zeilen gekürzt
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` und `last` müssen Nummern von Zeilen sein, die die Konsole enthält, `first` vor `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6747,8 +6747,14 @@ breakText Glyphs Γλύφοι
Ended at a limit Τερματίστηκε σε όριο
Stopped Διακόπηκε
Ended by an internal fault Τερματίστηκε λόγω εσωτερικού σφάλματος
{ms} ms, {calls} host calls {ms} ms, {calls} κλήσεις προς το κεντρικό σύστημα
{dropped} earlier lines dropped, {cut} lines cut {dropped} παλαιότερες γραμμές απορρίφθηκαν, {cut} γραμμές περικόπηκαν
time unavailable ο χρόνος δεν είναι διαθέσιμος
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} πιο αργές: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} κλήση προς το κεντρικό σύστημα|{n} κλήσεις προς το κεντρικό σύστημα
@plural {n} earlier line dropped|{n} earlier lines dropped {n} παλαιότερη γραμμή απορρίφθηκε|{n} παλαιότερες γραμμές απορρίφθηκαν
@plural {n} line cut|{n} lines cut {n} γραμμή περικόπηκε|{n} γραμμές περικόπηκαν
@msg `first` and `last` must be numbers of lines the console holds, first before last Τα `first` και `last` πρέπει να είναι αριθμοί γραμμών που κρατά η κονσόλα, το `first` πριν από το `last`
# Deferred plug-in effects
Can't render this file because it is too large.
+8 -2
View File
@@ -6738,8 +6738,14 @@ breakText Glyphs Glifos
Ended at a limit Terminado en un límite
Stopped Detenido
Ended by an internal fault Terminado por un error interno
{ms} ms, {calls} host calls {ms} ms, {calls} llamadas al host
{dropped} earlier lines dropped, {cut} lines cut {dropped} líneas anteriores descartadas, {cut} líneas recortadas
time unavailable tiempo no disponible
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} más lentos: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} llamada al host|{n} llamadas al host
@plural {n} earlier line dropped|{n} earlier lines dropped {n} línea anterior descartada|{n} líneas anteriores descartadas
@plural {n} line cut|{n} lines cut {n} línea recortada|{n} líneas recortadas
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` y `last` deben ser números de líneas que la consola conserva, `first` antes que `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6830,8 +6830,14 @@ breakText Glyphs Merkit
Ended at a limit Päättyi rajaan
Stopped Pysäytetty
Ended by an internal fault Päättyi sisäiseen virheeseen
{ms} ms, {calls} host calls {ms} ms, {calls} isäntäkutsua
{dropped} earlier lines dropped, {cut} lines cut {dropped} aiempaa riviä poistettu, {cut} riviä katkaistu
time unavailable aika ei käytettävissä
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} hitaimmat: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} isäntäkutsu|{n} isäntäkutsua
@plural {n} earlier line dropped|{n} earlier lines dropped {n} aiempi rivi poistettu|{n} aiempaa riviä poistettu
@plural {n} line cut|{n} lines cut {n} rivi katkaistu|{n} riviä katkaistu
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` ja `last` on oltava konsolin säilyttämien rivien numeroita, `first` ennen arvoa `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6790,8 +6790,14 @@ breakText Glyphs Glyphes
Ended at a limit Terminé à une limite
Stopped Arrêté
Ended by an internal fault Terminé par une erreur interne
{ms} ms, {calls} host calls {ms} ms, {calls} appels à l’hôte
{dropped} earlier lines dropped, {cut} lines cut {dropped} lignes antérieures supprimées, {cut} lignes coupées
time unavailable temps indisponible
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} plus lentes : {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} appel à l’hôte|{n} appels à l’hôte
@plural {n} earlier line dropped|{n} earlier lines dropped {n} ligne antérieure supprimée|{n} lignes antérieures supprimées
@plural {n} line cut|{n} lines cut {n} ligne coupée|{n} lignes coupées
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` et `last` doivent être des numéros de lignes que la console contient, `first` avant `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -8236,8 +8236,14 @@ theme Appearance अपीयरेंस
Ended at a limit सीमा पर समाप्त
Stopped रोका गया
Ended by an internal fault आंतरिक त्रुटि से समाप्त
{ms} ms, {calls} host calls {ms} ms, {calls} होस्ट कॉल
{dropped} earlier lines dropped, {cut} lines cut {dropped} पुरानी पंक्तियाँ हटाई गईं, {cut} पंक्तियाँ काटी गईं
time unavailable समय उपलब्ध नहीं
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} सबसे धीमे: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} होस्ट कॉल|{n} होस्ट कॉल
@plural {n} earlier line dropped|{n} earlier lines dropped {n} पुरानी पंक्ति हटाई गई|{n} पुरानी पंक्तियाँ हटाई गईं
@plural {n} line cut|{n} lines cut {n} पंक्ति काटी गई|{n} पंक्तियाँ काटी गईं
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` और `last` कंसोल में रखी पंक्तियों की संख्याएँ होनी चाहिए, `first` पहले और `last` बाद में
# Deferred plug-in effects
Can't render this file because it is too large.
+8 -2
View File
@@ -6779,8 +6779,14 @@ breakText Glyphs Glifák
Ended at a limit Korlátnál ért véget
Stopped Leállítva
Ended by an internal fault Belső hiba miatt ért véget
{ms} ms, {calls} host calls {ms} ms, {calls} gazdahívás
{dropped} earlier lines dropped, {cut} lines cut {dropped} korábbi sor elvetve, {cut} sor levágva
time unavailable az idő nem érhető el
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} leglassabbak: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} gazdahívás
@plural {n} earlier line dropped|{n} earlier lines dropped {n} korábbi sor elvetve
@plural {n} line cut|{n} lines cut {n} sor levágva
@msg `first` and `last` must be numbers of lines the console holds, first before last A `first` és a `last` a konzolban tárolt sorok számai legyenek, a `first` a `last` előtt
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6535,8 +6535,14 @@ breakText Glyphs Glif
Ended at a limit Berakhir pada batas
Stopped Dihentikan
Ended by an internal fault Berakhir karena galat internal
{ms} ms, {calls} host calls {ms} ms, {calls} panggilan host
{dropped} earlier lines dropped, {cut} lines cut {dropped} baris lama dibuang, {cut} baris dipotong
time unavailable waktu tidak tersedia
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} paling lambat: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} panggilan host
@plural {n} earlier line dropped|{n} earlier lines dropped {n} baris lama dibuang
@plural {n} line cut|{n} lines cut {n} baris dipotong
# Deferred plug-in effects
Refresh Deferred Effects Segarkan efek yang ditunda
Can't render this file because it contains an unexpected character in line 2327 and column 5.
+8 -2
View File
@@ -6416,8 +6416,14 @@ breakText Glyphs Glifi
Ended at a limit Terminato a un limite
Stopped Interrotto
Ended by an internal fault Terminato per un errore interno
{ms} ms, {calls} host calls {ms} ms, {calls} chiamate all'host
{dropped} earlier lines dropped, {cut} lines cut {dropped} righe precedenti scartate, {cut} righe tagliate
time unavailable tempo non disponibile
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} più lenti: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} chiamata all'host|{n} chiamate all'host
@plural {n} earlier line dropped|{n} earlier lines dropped {n} riga precedente scartata|{n} righe precedenti scartate
@plural {n} line cut|{n} lines cut {n} riga tagliata|{n} righe tagliate
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` e `last` devono essere numeri di righe presenti nella console, `first` prima di `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6481,8 +6481,14 @@ breakText Glyphs 字形
Ended at a limit 上限で終了
Stopped 停止
Ended by an internal fault 内部エラーで終了
{ms} ms, {calls} host calls {ms} ms、ホスト呼び出し {calls} 回
{dropped} earlier lines dropped, {cut} lines cut 古い行 {dropped} 行を破棄、{cut} 行を切り詰め
time unavailable 時間を取得できません
{ms} ms {ms} ms
{time}, {calls} {time}、{calls}
slowest: {commands} 最も遅いコマンド:{commands}
{dropped}, {cut} {dropped}、{cut}
@plural {n} host call|{n} host calls ホスト呼び出し {n} 回
@plural {n} earlier line dropped|{n} earlier lines dropped 古い行 {n} 行を破棄
@plural {n} line cut|{n} lines cut {n} 行を切り詰め
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` と `last` には、コンソールが保持している行の番号を、`first` が `last` より前になるよう指定してください
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6856,8 +6856,14 @@ font-script Japanese იაპონური
Ended at a limit დასრულდა ლიმიტზე
Stopped შეჩერდა
Ended by an internal fault დასრულდა შიდა შეცდომის გამო
{ms} ms, {calls} host calls {ms} ms, ჰოსტის {calls} გამოძახება
{dropped} earlier lines dropped, {cut} lines cut {dropped} ძველი სტრიქონი წაიშალა, {cut} სტრიქონი შემოკლდა
time unavailable დრო მიუწვდომელია
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} ყველაზე ნელი: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls ჰოსტის {n} გამოძახება
@plural {n} earlier line dropped|{n} earlier lines dropped {n} ძველი სტრიქონი წაიშალა
@plural {n} line cut|{n} lines cut {n} სტრიქონი შემოკლდა
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` და `last` უნდა იყოს კონსოლში შენახული სტრიქონების ნომრები და `first` უნდა იყოს `last`-ზე წინ
# Deferred plug-in effects
Can't render this file because it is too large.
+8 -2
View File
@@ -6377,8 +6377,14 @@ breakText Glyphs 글리프
Ended at a limit 한도에서 종료됨
Stopped 중지됨
Ended by an internal fault 내부 오류로 종료됨
{ms} ms, {calls} host calls {ms} ms, 호스트 호출 {calls}회
{dropped} earlier lines dropped, {cut} lines cut 이전 줄 {dropped}개 삭제됨, 줄 {cut}개 잘림
time unavailable 시간 정보 없음
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} 가장 느린 명령: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls 호스트 호출 {n}회
@plural {n} earlier line dropped|{n} earlier lines dropped 이전 줄 {n}개 삭제됨
@plural {n} line cut|{n} lines cut 줄 {n}개 잘림
@msg `first` and `last` must be numbers of lines the console holds, first before last `first`와 `last`는 콘솔에 있는 줄 번호여야 하며 `first`가 `last`보다 앞서야 합니다
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6586,8 +6586,14 @@ breakText Glyphs Glyphs
Ended at a limit Beëindigd bij een limiet
Stopped Gestopt
Ended by an internal fault Beëindigd door een interne fout
{ms} ms, {calls} host calls {ms} ms, {calls} host-aanroepen
{dropped} earlier lines dropped, {cut} lines cut {dropped} eerdere regels verwijderd, {cut} regels ingekort
time unavailable tijd niet beschikbaar
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} traagste: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} host-aanroep|{n} host-aanroepen
@plural {n} earlier line dropped|{n} earlier lines dropped {n} eerdere regel verwijderd|{n} eerdere regels verwijderd
@plural {n} line cut|{n} lines cut {n} regel ingekort|{n} regels ingekort
# Deferred plug-in effects
Refresh Deferred Effects Uitgestelde effecten vernieuwen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6651,8 +6651,14 @@ breakText Glyphs Glify
Ended at a limit Zakończono na limicie
Stopped Zatrzymano
Ended by an internal fault Zakończono z powodu błędu wewnętrznego
{ms} ms, {calls} host calls {ms} ms, wywołania hosta: {calls}
{dropped} earlier lines dropped, {cut} lines cut Odrzucone wcześniejsze wiersze: {dropped}, skrócone wiersze: {cut}
time unavailable czas niedostępny
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} najwolniejsze: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} wywołanie hosta|{n} wywołania hosta|{n} wywołań hosta
@plural {n} earlier line dropped|{n} earlier lines dropped {n} odrzucony wcześniejszy wiersz|{n} odrzucone wcześniejsze wiersze|{n} odrzuconych wcześniejszych wierszy
@plural {n} line cut|{n} lines cut {n} skrócony wiersz|{n} skrócone wiersze|{n} skróconych wierszy
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` i `last` muszą być numerami wierszy przechowywanych w konsoli, `first` przed `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6868,8 +6868,14 @@ swatch Yellow Green Verde-amarelado
Ended at a limit Encerrado em um limite
Stopped Interrompido
Ended by an internal fault Encerrado por um erro interno
{ms} ms, {calls} host calls {ms} ms, {calls} chamadas ao host
{dropped} earlier lines dropped, {cut} lines cut {dropped} linhas anteriores descartadas, {cut} linhas cortadas
time unavailable tempo indisponível
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} mais lentos: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} chamada ao host|{n} chamadas ao host
@plural {n} earlier line dropped|{n} earlier lines dropped {n} linha anterior descartada|{n} linhas anteriores descartadas
@plural {n} line cut|{n} lines cut {n} linha cortada|{n} linhas cortadas
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` e `last` devem ser números de linhas que o console mantém, `first` antes de `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 6 and column 47.
+8 -2
View File
@@ -6884,8 +6884,14 @@ shape Open Открытый
Ended at a limit Завершено на лимите
Stopped Остановлено
Ended by an internal fault Завершено из-за внутренней ошибки
{ms} ms, {calls} host calls {ms} мс, вызовов хоста: {calls}
{dropped} earlier lines dropped, {cut} lines cut Отброшено прежних строк: {dropped}, обрезано строк: {cut}
time unavailable время недоступно
{ms} ms {ms} мс
{time}, {calls} {time}, {calls}
slowest: {commands} самые медленные: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} вызов хоста|{n} вызова хоста|{n} вызовов хоста
@plural {n} earlier line dropped|{n} earlier lines dropped {n} прежняя строка отброшена|{n} прежние строки отброшены|{n} прежних строк отброшено
@plural {n} line cut|{n} lines cut {n} строка обрезана|{n} строки обрезаны|{n} строк обрезано
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` и `last` должны быть номерами строк, которые хранит консоль, причём `first` не позже `last`
# Deferred plug-in effects
Can't render this file because it is too large.
+8 -2
View File
@@ -8280,8 +8280,14 @@ digits Hindi Hindi
Ended at a limit Avslutades vid en gräns
Stopped Stoppad
Ended by an internal fault Avslutades av ett internt fel
{ms} ms, {calls} host calls {ms} ms, {calls} värdanrop
{dropped} earlier lines dropped, {cut} lines cut {dropped} tidigare rader borttagna, {cut} rader avkortade
time unavailable tid ej tillgänglig
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} långsammast: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} värdanrop|{n} värdanrop
@plural {n} earlier line dropped|{n} earlier lines dropped {n} tidigare rad borttagen|{n} tidigare rader borttagna
@plural {n} line cut|{n} lines cut {n} rad avkortad|{n} rader avkortade
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` och `last` måste vara nummer på rader som konsolen har kvar, `first` före `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 5 and column 25.
+18
View File
@@ -227,6 +227,24 @@ fn each_language_counts_with_its_plural_rule() {
}
}
/// The Script Console's counted phrases have a row in every complete catalog, and a count picks
/// the language's own form.
#[test]
fn script_console_counts_take_each_language_s_form() {
const COUNTED: [(&str, &str); 3] =
[("{n} host call", "{n} host calls"), ("{n} earlier line dropped", "{n} earlier lines dropped"), ("{n} line cut", "{n} lines cut")];
for l in LANGUAGES.iter().filter(|l| l.complete_menus) {
for (one, other) in COUNTED {
assert!(l.catalog().plural(one, other, 0).is_some(), "{} is missing the row for {other}", l.code);
}
}
assert_eq!([1, 2].map(|n| trn(de(), n, COUNTED[0].0, COUNTED[0].1)), ["1 Host-Aufruf", "2 Host-Aufrufe"]);
let ru = Lang::from_code("ru").expect("registered");
assert_eq!([1, 3, 5].map(|n| trn(ru, n, COUNTED[2].0, COUNTED[2].1)), ["1 строка обрезана", "3 строки обрезаны", "5 строк обрезано"]);
let fr = Lang::from_code("fr").expect("registered");
assert_eq!([0, 1].map(|n| trn(fr, n, COUNTED[1].0, COUNTED[1].1)), ["0 ligne antérieure supprimée", "1 ligne antérieure supprimée"]);
}
#[test]
fn candidates_walk_from_specific_to_general() {
assert_eq!(candidates("pt_BR.UTF-8"), ["pt-br", "pt"]);
+8 -2
View File
@@ -6751,8 +6751,14 @@ breakText Glyphs Glifler
Ended at a limit Bir sınırda sona erdi
Stopped Durduruldu
Ended by an internal fault Bir iç hata nedeniyle sona erdi
{ms} ms, {calls} host calls {ms} ms, {calls} ana bilgisayar çağrısı
{dropped} earlier lines dropped, {cut} lines cut {dropped} eski satır atıldı, {cut} satır kesildi
time unavailable süre kullanılamıyor
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} en yavaşlar: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} ana bilgisayar çağrısı
@plural {n} earlier line dropped|{n} earlier lines dropped {n} eski satır atıldı
@plural {n} line cut|{n} lines cut {n} satır kesildi
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` ve `last`, konsolun tuttuğu satırların numaraları olmalı ve `first`, `last` değerinden önce gelmeli
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 5 and column 25.
+8 -2
View File
@@ -6396,8 +6396,14 @@ breakText Glyphs Гліфи
Ended at a limit Завершено на ліміті
Stopped Зупинено
Ended by an internal fault Завершено через внутрішню помилку
{ms} ms, {calls} host calls {ms} мс, викликів хоста: {calls}
{dropped} earlier lines dropped, {cut} lines cut Відкинуто попередніх рядків: {dropped}, обрізано рядків: {cut}
time unavailable час недоступний
{ms} ms {ms} мс
{time}, {calls} {time}, {calls}
slowest: {commands} найповільніші: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} виклик хоста|{n} виклики хоста|{n} викликів хоста
@plural {n} earlier line dropped|{n} earlier lines dropped {n} попередній рядок відкинуто|{n} попередні рядки відкинуто|{n} попередніх рядків відкинуто
@plural {n} line cut|{n} lines cut {n} рядок обрізано|{n} рядки обрізано|{n} рядків обрізано
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` і `last` мають бути номерами рядків, які зберігає консоль, причому `first` не пізніше `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 182 and column 5.
+8 -2
View File
@@ -6397,8 +6397,14 @@ breakText Glyphs Glyph
Ended at a limit Kết thúc ở giới hạn
Stopped Đã dừng
Ended by an internal fault Kết thúc do lỗi nội bộ
{ms} ms, {calls} host calls {ms} ms, {calls} lệnh gọi máy chủ
{dropped} earlier lines dropped, {cut} lines cut Đã bỏ {dropped} dòng cũ, đã cắt {cut} dòng
time unavailable không có thời gian
{ms} ms {ms} ms
{time}, {calls} {time}, {calls}
slowest: {commands} chậm nhất: {commands}
{dropped}, {cut} {dropped}, {cut}
@plural {n} host call|{n} host calls {n} lệnh gọi máy chủ
@plural {n} earlier line dropped|{n} earlier lines dropped đã bỏ {n} dòng cũ
@plural {n} line cut|{n} lines cut đã cắt {n} dòng
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` và `last` phải là số thứ tự của các dòng mà bảng điều khiển đang giữ, `first` đứng trước `last`
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 204 and column 5.
+8 -2
View File
@@ -6633,8 +6633,14 @@ breakText Glyphs 字形
Ended at a limit 在限制处结束
Stopped 已停止
Ended by an internal fault 因内部错误结束
{ms} ms, {calls} host calls {ms} 毫秒,宿主调用 {calls} 次
{dropped} earlier lines dropped, {cut} lines cut 已丢弃 {dropped} 行较早内容,已截断 {cut} 行
time unavailable 时间不可用
{ms} ms {ms} 毫秒
{time}, {calls} {time},{calls}
slowest: {commands} 最慢的命令:{commands}
{dropped}, {cut} {dropped},{cut}
@plural {n} host call|{n} host calls 宿主调用 {n} 次
@plural {n} earlier line dropped|{n} earlier lines dropped 已丢弃 {n} 行较早内容
@plural {n} line cut|{n} lines cut 已截断 {n} 行
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` 和 `last` 必须是控制台保留的行号,且 `first` 不能在 `last` 之后
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8 -2
View File
@@ -6431,8 +6431,14 @@ breakText Glyphs 字符
Ended at a limit 在限制處結束
Stopped 已停止
Ended by an internal fault 因內部錯誤結束
{ms} ms, {calls} host calls {ms} 毫秒,主機呼叫 {calls} 次
{dropped} earlier lines dropped, {cut} lines cut 已捨棄 {dropped} 行較早內容,已截斷 {cut} 行
time unavailable 時間無法取得
{ms} ms {ms} 毫秒
{time}, {calls} {time},{calls}
slowest: {commands} 最慢的命令:{commands}
{dropped}, {cut} {dropped},{cut}
@plural {n} host call|{n} host calls 主機呼叫 {n} 次
@plural {n} earlier line dropped|{n} earlier lines dropped 已捨棄 {n} 行較早內容
@plural {n} line cut|{n} lines cut 已截斷 {n} 行
@msg `first` and `last` must be numbers of lines the console holds, first before last `first` 和 `last` 必須是主控台保留的行號,且 `first` 不能在 `last` 之後
# Deferred plug-in effects
Can't render this file because it contains an unexpected character in line 6 and column 47.
+15 -8
View File
@@ -39,10 +39,13 @@ pub(crate) fn shown(line: &ConsoleLine) -> String {
Entry::Output(t) | Entry::Log(t) | Entry::Error(t) => t.clone(),
Entry::End(e) => format!("■ {}", ending_label(*e)),
Entry::Report { ms, host_calls, slowest } => {
let ms = ms.map_or_else(|| "?".to_string(), |ms| ms.to_string());
let mut s = crate::i18n::fmt(tl!("{ms} ms, {calls} host calls"), &[("ms", &ms), ("calls", &host_calls.to_string())]);
for c in slowest {
s.push_str(&format!("; {} ×{} {} ms", c.id, c.calls, c.total_ms));
let time = ms.map_or_else(|| tl!("time unavailable").to_string(), millis);
let calls = crate::i18n::tn(*host_calls, "{n} host call", "{n} host calls");
let mut s = crate::i18n::fmt(tl!("{time}, {calls}"), &[("time", &time), ("calls", &calls)]);
if !slowest.is_empty() {
let list: Vec<String> = slowest.iter().map(|c| format!("{} ×{} {}", c.id, c.calls, millis(c.total_ms))).collect();
s.push_str("; ");
s.push_str(&crate::i18n::fmt(tl!("slowest: {commands}"), &[("commands", &list.join(", "))]));
}
s
}
@@ -50,13 +53,17 @@ pub(crate) fn shown(line: &ConsoleLine) -> String {
format!("#{} {}: {}", line.run, line.name, text)
}
/// A time in milliseconds, in the UI language.
fn millis(ms: u64) -> String {
crate::i18n::fmt(tl!("{ms} ms"), &[("ms", &ms.to_string())])
}
/// The notice of the lines the console dropped and cut, or `None` when it has done neither.
fn notice(c: &Console) -> Option<String> {
(c.dropped_lines() > 0 || c.truncated_lines() > 0).then(|| {
crate::i18n::fmt(
tl!("{dropped} earlier lines dropped, {cut} lines cut"),
&[("dropped", &c.dropped_lines().to_string()), ("cut", &c.truncated_lines().to_string())],
)
let dropped = crate::i18n::tn(c.dropped_lines(), "{n} earlier line dropped", "{n} earlier lines dropped");
let cut = crate::i18n::tn(c.truncated_lines(), "{n} line cut", "{n} lines cut");
crate::i18n::fmt(tl!("{dropped}, {cut}"), &[("dropped", &dropped), ("cut", &cut)])
})
}
@@ -6,7 +6,7 @@ use serde_json::json;
use vectorcraft_engine::Session;
use super::pstate;
use super::script_console::{DRAWN, ID, show};
use super::script_console::{DRAWN, ID, show, shown};
use crate::tests_labels::painted_text;
use crate::{Services, VectorcraftApp, script_runs};
@@ -96,3 +96,48 @@ fn copy_all_and_copy_selected_put_the_lines_on_the_clipboard() {
let all = app.clipboard_out.take().unwrap();
assert!(all.lines().next().is_some_and(|l| l.contains("lines dropped")), "Copy All starts with the notice: {}", &all[..80]);
}
/// A report line reads in English as `script.console` gives it: "time unavailable" for a run
/// without a clock, one host call in the singular, and the slowest commands.
#[test]
fn a_report_line_reads_as_script_console_gives_it() {
use vectorcraft_engine::script::console::{ConsoleLine, Entry, Slow};
let slow = vec![Slow { id: "a.b".into(), calls: 2, total_ms: 7, max_ms: 4 }, Slow { id: "c".into(), calls: 1, total_ms: 1, max_ms: 1 }];
for ms in [None, Some(0), Some(12)] {
for host_calls in [0, 1, 2] {
for slowest in [vec![], slow.clone()] {
let entry = Entry::Report { ms, host_calls, slowest };
let want = format!("#3 c.js: {}", entry.text());
assert_eq!(shown(&ConsoleLine { run: 3, name: "c.js".into(), entry }), want);
}
}
}
let line = |ms, host_calls| shown(&ConsoleLine { run: 1, name: "c.js".into(), entry: Entry::Report { ms, host_calls, slowest: vec![] } });
assert_eq!(line(None, 1), "#1 c.js: time unavailable, 1 host call");
assert_eq!(line(Some(0), 2), "#1 c.js: 0 ms, 2 host calls");
}
/// The notice counts the lines dropped and the lines cut each in its own form, a count of one in
/// the singular; Copy All starts with it.
#[test]
fn the_notice_counts_one_line_in_the_singular() {
for (dropped, cut, want) in [
(1, 0, "1 earlier line dropped, 0 lines cut"),
(0, 1, "0 earlier lines dropped, 1 line cut"),
(1, 2, "1 earlier line dropped, 2 lines cut"),
(2, 1, "2 earlier lines dropped, 1 line cut"),
(2, 2, "2 earlier lines dropped, 2 lines cut"),
] {
let mut app = app();
// A run adds a start, an end and a report line to its output lines.
let lines = if dropped > 0 { vectorcraft_engine::script::console::MAX_CONSOLE_LINES - 3 + dropped } else { cut + 1 };
let source = format!("var big = 'x'; while (big.length < 5000) big += big; for (var i = 0; i < {lines}; i++) $.writeln(i < {cut} ? big : i)");
run(&mut app, &source);
let c = app.session.script_console();
assert_eq!((c.dropped_lines(), c.truncated_lines()), (dropped as u64, cut as u64));
app.run("ui.copyScriptConsole", json!({})).unwrap();
let all = app.clipboard_out.take().unwrap();
assert_eq!(all.lines().next(), Some(want));
assert!(painted_text(&mut app, show).contains(want));
}
}
+6 -1
View File
@@ -1744,7 +1744,12 @@ at the bottom of the panel and in its menu. Output goes on while the panel is cl
- `script.console {}` returns `{lines: [{run, name, kind, text}], droppedLines, truncatedLines}`. `run` is the
run's number in the session, and `kind` is `start`, `output`, `log`, `error`, `end` or `report`. An end line's text
is `ended:`, a space, and then `finished`, `error`, `fuel`, `hostLimit`, `limit`, `stopped` or `internal`.
is `ended:`, a space, and then `finished`, `error`, `fuel`, `hostLimit`, `limit`, `stopped` or `internal`. A report
line's text is the run's time (`12 ms`, or `time unavailable` on a platform without a clock), a comma and its host
calls (`1 host call`, `2 host calls`), then `; slowest:` and the slowest commands when any were timed. The panel
shows the same text in the interface language. Every run that starts gets an end line. When a run is no longer
available, its end line is `stopped` if the run was abandoned and `internal` if the next slice found it missing, and
no report line follows.
- `script.console.clear {}` empties the console, sets its counts to zero and returns `{cleared}`. A running script
goes on writing, and `scriptReport` keeps the last run's console.
- `ui.copyScriptConsole {first?, last?}` copies lines `first` to `last` to the clipboard as the panel shows them, or
+1
View File
@@ -732,6 +732,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- The control server writes each reply through a 64 KiB buffer. On Windows 11 it sends the 17 MB `document.inspect` reply of a document with 50,000 objects on one layer to a client over loopback in 0.09 s instead of 34 s.
- Scripts: File › Scripts › Other Script… and `script.run` run a JavaScript file against the open documents as one transaction with one undo step for each changed document. A failed or stopped run puts every document back. `crates/engine/src/tests/script_run.rs`.
- Scripts: Window › Script Console shows each run's `$.write` and `$.writeln` output, `vc.log` entries, uncaught error with the script's name, line and column, and a report of its time, host calls and slowest commands, keeping at most 2,048 lines and counting the lines it dropped. `script.run`'s report carries the same data, and `script.console`, `script.console.clear` and `ui.copyScriptConsole` read, clear and copy it. `crates/engine/src/tests/script_console.rs`.
- Scripts: the Script Console panel's report line reads as `script.console` gives it, `time unavailable` included on a platform without a clock; counts of host calls, dropped lines and cut lines take the singular for one, in each language's own plural forms; and a run that is no longer available still gets its end line (`crates/engine/src/tests/script_console.rs`, `crates/ui-egui/src/panels/tests_script_console.rs`).
- Scripts: an error `$.evalFile` throws for a file it cannot run is reported at that file's line and column only while no `catch` has taken it. Caught and thrown again later, in the script or in another file, it is reported where it was thrown again, and a new error whose message holds its text is reported where that error was thrown. VectorCraft's own rule. `crates/engine/src/tests/script_include.rs`, `crates/js/src/host_tests.rs`.
- Scripts: a run reads its preprocessor lines before any of it runs: `#include` and `//@include` put another file in place of the line, looked for beside the including file, in the `#includepath` folders and in `JSINCLUDE`; `#script` names the run; `#target` and `#strict off` are taken, `#strict on` turns on strict write checks (below), and `#targetengine` and `#engine` are refused. `$.evalFile` runs a file with the same globals and returns its last value, and `$.fileName` and `$.includePath` give the file being run and the include folders. A script run from a file reads these files only in its folder and below, without `..` names or links that lead out, and an error in one is reported at its own file, line and column ([preprocessor directives](https://extendscript.docsforadobe.dev/extendscript-tools-features/preprocessor-directives/), [the `$` object](https://extendscript.docsforadobe.dev/extendscript-tools-features/dollar-object/); `crates/engine/src/tests/script_include.rs`).
- Scripts: `#strict on` (and `//@strict on`) runs the script with the checks the [`$` object](https://extendscript.docsforadobe.dev/extendscript-tools-features/dollar-object/) help page gives for `$.strict`: an assignment to a read-only property throws a `TypeError`, and so does a new property on an object that takes none. VectorCraft's own rules: a getter-only property counts as read-only, the objects that take no new properties are the non-extensible ones (`Object.preventExtensions`, `seal`, `freeze`, and the frozen `vc` and `$` objects), the last `#strict` line of the script and its includes sets the whole run, a file `$.evalFile` runs keeps its own line's checks for its code, and the rest of ES5 strict mode stays with `'use strict'`. Without the line, runs are unchanged. `crates/engine/src/tests/script_include.rs` (`strict_on_*`, `strict_mode_in_eval_file_is_scoped`), `crates/js/src/interp_tests.rs` (`strict_writes_are_separate_from_es5_strict`).