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,275 @@
1
+ /*
2
+ * MH-TextEditor - An Advanced and optimized TextEditor for android
3
+ * Copyright 2025, developer-krushna
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are
7
+ * met:
8
+ *
9
+ * * Redistributions of source code must retain the above copyright
10
+ * notice, this list of conditions and the following disclaimer.
11
+ * * Redistributions in binary form must reproduce the above
12
+ * copyright notice, this list of conditions and the following disclaimer
13
+ * in the documentation and/or other materials provided with the
14
+ * distribution.
15
+ * * Neither the name of developer-krushna nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+
31
+
32
+ * Please contact Krushna by email modder-hub@zohomail.in if you need
33
+ * additional information or have any questions
34
+ */
35
+
36
+ package com.aetherlink.dexeditor.editor;
37
+
38
+ import android.util.Log;
39
+ import com.aetherlink.dexeditor.editor.EditView;
40
+ import com.aetherlink.dexeditor.editor.GapBuffer;
41
+ import java.util.ArrayList;
42
+ import java.util.List;
43
+
44
+ // Not ready yet
45
+ public class WordWrapLayout {
46
+ private static final String TAG = "WordWrapLayout";
47
+
48
+ private EditView mEditView;
49
+ private GapBuffer mGapBuffer;
50
+ private List<RowRegion> mRowTable;
51
+ private int mEditorWidth;
52
+ private boolean mEnabled = false;
53
+
54
+ public WordWrapLayout(EditView editView) {
55
+ this.mEditView = editView;
56
+ this.mGapBuffer = editView.getBuffer();
57
+ this.mRowTable = new ArrayList<>();
58
+ this.mEditorWidth = editView.getWidth();
59
+ }
60
+
61
+ public void setEnabled(boolean enabled) {
62
+ this.mEnabled = enabled;
63
+ if (enabled) {
64
+ breakAllLines();
65
+ } else {
66
+ mRowTable.clear();
67
+ }
68
+ }
69
+
70
+ public boolean isEnabled() {
71
+ return mEnabled;
72
+ }
73
+
74
+ public void setEditorWidth(int width) {
75
+ this.mEditorWidth = width;
76
+ if (mEnabled) {
77
+ breakAllLines();
78
+ }
79
+ }
80
+
81
+ private void breakAllLines() {
82
+ if (!mEnabled || mGapBuffer == null) return;
83
+
84
+ mRowTable.clear();
85
+
86
+ for (int line = 1; line <= mGapBuffer.getLineCount(); line++) {
87
+ breakLine(line);
88
+ }
89
+
90
+ Log.d(TAG, "Word wrap completed. Total rows: " + mRowTable.size());
91
+ }
92
+
93
+ private void breakLine(int lineNumber) {
94
+ if (!mEnabled) return;
95
+
96
+ String lineText = mGapBuffer.getLine(lineNumber);
97
+ if (lineText == null || lineText.isEmpty()) {
98
+ // Empty line still needs one row
99
+ mRowTable.add(new RowRegion(lineNumber, 0, 0));
100
+ return;
101
+ }
102
+
103
+ int lineStart = mGapBuffer.getLineOffset(lineNumber);
104
+ int currentPos = 0;
105
+ int lineLength = lineText.length();
106
+
107
+ while (currentPos < lineLength) {
108
+ int breakPoint = findBreakPoint(lineText, currentPos, lineLength);
109
+
110
+ if (breakPoint == currentPos) {
111
+ // Force break if no space found
112
+ breakPoint = Math.min(currentPos + 1, lineLength);
113
+ }
114
+
115
+ mRowTable.add(new RowRegion(lineNumber, lineStart + currentPos, lineStart + breakPoint));
116
+ currentPos = breakPoint;
117
+ }
118
+
119
+ // If we didn't add any rows (shouldn't happen), add at least one
120
+ if (mRowTable.isEmpty() || mRowTable.get(mRowTable.size() - 1).line != lineNumber) {
121
+ mRowTable.add(new RowRegion(lineNumber, lineStart, lineStart + lineLength));
122
+ }
123
+ }
124
+
125
+ private int findBreakPoint(String text, int start, int maxLength) {
126
+ if (start >= maxLength) return maxLength;
127
+
128
+ int availableWidth = mEditorWidth - mEditView.getLeftSpace() - 20; // Some padding
129
+ int currentWidth = 0;
130
+ int lastBreakable = start;
131
+
132
+ for (int i = start; i < maxLength; i++) {
133
+ char c = text.charAt(i);
134
+ String charStr = String.valueOf(c);
135
+ int charWidth = mEditView.measureText(charStr);
136
+
137
+ // Check if adding this character would exceed available width
138
+ if (currentWidth + charWidth > availableWidth) {
139
+ if (lastBreakable > start) {
140
+ // Break at last breakable position
141
+ return lastBreakable;
142
+ } else {
143
+ // No breakable position found, break here
144
+ return i;
145
+ }
146
+ }
147
+
148
+ currentWidth += charWidth;
149
+
150
+ // Mark breakable positions (spaces, punctuation)
151
+ if (isBreakableChar(c)) {
152
+ lastBreakable = i + 1; // Break after the space/punctuation
153
+ }
154
+
155
+ // Break at newlines
156
+ if (c == '\n') {
157
+ return i + 1;
158
+ }
159
+ }
160
+
161
+ return maxLength;
162
+ }
163
+
164
+ private boolean isBreakableChar(char c) {
165
+ return c == ' ' || c == '\t' || c == '.' || c == ',' || c == ';' || c == ':' ||
166
+ c == '!' || c == '?' || c == ')' || c == ']' || c == '}';
167
+ }
168
+
169
+ public void onTextChanged(int startLine, int endLine) {
170
+ if (!mEnabled) return;
171
+
172
+ // Remove affected rows
173
+ for (int i = mRowTable.size() - 1; i >= 0; i--) {
174
+ RowRegion region = mRowTable.get(i);
175
+ if (region.line >= startLine && region.line <= endLine) {
176
+ mRowTable.remove(i);
177
+ }
178
+ }
179
+
180
+ // Re-break affected lines
181
+ for (int line = startLine; line <= endLine; line++) {
182
+ breakLine(line);
183
+ }
184
+
185
+ // Update line numbers for subsequent lines
186
+ int delta = endLine - startLine;
187
+ if (delta != 0) {
188
+ for (int i = 0; i < mRowTable.size(); i++) {
189
+ RowRegion region = mRowTable.get(i);
190
+ if (region.line > endLine) {
191
+ region.line += delta;
192
+ }
193
+ }
194
+ }
195
+ }
196
+
197
+ public int getRowCount() {
198
+ if (!mEnabled) {
199
+ return mGapBuffer.getLineCount();
200
+ }
201
+ return mRowTable.size();
202
+ }
203
+
204
+ public int getLineForRow(int row) {
205
+ if (!mEnabled || row < 0 || row >= mRowTable.size()) {
206
+ return Math.max(1, Math.min(row + 1, mGapBuffer.getLineCount()));
207
+ }
208
+ return mRowTable.get(row).line;
209
+ }
210
+
211
+ public RowRegion getRowRegion(int row) {
212
+ if (!mEnabled || row < 0 || row >= mRowTable.size()) {
213
+ int line = Math.max(1, Math.min(row + 1, mGapBuffer.getLineCount()));
214
+ int lineStart = mGapBuffer.getLineOffset(line);
215
+ int lineEnd = lineStart + mGapBuffer.getLine(line).length();
216
+ return new RowRegion(line, lineStart, lineEnd);
217
+ }
218
+ return mRowTable.get(row);
219
+ }
220
+
221
+ public int findRowForPosition(int line, int column) {
222
+ if (!mEnabled) {
223
+ return line - 1; // Convert to 0-based
224
+ }
225
+
226
+ for (int row = 0; row < mRowTable.size(); row++) {
227
+ RowRegion region = mRowTable.get(row);
228
+ if (region.line == line && column >= region.start && column < region.end) {
229
+ return row;
230
+ }
231
+ }
232
+
233
+ // Fallback: find the first row for this line
234
+ for (int row = 0; row < mRowTable.size(); row++) {
235
+ if (mRowTable.get(row).line == line) {
236
+ return row;
237
+ }
238
+ }
239
+
240
+ return Math.max(0, line - 1);
241
+ }
242
+
243
+ public int getRowHeight() {
244
+ return mEditView.getLineHeight();
245
+ }
246
+
247
+ public int getTotalHeight() {
248
+ return getRowCount() * getRowHeight();
249
+ }
250
+
251
+ public class RowRegion {
252
+ public int line;
253
+ public int start;
254
+ public int end;
255
+
256
+ public RowRegion(int line, int start, int end) {
257
+ this.line = line;
258
+ this.start = start;
259
+ this.end = end;
260
+ }
261
+
262
+ public boolean isFirstRow() {
263
+ return start == mGapBuffer.getLineOffset(line);
264
+ }
265
+
266
+ public int getLength() {
267
+ return end - start;
268
+ }
269
+
270
+ @Override
271
+ public String toString() {
272
+ return "RowRegion{line=" + line + ", start=" + start + ", end=" + end + "}";
273
+ }
274
+ }
275
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * A LRU cache that stores the last seek line and its corresponding index so that future lookups can
3
+ * start from the cached position instead of the beginning of the file
4
+ *
5
+ * <p>_cache.Pair.First = line index _cache.Pair.Second = character offset of first character in
6
+ * that line
7
+ *
8
+ * <p>TextBufferCache always has one valid entry (0,0) signifying that in line 0, the first
9
+ * character is at offset 0. This is true even for an "empty" file, which is not really empty
10
+ * because TextBuffer inserts a EOF character in it.
11
+ *
12
+ * <p>Therefore, _cache[0] is always occupied by the entry (0,0). It is not affected by
13
+ * invalidateCache, cache miss, etc. operations
14
+ */
15
+ package com.aetherlink.dexeditor.editor.buffer;
16
+
17
+ import com.aetherlink.dexeditor.editor.utils.Pair;
18
+
19
+ public class BufferCache {
20
+ private final int CACHE_SIZE = 4; // minimum = 1
21
+ private Pair<Integer, Integer>[] _cache;
22
+
23
+ public BufferCache() {
24
+ _cache = new Pair[CACHE_SIZE];
25
+ _cache[0] = new Pair<Integer, Integer>(0, 0); // invariant lineIndex and charOffset relation
26
+ for (int i = 1; i < CACHE_SIZE; ++i) {
27
+ _cache[i] = new Pair<Integer, Integer>(-1, -1);
28
+ // -1 line index is used implicitly in calculations in getNearestMatch
29
+ }
30
+ }
31
+
32
+ // TODO consider extracting common parts with getNearestCharOffset(int)
33
+ public Pair<Integer, Integer> getNearestLine(int lineIndex) {
34
+ int nearestMatch = 0;
35
+ int nearestDistance = Integer.MAX_VALUE;
36
+ for (int i = 0; i < CACHE_SIZE; ++i) {
37
+ int distance = Math.abs(lineIndex - _cache[i].first);
38
+ if (distance < nearestDistance) {
39
+ nearestDistance = distance;
40
+ nearestMatch = i;
41
+ }
42
+ }
43
+
44
+ Pair<Integer, Integer> nearestEntry = _cache[nearestMatch];
45
+ makeHead(nearestMatch);
46
+ return nearestEntry;
47
+ }
48
+
49
+ public Pair<Integer, Integer> getNearestCharOffset(int charOffset) {
50
+ int nearestMatch = 0;
51
+ int nearestDistance = Integer.MAX_VALUE;
52
+ for (int i = 0; i < CACHE_SIZE; ++i) {
53
+ int distance = Math.abs(charOffset - _cache[i].second);
54
+ if (distance < nearestDistance) {
55
+ nearestDistance = distance;
56
+ nearestMatch = i;
57
+ }
58
+ }
59
+
60
+ Pair<Integer, Integer> nearestEntry = _cache[nearestMatch];
61
+ makeHead(nearestMatch);
62
+ return nearestEntry;
63
+ }
64
+
65
+ /** Place _cache[newHead] at the top of the list */
66
+ private void makeHead(int newHead) {
67
+ if (newHead == 0) {
68
+ return; // nothing to do for _cache[0]
69
+ }
70
+
71
+ Pair<Integer, Integer> temp = _cache[newHead];
72
+ for (int i = newHead; i > 1; --i) {
73
+ _cache[i] = _cache[i - 1];
74
+ }
75
+ _cache[1] = temp; // _cache[0] is always occupied by (0,0)
76
+ }
77
+
78
+ public void updateEntry(int lineIndex, int charOffset) {
79
+ if (lineIndex <= 0) {
80
+ // lineIndex 0 always has 0 charOffset; ignore. Also ignore negative lineIndex
81
+ return;
82
+ }
83
+
84
+ if (!replaceEntry(lineIndex, charOffset)) {
85
+ insertEntry(lineIndex, charOffset);
86
+ }
87
+ }
88
+
89
+ private boolean replaceEntry(int lineIndex, int charOffset) {
90
+ for (int i = 1; i < CACHE_SIZE; ++i) {
91
+ if (_cache[i].first == lineIndex) {
92
+ _cache[i].second = charOffset;
93
+ return true;
94
+ }
95
+ }
96
+ return false;
97
+ }
98
+
99
+ private void insertEntry(int lineIndex, int charOffset) {
100
+ makeHead(CACHE_SIZE - 1); // rotate right list of entries
101
+ // replace head (most recently used entry) with new entry
102
+ _cache[1] = new Pair<Integer, Integer>(lineIndex, charOffset);
103
+ }
104
+
105
+ /** Invalidate all cache entries that have char offset >= fromCharOffset */
106
+ public void invalidateCache(int fromCharOffset) {
107
+ for (int i = 1; i < CACHE_SIZE; ++i) {
108
+ if (_cache[i].second >= fromCharOffset) {
109
+ _cache[i] = new Pair<Integer, Integer>(-1, -1);
110
+ }
111
+ }
112
+ }
113
+ }