capacitor-dex-editor 0.0.38 → 0.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/android/src/main/AndroidManifest.xml +8 -0
  2. package/android/src/main/assets/availableSyntax.json +54 -0
  3. package/android/src/main/assets/c.json +42 -0
  4. package/android/src/main/assets/colors.json +21 -0
  5. package/android/src/main/assets/cpp.json +79 -0
  6. package/android/src/main/assets/dart.json +108 -0
  7. package/android/src/main/assets/java.json +46 -0
  8. package/android/src/main/assets/js.json +54 -0
  9. package/android/src/main/assets/json.json +33 -0
  10. package/android/src/main/assets/kotlin.json +53 -0
  11. package/android/src/main/assets/lua.json +54 -0
  12. package/android/src/main/assets/php.json +69 -0
  13. package/android/src/main/assets/python.json +87 -0
  14. package/android/src/main/assets/rust.json +139 -0
  15. package/android/src/main/assets/smali.json +131 -0
  16. package/android/src/main/assets/xml.json +96 -0
  17. package/android/src/main/java/com/aetherlink/dexeditor/DexEditorPluginPlugin.java +48 -0
  18. package/android/src/main/java/com/aetherlink/dexeditor/SmaliEditorActivity.java +205 -0
  19. package/android/src/main/java/com/aetherlink/dexeditor/editor/EditView.java +4022 -0
  20. package/android/src/main/java/com/aetherlink/dexeditor/editor/WordWrapLayout.java +275 -0
  21. package/android/src/main/java/com/aetherlink/dexeditor/editor/buffer/BufferCache.java +113 -0
  22. package/android/src/main/java/com/aetherlink/dexeditor/editor/buffer/GapBuffer.java +685 -0
  23. package/android/src/main/java/com/aetherlink/dexeditor/editor/component/ClipboardPanel.java +1380 -0
  24. package/android/src/main/java/com/aetherlink/dexeditor/editor/component/Magnifier.java +363 -0
  25. package/android/src/main/java/com/aetherlink/dexeditor/editor/highlight/Candidate.java +52 -0
  26. package/android/src/main/java/com/aetherlink/dexeditor/editor/highlight/CommentDef.java +47 -0
  27. package/android/src/main/java/com/aetherlink/dexeditor/editor/highlight/LineResult.java +49 -0
  28. package/android/src/main/java/com/aetherlink/dexeditor/editor/highlight/MHSyntaxHighlightEngine.java +841 -0
  29. package/android/src/main/java/com/aetherlink/dexeditor/editor/highlight/Rule.java +53 -0
  30. package/android/src/main/java/com/aetherlink/dexeditor/editor/highlight/Token.java +48 -0
  31. package/android/src/main/java/com/aetherlink/dexeditor/editor/listener/OnTextChangedListener.java +6 -0
  32. package/android/src/main/java/com/aetherlink/dexeditor/editor/utils/Pair.java +80 -0
  33. package/android/src/main/java/com/aetherlink/dexeditor/editor/utils/ScreenUtils.java +63 -0
  34. package/android/src/main/res/drawable/abc_text_cursor_material.xml +9 -0
  35. package/android/src/main/res/drawable/abc_text_select_handle_left_mtrl.png +0 -0
  36. package/android/src/main/res/drawable/abc_text_select_handle_middle_mtrl.png +0 -0
  37. package/android/src/main/res/drawable/abc_text_select_handle_right_mtrl.png +0 -0
  38. package/android/src/main/res/drawable/ic_arrow_back.xml +12 -0
  39. package/android/src/main/res/drawable/ic_copy.xml +11 -0
  40. package/android/src/main/res/drawable/ic_cut.xml +10 -0
  41. package/android/src/main/res/drawable/ic_delete.xml +12 -0
  42. package/android/src/main/res/drawable/ic_edit_white_24dp.xml +10 -0
  43. package/android/src/main/res/drawable/ic_goto.xml +10 -0
  44. package/android/src/main/res/drawable/ic_launcher_background.xml +186 -0
  45. package/android/src/main/res/drawable/ic_look_white_24dp.xml +11 -0
  46. package/android/src/main/res/drawable/ic_more.xml +10 -0
  47. package/android/src/main/res/drawable/ic_open_link.xml +11 -0
  48. package/android/src/main/res/drawable/ic_paste.xml +11 -0
  49. package/android/src/main/res/drawable/ic_redo_white_24dp.xml +10 -0
  50. package/android/src/main/res/drawable/ic_select.xml +11 -0
  51. package/android/src/main/res/drawable/ic_select_all.xml +10 -0
  52. package/android/src/main/res/drawable/ic_share.xml +11 -0
  53. package/android/src/main/res/drawable/ic_toggle_comment.xml +12 -0
  54. package/android/src/main/res/drawable/ic_translate.xml +10 -0
  55. package/android/src/main/res/drawable/ic_undo_white_24dp.xml +11 -0
  56. package/android/src/main/res/drawable/magnifier_bg.xml +5 -0
  57. package/android/src/main/res/drawable/popup_background.xml +8 -0
  58. package/android/src/main/res/drawable/ripple_effect.xml +9 -0
  59. package/android/src/main/res/drawable/selection_menu_background.xml +5 -0
  60. package/android/src/main/res/layout/custom_selection_menu.xml +44 -0
  61. package/android/src/main/res/layout/expand_button.xml +23 -0
  62. package/android/src/main/res/layout/item_autocomplete.xml +25 -0
  63. package/android/src/main/res/layout/magnifier_popup.xml +17 -0
  64. package/android/src/main/res/layout/menu_item.xml +30 -0
  65. package/android/src/main/res/layout/text_selection_menu.xml +36 -0
  66. package/package.json +1 -1
@@ -0,0 +1,685 @@
1
+ package com.aetherlink.dexeditor.editor.buffer;
2
+
3
+ import java.util.LinkedList;
4
+ import com.aetherlink.dexeditor.editor.utils.Pair;
5
+
6
+ /**
7
+ * GapBuffer is a threadsafe EditBuffer that is optimized for editing with a cursor which tends to
8
+ * make a sequence of inserts and deletes at the same place in the buffer
9
+ */
10
+ public class GapBuffer implements CharSequence {
11
+
12
+ private char[] _contents;
13
+ private int _gapStartIndex;
14
+ private int _gapEndIndex;
15
+ private int _lineCount;
16
+ private BufferCache _cache;
17
+ private UndoStack _undoStack;
18
+
19
+ private int _lastUndoSelStart = -1;
20
+ private int _lastUndoSelEnd = -1;
21
+ private boolean _lastUndoSelMode = false;
22
+
23
+ private int _lastRedoSelStart = -1;
24
+ private int _lastRedoSelEnd = -1;
25
+ private boolean _lastRedoSelMode = false;
26
+
27
+ private final int EOF = '\uFFFF';
28
+ private final int NEWLINE = '\n';
29
+
30
+ public GapBuffer() {
31
+ _contents = new char[16];
32
+ _lineCount = 1;
33
+ _gapStartIndex = 0;
34
+ _gapEndIndex = _contents.length;
35
+ _cache = new BufferCache();
36
+ _undoStack = new UndoStack();
37
+ }
38
+
39
+ public GapBuffer(String buffer) {
40
+ this();
41
+ insert(0, buffer, false);
42
+ }
43
+
44
+ public GapBuffer(char[] buffer) {
45
+ _contents = buffer;
46
+ _lineCount = 1;
47
+ _cache = new BufferCache();
48
+ _undoStack = new UndoStack();
49
+ for (char c : _contents) {
50
+ if (c == NEWLINE) _lineCount++;
51
+ }
52
+ }
53
+
54
+ public synchronized String getLine(int lineNumber) {
55
+ int startIndex = getLineOffset(lineNumber);
56
+ int length = getLineLength(lineNumber);
57
+ return substring(startIndex, startIndex + length);
58
+ }
59
+
60
+ public synchronized int getLineOffset(int lineNumber) {
61
+ if (lineNumber <= 0 || lineNumber > getLineCount()) {
62
+ throw new IllegalArgumentException("line index is invalid");
63
+ }
64
+
65
+ int lineIndex = --lineNumber;
66
+ Pair<Integer, Integer> cacheEntry = _cache.getNearestLine(lineIndex);
67
+ int cacheLine = cacheEntry.first;
68
+ int cacheOffset = cacheEntry.second;
69
+
70
+ int offset = 0;
71
+ if (lineIndex > cacheLine) {
72
+ offset = findCharOffset(lineIndex, cacheLine, cacheOffset);
73
+ } else if (lineIndex < cacheLine) {
74
+ offset = findCharOffsetBackward(lineIndex, cacheLine, cacheOffset);
75
+ } else {
76
+ offset = cacheOffset;
77
+ }
78
+
79
+ if (offset >= 0) {
80
+ _cache.updateEntry(lineIndex, offset);
81
+ }
82
+ return offset;
83
+ }
84
+
85
+ private int findCharOffset(int targetLine, int startLine, int startOffset) {
86
+ int currLine = startLine;
87
+ int offset = getRealIndex(startOffset);
88
+
89
+ while ((currLine < targetLine) && (offset < _contents.length)) {
90
+ if (_contents[offset] == NEWLINE) ++currLine;
91
+ ++offset;
92
+ if (offset == _gapStartIndex) offset = _gapEndIndex;
93
+ }
94
+
95
+ if (currLine != targetLine) return -1;
96
+ return getLogicalIndex(offset);
97
+ }
98
+
99
+ private int findCharOffsetBackward(int targetLine, int startLine, int startOffset) {
100
+ if (targetLine == 0) return 0;
101
+
102
+ int currLine = startLine;
103
+ int offset = getRealIndex(startOffset);
104
+ while (currLine > (targetLine - 1) && offset >= 0) {
105
+ if (offset == _gapEndIndex) offset = _gapStartIndex;
106
+ --offset;
107
+ if (_contents[offset] == NEWLINE) --currLine;
108
+ }
109
+
110
+ int charOffset;
111
+ if (offset >= 0) {
112
+ charOffset = getLogicalIndex(offset);
113
+ ++charOffset;
114
+ } else {
115
+ charOffset = -1;
116
+ }
117
+ return charOffset;
118
+ }
119
+
120
+ public synchronized int findLineNumber(int charOffset) {
121
+ if (charOffset < 0) return 1;
122
+ if (charOffset >= length()) return getLineCount();
123
+
124
+ Pair<Integer, Integer> cachedEntry = _cache.getNearestCharOffset(charOffset);
125
+ int line = cachedEntry.first;
126
+ int offset = getRealIndex(cachedEntry.second);
127
+ int targetOffset = getRealIndex(charOffset);
128
+ int lastKnownLine = -1;
129
+ int lastKnownCharOffset = -1;
130
+
131
+ if (targetOffset > offset) {
132
+ while ((offset < targetOffset) && (offset < _contents.length)) {
133
+ if (_contents[offset] == NEWLINE) {
134
+ ++line;
135
+ lastKnownLine = line;
136
+ lastKnownCharOffset = getLogicalIndex(offset) + 1;
137
+ }
138
+ ++offset;
139
+ if (offset == _gapStartIndex) offset = _gapEndIndex;
140
+ }
141
+ } else if (targetOffset < offset) {
142
+ while ((offset > targetOffset) && (offset > 0)) {
143
+ if (offset == _gapEndIndex) offset = _gapStartIndex;
144
+ --offset;
145
+ if (_contents[offset] == NEWLINE) {
146
+ lastKnownLine = line;
147
+ lastKnownCharOffset = getLogicalIndex(offset) + 1;
148
+ --line;
149
+ }
150
+ }
151
+ }
152
+
153
+ if (offset == targetOffset) {
154
+ if (lastKnownLine != -1) {
155
+ _cache.updateEntry(lastKnownLine, lastKnownCharOffset);
156
+ }
157
+ return line + 1;
158
+ } else {
159
+ return 1;
160
+ }
161
+ }
162
+
163
+ public synchronized int getLineLength(int lineNumber) {
164
+ int lineLength = 0;
165
+ int pos = getLineOffset(lineNumber);
166
+ pos = getRealIndex(pos);
167
+
168
+ while (pos < _contents.length && _contents[pos] != NEWLINE && _contents[pos] != EOF) {
169
+ ++lineLength;
170
+ ++pos;
171
+ if (pos == _gapStartIndex) pos = _gapEndIndex;
172
+ }
173
+ return lineLength;
174
+ }
175
+
176
+ public synchronized char charAt(int charOffset) {
177
+ if (charOffset < 0 || charOffset >= length()) return '\0';
178
+ int realIndex = getRealIndex(charOffset);
179
+ if (realIndex < 0 || realIndex >= _contents.length) return '\0';
180
+ return _contents[realIndex];
181
+ }
182
+
183
+ public synchronized CharSequence subSequence(int start, int end) {
184
+ if (start < 0) start = 0;
185
+ if (end > length()) end = length();
186
+ if (start >= end) return "";
187
+
188
+ int count = end - start;
189
+ int realIndex = getRealIndex(start);
190
+ char[] chars = new char[count];
191
+
192
+ for (int i = 0; i < count; ++i) {
193
+ if (realIndex >= _contents.length) break;
194
+ chars[i] = _contents[realIndex];
195
+ if (++realIndex == _gapStartIndex) realIndex = _gapEndIndex;
196
+ }
197
+ return new String(chars);
198
+ }
199
+
200
+ public synchronized String substring(int start, int end) {
201
+ if (start < 0) start = 0;
202
+ if (end > length()) end = length();
203
+ if (start >= end) return "";
204
+ return subSequence(start, end).toString();
205
+ }
206
+
207
+ public synchronized GapBuffer insert(int offset, String str, boolean capture) {
208
+ return insert(offset, str, capture, System.nanoTime());
209
+ }
210
+
211
+ public synchronized GapBuffer insert(int offset, String str, boolean capture, long timestamp) {
212
+ int length = str.length();
213
+ if (capture && length > 0) {
214
+ _undoStack.captureInsert(offset, offset + length, timestamp);
215
+ }
216
+
217
+ int insertIndex = getRealIndex(offset);
218
+
219
+ if (insertIndex != _gapEndIndex) {
220
+ if (isBeforeGap(insertIndex)) {
221
+ shiftGapLeft(insertIndex);
222
+ } else {
223
+ shiftGapRight(insertIndex);
224
+ }
225
+ }
226
+
227
+ if (length >= gapSize()) {
228
+ expandBuffer(length - gapSize());
229
+ }
230
+
231
+ for (int i = 0; i < length; ++i) {
232
+ char c = str.charAt(i);
233
+ if (c == NEWLINE) ++_lineCount;
234
+ _contents[_gapStartIndex] = c;
235
+ ++_gapStartIndex;
236
+ }
237
+
238
+ _cache.invalidateCache(offset);
239
+ return this;
240
+ }
241
+
242
+ public synchronized GapBuffer append(String str, boolean capture) {
243
+ insert(length(), str, capture);
244
+ return this;
245
+ }
246
+
247
+ public synchronized GapBuffer append(String str) {
248
+ insert(length(), str, false);
249
+ return this;
250
+ }
251
+
252
+ public synchronized GapBuffer delete(int start, int end, boolean capture) {
253
+ return delete(start, end, capture, System.nanoTime());
254
+ }
255
+
256
+ public synchronized GapBuffer delete(int start, int end, boolean capture, long timestamp) {
257
+ if (capture && start < end) {
258
+ _undoStack.captureDelete(start, end, timestamp);
259
+ }
260
+
261
+ int newGapStart = end;
262
+
263
+ if (newGapStart != _gapStartIndex) {
264
+ if (isBeforeGap(newGapStart)) {
265
+ shiftGapLeft(newGapStart);
266
+ } else {
267
+ shiftGapRight(newGapStart + gapSize());
268
+ }
269
+ }
270
+
271
+ int len = end - start;
272
+ for (int i = 0; i < len; ++i) {
273
+ --_gapStartIndex;
274
+ if (_contents[_gapStartIndex] == NEWLINE) --_lineCount;
275
+ }
276
+
277
+ _cache.invalidateCache(start);
278
+ return this;
279
+ }
280
+
281
+ public synchronized GapBuffer replace(int start, int end, String str, boolean capture) {
282
+ delete(start, end, capture);
283
+ insert(start, str, capture);
284
+ return this;
285
+ }
286
+
287
+ private char[] gapSubSequence(int charCount) {
288
+ char[] chars = new char[charCount];
289
+ for (int i = 0; i < charCount; ++i) {
290
+ chars[i] = _contents[_gapStartIndex + i];
291
+ }
292
+ return chars;
293
+ }
294
+
295
+ private synchronized void shiftGapStart(int displacement) {
296
+ if (displacement >= 0)
297
+ _lineCount += countNewlines(_gapStartIndex, displacement);
298
+ else
299
+ _lineCount -= countNewlines(_gapStartIndex + displacement, -displacement);
300
+
301
+ _gapStartIndex += displacement;
302
+ _cache.invalidateCache(getLogicalIndex(_gapStartIndex - 1) + 1);
303
+ }
304
+
305
+ private int countNewlines(int start, int totalChars) {
306
+ int newlines = 0;
307
+ for (int i = start; i < (start + totalChars); ++i) {
308
+ if (_contents[i] == NEWLINE) ++newlines;
309
+ }
310
+ return newlines;
311
+ }
312
+
313
+ private void shiftGapLeft(int newGapStart) {
314
+ while (_gapStartIndex > newGapStart) {
315
+ _gapEndIndex--;
316
+ _gapStartIndex--;
317
+ _contents[_gapEndIndex] = _contents[_gapStartIndex];
318
+ }
319
+ }
320
+
321
+ private void shiftGapRight(int newGapEnd) {
322
+ while (_gapEndIndex < newGapEnd) {
323
+ _contents[_gapStartIndex] = _contents[_gapEndIndex];
324
+ _gapStartIndex++;
325
+ _gapEndIndex++;
326
+ }
327
+ }
328
+
329
+ private void expandBuffer(int minIncrement) {
330
+ int incrSize = Math.max(minIncrement, _contents.length * 2 + 2);
331
+ char[] temp = new char[_contents.length + incrSize];
332
+
333
+ int i = 0;
334
+ while (i < _gapStartIndex) {
335
+ temp[i] = _contents[i];
336
+ ++i;
337
+ }
338
+
339
+ i = _gapEndIndex;
340
+ while (i < _contents.length) {
341
+ temp[i + incrSize] = _contents[i];
342
+ ++i;
343
+ }
344
+
345
+ _gapEndIndex += incrSize;
346
+ _contents = temp;
347
+ }
348
+
349
+ private boolean isValid(int charOffset) {
350
+ return (charOffset >= 0 && charOffset <= this.length());
351
+ }
352
+
353
+ private int gapSize() {
354
+ return _gapEndIndex - _gapStartIndex;
355
+ }
356
+
357
+ private int getRealIndex(int index) {
358
+ if (index < 0) return 0;
359
+ if (index >= length()) return _contents.length - 1;
360
+
361
+ if (isBeforeGap(index)) {
362
+ return index;
363
+ } else {
364
+ int realIndex = index + gapSize();
365
+ return Math.min(realIndex, _contents.length - 1);
366
+ }
367
+ }
368
+
369
+ private int getLogicalIndex(int index) {
370
+ if (isBeforeGap(index)) return index;
371
+ else return index - gapSize();
372
+ }
373
+
374
+ private boolean isBeforeGap(int index) {
375
+ return index < _gapStartIndex;
376
+ }
377
+
378
+ public synchronized int getLineCount() {
379
+ return _lineCount;
380
+ }
381
+
382
+ @Override
383
+ public synchronized int length() {
384
+ return _contents.length - gapSize();
385
+ }
386
+
387
+ @Override
388
+ public synchronized String toString() {
389
+ StringBuffer buf = new StringBuffer();
390
+ int len = this.length();
391
+ for (int i = 0; i < len; i++) {
392
+ buf.append(charAt(i));
393
+ }
394
+ return new String(buf);
395
+ }
396
+
397
+ public boolean canUndo() { return _undoStack.canUndo(); }
398
+ public boolean canRedo() { return _undoStack.canRedo(); }
399
+
400
+ public int undo() {
401
+ _lastUndoSelStart = _lastUndoSelEnd = -1;
402
+ _lastUndoSelMode = false;
403
+ return _undoStack.undo();
404
+ }
405
+
406
+ public int redo() {
407
+ _lastRedoSelStart = _lastRedoSelEnd = -1;
408
+ _lastRedoSelMode = false;
409
+ return _undoStack.redo();
410
+ }
411
+
412
+ public void markSelectionBefore(int selStart, int selEnd, boolean selMode) {
413
+ _undoStack.setPendingSelectionBefore(selStart, selEnd, selMode);
414
+ }
415
+
416
+ public void markSelectionAfter(int selStart, int selEnd, boolean selMode) {
417
+ _undoStack.setPendingSelectionAfter(selStart, selEnd, selMode);
418
+ }
419
+
420
+ public int getLastUndoSelectionStart() { return _lastUndoSelStart; }
421
+ public int getLastUndoSelectionEnd() { return _lastUndoSelEnd; }
422
+ public boolean getLastUndoSelectionMode() { return _lastUndoSelMode; }
423
+ public int getLastRedoSelectionStart() { return _lastRedoSelStart; }
424
+ public int getLastRedoSelectionEnd() { return _lastRedoSelEnd; }
425
+ public boolean getLastRedoSelectionMode() { return _lastRedoSelMode; }
426
+
427
+ public void beginBatchEdit() { _undoStack.beginBatchEdit(); }
428
+ public void endBatchEdit() { _undoStack.endBatchEdit(); }
429
+ public boolean isBatchEdit() { return _undoStack.isBatchEdit(); }
430
+
431
+ class UndoStack {
432
+ private boolean _isBatchEdit;
433
+ private int _groupId;
434
+ private int _top;
435
+ private long _lastEditTime = -1L;
436
+ private LinkedList<Action> _stack = new LinkedList<>();
437
+ private static final int MAX_UNDO_SIZE = 50000;
438
+ public final long MERGE_TIME = 1000000000;
439
+
440
+ private int _pendingSelBeforeStart = -1;
441
+ private int _pendingSelBeforeEnd = -1;
442
+ private boolean _pendingSelBeforeMode = false;
443
+ private int _pendingSelAfterStart = -1;
444
+ private int _pendingSelAfterEnd = -1;
445
+ private boolean _pendingSelAfterMode = false;
446
+
447
+ public int undo() {
448
+ if (canUndo()) {
449
+ Action lastUndo = _stack.get(_top - 1);
450
+ int group = lastUndo._group;
451
+ do {
452
+ Action action = _stack.get(_top - 1);
453
+ if (action._group != group) break;
454
+ lastUndo = action;
455
+ action.undo();
456
+ _top--;
457
+ } while (canUndo());
458
+
459
+ _lastUndoSelStart = lastUndo._selBeforeStart;
460
+ _lastUndoSelEnd = lastUndo._selBeforeEnd;
461
+ _lastUndoSelMode = lastUndo._selBeforeMode;
462
+ return lastUndo.findUndoPosition();
463
+ }
464
+ return -1;
465
+ }
466
+
467
+ public int redo() {
468
+ if (canRedo()) {
469
+ Action lastRedo = _stack.get(_top);
470
+ int group = lastRedo._group;
471
+ do {
472
+ Action action = _stack.get(_top);
473
+ if (action._group != group) break;
474
+ lastRedo = action;
475
+ action.redo();
476
+ _top++;
477
+ } while (canRedo());
478
+
479
+ _lastRedoSelStart = lastRedo._selAfterStart;
480
+ _lastRedoSelEnd = lastRedo._selAfterEnd;
481
+ _lastRedoSelMode = lastRedo._selAfterMode;
482
+ return lastRedo.findRedoPosition();
483
+ }
484
+ return -1;
485
+ }
486
+
487
+ public void setPendingSelectionBefore(int s, int e, boolean mode) {
488
+ _pendingSelBeforeStart = s; _pendingSelBeforeEnd = e; _pendingSelBeforeMode = mode;
489
+ }
490
+
491
+ public void setPendingSelectionAfter(int s, int e, boolean mode) {
492
+ _pendingSelAfterStart = s; _pendingSelAfterEnd = e; _pendingSelAfterMode = mode;
493
+ }
494
+
495
+ public void captureInsert(int start, int end, long time) {
496
+ int len = end - start;
497
+ boolean mergeSuccess = false;
498
+
499
+ if (canUndo()) {
500
+ Action action = _stack.get(_top - 1);
501
+ if (action instanceof InsertAction &&
502
+ (time - _lastEditTime) < MERGE_TIME &&
503
+ start == action._end &&
504
+ (action._end - action._start + len <= MAX_UNDO_SIZE)) {
505
+ action._end += len;
506
+ mergeSuccess = true;
507
+ } else {
508
+ action.recordData();
509
+ }
510
+ }
511
+
512
+ if (!mergeSuccess && len <= MAX_UNDO_SIZE) {
513
+ InsertAction a = new InsertAction(start, end, _groupId);
514
+ a._selBeforeStart = _pendingSelBeforeStart;
515
+ a._selBeforeEnd = _pendingSelBeforeEnd;
516
+ a._selBeforeMode = _pendingSelBeforeMode;
517
+ a._selAfterStart = _pendingSelAfterStart;
518
+ a._selAfterEnd = _pendingSelAfterEnd;
519
+ a._selAfterMode = _pendingSelAfterMode;
520
+ push(a);
521
+ if (!_isBatchEdit) _groupId++;
522
+ }
523
+ _lastEditTime = time;
524
+ }
525
+
526
+ public void captureDelete(int start, int end, long time) {
527
+ int len = end - start;
528
+ boolean mergeSuccess = false;
529
+
530
+ if (canUndo()) {
531
+ Action action = _stack.get(_top - 1);
532
+ if (action instanceof DeleteAction &&
533
+ (time - _lastEditTime) < MERGE_TIME &&
534
+ end == action._start &&
535
+ (action._end - start <= MAX_UNDO_SIZE)) {
536
+ action._start = start;
537
+ mergeSuccess = true;
538
+ } else {
539
+ action.recordData();
540
+ }
541
+ }
542
+
543
+ if (!mergeSuccess && len <= MAX_UNDO_SIZE) {
544
+ DeleteAction a = new DeleteAction(start, end, _groupId);
545
+ a._selBeforeStart = _pendingSelBeforeStart;
546
+ a._selBeforeEnd = _pendingSelBeforeEnd;
547
+ a._selBeforeMode = _pendingSelBeforeMode;
548
+ a._selAfterStart = _pendingSelAfterStart;
549
+ a._selAfterEnd = _pendingSelAfterEnd;
550
+ a._selAfterMode = _pendingSelAfterMode;
551
+ push(a);
552
+ if (!_isBatchEdit) _groupId++;
553
+ }
554
+ _lastEditTime = time;
555
+ }
556
+
557
+ private void push(Action action) {
558
+ trimStack();
559
+ _top++;
560
+ _stack.add(action);
561
+ }
562
+
563
+ private void trimStack() {
564
+ while (_stack.size() > _top) _stack.removeLast();
565
+ }
566
+
567
+ public final boolean canUndo() { return _top > 0; }
568
+ public final boolean canRedo() { return _top < _stack.size(); }
569
+ public boolean isBatchEdit() { return _isBatchEdit; }
570
+ public void beginBatchEdit() { _isBatchEdit = true; }
571
+ public void endBatchEdit() { _isBatchEdit = false; _groupId++; }
572
+
573
+ private abstract class Action {
574
+ public int _start;
575
+ public int _end;
576
+ public String _data;
577
+ public int _group;
578
+ public final long MERGE_TIME = 750000000L;
579
+
580
+ public int _selBeforeStart = -1;
581
+ public int _selBeforeEnd = -1;
582
+ public boolean _selBeforeMode = false;
583
+ public int _selAfterStart = -1;
584
+ public int _selAfterEnd = -1;
585
+ public boolean _selAfterMode = false;
586
+
587
+ public abstract void undo();
588
+ public abstract void redo();
589
+ public abstract void recordData();
590
+ public abstract int findUndoPosition();
591
+ public abstract int findRedoPosition();
592
+ public abstract boolean merge(int start, int end, long time);
593
+ }
594
+
595
+ private class InsertAction extends Action {
596
+ public InsertAction(int start, int end, int group) {
597
+ this._start = start;
598
+ this._end = end;
599
+ this._group = group;
600
+ }
601
+
602
+ @Override
603
+ public boolean merge(int start, int end, long time) {
604
+ if (_lastEditTime < 0) return false;
605
+ if ((time - _lastEditTime) < MERGE_TIME && start == _end) {
606
+ _end += end - start;
607
+ trimStack();
608
+ return true;
609
+ }
610
+ return false;
611
+ }
612
+
613
+ @Override
614
+ public void recordData() {
615
+ _data = substring(_start, _end);
616
+ }
617
+
618
+ @Override
619
+ public void undo() {
620
+ if (_data == null) {
621
+ recordData();
622
+ shiftGapStart(-(_end - _start));
623
+ } else {
624
+ delete(_start, _end, false, 0);
625
+ }
626
+ }
627
+
628
+ @Override
629
+ public void redo() {
630
+ insert(_start, _data, false, 0);
631
+ }
632
+
633
+ @Override
634
+ public int findRedoPosition() { return _end; }
635
+
636
+ @Override
637
+ public int findUndoPosition() { return _start; }
638
+ }
639
+
640
+ private class DeleteAction extends Action {
641
+ public DeleteAction(int start, int end, int group) {
642
+ this._start = start;
643
+ this._end = end;
644
+ this._group = group;
645
+ }
646
+
647
+ @Override
648
+ public boolean merge(int start, int end, long time) {
649
+ if (_lastEditTime < 0) return false;
650
+ if ((time - _lastEditTime) < MERGE_TIME && end == _start) {
651
+ _start = start;
652
+ trimStack();
653
+ return true;
654
+ }
655
+ return false;
656
+ }
657
+
658
+ @Override
659
+ public void recordData() {
660
+ _data = new String(gapSubSequence(_end - _start));
661
+ }
662
+
663
+ @Override
664
+ public void undo() {
665
+ if (_data == null) {
666
+ recordData();
667
+ shiftGapStart(_end - _start);
668
+ } else {
669
+ insert(_start, _data, false, 0);
670
+ }
671
+ }
672
+
673
+ @Override
674
+ public void redo() {
675
+ delete(_start, _end, false, 0);
676
+ }
677
+
678
+ @Override
679
+ public int findRedoPosition() { return _start; }
680
+
681
+ @Override
682
+ public int findUndoPosition() { return _end; }
683
+ }
684
+ }
685
+ }