diff --git a/sakura_core/CRegexKeyword.h b/sakura_core/CRegexKeyword.h index 43b060636e..5df7a71ffa 100644 --- a/sakura_core/CRegexKeyword.h +++ b/sakura_core/CRegexKeyword.h @@ -43,6 +43,8 @@ typedef struct RegexInfo_t { int nFlag; //色指定のチェックが入っているか? YES=RK_EMPTY, NO=RK_NOMATCH } REGEX_INFO; +class CStringRef; + //! 正規表現キーワードクラス /*! 正規表現キーワードを扱う。 diff --git a/sakura_core/EditInfo.h b/sakura_core/EditInfo.h index cb9332b99d..737ca196c8 100644 --- a/sakura_core/EditInfo.h +++ b/sakura_core/EditInfo.h @@ -28,9 +28,7 @@ #include "basis/SakuraBasis.h" #include "config/maxdata.h" -#include "charset/charcode.h" -#include "mem/CNativeW.h" -#include "types/CType.h" +#include "charset/charset.h" /*! * ファイル情報 diff --git a/sakura_core/charset/charcode.cpp b/sakura_core/charset/charcode.cpp index a56a8e25cf..b3238ac12f 100644 --- a/sakura_core/charset/charcode.cpp +++ b/sakura_core/charset/charcode.cpp @@ -49,7 +49,7 @@ namespace WCODE bool CalcHankakuByFont(wchar_t); //2007.08.30 kobake 追加 - bool IsHankaku(wchar_t wc) + bool IsHankaku(wchar_t wc, const CCharWidthCache& cache) { //※ほぼ未検証。ロジックが確定したらインライン化すると良い。 @@ -83,7 +83,7 @@ namespace WCODE } //$$ 仮。もう動的に計算しちゃえ。(初回のみ) - return CalcHankakuByFont(wc); + return cache.CalcHankakuByFont(wc); } //!制御文字であるかどうか diff --git a/sakura_core/charset/charcode.h b/sakura_core/charset/charcode.h index 8aabcdfb25..d76405c0a8 100644 --- a/sakura_core/charset/charcode.h +++ b/sakura_core/charset/charcode.h @@ -88,15 +88,6 @@ namespace WCODE return c>=front && c<=back; } - //!半角文字(縦長長方形)かどうか判定 - bool IsHankaku(wchar_t wc); - - //!全角文字(正方形)かどうか判定 - inline bool IsZenkaku(wchar_t wc) - { - return !IsHankaku(wc); - } - //!使用フォント番号を返す // (0:半角/1:全角) int GetFontNo(wchar_t c); @@ -262,9 +253,9 @@ class CCharWidthCache { void Clear(); [[nodiscard]] bool GetMultiFont() const { return m_bMultiFont; } - bool CalcHankakuByFont(wchar_t c) const; - int CalcPxWidthByFont(wchar_t c); - int CalcPxWidthByFont2(const wchar_t* pc2) const; + virtual bool CalcHankakuByFont(wchar_t c) const; + virtual int CalcPxWidthByFont(wchar_t c); + virtual int CalcPxWidthByFont2(const wchar_t* pc2) const; private: void DeleteLocalData(); @@ -290,4 +281,11 @@ void InitCharWidthCache( const LOGFONT &lf, ECharWidthFontMode fMode=CWM_FONT_ED void InitCharWidthCacheFromDC(const LOGFONT* lfs, ECharWidthFontMode fMode, HDC hdcOrg ); [[nodiscard]] CCharWidthCache& GetCharWidthCache(); +namespace WCODE { + //!半角文字(縦長長方形)かどうか判定 + bool IsHankaku(wchar_t wc, const CCharWidthCache& cache = GetCharWidthCache()); + //!全角文字(正方形)かどうか判定 + inline bool IsZenkaku(wchar_t wc) { return !IsHankaku(wc); } +} + #endif /* SAKURA_CHARCODE_4C34C669_0BAB_441A_9B1D_2B9AC1895380_H_ */ diff --git a/sakura_core/doc/CBlockComment.h b/sakura_core/doc/CBlockComment.h index abd595d798..886059f447 100644 --- a/sakura_core/doc/CBlockComment.h +++ b/sakura_core/doc/CBlockComment.h @@ -34,6 +34,8 @@ enum ECommentType{ #define BLOCKCOMMENT_NUM 2 #define BLOCKCOMMENT_BUFFERSIZE 16 +class CStringRef; + // 2005.11.10 Moca アクセス関数追加 class CBlockComment{ public: diff --git a/sakura_core/doc/CLineComment.h b/sakura_core/doc/CLineComment.h index 249f0a7d0a..718e0fc497 100644 --- a/sakura_core/doc/CLineComment.h +++ b/sakura_core/doc/CLineComment.h @@ -24,6 +24,8 @@ #define COMMENT_DELIMITER_NUM 3 #define COMMENT_DELIMITER_BUFFERSIZE 16 +class CStringRef; + /*! 行コメントデリミタを管理する @note CLineCommentは、共有メモリSTypeConfigに含まれるので、メンバ変数は常に実体を持っていなければならない。 diff --git a/sakura_core/env/CAppNodeManager.h b/sakura_core/env/CAppNodeManager.h index 0bcf2ba5b9..77d129155f 100644 --- a/sakura_core/env/CAppNodeManager.h +++ b/sakura_core/env/CAppNodeManager.h @@ -26,8 +26,9 @@ #define SAKURA_CAPPNODEMANAGER_CAE7A323_DEA3_47E4_91DE_C99A88C32683_H_ #pragma once -#include "util/design_template.h" +#include "basis/CMyString.h" #include "config/maxdata.h" +#include "util/design_template.h" class CAppNodeGroupHandle; diff --git a/sakura_core/mem/CNativeW.cpp b/sakura_core/mem/CNativeW.cpp index 764fa058fa..d11a72d7d3 100644 --- a/sakura_core/mem/CNativeW.cpp +++ b/sakura_core/mem/CNativeW.cpp @@ -348,7 +348,7 @@ CLogicInt CNativeW::GetSizeOfChar( const wchar_t* pData, int nDataLen, int nIdx } //! 指定した位置の文字が半角何個分かを返す -CKetaXInt CNativeW::GetKetaOfChar( const wchar_t* pData, int nDataLen, int nIdx ) +CKetaXInt CNativeW::GetKetaOfChar( const wchar_t* pData, int nDataLen, int nIdx, const CCharWidthCache& cache) { //文字列範囲外なら 0 if( nIdx >= nDataLen ) @@ -372,7 +372,7 @@ CKetaXInt CNativeW::GetKetaOfChar( const wchar_t* pData, int nDataLen, int nIdx } //半角文字なら 1 - if(WCODE::IsHankaku(pData[nIdx]) ) + if(WCODE::IsHankaku(pData[nIdx], cache)) return CKetaXInt(1); //全角文字なら 2 @@ -381,25 +381,26 @@ CKetaXInt CNativeW::GetKetaOfChar( const wchar_t* pData, int nDataLen, int nIdx } //! 指定した位置の文字の文字幅を返す -CHabaXInt CNativeW::GetHabaOfChar( const wchar_t* pData, int nDataLen, int nIdx ) +CHabaXInt CNativeW::GetHabaOfChar( const wchar_t* pData, int nDataLen, int nIdx, + CCharWidthCache& cache, bool bEnableExtEol ) { //文字列範囲外なら 0 if( nIdx >= nDataLen ){ return CHabaXInt(0); } // HACK:改行コードに対して1を返す - if( WCODE::IsLineDelimiter(pData[nIdx], GetDllShareData().m_Common.m_sEdit.m_bEnableExtEol) ){ + if( WCODE::IsLineDelimiter(pData[nIdx], bEnableExtEol) ){ return CHabaXInt(1); } // サロゲートチェック if(IsUTF16High(pData[nIdx]) && nIdx + 1 < nDataLen && IsUTF16Low(pData[nIdx + 1])){ - return CHabaXInt(WCODE::CalcPxWidthByFont2(pData + nIdx)); + return CHabaXInt(cache.CalcPxWidthByFont2(pData + nIdx)); }else if(IsUTF16Low(pData[nIdx]) && 0 < nIdx && IsUTF16High(pData[nIdx - 1])) { // サロゲートペア(下位) return CHabaXInt(0); // 不正位置 } - return CHabaXInt(WCODE::CalcPxWidthByFont(pData[nIdx])); + return CHabaXInt(cache.CalcPxWidthByFont(pData[nIdx])); } /* ポインタで示した文字の次にある文字の位置を返します */ diff --git a/sakura_core/mem/CNativeW.h b/sakura_core/mem/CNativeW.h index d8ef73d283..9dacbfc96d 100644 --- a/sakura_core/mem/CNativeW.h +++ b/sakura_core/mem/CNativeW.h @@ -28,7 +28,9 @@ #include "CNative.h" #include "basis/SakuraBasis.h" +#include "charset/charcode.h" #include "debug/Debug2.h" //assert +#include "env/DLLSHAREDATA.h" //! 文字列への参照を取得するインターフェース class IStringRef{ @@ -163,8 +165,12 @@ class CNativeW final : public CNative{ public: // -- -- staticインターフェース -- -- // static CLogicInt GetSizeOfChar( const wchar_t* pData, int nDataLen, int nIdx ); //!< 指定した位置の文字がwchar_t何個分かを返す - static CHabaXInt GetHabaOfChar( const wchar_t* pData, int nDataLen, int nIdx ); - static CKetaXInt GetKetaOfChar( const wchar_t* pData, int nDataLen, int nIdx ); //!< 指定した位置の文字が半角何個分かを返す + static CHabaXInt GetHabaOfChar( const wchar_t* pData, int nDataLen, int nIdx, + CCharWidthCache& cache = GetCharWidthCache(), + bool bEnableExtEol = GetDllShareData().m_Common.m_sEdit.m_bEnableExtEol ); + //! 指定した位置の文字が半角何個分かを返す + static CKetaXInt GetKetaOfChar( const wchar_t* pData, int nDataLen, int nIdx, + const CCharWidthCache& cache = GetCharWidthCache() ); static const wchar_t* GetCharNext( const wchar_t* pData, int nDataLen, const wchar_t* pDataCurrent ); //!< ポインタで示した文字の次にある文字の位置を返します static const wchar_t* GetCharPrev( const wchar_t* pData, int nDataLen, const wchar_t* pDataCurrent ); //!< ポインタで示した文字の直前にある文字の位置を返します diff --git a/sakura_core/recent/SShare_History.h b/sakura_core/recent/SShare_History.h index c623c085e5..ed8f6b25ad 100644 --- a/sakura_core/recent/SShare_History.h +++ b/sakura_core/recent/SShare_History.h @@ -25,6 +25,7 @@ #ifndef SAKURA_SSHARE_HISTORY_9F7E6200_FEE2_4CAC_A5D3_32EEC4130CFC_H_ #define SAKURA_SSHARE_HISTORY_9F7E6200_FEE2_4CAC_A5D3_32EEC4130CFC_H_ +#include "EditInfo.h" #include "config/maxdata.h" //共有メモリ内構造体 diff --git a/tests/unittests/test-cnative.cpp b/tests/unittests/test-cnative.cpp index 3468df26fb..4eb0bacd79 100644 --- a/tests/unittests/test-cnative.cpp +++ b/tests/unittests/test-cnative.cpp @@ -24,6 +24,7 @@ */ #include #include +#include "charset/charcode.h" #include "mem/CNativeW.h" #include "mem/CNativeA.h" @@ -741,3 +742,133 @@ TEST(CNativeW, globalOperatorAdd) CNativeW v4(L"後半"); EXPECT_STREQ(L"前半後半", (v3 + v4).GetStringPtr()); } + +/*! + * @brief GetSizeOfCharの仕様 + * @remark 指定した文字の符号単位数を返す。 + */ +TEST(CNativeW, GetSizeOfChar) +{ + // 基本多言語面の文字ならば1を返す。 + EXPECT_EQ(CNativeW::GetSizeOfChar(L"a", 1, 0), 1); + // 範囲外なら0を返す。 + EXPECT_EQ(CNativeW::GetSizeOfChar(L"", 0, 0), 0); + // 上位・下位サロゲートの組み合わせであれば2を返す。 + EXPECT_EQ(CNativeW::GetSizeOfChar(L"\xd83c\xdf38", 2, 0), 2); + // 指定位置が下位サロゲートならその他の文字と同様に1を返す。 + EXPECT_EQ(CNativeW::GetSizeOfChar(L"\xd83c\xdf38", 2, 1), 1); +} + +/*! + * @brief GetKetaOfCharの仕様 + * @remark 指定した文字の桁数を返す。 + */ +TEST(CNativeW, GetKetaOfChar) +{ + // 範囲外なら0を返す。 + EXPECT_EQ(CNativeW::GetKetaOfChar(L"", 0, 0), 0); + // 上位サロゲートなら2を返す。 + EXPECT_EQ(CNativeW::GetKetaOfChar(L"\xd83c\xdf38", 2, 0), 2); + // 上位サロゲートに続く下位サロゲートであれば0を返す。 + EXPECT_EQ(CNativeW::GetKetaOfChar(L"\xd83c\xdf38", 2, 1), 0); + // 下位サロゲートだけなら2を返す。 + EXPECT_EQ(CNativeW::GetKetaOfChar(L"\xdf38", 1, 0), 2); + + // サクラエディタでは Unicode で表現できない文字コードの破壊を防ぐため、 + // 不明バイトを下位サロゲートにマップして保持している。 + // この1バイト文字は半角として扱わなければ不自然なので、 + // 上位対を持たない下位サロゲート 0xdc00 ~ 0xdcff の範囲に限り、1を返すことになっている。 + // + // https://sourceforge.net/p/sakura-editor/patchunicode/57/ + // http://sakura-editor.sourceforge.net/cgi-bin/cyclamen/cyclamen.cgi?log=unicode&v=833 + EXPECT_EQ(CNativeW::GetKetaOfChar(L"\xdbff", 1, 0), 2); + for (wchar_t ch = 0xdc00; ch <= 0xdcff; ++ch) + EXPECT_EQ(CNativeW::GetKetaOfChar(&ch, 1, 0), 1); + EXPECT_EQ(CNativeW::GetKetaOfChar(L"\xdd00", 1, 0), 2); + + // 文字が半角なら1を返す。 + EXPECT_EQ(CNativeW::GetKetaOfChar(L"a", 1, 0), 1); + + // 文字が全角なら2を返す。 + class FakeCache : public CCharWidthCache { + public: + bool CalcHankakuByFont(wchar_t c) const override { return false; } + } cache; + EXPECT_EQ(CNativeW::GetKetaOfChar(L"あ", 1, 0, cache), 2); +} + +/*! + * @brief GetKetaOfCharの仕様 + * @remark 指定した文字のピクセル単位幅を返す。 + */ +TEST(CNativeW, GetHabaOfChar) +{ + // 範囲外なら0を返す。 + EXPECT_EQ((Int)CNativeW::GetHabaOfChar(L"", 0, 1, GetCharWidthCache(), false), 0); + + // 改行コードなら1を返す。 + EXPECT_EQ((Int)CNativeW::GetHabaOfChar(L"\r\n", 2, 0, GetCharWidthCache(), false), 1); + EXPECT_EQ((Int)CNativeW::GetHabaOfChar(L"\r\n", 2, 1, GetCharWidthCache(), false), 1); + + // CalcPxWidthByFont で計算した結果を返す。 + class FakeCache1 : public CCharWidthCache { + public: + int CalcPxWidthByFont(wchar_t ch) override { + if (ch == L'a') return 10000; + else if (ch == L'b') return 20000; + else return 0; + } + } cache1; + EXPECT_EQ((Int)CNativeW::GetHabaOfChar(L"ab", 2, 0, cache1, false), 10000); + EXPECT_EQ((Int)CNativeW::GetHabaOfChar(L"ab", 2, 1, cache1, false), 20000); + + // サロゲートペアの幅は CalcPxWidthByFont2 で計算する。 + // 指定された位置が下位サロゲートなら0を返す。 + class FakeCache2 : public CCharWidthCache { + public: + int CalcPxWidthByFont2(const wchar_t* pc2) const override { + return 20000; + } + } cache2; + EXPECT_EQ((Int)CNativeW::GetHabaOfChar(L"\xd83c\xdf38", 2, 0, cache2, false), 20000); + EXPECT_EQ((Int)CNativeW::GetHabaOfChar(L"\xd83c\xdf38", 2, 1, cache2, false), 0); + + // サロゲートペアが片方しかないときは CalcPxWidthByFont で計算している。 + class FakeCache3 : public CCharWidthCache { + public: + int CalcPxWidthByFont(wchar_t c) override { + return 10000; + } + } cache3; + EXPECT_EQ((Int)CNativeW::GetHabaOfChar(L"\xd83cあ", 2, 0, cache3, false), 10000); + EXPECT_EQ((Int)CNativeW::GetHabaOfChar(L"\xdf38あ", 2, 0, cache3, false), 10000); + EXPECT_EQ((Int)CNativeW::GetHabaOfChar(L"あ\xdf38", 2, 1, cache3, false), 10000); +} + +/*! + * @brief GetCharNextの仕様 + */ +TEST(CNativeW, GetCharNext) +{ + constexpr wchar_t* text = L"a\xd83c\xdf38"; + // 次の文字のアドレスを返す。 + EXPECT_EQ(CNativeW::GetCharNext(text, 3, text), text + 1); + // 上位サロゲートが渡された場合は下位サロゲートを飛ばす。 + EXPECT_EQ(CNativeW::GetCharNext(text, 3, text + 1), text + 3); + // ポインタを進めた結果が範囲外なら &pData[nDataLen] を返す。 + EXPECT_EQ(CNativeW::GetCharNext(text, 3, text + 3), text + 3); +} + +/*! + * @brief GetCharPrevの仕様 + */ +TEST(CNativeW, GetCharPrev) +{ + constexpr wchar_t* text = L"a\xd83c\xdf38" L"d"; + // 前の文字のアドレスを返す。 + EXPECT_EQ(CNativeW::GetCharPrev(text, 4, text + 1), text); + // 前の文字が下位サロゲートだった場合は下位サロゲートを飛ばす。 + EXPECT_EQ(CNativeW::GetCharPrev(text, 4, text + 3), text + 1); + // ポインタを戻した結果が範囲外なら pData を返す。 + EXPECT_EQ(CNativeW::GetCharPrev(text, 4, text), text); +}