| 1 | #Region "Microsoft.VisualBasic::47578ae17b669ca481b9c05de7e76a6a, Microsoft.VisualBasic.Core\ApplicationServices\Terminal\InteractiveIODevice\InteractiveDevice.vb" |
| 2 | |
| 3 | ' Author: |
| 4 | ' |
| 5 | ' asuka (amethyst.asuka@gcmodeller.org) |
| 6 | ' xie (genetics@smrucc.org) |
| 7 | ' xieguigang (xie.guigang@live.com) |
| 8 | ' |
| 9 | ' Copyright (c) 2018 GPL3 Licensed |
| 10 | ' |
| 11 | ' |
| 12 | ' GNU GENERAL PUBLIC LICENSE (GPL3) |
| 13 | ' |
| 14 | ' |
| 15 | ' This program is free software: you can redistribute it and/or modify |
| 16 | ' it under the terms of the GNU General Public License as published by |
| 17 | ' the Free Software Foundation, either version 3 of the License, or |
| 18 | ' (at your option) any later version. |
| 19 | ' |
| 20 | ' This program is distributed in the hope that it will be useful, |
| 21 | ' but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 23 | ' GNU General Public License for more details. |
| 24 | ' |
| 25 | ' You should have received a copy of the GNU General Public License |
| 26 | ' along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 27 | |
| 28 | |
| 29 | |
| 30 | ' /********************************************************************************/ |
| 31 | |
| 32 | ' Summaries: |
| 33 | |
| 34 | ' Class InteractiveDevice |
| 35 | ' |
| 36 | ' Properties: PromptText |
| 37 | ' |
| 38 | ' Constructor: (+1 Overloads) Sub New |
| 39 | ' |
| 40 | ' Function: ReadKey, ReadLine, (+2 Overloads) Save |
| 41 | ' |
| 42 | ' Sub: (+2 Overloads) Dispose, InternalClearLine, PrintPrompted |
| 43 | ' |
| 44 | ' |
| 45 | ' /********************************************************************************/ |
| 46 | |
| 47 | #End Region |
| 48 | |
| 49 | Imports System.Text |
| 50 | Imports Microsoft.VisualBasic.ComponentModel |
| 51 | Imports Microsoft.VisualBasic.Text |
| 52 | |
| 53 | Namespace Terminal |
| 54 | |
| 55 | Public Class InteractiveDevice : Inherits Terminal |
| 56 | Implements System.IDisposable |
| 57 | Implements ISaveHandle |
| 58 | |
| 59 | Dim _innerBuffer As StringBuilder = New StringBuilder(2048) |
| 60 | Dim _cmdsHistory As HistoryStacks |
| 61 | Dim Blanks As String |
| 62 | |
| 63 | ''' <summary> |
| 64 | ''' |
| 65 | ''' </summary> |
| 66 | ''' <param name="s"></param> |
| 67 | ''' <remarks></remarks> |
| 68 | Public Event NewOutputMessage(s As String) |
| 69 | |
| 70 | Public Property PromptText As String |
| 71 | |
| 72 | ''' <summary> |
| 73 | ''' |
| 74 | ''' </summary> |
| 75 | ''' <param name="HistoryFile">历史数据文件的存放位置,假若为空,则使用默认文件路径</param> |
| 76 | ''' <remarks></remarks> |
| 77 | Sub New(Optional HistoryFile As String = "") |
| 78 | If String.IsNullOrEmpty(HistoryFile) Then |
| 79 | HistoryFile = $"{Application.ExecutablePath}.commandline_histories.dat" |
| 80 | End If |
| 81 | |
| 82 | Try |
| 83 | _cmdsHistory = HistoryFile.LoadXml(Of HistoryStacks)() |
| 84 | Catch ex As Exception |
| 85 | _cmdsHistory = New HistoryStacks() |
| 86 | Call _cmdsHistory.Save(HistoryFile) |
| 87 | End Try |
| 88 | |
| 89 | If _cmdsHistory Is Nothing Then |
| 90 | _cmdsHistory = New HistoryStacks(HistoryFile) |
| 91 | End If |
| 92 | |
| 93 | Call _cmdsHistory.StartInitialize() |
| 94 | |
| 95 | PromptText = "#" |
| 96 | Blanks = New String(" "c, Console.BufferWidth) |
| 97 | End Sub |
| 98 | |
| 99 | Dim _cacheReadLine As String |
| 100 | Dim _EmptyHistory As Boolean, _historyControl As Boolean = True |
| 101 | |
| 102 | Public Overrides Function ReadKey() As ConsoleKeyInfo |
| 103 | Dim n = Console.ReadKey |
| 104 | |
| 105 | _historyControl = True |
| 106 | |
| 107 | Select Case n.Key |
| 108 | Case ConsoleKey.UpArrow |
| 109 | _cacheReadLine = _cmdsHistory.MovePrevious |
| 110 | Case ConsoleKey.DownArrow |
| 111 | _cacheReadLine = _cmdsHistory.MoveNext |
| 112 | Case ConsoleKey.Home |
| 113 | _cacheReadLine = _cmdsHistory.MoveFirst |
| 114 | Case ConsoleKey.End |
| 115 | _cacheReadLine = _cmdsHistory.MoveLast |
| 116 | Case Else |
| 117 | _historyControl = False |
| 118 | End Select |
| 119 | |
| 120 | If _historyControl Then |
| 121 | Call Console.SetCursorPosition(Console.CursorLeft - 1, CursorTop) '回移一格,因为控制符也会被输出的 |
| 122 | End If |
| 123 | |
| 124 | If Not String.IsNullOrEmpty(_cacheReadLine) Then |
| 125 | Call InternalClearLine(Console.CursorTop) |
| 126 | Call Console.Write(_cacheReadLine) |
| 127 | _EmptyHistory = False |
| 128 | ElseIf Not String.IsNullOrEmpty(HistoryCallerStack) Then |
| 129 | _cacheReadLine = HistoryCallerStack |
| 130 | HistoryCallerStack = "" |
| 131 | _EmptyHistory = False |
| 132 | Else |
| 133 | _cacheReadLine = "" |
| 134 | _EmptyHistory = True '空的历史 |
| 135 | End If |
| 136 | |
| 137 | Return n |
| 138 | End Function |
| 139 | |
| 140 | ''' <summary> |
| 141 | ''' ReadLine函数的递归返回值 |
| 142 | ''' </summary> |
| 143 | ''' <remarks></remarks> |
| 144 | Dim HistoryCallerStack As String |
| 145 | |
| 146 | Public Overrides Function ReadLine() As String |
| 147 | Dim strCommand As String = "", n = Me.ReadKey() |
| 148 | |
| 149 | If _historyControl Then '用户浏览了历史记录 |
| 150 | HistoryCallerStack = _cacheReadLine |
| 151 | Return Me.ReadLine |
| 152 | Else |
| 153 | If n.Key = ConsoleKey.Enter Then '用户输入了数据 |
| 154 | strCommand = _cacheReadLine |
| 155 | Else '用户还没有完成输入 |
| 156 | EXIT_INPUT: strCommand = HistoryCallerStack & n.KeyChar & MyBase.ReadLine |
| 157 | End If |
| 158 | End If |
| 159 | |
| 160 | HistoryCallerStack = "" |
| 161 | _cacheReadLine = "" |
| 162 | |
| 163 | ' If Not String.IsNullOrEmpty(_InternalCacheReadLine) Then |
| 164 | ' strCommand = n.KeyChar & _InternalCacheReadLine |
| 165 | ' _InternalCacheReadLine = "" |
| 166 | ' If Not n.Key = ConsoleKey.Enter Then |
| 167 | ' If _historyControl Then |
| 168 | ' '用户调出了历史,但是没有按下回车,可能还会继续浏览历史 |
| 169 | ' HistoryCallerStack = strCommand |
| 170 | ' Return Me.ReadLine() '上一个按键是方向键 |
| 171 | ' Else |
| 172 | ' GoTo EXIT_INPUT '不是方向键则输出 |
| 173 | ' End If |
| 174 | ' Else |
| 175 | ' If _EmptyHistory Then |
| 176 | ' Call Me.PrintPrompted(Lf:=True) |
| 177 | ' Return Me.ReadLine() |
| 178 | ' Else |
| 179 | ' Call Console.WriteLine() |
| 180 | ' End If |
| 181 | ' End If |
| 182 | ' Else |
| 183 | ' If _historyControl Then '历史记录是空的,但是任然算是浏览了历史记录 |
| 184 | ' Return Me.ReadLine |
| 185 | ' End If |
| 186 | |
| 187 | ' If n.Key = ConsoleKey.Enter Then |
| 188 | ' Return Me.ReadLine '没有输入,则换行并要求重新输入 |
| 189 | ' Else |
| 190 | 'EXIT_INPUT: strCommand = strCommand & MyBase.ReadLine |
| 191 | ' End If |
| 192 | ' End If |
| 193 | |
| 194 | Call _cmdsHistory.PushStack(strCommand) |
| 195 | |
| 196 | Return strCommand |
| 197 | End Function |
| 198 | |
| 199 | Public Sub PrintPrompted(Optional Lf As Boolean = False) |
| 200 | If Lf Then Call Console.WriteLine() |
| 201 | Call Console.Write(PromptText & " ") |
| 202 | End Sub |
| 203 | |
| 204 | Private Sub InternalClearLine(top As Integer) |
| 205 | Dim current As Integer = Console.CursorTop |
| 206 | |
| 207 | Call Console.SetCursorPosition(Len(PromptText) + 2, top) |
| 208 | Call Console.Write(Blanks) |
| 209 | Call Console.SetCursorPosition(Len(PromptText) + 2, current) |
| 210 | End Sub |
| 211 | |
| 212 | #Region "IDisposable Support" |
| 213 | Private disposedValue As Boolean ' To detect redundant calls |
| 214 | |
| 215 | ' IDisposable |
| 216 | Protected Overridable Sub Dispose(disposing As Boolean) |
| 217 | If Not Me.disposedValue Then |
| 218 | If disposing Then |
| 219 | Call Me.Save(encoding:=Encodings.UTF8) |
| 220 | ' TODO: dispose managed state (managed objects). |
| 221 | End If |
| 222 | |
| 223 | ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below. |
| 224 | ' TODO: set large fields to null. |
| 225 | End If |
| 226 | Me.disposedValue = True |
| 227 | End Sub |
| 228 | |
| 229 | ' TODO: override Finalize() only if Dispose( disposing As Boolean) above has code to free unmanaged resources. |
| 230 | 'Protected Overrides Sub Finalize() |
| 231 | ' ' Do not change this code. Put cleanup code in Dispose( disposing As Boolean) above. |
| 232 | ' Dispose(False) |
| 233 | ' MyBase.Finalize() |
| 234 | 'End Sub |
| 235 | |
| 236 | ' This code added by Visual Basic to correctly implement the disposable pattern. |
| 237 | Public Sub Dispose() Implements IDisposable.Dispose |
| 238 | ' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above. |
| 239 | Dispose(True) |
| 240 | GC.SuppressFinalize(Me) |
| 241 | End Sub |
| 242 | #End Region |
| 243 | |
| 244 | ''' <summary> |
| 245 | ''' 保存历史数据 |
| 246 | ''' </summary> |
| 247 | ''' <param name="Path"></param> |
| 248 | ''' <param name="encoding"></param> |
| 249 | ''' <returns></returns> |
| 250 | ''' <remarks></remarks> |
| 251 | Public Function Save(Optional Path As String = "", Optional encoding As Encoding = Nothing) As Boolean Implements ISaveHandle.Save |
| 252 | Return _cmdsHistory.Save(Path, encoding) |
| 253 | End Function |
| 254 | |
| 255 | Public Function Save(Optional Path As String = "", Optional encoding As Encodings = Encodings.UTF8) As Boolean Implements ISaveHandle.Save |
| 256 | Return Save(Path, encoding.CodePage) |
| 257 | End Function |
| 258 | End Class |
| 259 | End Namespace |