From af775c5b2274a4cb0b465b4f6fae531a68db8890 Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Mon, 2 Dec 2024 20:42:42 +0000 Subject: [PATCH] Use DECRQM 2027 to check grapheme clusters state (thanks to DHowett) --- far/changelog | 5 ++++ far/char_width.cpp | 17 +++++++++-- far/console.cpp | 71 ++++++++++++++++++++++++++++++++++++---------- far/console.hpp | 4 +++ far/vbuild.m4 | 2 +- 5 files changed, 81 insertions(+), 18 deletions(-) diff --git a/far/changelog b/far/changelog index 48e538628c..9d16993c5e 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,8 @@ +-------------------------------------------------------------------------------- +drkns 2024-12-02 20:39:53+00:00 - build 6398 + +1. Use DECRQM 2027 to check grapheme clusters state (thanks to DHowett). + -------------------------------------------------------------------------------- drkns 2024-12-01 23:24:48+00:00 - build 6397 diff --git a/far/char_width.cpp b/far/char_width.cpp index 52011215da..6b0e0006ed 100644 --- a/far/char_width.cpp +++ b/far/char_width.cpp @@ -39,6 +39,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Internal: #include "console.hpp" #include "locale.hpp" +#include "log.hpp" // Platform: @@ -555,7 +556,19 @@ namespace char_width bool is_grapheme_clusters_on() { - static const auto Result = console.GetWidthPreciseExpensive(L"à"sv); - return Result == 1; + static const auto Result = [] + { + if (const auto IsOn = console.is_grapheme_clusters_on(); IsOn) + { + LOGDEBUG(L"Grapheme clusters (VT): {}"sv, *IsOn); + return *IsOn; + } + + const auto IsOn = console.GetWidthPreciseExpensive(L"à"sv) == 1; + LOGDEBUG(L"Grapheme clusters (heuristics): {}"sv, IsOn); + return IsOn; + }(); + + return Result; } } diff --git a/far/console.cpp b/far/console.cpp index 30c7aabbac..e95d369788 100644 --- a/far/console.cpp +++ b/far/console.cpp @@ -631,15 +631,15 @@ namespace console_detail std::optional FirstTokenPrefixPos, - FirstTokenSuffixPos, - SecondTokenPrefixPos, - SecondTokenSuffixPos; + FirstTokenSuffixPos; const auto TokenPrefix = CSI "?"sv, TokenSuffix = L"c"sv; - while (!SecondTokenSuffixPos) + size_t DA_ResponseSize{}; + + for (;;) { wchar_t ResponseBuffer[8192]; size_t ResponseSize; @@ -655,21 +655,19 @@ namespace console_detail if (FirstTokenPrefixPos && !FirstTokenSuffixPos) if (const auto Pos = Response.find(TokenSuffix, *FirstTokenPrefixPos + TokenPrefix.size()); Pos != Response.npos) + { FirstTokenSuffixPos = Pos; + DA_ResponseSize = Pos + TokenSuffix.size(); + } - if (FirstTokenSuffixPos && !SecondTokenPrefixPos) - if (const auto Pos = Response.find(TokenPrefix, *FirstTokenSuffixPos + TokenSuffix.size()); Pos != Response.npos) - SecondTokenPrefixPos = Pos; - - if (SecondTokenPrefixPos && !SecondTokenSuffixPos) - if (const auto Pos = Response.find(TokenSuffix, *SecondTokenPrefixPos + TokenPrefix.size()); Pos != Response.npos) - SecondTokenSuffixPos = Pos; + if (DA_ResponseSize && Response.size() >= DA_ResponseSize * 2) + { + if (const auto DA_Response = string_view(Response).substr(*FirstTokenPrefixPos, DA_ResponseSize); Response.ends_with(DA_Response)) + break; + } } - Response.resize(*SecondTokenPrefixPos); - Response.erase(0, *FirstTokenSuffixPos + TokenSuffix.size()); - - return Response; + return Response.substr(DA_ResponseSize, Response.size() - DA_ResponseSize * 2); } console::console(): @@ -3100,6 +3098,49 @@ namespace console_detail send_vt_command(far::format(OSC("9001;CmdNotFound;{}"), Command)); } + std::optional console::is_grapheme_clusters_on() const + { + try + { +#define DECRQM_REQUEST "?2027" + + const auto ResponseData = query_vt(CSI DECRQM_REQUEST "$p"sv); + if (ResponseData.empty()) + { + LOGWARNING(L"DECRQM 2027 query is not supported"sv); + return {}; + } + + const auto + Prefix = CSI DECRQM_REQUEST ";"sv, + Suffix = L"$y"sv; + +#undef DECRQM_REQUEST + + const auto give_up = [&] + { + throw far_exception(far::format(L"Incorrect response: {}"sv, ResponseData), false); + }; + + if (ResponseData.size() != Prefix.size() + 1 + Suffix.size() || !ResponseData.starts_with(Prefix) || !ResponseData.ends_with(Suffix)) + give_up(); + + switch (ResponseData[Prefix.size()]) + { + case L'3': return true; + case L'4': return false; + default: + give_up(); + std::unreachable(); + } + } + catch (far_exception const& e) + { + LOGERROR(L"{}"sv, e); + return {}; + } + } + bool console::GetCursorRealPosition(point& Position) const { CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo; diff --git a/far/console.hpp b/far/console.hpp index 76efff8a76..1482e04027 100644 --- a/far/console.hpp +++ b/far/console.hpp @@ -191,6 +191,7 @@ namespace console_detail [[nodiscard]] size_t GetWidthPreciseExpensive(string_view Str); + [[nodiscard]] size_t GetWidthPreciseExpensive(char32_t Codepoint); void ClearWideCache(); @@ -213,6 +214,9 @@ namespace console_detail void command_finished(int ExitCode) const; void command_not_found(string_view Command) const; + [[nodiscard]] + std::optional is_grapheme_clusters_on() const; + [[nodiscard]] short GetDelta() const; diff --git a/far/vbuild.m4 b/far/vbuild.m4 index 495c1df87c..602f247530 100644 --- a/far/vbuild.m4 +++ b/far/vbuild.m4 @@ -1 +1 @@ -6397 +6398