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