Terminal.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. using EsPy.Units;
  2. using ScintillaNET;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Drawing;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using System.Windows.Forms;
  11. namespace EsPy.Components
  12. {
  13. public class Terminal : ExScintilla
  14. {
  15. const int WM_LBUTTONDOWN = 0x0201;
  16. const int WM_RBUTTONDOWN = 0x0204;
  17. const int WM_KEYDOWN = 0x100;
  18. const int WM_SYSKEYDOWN = 0x104;
  19. int PromptPos = 0;
  20. //int ActPos = 0;
  21. int BeforeMouse = -1;
  22. int HistoryPos = 0;
  23. public bool HistoryModified = false;
  24. public List<string> History = new List<string>();
  25. public Terminal() : base()
  26. {
  27. this.Lexer = Lexer.Python;
  28. this.Lang = "python";
  29. this.SetLang();
  30. this.ReadOnly = true;
  31. }
  32. private PySerial FPort = null;
  33. public PySerial Port
  34. {
  35. get { return this.FPort; }
  36. set
  37. {
  38. if (this.FPort != null)
  39. {
  40. this.FPort.PortOpen -= FPort_PortOpen;
  41. this.FPort.PortClose -= FPort_PortClose;
  42. this.FPort.DataReceived -= FPort_DataReceived;
  43. this.FPort.ErrorReceived -= FPort_ErrorReceived;
  44. this.FPort.PortBusy -= FPort_PortBusy;
  45. this.FPort.PortFree -= FPort_PortFree;
  46. }
  47. this.FPort = value;
  48. if (this.FPort != null)
  49. {
  50. this.FPort.PortOpen += FPort_PortOpen;
  51. this.FPort.PortClose += FPort_PortClose;
  52. this.FPort.DataReceived += FPort_DataReceived;
  53. this.FPort.ErrorReceived += FPort_ErrorReceived;
  54. this.FPort.PortBusy += FPort_PortBusy;
  55. this.FPort.PortFree += FPort_PortFree;
  56. }
  57. }
  58. }
  59. private delegate void UpdateStatusEvent(bool busy);
  60. public void UpdateBusy(bool busy)
  61. {
  62. if (this.InvokeRequired)
  63. {
  64. this.Invoke(new UpdateStatusEvent(UpdateBusy), new object[] { busy });
  65. }
  66. else
  67. {
  68. }
  69. }
  70. private void FPort_PortFree(object sender, EventArgs e)
  71. {
  72. //throw new NotImplementedException();
  73. }
  74. private void FPort_PortBusy(object sender, EventArgs e)
  75. {
  76. //throw new NotImplementedException();
  77. }
  78. public void Append(string text)
  79. {
  80. this.GotoPosition(this.TextLength);
  81. this.AddText(text);
  82. this.GotoPosition(this.TextLength);
  83. this.EmptyUndoBuffer();
  84. }
  85. public void Clean()
  86. {
  87. this.Text = "";
  88. if(this.Port != null && this.Port.IsOpen)
  89. this.Port.WriteLine("");
  90. }
  91. private void FPort_PortClose(object sender, EventArgs e)
  92. {
  93. }
  94. private void FPort_PortOpen(object sender, EventArgs e)
  95. {
  96. }
  97. private void FPort_DataReceived(object sender, string data)
  98. {
  99. this.UpdateTerminal(data, "");
  100. }
  101. private void FPort_ErrorReceived(object sender, string data)
  102. {
  103. // this.UpdateTerminal(data);
  104. }
  105. public delegate string PreprocessEvent(string text);
  106. public PreprocessEvent Preprocess = null;
  107. const int LineLength = 180;
  108. char[] Buffer = new char[LineLength]; // new StringBuilder(" ", LineLength);
  109. int Cp = 0;
  110. char LastChar = (char)0;
  111. char LastMode = (char)0;
  112. public bool Locked = false;
  113. //public int FindPrompPos()
  114. //{
  115. // Line line = this.Lines[this.LineFromPosition(this.CurrentPosition)];
  116. // if()
  117. //}
  118. private char C0 = '\0';
  119. private char C1 = '\0';
  120. private char C2 = '\0';
  121. private char C3 = '\0';
  122. private delegate void UpdateTerminalEvent(string data, string message);
  123. public void UpdateTerminal(string data, string message)
  124. {
  125. if (this.Locked)
  126. return;
  127. if (this.InvokeRequired)
  128. {
  129. this.Invoke(new UpdateTerminalEvent(UpdateTerminal), new object[] { data, message});
  130. }
  131. else
  132. {
  133. //if (this.Preprocess != null)
  134. //{
  135. // text = this.Preprocess(text);
  136. //}
  137. //http://www.termsys.demon.co.uk/vtansi.htm
  138. //if (text[0] == '\b' && this.TextLength > 0)
  139. //{
  140. // this.DeleteRange(this.TextLength - 1, 1);
  141. //}
  142. if (message != "")
  143. {
  144. if (message.Contains("\r\n"))
  145. {
  146. this.AppendText(message);
  147. this.PromptPos = this.TextLength;
  148. }
  149. else
  150. {
  151. this.DeleteRange(this.PromptPos, 100);
  152. this.AppendText(message);
  153. }
  154. //this.PromptPos = this.TextLength;
  155. //this.ActPos = this.PromptPos;
  156. return;
  157. }
  158. //while (this.Lines.Count > 500)
  159. //{
  160. // this.DeleteRange(0, this.Lines[0].EndPosition);
  161. //}
  162. string res = this.Text ;
  163. for (int i = 0; i < data.Length; i++)
  164. {
  165. char c = data[i];
  166. // Todo: \[K
  167. //this.C0 = this.C1;
  168. //this.C1 = this.C2;
  169. //this.C2 = this.C3;
  170. //this.C3 = c;
  171. if (c == '\b' && res.Length >= 1)
  172. {
  173. res = res.Remove(res.Length - 1, 1);
  174. }
  175. else if (this.LastChar == 27)
  176. {
  177. if (c == '[')
  178. {
  179. LastMode = c;
  180. continue;
  181. }
  182. if (LastMode == '[' && c == 'K')
  183. {
  184. LastMode = (char)0;
  185. continue;
  186. }
  187. }
  188. else if (c != 27)
  189. {
  190. res += data[i];
  191. }
  192. this.LastChar = data[i];
  193. }
  194. //this.AppendText(text);
  195. this.Text = res;
  196. this.GotoPosition(this.TextLength);
  197. //this.ActPos = this.TextLength;
  198. Line line = this.Lines[this.CurrentLine];
  199. if (line.Text.StartsWith(SerialPort.PROMPT))
  200. this.PromptPos = line.Position + SerialPort.PROMPT.Length;
  201. else if (line.Text.StartsWith("... "))
  202. this.PromptPos = this.TextLength;
  203. //else //if (line.Text.StartsWith("=== "))
  204. // this.PromptPos = this.TextLength;
  205. this.PromptPos = Math.Max(this.PromptPos, this.TextLength);
  206. this.EmptyUndoBuffer();
  207. this.BeforeMouse = -1;
  208. }
  209. }
  210. private void AddToHistory()
  211. {
  212. string text = this.Lines[this.CurrentLine].Text;
  213. if (text.StartsWith(PySerial.PROMPT))
  214. text = text.Substring(PySerial.PROMPT.Length, text.Length - PySerial.PROMPT.Length);
  215. else if (text.StartsWith(PySerial.DOTS))
  216. text = text.Substring(PySerial.DOTS.Length, text.Length - PySerial.DOTS.Length);
  217. else return;
  218. text = text.Trim();
  219. if (text.Length > 0 && (this.History.Count == 0 || (this.History.Count > 0 && this.History.Last() != text)))
  220. {
  221. this.History.Add(text);
  222. this.HistoryPos = this.History.Count;
  223. this.HistoryModified = true;
  224. while(this.History.Count > 100)
  225. {
  226. this.History.RemoveAt(0);
  227. }
  228. }
  229. }
  230. public void SaveHistory(string fname)
  231. {
  232. File.WriteAllText(fname, String.Join("\r\n", this.History));
  233. this.HistoryModified = false;
  234. }
  235. public void LoadHistory(string fname)
  236. {
  237. if (File.Exists(fname))
  238. {
  239. this.History.Clear();
  240. this.History.AddRange(File.ReadAllLines(fname));
  241. this.HistoryPos = this.History.Count;
  242. this.HistoryModified = false;
  243. }
  244. }
  245. private string GetAndRemoveLine(bool history)
  246. {
  247. Line line = this.Lines[this.CurrentLine];
  248. string text = this.GetTextRange(this.PromptPos, this.TextLength - this.PromptPos);
  249. //string text = this.GetTextRange(this.PromptPos, this.TextLength - this.PromptPos);
  250. //text = this.GetTextRange(line.Position + SerialPort.PROMPT.Length, this.TextLength - line.Position);
  251. if (history)
  252. {
  253. this.AddToHistory();
  254. }
  255. line = this.Lines[this.CurrentLine];
  256. this.DeleteRange(this.PromptPos, this.TextLength);
  257. return text;
  258. }
  259. protected override void WndProc(ref Message m)
  260. {
  261. if (m.Msg == WM_LBUTTONDOWN || m.Msg == WM_RBUTTONDOWN)
  262. {
  263. if (this.BeforeMouse == -1)
  264. this.BeforeMouse = this.CurrentPosition;
  265. }
  266. base.WndProc(ref m);
  267. }
  268. protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
  269. {
  270. Line line = null;
  271. if (this.BeforeMouse >= 0)
  272. {
  273. this.GotoPosition (this.BeforeMouse);
  274. this.BeforeMouse = -1;
  275. }
  276. //if (this.CurrentPosition < this.ActPos)
  277. // this.CurrentPosition = this.ActPos;
  278. if (this.CurrentPosition < this.PromptPos)
  279. this.CurrentPosition = this.PromptPos;
  280. // this.ActPos = this.CurrentPosition;
  281. if (msg.Msg == WM_KEYDOWN || msg.Msg == WM_SYSKEYDOWN)
  282. {
  283. // if (this.Port is PySerial)
  284. {
  285. //PythonSerial port = this.Port as PythonSerial;
  286. switch (keyData)
  287. {
  288. case Keys.Control | Keys.V:
  289. if (Clipboard.ContainsText())
  290. {
  291. this.Port.Clean();
  292. this.Port.PasteMode();
  293. this.Port.ReadAllLines();
  294. byte[] buff = Encoding.UTF8.GetBytes(Clipboard.GetText().Replace("\r", ""));
  295. this.Port.Write(buff, 0, buff.Length);
  296. this.Port.SoftReset();
  297. }
  298. return true;
  299. break;
  300. case Keys.Control | Keys.A:
  301. this.Port.EnterRawMode();
  302. return true;
  303. case Keys.Control | Keys.B:
  304. //this.Port.Write(new byte[] { 2 });
  305. this.Port.LeaveRawMode();
  306. return true;
  307. case Keys.Control | Keys.C:
  308. //this.Port.Write(new byte[] { 3 });
  309. this.Port.Interrupt();
  310. return true;
  311. case Keys.Control | Keys.D:
  312. //this.Port.Write(new byte[] { 4 });
  313. this.Port.SoftReset();
  314. return true;
  315. case Keys.Control | Keys.E:
  316. //this.Port.Write(new byte[] { 5 });
  317. this.Port.PasteMode();
  318. return true;
  319. //case Keys.Control | Keys.Tab:
  320. // this.Port.WriteLine(this.GetLine());
  321. // this.Port.Write(new byte[] { 9 });
  322. // return true;
  323. case Keys.Tab:
  324. string l = this.GetAndRemoveLine(false);
  325. this.Port.Write(l);
  326. this.Port.Write(9);
  327. return true;
  328. //break;
  329. case Keys.Down:
  330. this.HistoryPos++;
  331. if (this.HistoryPos > this.History.Count)
  332. this.HistoryPos = this.History.Count;
  333. if (this.HistoryPos <= 0)
  334. this.HistoryPos = 1;
  335. if (this.HistoryPos <= this.History.Count - 1)
  336. {
  337. this.DeleteRange(this.PromptPos, this.TextLength);
  338. this.Append(this.History[this.HistoryPos]);
  339. }
  340. else
  341. {
  342. line = this.Lines[this.CurrentLine];
  343. this.DeleteRange(this.PromptPos, this.TextLength);
  344. }
  345. return true;
  346. case Keys.Up:
  347. this.HistoryPos--;
  348. if (this.HistoryPos < 0)
  349. this.HistoryPos = 0;
  350. if (this.HistoryPos >= this.History.Count)
  351. this.HistoryPos = this.History.Count - 1;
  352. if (this.HistoryPos >= 0)
  353. {
  354. this.DeleteRange(this.PromptPos, this.TextLength);
  355. this.Append(this.History[this.HistoryPos]);
  356. }
  357. return true;
  358. case Keys.Left:
  359. if (this.PromptPos >= this.CurrentPosition)
  360. return true;
  361. break;
  362. case Keys.Home:
  363. this.CurrentPosition = this.PromptPos;
  364. return true;
  365. //case Keys.End:
  366. // return true;
  367. case Keys.PageDown:
  368. return true;
  369. case Keys.PageUp:
  370. return true;
  371. case Keys.Back:
  372. if (this.PromptPos == this.CurrentPosition)
  373. {
  374. this.Port.Write(8);
  375. return true;
  376. }
  377. //else if()
  378. break;
  379. case Keys.Enter:
  380. this.Port.WriteLine(this.GetAndRemoveLine(true));
  381. return true;
  382. //default:
  383. // if()
  384. // break;
  385. }
  386. }
  387. }
  388. //KeysConverter kc = new KeysConverter();
  389. //string keyChar = kc.ConvertToString(keyData);
  390. //if(keyData != Keys.ShiftKey)
  391. // this.Port.Write(keyChar.ToLower());
  392. //else
  393. bool res = base.ProcessCmdKey(ref msg, keyData);
  394. //this.ActPos = this.CurrentPosition;
  395. return res;
  396. }
  397. private void InitializeComponent()
  398. {
  399. this.SuspendLayout();
  400. //
  401. // Terminal
  402. //
  403. this.IndentationGuides = ScintillaNET.IndentView.Real;
  404. this.IndentWidth = 4;
  405. this.Lexer = ScintillaNET.Lexer.Python;
  406. this.ResumeLayout(false);
  407. }
  408. }
  409. }