#include <iostream>
#include "cangjie/Utils/Unicode.h"
#include "UnicodeTables/WidthData.generated.inc"
namespace Cangjie::Unicode {
int LookupWidth(UTF32 cp, bool isCJK)
{
auto t1Offset = TABLES_0[(cp >> 13) & 0xFF];
auto t2Offset = TABLES_1[128 * t1Offset + ((cp >> 6) & 0x7F)];
auto packedWidths = TABLES_2[16 * t2Offset + ((cp >> 2) & 0xF)];
auto width = (packedWidths >> (2 * (cp & 0b11))) & 0b11;
return width == 3 ? isCJK ? 2 : 1 : width;
}
namespace {
enum class NextCharInfo {
DEFAULT,
LF,
COMBINING_LONG_SOLIDUS_OVERLAY,
TRAILING_LISU_TONE_LETTER,
VS15,
VS16
};
std::pair<size_t, NextCharInfo> WidthInStr(UTF32 c, bool isCJK, NextCharInfo nextInfo)
{
if ((isCJK && nextInfo == NextCharInfo::COMBINING_LONG_SOLIDUS_OVERLAY && (c == '<' || c == '=' || c == '>')) ||
(nextInfo == NextCharInfo::VS16 && StartsEmojiPresentationSeq(c))) {
return {2, NextCharInfo::DEFAULT};
}
if (c <= 0xa0) {
if (c == '\n') {
return {1, NextCharInfo::LF};
}
if (c == '\r' && nextInfo == NextCharInfo::LF) {
return {0, NextCharInfo::DEFAULT};
}
return {1, NextCharInfo::DEFAULT};
}
if (c >= 0xa4f8 && c <= 0xa4fb && nextInfo == NextCharInfo::TRAILING_LISU_TONE_LETTER) {
return {0, NextCharInfo::DEFAULT};
}
if (c == 0x0338) {
return {0, NextCharInfo::COMBINING_LONG_SOLIDUS_OVERLAY};
}
if (c >= 0xa4fc && c <= 0xa4fd) {
return {1, NextCharInfo::TRAILING_LISU_TONE_LETTER};
}
if (c == 0xfe0e) {
return {0, NextCharInfo::VS15};
}
if (c == 0xfe0f) {
return {0, NextCharInfo::VS16};
}
if (nextInfo == NextCharInfo::VS15 && !isCJK && StartsNonIdeographicTextPresentationSeq(c)) {
return {1, NextCharInfo::DEFAULT};
}
return {LookupWidth(c, isCJK), NextCharInfo::DEFAULT};
}
}
int StrWidth(StringRef s, bool isCJK)
{
auto str = s.ToUTF32();
auto res{0};
NextCharInfo st{NextCharInfo::DEFAULT};
for (auto it = str.crbegin(); it != str.crend(); ++it) {
UTF32 c = *it;
auto r = WidthInStr(c, isCJK, st);
res += static_cast<int>(r.first);
st = r.second;
}
return res;
}
int SingleCharWidth(UTF32 codepoint, bool isCJK)
{
if (codepoint < 0x7f) {
if (codepoint >= 0x20) {
return 1;
}
return 1;
}
if (codepoint >= 0xa0) {
return LookupWidth(codepoint, isCJK);
}
return 1;
}
constexpr static UTF32 TAB = 0x9;
constexpr static int TAB_LENGTH{4};
constexpr static int UNICODE_ESCAPE_WIDTH{8};
int DisplayWidth(StringRef s, bool isCJK)
{
auto str = s.ToUTF32();
auto res{0};
NextCharInfo st{NextCharInfo::DEFAULT};
for (auto it = str.crbegin(); it != str.crend(); ++it) {
UTF32 c = *it;
if (c == TAB) {
res += 4;
st = NextCharInfo::DEFAULT;
continue;
}
if (c <= 0x8 || (0xb <= c && c <= 0x1f) || c == 0x7f) {
res += UNICODE_ESCAPE_WIDTH;
st = NextCharInfo::DEFAULT;
continue;
}
auto r = WidthInStr(c, isCJK, st);
res += static_cast<int>(r.first);
st = r.second;
}
return res;
}
int DisplayWidth(UTF32 cp, bool isCJK)
{
if (cp <= 0x8 || (0xb <= cp && cp <= 0x1f) || cp == 0x7f) {
return UNICODE_ESCAPE_WIDTH;
}
if (cp == TAB) {
return TAB_LENGTH;
}
return SingleCharWidth(cp, isCJK);
}
}