1
+ using System ;
2
+ using System . Collections . Generic ;
3
+
4
+ namespace LLCOM . Models ;
5
+
6
+ public enum TerminalCommand
7
+ {
8
+ None , //没匹配上任何命令
9
+
10
+ Bs , //退格 0x08
11
+ Ht , //水平制表符 0x09
12
+ Lf , //换行 0x0A
13
+ Cr , //回车 0x0D
14
+
15
+ Hide , //隐藏光标 \x1b[?25l
16
+ Show , //显示光标 \x1b[?25h
17
+
18
+ ClearLineEnd , //清除光标到行尾 \x1b[K
19
+ ClearLineStart , //清除光标到行首 \x1b[1K
20
+ ClearLine , //清除当前行 \x1b[2K
21
+
22
+ ClearScreenEnd , //清除光标到屏幕末尾 \x1b[J
23
+ ClearScreenStart , //清除光标到屏幕开头 \x1b[1J
24
+ ClearScreen , //清除屏幕 \x1b[2J
25
+
26
+ MoveCursorUp , //光标上移 \x1b[{n}A
27
+ MoveCursorDown , //光标下移 \x1b[{n}B
28
+ MoveCursorRight , //光标右移 \x1b[{n}C
29
+ MoveCursorLeft , //光标左移 \x1b[{n}D
30
+ ResetCursor , //光标移动到左上角 \x1b[H
31
+ MoveCursorTo , //光标移动到指定位置 \x1b[{n};{m}H
32
+ SaveCursor , //保存光标位置 \x1b[s
33
+ RestoreCursor , //恢复光标位置 \x1b[u
34
+
35
+ ResetStyle , //重置样式 \x1b[m
36
+ Bold , //加粗 \x1b[1m
37
+ Underline , //下划线 \x1b[4m
38
+ Reverse , //反转颜色 \x1b[7m
39
+ ForegroundColor , //前景色 \x1b[3{n}m
40
+ BackgroundColor , //背景色 \x1b[4{n}m
41
+ }
42
+
43
+ public class TerminalCommandCheck
44
+ {
45
+ /// <summary>
46
+ /// 分析给定的字符切片,判断是否为终端命令
47
+ /// </summary>
48
+ /// <param name="slice">切片,函数将会判断开头</param>
49
+ /// <returns></returns>
50
+ public static ( ( TerminalCommand , ( int , int ) ) , int ) Do ( ReadOnlySpan < char > slice )
51
+ {
52
+ if ( slice . Length == 0 )
53
+ return ( ( TerminalCommand . None , ( 0 , 0 ) ) , 0 ) ;
54
+ //先判断下是否为单字符命令
55
+ var singleCmd = slice [ 0 ] switch
56
+ {
57
+ '\b ' => TerminalCommand . Bs , //退格
58
+ '\t ' => TerminalCommand . Ht , //水平制表符
59
+ '\n ' => TerminalCommand . Lf , //换行
60
+ '\r ' => TerminalCommand . Cr , //回车
61
+ _ => TerminalCommand . None
62
+ } ;
63
+ if ( singleCmd != TerminalCommand . None )
64
+ {
65
+ return ( ( singleCmd , ( 0 , 0 ) ) , 1 ) ;
66
+ }
67
+ //判断是否为转义字符
68
+ if ( slice [ 0 ] != '\x1b ' || slice [ 1 ] != '[' || slice . Length < 3 )
69
+ {
70
+ return ( ( TerminalCommand . None , ( 0 , 0 ) ) , 0 ) ;
71
+ }
72
+ //看看是否为显示/隐藏光标
73
+ if ( slice . Length >= 6 && slice [ 2 ] == '?' && slice [ 3 ] == '2' && slice [ 4 ] == '5' )
74
+ {
75
+ if ( slice [ 5 ] == 'l' )
76
+ return ( ( TerminalCommand . Hide , ( 25 , 0 ) ) , 6 ) ;
77
+ if ( slice [ 5 ] == 'h' )
78
+ return ( ( TerminalCommand . Show , ( 25 , 0 ) ) , 6 ) ;
79
+ }
80
+ //其他命令就按正常格式分析
81
+ //\x1b[{数字}{字母} 数字可能是2个字符也可能不存在
82
+ int code = 0 ;
83
+ char cmd = '\0 ' ;
84
+ int i = 2 ; //从第三个字符开始分析,最多分析到第四个字符
85
+ while ( i < slice . Length && i < 5 && char . IsDigit ( slice [ i ] ) )
86
+ {
87
+ code = code * 10 + ( slice [ i ] - '0' ) ; //将数字字符转换为数字
88
+ i ++ ;
89
+ }
90
+ if ( i < slice . Length )
91
+ {
92
+ cmd = slice [ i ] ;
93
+ i ++ ;
94
+ }
95
+ if ( cmd == '\0 ' )
96
+ {
97
+ return ( ( TerminalCommand . None , ( 0 , 0 ) ) , 0 ) ; //没有命令
98
+ }
99
+ //根据命令字符返回对应的命令
100
+ switch ( cmd )
101
+ {
102
+ case 'K' : //清除行
103
+ var kr = code switch
104
+ {
105
+ 2 => TerminalCommand . ClearLineEnd , //清除光标到行尾
106
+ 3 => TerminalCommand . ClearLineStart , //清除光标到行首
107
+ 4 => TerminalCommand . ClearLine , //清除当前行
108
+ _ => TerminalCommand . None //不匹配
109
+ } ;
110
+ if ( kr != TerminalCommand . None )
111
+ return ( ( kr , ( code , 0 ) ) , i ) ;
112
+ break ;
113
+ case 'J' : //清除屏幕
114
+ var jr = code switch
115
+ {
116
+ 0 => TerminalCommand . ClearScreenEnd , //清除光标到屏幕末尾
117
+ 1 => TerminalCommand . ClearScreenStart , //清除光标到屏幕开头
118
+ 2 => TerminalCommand . ClearScreen , //清除屏幕
119
+ _ => TerminalCommand . None //不匹配
120
+ } ;
121
+ if ( jr != TerminalCommand . None )
122
+ return ( ( jr , ( code , 0 ) ) , i ) ;
123
+ break ;
124
+ case 'A' : //光标上移
125
+ if ( code > 0 )
126
+ return ( ( TerminalCommand . MoveCursorUp , ( code , 0 ) ) , i ) ;
127
+ break ;
128
+ case 'B' : //光标下移
129
+ if ( code > 0 )
130
+ return ( ( TerminalCommand . MoveCursorDown , ( code , 0 ) ) , i ) ;
131
+ break ;
132
+ case 'C' : //光标右移
133
+ if ( code > 0 )
134
+ return ( ( TerminalCommand . MoveCursorRight , ( code , 0 ) ) , i ) ;
135
+ break ;
136
+ case 'D' : //光标左移
137
+ if ( code > 0 )
138
+ return ( ( TerminalCommand . MoveCursorLeft , ( code , 0 ) ) , i ) ;
139
+ break ;
140
+ case 'H' : //光标移动到指定位置
141
+ return ( ( TerminalCommand . ResetCursor , ( code , 0 ) ) , i ) ;
142
+ case 's' : //保存光标位置
143
+ return ( ( TerminalCommand . SaveCursor , ( 0 , 0 ) ) , i ) ;
144
+ case 'u' : //恢复光标位置
145
+ return ( ( TerminalCommand . RestoreCursor , ( 0 , 0 ) ) , i ) ;
146
+ case 'm' : //样式
147
+ var mr = code switch
148
+ {
149
+ 0 => TerminalCommand . ResetStyle , //重置样式
150
+ 1 => TerminalCommand . Bold , //加粗
151
+ 4 => TerminalCommand . Underline , //下划线
152
+ 7 => TerminalCommand . Reverse , //反转颜色
153
+ _ when code >= 30 && code <= 37 => TerminalCommand . ForegroundColor , //前景色
154
+ _ when code >= 40 && code <= 47 => TerminalCommand . BackgroundColor , //背景色
155
+ _ => TerminalCommand . None //不匹配
156
+ } ;
157
+ if ( mr != TerminalCommand . None )
158
+ {
159
+ return ( ( mr , ( code , 0 ) ) , i ) ;
160
+ }
161
+ break ;
162
+ }
163
+ //检查是不是匹配\x1b[{n};{m}H
164
+ if ( cmd != ';' )
165
+ return ( ( TerminalCommand . None , ( 0 , 0 ) ) , 0 ) ;
166
+ //如果是分号,说明可能是光标移动到指定位置
167
+ //需要检查后面的数字
168
+ int col = code , row = 0 ;
169
+ while ( i < slice . Length && char . IsDigit ( slice [ i ] ) )
170
+ {
171
+ row = row * 10 + ( slice [ i ] - '0' ) ; //将数字字符转换为数字
172
+ i ++ ;
173
+ }
174
+ if ( i < slice . Length && slice [ i ] == 'H' )
175
+ {
176
+ return ( ( TerminalCommand . MoveCursorTo , ( col , row ) ) , i + 1 ) ;
177
+ }
178
+ return ( ( TerminalCommand . None , ( 0 , 0 ) ) , 0 ) ;
179
+ }
180
+
181
+ /// <summary>
182
+ /// 分析给定的字符数组,判断是否为终端命令
183
+ /// </summary>
184
+ /// <param name="arr">数组</param>
185
+ /// <param name="offset">从哪里开始</param>
186
+ /// <param name="length">判断的长度,留空则为剩余全长</param>
187
+ /// <returns></returns>
188
+ public static ( ( TerminalCommand , ( int , int ) ) , int ) Do ( char [ ] arr , int offset , int ? length = null )
189
+ {
190
+ Span < char > slice = arr . AsSpan ( offset , length ?? arr . Length - offset ) ;
191
+ return Do ( slice ) ;
192
+ }
193
+ }
0 commit comments