Skip to content

Commit 858018a

Browse files
fix: properly encode unicode codepoints outside BMP (#497)
Co-authored-by: edayot <pro.e.dayot@gmail.com>
1 parent d7522ae commit 858018a

4 files changed

Lines changed: 108 additions & 0 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
data_pack:
3+
load: .
4+
5+
output: build
6+
7+
require:
8+
- bolt
9+
10+
pipeline:
11+
- mecha
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
say "🗡 "
2+
tellraw @a {text: "🗡"}
3+
4+
5+
recipe example:sharp_diamond {
6+
"type":"minecraft:crafting_shapeless",
7+
"ingredients":[["minecraft:diamond"]],
8+
"result":{
9+
"id":"minecraft:diamond",
10+
"components":{"minecraft:item_name":"🗡"}
11+
}
12+
}
13+
TEAM_1 = "duels.team_1"
14+
TEAM_2 = "duels.team_2"
15+
16+
execute function ./load:
17+
team add TEAM_1 "Duels Team 1"
18+
team add TEAM_2 "Duels Team 2"
19+
20+
for team in [TEAM_1, TEAM_2]:
21+
team modify team friendlyFire false
22+
team modify team prefix "🗡 "
23+
team modify team seeFriendlyInvisibles false
24+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Lectern snapshot
2+
3+
## Data pack
4+
5+
`@data_pack pack.mcmeta`
6+
7+
```json
8+
{
9+
"pack": {
10+
"min_format": [
11+
94,
12+
1
13+
],
14+
"max_format": [
15+
94,
16+
1
17+
],
18+
"description": ""
19+
}
20+
}
21+
```
22+
23+
### example
24+
25+
`@function example:say`
26+
27+
```mcfunction
28+
say "🗡 "
29+
tellraw @a {text: "\U0001f5e1"}
30+
function example:load
31+
```
32+
33+
`@function example:load`
34+
35+
```mcfunction
36+
team add duels.team_1 "Duels Team 1"
37+
team add duels.team_2 "Duels Team 2"
38+
team modify duels.team_1 friendlyFire false
39+
team modify duels.team_1 prefix "\U0001f5e1 "
40+
team modify duels.team_1 seeFriendlyInvisibles false
41+
team modify duels.team_2 friendlyFire false
42+
team modify duels.team_2 prefix "\U0001f5e1 "
43+
team modify duels.team_2 seeFriendlyInvisibles false
44+
```
45+
46+
`@recipe example:sharp_diamond`
47+
48+
```json
49+
{
50+
"type": "minecraft:crafting_shapeless",
51+
"ingredients": [
52+
[
53+
"minecraft:diamond"
54+
]
55+
],
56+
"result": {
57+
"id": "minecraft:diamond",
58+
"components": {
59+
"minecraft:item_name": "\ud83d\udde1"
60+
}
61+
}
62+
}
63+
```

packages/mecha/src/mecha/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,18 @@ def handle_substitution(self, token: Token, match: "re.Match[str]") -> str:
130130
return chr(int(unicode_hex, 16))
131131
return super().handle_substitution(token, match)
132132

133+
def encode_non_bmp_char(self, codepoint: int) -> str:
134+
return f"\\U{codepoint:08x}"
135+
133136
def handle_quoting(self, value: str) -> str:
134137
value = super().handle_quoting(value)
135138

136139
def escape_char(char: str) -> str:
137140
codepoint = ord(char)
138141
if codepoint < 128:
139142
return char
143+
if codepoint > 65535:
144+
return self.encode_non_bmp_char(codepoint)
140145
return f"\\u{codepoint:04x}"
141146

142147
return "".join(escape_char(c) for c in value)
@@ -156,6 +161,11 @@ class JsonQuoteHelper(QuoteHelperWithUnicode):
156161
}
157162
)
158163

164+
def encode_non_bmp_char(self, codepoint: int) -> str:
165+
low_surrogate = 0xD800 + ((codepoint - 0x10000) >> 10)
166+
high_surrogate = 0xDC00 + (codepoint & 0x3FF)
167+
return f"\\u{low_surrogate:04x}\\u{high_surrogate:04x}"
168+
159169

160170
@dataclass
161171
class NbtQuoteHelper(QuoteHelperWithUnicode):

0 commit comments

Comments
 (0)