HistoryText.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. namespace Terminal.Gui.Views;
  2. internal class HistoryText
  3. {
  4. private readonly List<HistoryTextItemEventArgs> _historyTextItems = [];
  5. private int _idxHistoryText = -1;
  6. private readonly List<List<Cell>> _originalCellsList = [];
  7. public bool HasHistoryChanges => _idxHistoryText > -1;
  8. public bool IsFromHistory { get; private set; }
  9. public void Add (List<List<Cell>> lines, Point curPos, TextEditingLineStatus lineStatus = TextEditingLineStatus.Original)
  10. {
  11. if (lineStatus == TextEditingLineStatus.Original && _historyTextItems.Count > 0 && _historyTextItems.Last ().LineStatus == TextEditingLineStatus.Original)
  12. {
  13. return;
  14. }
  15. if (lineStatus == TextEditingLineStatus.Replaced && _historyTextItems.Count > 0 && _historyTextItems.Last ().LineStatus == TextEditingLineStatus.Replaced)
  16. {
  17. return;
  18. }
  19. if (_historyTextItems.Count == 0 && lineStatus != TextEditingLineStatus.Original)
  20. {
  21. throw new ArgumentException ("The first item must be the original.");
  22. }
  23. if (_idxHistoryText >= 0 && _idxHistoryText + 1 < _historyTextItems.Count)
  24. {
  25. _historyTextItems.RemoveRange (
  26. _idxHistoryText + 1,
  27. _historyTextItems.Count - _idxHistoryText - 1
  28. );
  29. }
  30. _historyTextItems.Add (new (lines, curPos, lineStatus));
  31. _idxHistoryText++;
  32. }
  33. public event EventHandler<HistoryTextItemEventArgs>? ChangeText;
  34. public void Clear (List<List<Cell>> cellsList)
  35. {
  36. _historyTextItems.Clear ();
  37. _idxHistoryText = -1;
  38. _originalCellsList.Clear ();
  39. // Save a copy of the original, not the reference
  40. foreach (List<Cell> cells in cellsList)
  41. {
  42. _originalCellsList.Add ([.. cells]);
  43. }
  44. OnChangeText (null);
  45. }
  46. public bool IsDirty (List<List<Cell>> cellsList)
  47. {
  48. if (cellsList.Count != _originalCellsList.Count)
  49. {
  50. return true;
  51. }
  52. for (var r = 0; r < cellsList.Count; r++)
  53. {
  54. List<Cell> cells = cellsList [r];
  55. List<Cell> originalCells = _originalCellsList [r];
  56. if (cells.Count != originalCells.Count)
  57. {
  58. return true;
  59. }
  60. for (var c = 0; c < cells.Count; c++)
  61. {
  62. Cell cell = cells [c];
  63. Cell originalCell = originalCells [c];
  64. if (!cell.Equals (originalCell))
  65. {
  66. return true;
  67. }
  68. }
  69. }
  70. return false;
  71. }
  72. public void Redo ()
  73. {
  74. if (_historyTextItems?.Count > 0 && _idxHistoryText < _historyTextItems.Count - 1)
  75. {
  76. IsFromHistory = true;
  77. _idxHistoryText++;
  78. var historyTextItem = new HistoryTextItemEventArgs (_historyTextItems [_idxHistoryText]) { IsUndoing = false };
  79. ProcessChanges (ref historyTextItem);
  80. IsFromHistory = false;
  81. }
  82. }
  83. public void ReplaceLast (List<List<Cell>> lines, Point curPos, TextEditingLineStatus lineStatus)
  84. {
  85. HistoryTextItemEventArgs? found = _historyTextItems.FindLast (x => x.LineStatus == lineStatus);
  86. if (found is { })
  87. {
  88. found.Lines = lines;
  89. found.CursorPosition = curPos;
  90. }
  91. }
  92. public void Undo ()
  93. {
  94. if (_historyTextItems?.Count > 0 && _idxHistoryText > 0)
  95. {
  96. IsFromHistory = true;
  97. _idxHistoryText--;
  98. var historyTextItem = new HistoryTextItemEventArgs (_historyTextItems [_idxHistoryText]) { IsUndoing = true };
  99. ProcessChanges (ref historyTextItem);
  100. IsFromHistory = false;
  101. }
  102. }
  103. private void OnChangeText (HistoryTextItemEventArgs? lines) { ChangeText?.Invoke (this, lines!); }
  104. private void ProcessChanges (ref HistoryTextItemEventArgs historyTextItem)
  105. {
  106. if (historyTextItem.IsUndoing)
  107. {
  108. if (_idxHistoryText - 1 > -1
  109. && (_historyTextItems [_idxHistoryText - 1].LineStatus == TextEditingLineStatus.Added
  110. || _historyTextItems [_idxHistoryText - 1].LineStatus == TextEditingLineStatus.Removed
  111. || (historyTextItem.LineStatus == TextEditingLineStatus.Replaced && _historyTextItems [_idxHistoryText - 1].LineStatus == TextEditingLineStatus.Original)
  112. || (historyTextItem.LineStatus == TextEditingLineStatus.Attribute && _historyTextItems [_idxHistoryText - 1].LineStatus == TextEditingLineStatus.Original)))
  113. {
  114. _idxHistoryText--;
  115. while (_historyTextItems [_idxHistoryText].LineStatus == TextEditingLineStatus.Added
  116. && _historyTextItems [_idxHistoryText - 1].LineStatus == TextEditingLineStatus.Removed)
  117. {
  118. _idxHistoryText--;
  119. }
  120. historyTextItem = new (_historyTextItems [_idxHistoryText]);
  121. historyTextItem.IsUndoing = true;
  122. historyTextItem.FinalCursorPosition = historyTextItem.CursorPosition;
  123. }
  124. if (historyTextItem.LineStatus == TextEditingLineStatus.Removed && _historyTextItems [_idxHistoryText + 1].LineStatus == TextEditingLineStatus.Added)
  125. {
  126. historyTextItem.RemovedOnAdded =
  127. new (_historyTextItems [_idxHistoryText + 1]);
  128. }
  129. if ((historyTextItem.LineStatus == TextEditingLineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == TextEditingLineStatus.Original)
  130. || (historyTextItem.LineStatus == TextEditingLineStatus.Removed && _historyTextItems [_idxHistoryText - 1].LineStatus == TextEditingLineStatus.Original)
  131. || (historyTextItem.LineStatus == TextEditingLineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == TextEditingLineStatus.Removed))
  132. {
  133. if (!historyTextItem.Lines [0]
  134. .SequenceEqual (_historyTextItems [_idxHistoryText - 1].Lines [0])
  135. && historyTextItem.CursorPosition == _historyTextItems [_idxHistoryText - 1].CursorPosition)
  136. {
  137. historyTextItem.Lines [0] =
  138. new (_historyTextItems [_idxHistoryText - 1].Lines [0]);
  139. }
  140. if (historyTextItem.LineStatus == TextEditingLineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == TextEditingLineStatus.Removed)
  141. {
  142. historyTextItem.FinalCursorPosition =
  143. _historyTextItems [_idxHistoryText - 2].CursorPosition;
  144. }
  145. else
  146. {
  147. historyTextItem.FinalCursorPosition =
  148. _historyTextItems [_idxHistoryText - 1].CursorPosition;
  149. }
  150. }
  151. else
  152. {
  153. historyTextItem.FinalCursorPosition = historyTextItem.CursorPosition;
  154. }
  155. OnChangeText (historyTextItem);
  156. while (_historyTextItems [_idxHistoryText].LineStatus == TextEditingLineStatus.Removed
  157. || _historyTextItems [_idxHistoryText].LineStatus == TextEditingLineStatus.Added)
  158. {
  159. _idxHistoryText--;
  160. }
  161. }
  162. else if (!historyTextItem.IsUndoing)
  163. {
  164. if (_idxHistoryText + 1 < _historyTextItems.Count
  165. && (historyTextItem.LineStatus == TextEditingLineStatus.Original
  166. || _historyTextItems [_idxHistoryText + 1].LineStatus == TextEditingLineStatus.Added
  167. || _historyTextItems [_idxHistoryText + 1].LineStatus == TextEditingLineStatus.Removed))
  168. {
  169. _idxHistoryText++;
  170. historyTextItem = new (_historyTextItems [_idxHistoryText]);
  171. historyTextItem.IsUndoing = false;
  172. historyTextItem.FinalCursorPosition = historyTextItem.CursorPosition;
  173. }
  174. if (historyTextItem.LineStatus == TextEditingLineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == TextEditingLineStatus.Removed)
  175. {
  176. historyTextItem.RemovedOnAdded =
  177. new (_historyTextItems [_idxHistoryText - 1]);
  178. }
  179. if ((historyTextItem.LineStatus == TextEditingLineStatus.Removed && _historyTextItems [_idxHistoryText + 1].LineStatus == TextEditingLineStatus.Replaced)
  180. || (historyTextItem.LineStatus == TextEditingLineStatus.Removed && _historyTextItems [_idxHistoryText + 1].LineStatus == TextEditingLineStatus.Original)
  181. || (historyTextItem.LineStatus == TextEditingLineStatus.Added && _historyTextItems [_idxHistoryText + 1].LineStatus == TextEditingLineStatus.Replaced))
  182. {
  183. if (historyTextItem.LineStatus == TextEditingLineStatus.Removed
  184. && !historyTextItem.Lines [0]
  185. .SequenceEqual (_historyTextItems [_idxHistoryText + 1].Lines [0]))
  186. {
  187. historyTextItem.Lines [0] =
  188. new (_historyTextItems [_idxHistoryText + 1].Lines [0]);
  189. }
  190. historyTextItem.FinalCursorPosition =
  191. _historyTextItems [_idxHistoryText + 1].CursorPosition;
  192. }
  193. else
  194. {
  195. historyTextItem.FinalCursorPosition = historyTextItem.CursorPosition;
  196. }
  197. OnChangeText (historyTextItem);
  198. while (_historyTextItems [_idxHistoryText].LineStatus == TextEditingLineStatus.Removed
  199. || _historyTextItems [_idxHistoryText].LineStatus == TextEditingLineStatus.Added)
  200. {
  201. _idxHistoryText++;
  202. }
  203. }
  204. }
  205. }