diff --git a/common/web/types/src/ldml-keyboard/pattern-parser.ts b/common/web/types/src/ldml-keyboard/pattern-parser.ts index 3e56096ec6..297de9d0b2 100644 --- a/common/web/types/src/ldml-keyboard/pattern-parser.ts +++ b/common/web/types/src/ldml-keyboard/pattern-parser.ts @@ -81,7 +81,12 @@ export class MarkerParser { /** * Pattern for matching a marker reference, OR the special marker \m{.} */ - public static readonly REFERENCE = /\\m{([0-9A-Za-z_]{1,32}|\.)}/g; + public static readonly REFERENCE = /(? { // indirectly tests REFERENCE it('should match reference strings', () => { const cases: string[][] = [ - ['\\m{acute}', 'acute'], + ['\\m{acute} but not \\\\m{chronic}', 'acute'], // second marker is escaped ['\\m{acute}≈\\m{acute}', 'acute acute'], // not deduped ['\\m{grave}≠\\m{acute}', 'grave acute'], [MarkerParser.ANY_MARKER, MarkerParser.ANY_MARKER_ID], @@ -55,6 +55,19 @@ describe('Test of Pattern Parsers', () => { assert.deepEqual(MarkerParser.allReferences(str), [], `expected no markers: ${str}`); } }); + it('should match broken reference strings', () => { + const cases: string[][] = [ + // hyphenated marker id - illegal + ['\\m{chronic} \\m{a-cute} \\\\m{a-choo}', 'a-cute'], // \\m{a-choo} is literal + // marker through end of line + ['\\m{chronic} \\m{oopsIforGot to terminate it', 'oopsIforGot to terminate it'], + // marker terminated by other valid marker + ['\\m{chronic} \\m{what \\m{does} \\m{this button do?', 'what ', 'this button do?'], + ]; + for (const [str, ...reflist] of cases) { + assert.sameDeepMembers(MarkerParser.allBrokenReferences(str), reflist, `for ${str}`); + } + }); it('should be able to emit sentinel values', () => { assert.equal(MarkerParser.markerOutput(295), '\uFFFF\u0008\u0127', 'Wrong sentinel value emitted'); assert.equal(MarkerParser.markerOutput(MarkerParser.ANY_MARKER_INDEX), '\uFFFF\u0008\uD7FF', 'Wrong sentinel value emitted for ANY_MARKER_INDEX');