Skip to content

Commit d46f8a4

Browse files
fix
1 parent 629cf86 commit d46f8a4

30 files changed

Lines changed: 440 additions & 267 deletions

lib/components/inspector/preview_match_editor.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ class PreviewMatchEditor extends StatelessWidget {
327327
title: 'Install on the server',
328328
children: <Widget>[
329329
HuiDetailRow('Preview file', '$huiPreviewFolder${store.menuId}.json'),
330-
const HuiDetailRow('Permission', 'holoui.preview'),
330+
const HuiDetailRow('Permission', 'gloss.preview'),
331331
],
332332
);
333333

lib/config/field_docs.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,8 @@ const Map<String, HuiFieldDoc> huiFieldDocs = <String, HuiFieldDoc>{
604604
body:
605605
'A disabled emoji stays in the list (and in tab-complete data the '
606606
'service builds) but is filtered out of the replacer, so nothing '
607-
'substitutes until it is enabled again.',
607+
'substitutes until it is enabled again. A file that omits the key '
608+
'is enabled.',
608609
citation: 'EmojiReplacer.java:14-19',
609610
),
610611

lib/config/shipped_preview_json.dart

Lines changed: 55 additions & 55 deletions
Large diffs are not rendered by default.

lib/config/templates.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ const String kBlankHologramJson = r'''
131131
"type": "decoration",
132132
"icon": {
133133
"type": "text",
134-
"text": "&7Edit this with /holoui menu or the web editor."
134+
"text": "&7Edit this with /gloss menu or the web editor."
135135
}
136136
}
137137
},

lib/logic/bubble_lines.dart

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@
55
/// style.wordWrapChars())`: strip `§` colour pairs (only `§`, never `&` —
66
/// `stripColors`), soft-wrap through VolmLib's `Form.wrapWords`, split on
77
/// newlines and drop blank lines. [glossFormWrapWords] is a literal port of
8-
/// `Form.wrapPrefixed(s, "", len, null, true, " ")` as that code actually
9-
/// reads — including its one divergence from the commons-lang original: the
10-
/// FIRST space found in a window is recorded window-relative (no `+ offset`),
11-
/// so a window whose only space sits before the current absolute offset takes
12-
/// the hard-cut branch. The Java behavior is the contract; the quirk is
13-
/// ported, not fixed.
8+
/// `Form.wrap(s, len, null, true, " ")` as that code actually reads: every
9+
/// space found in a window is recorded at its absolute index (`start +
10+
/// offset`), so wrapping always breaks at the last space that fits the
11+
/// window and only hard-cuts a word longer than the window.
1412
library;
1513

1614
/// `BubbleLines.split`.
@@ -63,9 +61,7 @@ String glossFormWrapWords(String s, int len) {
6361
offset += matches.current.end;
6462
continue;
6563
}
66-
// The literal port: window-relative, no `+ offset` — see the library
67-
// comment.
68-
spaceToWrapAt = matches.current.start;
64+
spaceToWrapAt = matches.current.start + offset;
6965
}
7066

7167
if (inputLength - offset <= len) break;

lib/logic/preview_doc_validation.dart

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
library;
1515

1616
import 'preview_expr.dart';
17-
import 'preview_sim.dart' show previewSimGroupVariables;
17+
import 'preview_sim.dart'
18+
show
19+
kLegacyPreviewLangPrefix,
20+
previewRenamedLangKey,
21+
previewSimGroupVariables;
1822
import 'preview_variant_resolver.dart' show previewMatchesGlob;
1923
import 'validation.dart';
2024
import '../model/preview_doc.dart';
@@ -175,6 +179,41 @@ void previewCollectVarRefs(PExpr expr, void Function(String name) visit) {
175179
}
176180
}
177181

182+
/// Visits every `PStr` literal anywhere in [expr], mirroring
183+
/// [previewCollectVarRefs]'s traversal — used to spot legacy
184+
/// `holoui.preview.*` lang keys passed to `lang()` or concatenated into text.
185+
void _previewCollectStrLiterals(PExpr expr, void Function(String value) visit) {
186+
switch (expr) {
187+
case PStr(value: final String value):
188+
visit(value);
189+
case PList(items: final List<PExpr> items):
190+
for (final PExpr item in items) {
191+
_previewCollectStrLiterals(item, visit);
192+
}
193+
case PUnary(operand: final PExpr operand):
194+
_previewCollectStrLiterals(operand, visit);
195+
case PBinary(left: final PExpr left, right: final PExpr right):
196+
_previewCollectStrLiterals(left, visit);
197+
_previewCollectStrLiterals(right, visit);
198+
case PTernary(
199+
condition: final PExpr condition,
200+
ifTrue: final PExpr ifTrue,
201+
ifFalse: final PExpr ifFalse,
202+
):
203+
_previewCollectStrLiterals(condition, visit);
204+
_previewCollectStrLiterals(ifTrue, visit);
205+
_previewCollectStrLiterals(ifFalse, visit);
206+
case PCall(args: final List<PExpr> args):
207+
for (final PExpr arg in args) {
208+
_previewCollectStrLiterals(arg, visit);
209+
}
210+
case PNum():
211+
case PVar():
212+
case PBool():
213+
break;
214+
}
215+
}
216+
178217
/// True when [expr] contains no `PVar` and no `PCall` anywhere in its tree —
179218
/// the editor's mirror of the plugin's `ExprEvaluator.isConstant`. A call is
180219
/// never folded even with constant arguments: folding runs against an empty
@@ -364,6 +403,17 @@ List<HuiIssue> validatePreviewDoc(
364403

365404
final Set<String> declaredVars = previewDeclaredVars(doc);
366405

406+
void warnLegacyLangKey(String value, String path) {
407+
if (!value.startsWith(kLegacyPreviewLangPrefix)) return;
408+
add(
409+
HuiSeverity.warning,
410+
path,
411+
'Lang key "$value" uses the legacy "$kLegacyPreviewLangPrefix" prefix, '
412+
'renamed in Gloss; imported docs are rewritten on import.',
413+
fix: 'Use "${previewRenamedLangKey(value)}".',
414+
);
415+
}
416+
367417
/// Parses [raw] (when it is an expression string) and checks every
368418
/// variable reference plus, when the whole expression is constant, that it
369419
/// evaluates cleanly (catching a constant division by zero, for instance).
@@ -391,6 +441,9 @@ List<HuiIssue> validatePreviewDoc(
391441
);
392442
if (problem != null) add(problem.severity, path, problem.message);
393443
});
444+
_previewCollectStrLiterals(expr, (String value) {
445+
warnLegacyLangKey(value, path);
446+
});
394447
if (previewIsConstantExpr(expr)) {
395448
try {
396449
evalPreviewExpr(expr, _previewEmptyScope);
@@ -405,6 +458,7 @@ List<HuiIssue> validatePreviewDoc(
405458
if (value is num || value is bool) return;
406459
final String path = '$pathPrefix.$key';
407460
if (value is String) {
461+
warnLegacyLangKey(value, path);
408462
if (!value.startsWith('#')) return;
409463
try {
410464
final PExpr expr = parsePreviewExpr(value);

lib/logic/preview_sim.dart

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,27 @@ class SimSlotItem {
166166
String toString() => 'SimSlotItem($slot, $material x$count)';
167167
}
168168

169+
/// The lang-key prefix HoloUI shipped before the Gloss merger renamed the
170+
/// preview keys to [kPreviewLangPrefix]. Gloss's importer rewrites these on
171+
/// import; the editor resolves them for display and warns in validation.
172+
const String kLegacyPreviewLangPrefix = 'holoui.preview.';
173+
174+
/// The current preview lang-key prefix, matching `GlossMessages.java`.
175+
const String kPreviewLangPrefix = 'gloss.preview.';
176+
177+
/// The `gloss.preview.*` twin of a legacy `holoui.preview.*` id, or null when
178+
/// [key] does not carry the legacy prefix.
179+
String? previewRenamedLangKey(String key) =>
180+
key.startsWith(kLegacyPreviewLangPrefix)
181+
? '$kPreviewLangPrefix${key.substring(kLegacyPreviewLangPrefix.length)}'
182+
: null;
183+
169184
/// The English message templates `lang()` renders against.
170185
///
171186
/// `web/assets/catalog/preview-lang-en.json` is generated from the plugin's
172-
/// `HoloMessages.java` by `tool/extract_preview_lang.dart`; only the templates'
173-
/// placeholder names and order matter, which is why the snapshot has to come
174-
/// from the Java constants rather than a locale file.
187+
/// `GlossMessages.java` by `tool/extract_preview_lang.dart`; only the
188+
/// templates' placeholder names and order matter, which is why the snapshot
189+
/// has to come from the Java constants rather than a locale file.
175190
class PreviewLangCatalog {
176191
const PreviewLangCatalog(this.messages);
177192

@@ -186,6 +201,16 @@ class PreviewLangCatalog {
186201

187202
String? template(String key) => messages[key];
188203

204+
/// [template] for [key], falling back to its `gloss.preview.*` twin when
205+
/// [key] still carries the legacy `holoui.preview.*` prefix — the same
206+
/// rename Gloss's importer applies to a whole document on import.
207+
String? _resolvedTemplate(String key) {
208+
final String? direct = messages[key];
209+
if (direct != null) return direct;
210+
final String? renamed = previewRenamedLangKey(key);
211+
return renamed == null ? null : messages[renamed];
212+
}
213+
189214
/// Decodes `{"messages":{"<id>":"<template>"}}`. Never throws: a missing or
190215
/// malformed snapshot degrades to [empty], and every key then renders as
191216
/// itself — which is also what the plugin does for an id its catalog does not
@@ -206,15 +231,16 @@ class PreviewLangCatalog {
206231
/// Renders [key] with [args] bound positionally onto the template's own
207232
/// placeholder names: argument 1 fills the first `{name}`, argument 2 the
208233
/// second, and so on. That is what lets a document write
209-
/// `lang("holoui.preview.state.smelting_item", item, percent)` and get
234+
/// `lang("gloss.preview.state.smelting_item", item, percent)` and get
210235
/// `Smelting Iron Ore 42%` out of `Smelting {item} {percent}%`.
211236
///
212-
/// An unknown id renders as itself, arguments past the last placeholder go
213-
/// unused, and a placeholder no argument reached stays literal — the lenient
214-
/// rendering `HoloLocalization.renderTemplate` performs, which is also the
215-
/// path the plugin's own golden snapshots were captured through.
237+
/// An unknown id renders as itself (a legacy `holoui.preview.*` id first
238+
/// tries its `gloss.preview.*` twin), arguments past the last placeholder
239+
/// go unused, and a placeholder no argument reached stays literal — the
240+
/// lenient rendering `GlossLocalization.renderTemplate` performs, which is
241+
/// also the path the plugin's own golden snapshots were captured through.
216242
String render(String key, List<Object?> args) {
217-
final String template = messages[key] ?? key;
243+
final String template = _resolvedTemplate(key) ?? key;
218244
if (args.isEmpty) return template;
219245
final List<String> names = orderedPlaceholders(template);
220246
final Map<String, String> bound = <String, String>{};

lib/logic/tablist_selection.dart

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
/// Mirror of the static half of Gloss `TablistService.java`: which format a
22
/// player's list name uses, and how tokens substitute into it.
33
///
4-
/// `chooseListName` (`TablistService.java:54-66`) resolves in this order:
5-
/// an operator takes `_op` when it exists; a non-blank primary group takes
6-
/// its own entry; `default` catches the rest (keeping the player's group
7-
/// name for `$group`); and with no `default` the literal `$player` fallback
8-
/// applies. `substituteTokens` (`TablistService.java:46-52`) then replaces
4+
/// `chooseListName` (`TablistService.java`) resolves in this order: an
5+
/// operator takes `_op` when it exists; a non-blank primary group takes its
6+
/// own entry, looked up trimmed and lowercased the way `TablistDoc.copyFormats`
7+
/// normalizes the keys; `default` catches the rest (keeping the player's
8+
/// group name for `$group`); and with no `default` the literal `$player`
9+
/// fallback applies. `substituteTokens` (`TablistService.java:46-52`) then replaces
910
/// `$player` and `$group`. A blank chosen template makes the plugin RESET
1011
/// the list name to vanilla rather than applying an empty one
1112
/// (`TablistService.applyListName`).
@@ -30,10 +31,11 @@ GlossTablistChoice glossTablistChooseListName(
3031
groupName: glossTablistOpGroupKey,
3132
);
3233
}
33-
if (primaryGroup != null &&
34-
primaryGroup.trim().isNotEmpty &&
35-
nameFormats.containsKey(primaryGroup)) {
36-
return (template: nameFormats[primaryGroup]!, groupName: primaryGroup);
34+
if (primaryGroup != null && primaryGroup.trim().isNotEmpty) {
35+
final String groupKey = primaryGroup.trim().toLowerCase();
36+
if (nameFormats.containsKey(groupKey)) {
37+
return (template: nameFormats[groupKey]!, groupName: primaryGroup);
38+
}
3739
}
3840
if (nameFormats.containsKey(glossTablistDefaultGroupKey)) {
3941
return (

lib/model/gloss_emoji.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ final class GlossEmojiDoc extends GlossDoc {
8585
String emoji;
8686

8787
/// A disabled emoji stays listed but is never substituted
88-
/// (`EmojiReplacer` constructor filters on `enabled`).
88+
/// (`EmojiReplacer` constructor filters on `enabled`). A file that omits
89+
/// the key is ENABLED — `EmojiDoc.java` defaults a null wrapper to TRUE in
90+
/// its compact constructor.
8991
bool enabled;
9092

9193
Map<String, dynamic> extras;
@@ -103,7 +105,7 @@ final class GlossEmojiDoc extends GlossDoc {
103105
revision: glossReadRevision(map),
104106
trigger: huiReadString(map, 'trigger'),
105107
emoji: huiReadString(map, 'emoji'),
106-
enabled: huiReadBool(map, 'enabled'),
108+
enabled: map['enabled'] == null ? true : huiReadBool(map, 'enabled'),
107109
extras: huiCollectExtras(map, _docKnown),
108110
absentKeys: <String>{
109111
if (map['revision'] == null) 'revision',
@@ -122,7 +124,7 @@ final class GlossEmojiDoc extends GlossDoc {
122124
if (!absentKeys.contains('trigger') || trigger.isNotEmpty)
123125
'trigger': trigger,
124126
if (!absentKeys.contains('emoji') || emoji.isNotEmpty) 'emoji': emoji,
125-
if (!absentKeys.contains('enabled') || enabled) 'enabled': enabled,
127+
if (!absentKeys.contains('enabled') || !enabled) 'enabled': enabled,
126128
};
127129
return huiMergeExtras(out, extras);
128130
}

test/bubble_lines_test.dart

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/// The BubbleLines port: `§`-only colour stripping, the VolmLib soft wrap as
2-
/// it actually behaves (quirk included), and the blank-line drop.
1+
/// The BubbleLines port: `§`-only colour stripping, the VolmLib soft wrap,
2+
/// and the blank-line drop.
33
library;
44

55
import 'package:gloss_editor/logic/bubble_lines.dart';
@@ -23,18 +23,21 @@ void main() {
2323
expect(glossFormWrapWords('', 32), '');
2424
});
2525

26-
test('wraps on spaces within the window — quirk included', () {
27-
// First window ("one two th") wraps at its LAST space, absolute. The
28-
// second window ("three four") holds a single space, which the shipped
29-
// VolmLib records window-relative (5) and then compares against the
30-
// absolute offset (8) — so the wrap point is "missed" and the window
31-
// hard-cuts. Java behavior is the contract; the port replays it.
26+
test('wraps at the actual space instead of hard-cutting mid-word', () {
3227
expect(
3328
glossFormWrapWords('one two three four', 9),
34-
'one two\nthree fou\nr',
29+
'one two\nthree\nfour',
3530
);
3631
});
3732

33+
test('breaks a single-space pair at the space', () {
34+
expect(glossFormWrapWords('hello world', 5), 'hello\nworld');
35+
});
36+
37+
test('breaks exact-width words at every space', () {
38+
expect(glossFormWrapWords('aaa bbb ccc', 3), 'aaa\nbbb\nccc');
39+
});
40+
3841
test('hard-cuts a word longer than the window (soft mode)', () {
3942
expect(glossFormWrapWords('abcdefghij', 4), 'abcd\nefgh\nij');
4043
});
@@ -46,11 +49,9 @@ void main() {
4649

4750
group('glossBubbleSplit', () {
4851
test('strips, wraps at the style width and drops blank lines', () {
49-
// "wonderful wor" + "ld" is the shipped wrap's own answer — the
50-
// single-space window quirk again (see the wrap test above).
5152
expect(
5253
glossBubbleSplit('§dhello there wonderful world', 13),
53-
<String>['hello there', 'wonderful wor', 'ld'],
54+
<String>['hello there', 'wonderful', 'world'],
5455
);
5556
});
5657

0 commit comments

Comments
 (0)