Source code for batools.special_chars

# Released under the MIT License. See LICENSE for details.
#
"""Generate the engine's special-character table from the shared enum.

:class:`bacommon.text.SpecialChar` is the one list of the custom
characters the engine can draw. This emits it as
``base/generated/special_chars.h`` (the C++ enum plus each character's
UTF-8 form), so the engine cannot disagree with Python code and
servers that use the enum directly.
"""

from __future__ import annotations  # Docs-generation hack.

import os

from bacommon.text import SpecialChar


[docs] def cpp_name(char: SpecialChar) -> str: """``LEFT_ARROW`` -> ``kLeftArrow``.""" return 'k' + ''.join(part.capitalize() for part in char.name.split('_'))
def _cpp_string(value: str) -> str: """A string as a C++ literal of escaped UTF-8 bytes.""" return '"' + ''.join(f'\\x{b:02x}' for b in value.encode()) + '"'
[docs] def generate_cpp() -> str: """Return the header's contents.""" guard = 'BALLISTICA_BASE_GENERATED_SPECIAL_CHARS_H_' out = [ '// Released under the MIT License. See LICENSE for details.', '//', '// AUTO-GENERATED by `tools/pcommand gen_special_chars_cpp`.', '// DO NOT EDIT BY HAND.', '//', '// Generated from SpecialChar in tools/bacommon/text.py.', '', f'#ifndef {guard}', f'#define {guard}', '', '#include <cstdint>', '', 'namespace ballistica::base {', '', '/// Special characters the engine can display.', 'enum class SpecialChar : uint16_t {', ] out += [f' {cpp_name(char)},' for char in SpecialChar] out += [ ' kLast // Sentinel.', '};', '', '/// The UTF-8 form of each special character, indexed by its', '/// SpecialChar value.', 'inline constexpr const char* kSpecialCharStrings[] = {', ] out += [ f' {_cpp_string(char.value)}, // {cpp_name(char)}' for char in SpecialChar ] end = max(ord(char.value) for char in SpecialChar) + 1 pairs = [ (char, full) for char in SpecialChar if (full := char.full_size) is not None and full is not char ] out += [ '};', '', '/// One past the highest special character codepoint (the glyph', '/// table must cover through here).', f'inline constexpr uint32_t kSpecialCharCodepointEnd = 0x{end:04X};', '', '/// Two-form icons: (superscript, full-size) codepoints. The', "/// superscript's glyph is derived from its full-size twin's (see", '/// SpecialChar.full_size).', 'struct SpecialCharSuperscriptPair {', ' uint32_t superscript;', ' uint32_t full_size;', '};', 'inline constexpr SpecialCharSuperscriptPair' ' kSpecialCharSuperscriptPairs[] = {', ] out += [ f' {{0x{ord(char.value):04X}, 0x{ord(full.value):04X}}},' f' // {cpp_name(char)}' for char, full in pairs ] out += [ '};', '', '} // namespace ballistica::base', '', f'#endif // {guard}', '', ] return '\n'.join(out)
[docs] def write_cpp(out_path: str) -> None: """Write the header, leaving unchanged bytes alone.""" contents = generate_cpp() os.makedirs(os.path.dirname(out_path), exist_ok=True) if os.path.exists(out_path): with open(out_path, encoding='utf-8') as infile: if infile.read() == contents: # Leave the bytes alone (no spurious rebuilds # downstream) but bump the mtime so make sees the # target as newer than its sources. os.utime(out_path, None) return with open(out_path, 'w', encoding='utf-8') as outfile: outfile.write(contents)
# Docs-generation hack; import some stuff that we likely only forward-declared # in our actual source code so that docs tools can find it. from typing import (Coroutine, Any, Literal, Callable, Generator, Awaitable, Sequence, Self, TypeIs) import asyncio from concurrent.futures import Future from pathlib import Path from enum import Enum