Преглед на файлове

feat(xtintin): 将 xtintin 中有关颜色代码处理相关的函数进行拆分成单独的 raw.tin

dzp преди 10 месеца
родител
ревизия
c57b238466
променени са 3 файла, в които са добавени 274 реда и са изтрити 72 реда
  1. 1 0
      plugins/lib/xtintin/__init__.tin
  2. 273 0
      plugins/lib/xtintin/raw.tin
  3. 0 72
      plugins/lib/xtintin/string.tin

+ 1 - 0
plugins/lib/xtintin/__init__.tin

@@ -12,6 +12,7 @@ load-file plugins/lib/xtintin/doc.tin;
 load-file plugins/lib/xtintin/number.tin;
 load-file plugins/lib/xtintin/bool.tin;
 load-file plugins/lib/xtintin/string.tin;
+load-file plugins/lib/xtintin/raw.tin;
 load-file plugins/lib/xtintin/algo.tin;
 load-file plugins/lib/xtintin/list.tin;
 load-file plugins/lib/xtintin/table.tin;

+ 273 - 0
plugins/lib/xtintin/raw.tin

@@ -0,0 +1,273 @@
+#nop vim: set filetype=tt:;
+
+/*
+本文件属于 PaoTin++ 的一部分。
+PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp <danzipao@gmail.com>) 享有并保留一切法律权利
+你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。
+*/
+
+#nop 本文件是 xtintin 的一部分,实现了一些颜色字符串处理函数;
+
+///=== {
+///// 颜色字符串处理函数:
+//
+// #@ str.Plain <字符串>
+//    去除字符串中的颜色代码和 ANSI 控制字符。
+//
+// #@ str.Color <字符串>
+//    仅保留字符串中的颜色代码,去掉其余成分,方便比较颜色。
+// };
+
+#func {str.Plain}   {#format result {%P} {%0}};
+
+#func {str.Color} {
+    #local str      {};
+    #local state    {START};
+    #local ch       {};
+
+    #parse {%0} {ch} {
+        #switch {"$state/$ch"} {
+            #case {"START/\e"}  {#local state {ESCAPE}};
+            #case {"START/%*"}  {#0};
+            #case {"ESCAPE/["}  {#local state {INNER}};
+            #case {"ESCAPE/%*"} {#local state {OUTER}};
+            #case {"INNER/m"}   {#cat str {;}; #local state {OUTER}};
+            #case {"INNER/%*"}  {#cat str {$ch}};
+            #case {"OUTER/\e"}  {#local state {ESCAPE}};
+        };
+    };
+
+    #replace {str} {;$} {};
+
+    #return {$str};
+};
+
+///=== {
+// #@ util.ColorBar <字符串> <颜色1> <权重1> <颜色2> <权重2> [...]
+//    将字符串按照颜色及其对应的权重占比,渲染成彩色字符串。注意颜色参数须按顺序排列。
+// };
+#func {util.ColorBar} {
+    #local str {%1};
+
+    #local args {};
+    #list args create {%0};
+    #list args delete {1} {1};
+
+    #local parts {};
+    #list parts create {};
+    #local count {0};
+    #local sum {0};
+    #while { $count < &args[] } {
+        #local color    {$args[@math.Eval{$count + 1}]};
+        #local weight   {@defaultNum{$args[@math.Eval{$count + 2}];0}};
+        #list parts {add} {{
+            {color}{$color}
+            {weight}{$weight}
+        }};
+        #math count {$count + 2};
+        #math sum {$sum + $weight};
+    };
+
+    #local elem {};
+    #local len {@str.Len{$str}};
+    #local leftLen {0};
+    #local leftWeight {0};
+    #local colorStr {};
+    #foreach {$parts[%*]} {elem} {
+        #local elemLen {@math.Eval{($elem[weight] + $leftWeight) * $len / $sum - $leftLen}};
+        #local text {@str.Sub{{$str};$leftLen;$elemLen}};
+        #cat colorStr {$elem[color]$text<299>};
+        #math leftLen {$leftLen + $elemLen};
+        #math leftWeight {$leftWeight + $elem[weight]};
+    };
+
+    #return {$colorStr};
+};
+
+///=== {
+// #@ raw.ParseFragments <颜色字符串> [<之前的颜色>]
+//    解析包含颜色代码的字符串,将其拆分成一系列的文本片段,每个片段包含一段文本和对应的颜色属性。
+//    本函数实际上有两个返回值,以 table 形式打包返回,其中包含以下内容:
+//      - color: 字符串解析完毕之后,最后的颜色状态(可供之后调用),参见下面的 color 属性。
+//      - fragments: 一个表格列表,其中每个元素是一个表格,包含以下两个字段:
+//        - text: 文本片段
+//        - color:
+//          - fg: 前景色,可能是一个一位数(16色系统)或一个多位数(256色系统)或三个数(RGB颜色)
+//          - bg: 背景色,同上
+//          - attrs: 文本样式属性,是一个字符串集合(sset),包含以下可能的值:
+//              - bold
+//              - italic
+//              - underline
+//              - blink
+//              - reverse
+//              - dim
+//              - f256      表示前景色是 256 色系统
+//              - b256      表示背景色是 256 色系统
+//              - ftrue     表示前景色是真彩色
+//              - btrue     表示背景色是真彩色
+// };
+#func {raw.ParseFragments} {
+    #local str      {%1};
+    #local color    {%2};
+
+    #local fragments    {};
+    #local fragment     {};
+    #local tokens       {};
+    #local mode         {TEXT};
+
+    #while {1} {
+        #if { &tokens[] == 0 } {
+            raw.ParseFragments.nextToken;
+            #if { "$str" === "" } {
+                #break;
+            };
+
+            #if { "$str[text]" !== "" } {
+                #local fragment[text] {$str[text]};
+                #local fragment[color] {$color};
+                #list fragments add {{$fragment}};
+                #local fragment {};
+            };
+            #elseif { "$str[color]" !== "" } {
+                #replace str[color] {^\e[} {};
+                #replace str[color] {m$}   {};
+                #if { "$str[color]" !== "" } {
+                    #list tokens add {$str[color]};
+                    #local mode {SGR};
+                };
+            };
+
+            #local str {$str[rest]};
+            #continue;
+        };
+
+        #local token {$tokens[1]};
+        #list tokens delete 1;
+
+        #switch {"$mode/$token"} {
+            #case {"SGR/0"}             {#local color[fg] {0}; #local color[bg] {0}; #local color[attrs] {}};
+            #case {"SGR/1"}             {#local color[attrs] {@sset.Add{{$color[attrs]};bold}}};
+            #case {"SGR/2"}             {#local color[attrs] {@sset.Add{{$color[attrs]};dim}}};
+            #case {"SGR/3"}             {#local color[attrs] {@sset.Add{{$color[attrs]};italic}}};
+            #case {"SGR/4"}             {#local color[attrs] {@sset.Add{{$color[attrs]};underline}}};
+            #case {"SGR/5"}             {#local color[attrs] {@sset.Add{{$color[attrs]};blink}}};
+            #case {"SGR/7"}             {#local color[attrs] {@sset.Add{{$color[attrs]};reverse}}};
+            #case {"SGR/3{[0-7]}"}      {#local color[fg] {$token}};
+            #case {"SGR/4{[0-7]}"}      {#local color[bg] {$token}};
+            #case {"SGR/9{[0-7]}"}      {#local color[fg] {$token}};
+            #case {"SGR/10{[0-7]}"}     {#local color[bg] {$token}};
+            #case {"SGR/38"}            {#local mode {SGR38}};
+            #case {"SGR38/2"}           {#local mode {SGR382}};
+            #case {"SGR382/%+1..d"}     {#local mode {SGR}; #local color[fg] {$token}; #local color[attrs] {@sset.Add{{$color[attrs]};f256}}};
+            #case {"SGR38/5"}           {#local mode {SGR385}; #local color[fg] {}; #local color[attrs] {@sset.Add{{$color[attrs]};ftrue}}};
+            #case {"SGR385/%+1..d"}     {#local mode {SGR3851}; #local color[fg] {@slist.Append{{$color[fg]};$token}}};
+            #case {"SGR3851/%+1..d"}    {#local mode {SGR3852}; #local color[fg] {@slist.Append{{$color[fg]};$token}}};
+            #case {"SGR3852/%+1..d"}    {#local mode {SGR};     #local color[fg] {@slist.Append{{$color[fg]};$token}}};
+            #case {"SGR/48"}            {#local mode {SGR48}};
+            #case {"SGR48/2"}           {#local mode {SGR482}};
+            #case {"SGR482/%+1..d"}     {#local mode {SGR}; #local color[bg] {$token}; #local color[attrs] {@sset.Add{{$color[attrs]};b256}}};
+            #case {"SGR48/5"}           {#local mode {SGR485}; #local color[bg] {}; #local color[attrs] {@sset.Add{{$color[attrs]};btrue}}};
+            #case {"SGR485/%+1..d"}     {#local mode {SGR4851}; #local color[bg] {@slist.Append{{$color[bg]};$token}}};
+            #case {"SGR4851/%+1..d"}    {#local mode {SGR4852}; #local color[bg] {@slist.Append{{$color[bg]};$token}}};
+            #case {"SGR4852/%+1..d"}    {#local mode {SGR};     #local color[bg] {@slist.Append{{$color[bg]};$token}}};
+        };
+    };
+
+    #if { "$str[text]" !== "" } {
+        #local fragment[text] {$str[text]};
+        #local fragment[color] {$color};
+        #list fragments add {{$fragment}};
+    };
+
+    #return {
+        {fragments} {$fragments}
+        {color}     {$color}
+    };
+};
+
+#alias {raw.ParseFragments.nextToken} {
+    #if { "$str" === "" } {
+        #return;
+    };
+
+    #replace str {^{[^\e]+}%*$} {
+        {text}  {&1}
+        {rest}  {&2}
+    };
+
+    #var str {$str};
+
+    #if { "$str[text]" !== "" } {
+        #return {$str};
+    };
+
+    #replace str {^%+1c%*$} {
+        {color} {&1}
+        {rest}  {&2}
+    };
+
+    #var str {$str};
+
+    #if { "$str[text]" !== "" } {
+        #return {$str};
+    };
+
+    #replace str {^\e%*} {
+        {esc}   {&1}
+        {rest}  {&2}
+    };
+
+    #var str {$str};
+};
+
+///=== {
+// #@ raw.EraseInvisible <颜色字符串> [<是否压缩>]
+//    解析包含颜色代码的字符串,将其拆分成多个片段,并擦掉其中前景色与背景色相同的部分。
+//    如果同时指定了第二个参数为真,则将颜色相同的连续多个部分进行合并。
+//    最终返回值格式与 raw.ParseFragments 相同。
+// };
+#func {raw.EraseInvisible} {
+    #local str {%1};
+    #local zip {%2};
+
+    #local color {};
+    #local output {};
+
+    #local str {@raw.ParseFragments{{$str}}};
+    #foreach {*str[fragments][]} {fragment} {
+        #local fragment {$str[fragments][$fragment]};
+
+        #if {  ! @sset.Contains{{$fragment[color][attrs]};f256}
+            && ! @sset.Contains{{$fragment[color][attrs]};b256}
+            && ! @sset.Contains{{$fragment[color][attrs]};ftrue}
+            && ! @sset.Contains{{$fragment[color][attrs]};btrue}
+            && $fragment[color][fg] + 10 == $fragment[color][bg] } {
+            #continue;
+        };
+
+        #if {  @sset.Contains{{$fragment[color][attrs]};f256}
+            && @sset.Contains{{$fragment[color][attrs]};b256}
+            && "$fragment[color][fg]" === "$fragment[color][bg]" } {
+            #continue;
+        };
+
+        #if {  @sset.Contains{{$fragment[color][attrs]};ftrue}
+            && @sset.Contains{{$fragment[color][attrs]};btrue}
+            && "$fragment[color][fg]" === "$fragment[color][bg]" } {
+            #continue;
+        };
+
+        #if { @isTrue{$zip} && "$color" != "" && "$fragment[color]" === "$color" } {
+            #cat output[-1][text] {$fragment[text]};
+        };
+        #else {
+            #list output add {{$fragment}};
+            #local color {$fragment[color]};
+        };
+    };
+
+    #return {
+        {fragments} {$output}
+        {color}     {$color}
+    };
+};

+ 0 - 72
plugins/lib/xtintin/string.tin

@@ -53,12 +53,6 @@ PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp <danzipao@gmail.com>) 
 // #@ str.TrimAll <字符串>
 //    去除字符串中全部的空白。
 //    本函数也可以简写为 trimAll,两个名称用途和用法完全相同。
-//
-// #@ str.Plain <字符串>
-//    去除字符串中的颜色代码和 ANSI 控制字符。
-//
-// #@ str.Color <字符串>
-//    仅保留字符串中的颜色代码,去掉其余成分,方便比较颜色。
 // };
 #func {str.Trim}    {#format result {%p} {%0}};
 #func {trim}        {#return {@str.Trim{%0}}};
@@ -66,30 +60,6 @@ PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp <danzipao@gmail.com>) 
 #func {str.TrimAll} {#var result {%0}; #replace {result} {%+1..s} {}};
 #func {trimAll}     {#return {@str.TrimAll{%0}}};
 
-#func {str.Plain}   {#format result {%P} {%0}};
-
-#func {str.Color} {
-    #local str      {};
-    #local state    {START};
-    #local ch       {};
-
-    #parse {%0} {ch} {
-        #switch {"$state/$ch"} {
-            #case {"START/\e"}  {#local state {ESCAPE}};
-            #case {"START/%*"}  {#0};
-            #case {"ESCAPE/["}  {#local state {INNER}};
-            #case {"ESCAPE/%*"} {#local state {OUTER}};
-            #case {"INNER/m"}   {#cat str {;}; #local state {OUTER}};
-            #case {"INNER/%*"}  {#cat str {$ch}};
-            #case {"OUTER/\e"}  {#local state {ESCAPE}};
-        };
-    };
-
-    #replace {str} {;$} {};
-
-    #return {$str};
-};
-
 ///=== {
 // #@ str.Replace <字符串> <正则表达式> <替换表达式>
 //    在给定字符串中执行替换,凡是被正则表达式匹配的东西都会用替换表达式进行替换。
@@ -394,45 +364,3 @@ PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp <danzipao@gmail.com>) 
 };
 
 #func {chars} {#return {@str.Chars{%0}}};
-
-///=== {
-// #@ util.ColorBar <字符串> <颜色1> <权重1> <颜色2> <权重2> [...]
-//    将字符串按照颜色及其对应的权重占比,渲染成彩色字符串。注意颜色参数须按顺序排列。
-// };
-#func {util.ColorBar} {
-    #local str {%1};
-
-    #local args {};
-    #list args create {%0};
-    #list args delete {1} {1};
-
-    #local parts {};
-    #list parts create {};
-    #local count {0};
-    #local sum {0};
-    #while { $count < &args[] } {
-        #local color    {$args[@math.Eval{$count + 1}]};
-        #local weight   {@defaultNum{$args[@math.Eval{$count + 2}];0}};
-        #list parts {add} {{
-            {color}{$color}
-            {weight}{$weight}
-        }};
-        #math count {$count + 2};
-        #math sum {$sum + $weight};
-    };
-
-    #local elem {};
-    #local len {@str.Len{$str}};
-    #local leftLen {0};
-    #local leftWeight {0};
-    #local colorStr {};
-    #foreach {$parts[%*]} {elem} {
-        #local elemLen {@math.Eval{($elem[weight] + $leftWeight) * $len / $sum - $leftLen}};
-        #local text {@str.Sub{{$str};$leftLen;$elemLen}};
-        #cat colorStr {$elem[color]$text<299>};
-        #math leftLen {$leftLen + $elemLen};
-        #math leftWeight {$leftWeight + $elem[weight]};
-    };
-
-    #return {$colorStr};
-};