Skip to content

Commit

Permalink
Renumber list performance improvement, by @harshad1 (PR #1688)
Browse files Browse the repository at this point in the history
  • Loading branch information
harshad1 authored Apr 29, 2022
1 parent 7616818 commit 0142c2e
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 34 deletions.
21 changes: 18 additions & 3 deletions app/src/main/java/net/gsantner/markor/format/AutoFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import android.annotation.SuppressLint;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;

import net.gsantner.opoc.util.StringUtils;
Expand Down Expand Up @@ -236,7 +237,7 @@ public UnOrderedOrCheckListLine(CharSequence text, int position, PrefixPatterns
* @param position Position within current line
* @return OrderedListLine corresponding to top of current list
*/
private static OrderedListLine getOrderedListStart(final Editable text, int position, final PrefixPatterns prefixPatterns) {
private static OrderedListLine getOrderedListStart(final CharSequence text, int position, final PrefixPatterns prefixPatterns) {
position = Math.max(Math.min(position, text.length() - 1), 0);
OrderedListLine listStart = new OrderedListLine(text, position, prefixPatterns);

Expand All @@ -261,14 +262,18 @@ private static OrderedListLine getOrderedListStart(final Editable text, int posi
* <p>
* This is an unfortunately complex + complicated function. Tweak at your peril and test a *lot* :)
*/
public static void renumberOrderedList(final Editable text, int cursorPosition, final PrefixPatterns prefixPatterns) {
public static void renumberOrderedList(final Editable edit, int cursorPosition, final PrefixPatterns prefixPatterns) {

// Top of list
final OrderedListLine firstLine = getOrderedListStart(text, cursorPosition, prefixPatterns);
final OrderedListLine firstLine = getOrderedListStart(edit, cursorPosition, prefixPatterns);
if (!firstLine.isOrderedList) {
return;
}

// Copy all the text if we are going to process
// SpannableStringBuilder makes the spans _appear_ to transition smoothly
final Editable text = new SpannableStringBuilder(edit);

// Stack represents each level in the list up from current
final Stack<OrderedListLine> levels = new Stack<>();
levels.push(firstLine);
Expand All @@ -277,6 +282,7 @@ public static void renumberOrderedList(final Editable text, int cursorPosition,
int position;

try {
boolean madeChange = false;
// Loop to end of list
while (firstLine.isParentLevelOf(line) || firstLine.isMatchingList(line)) {

Expand Down Expand Up @@ -312,7 +318,9 @@ else if (!line.isEmpty) {
final OrderedListLine peek = levels.peek();
final String newValue = line.equals(peek) ? "1" : getNextOrderedValue(peek.value);
if (!newValue.equals(line.value)) {

text.replace(line.numStart, line.numEnd, newValue);
madeChange = true;

// Re-create line as it has changed
line = new OrderedListLine(text, line.lineStart, prefixPatterns);
Expand All @@ -329,6 +337,13 @@ else if (!line.isEmpty) {
break;
}
}

// Replace the text in Editable in one chunk
if (madeChange) {
final int[] diff = StringUtils.findDiff(edit, text);
edit.replace(diff[0], diff[1], text.subSequence(diff[0], diff[2]));
}

} catch (EmptyStackException ex) {
// Usually means that indents and de-indents did not match up
ex.printStackTrace();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,17 +167,28 @@ public void removeTextChangedListener(final TextWatcher listener) {
_activeListeners.remove(listener);
}

// Run some code with accessibility disabled
public void withAccessibilityDisabled(final Callback.a0 callback) {
try {
_accessibilityEnabled = false;
callback.callback();
} finally {
_accessibilityEnabled = true;
}
}

// Run some code with auto formatters disabled
// Also disables accessibility
public void withAutoFormatDisabled(final Callback.a0 callback) {
if (getAutoFormatEnabled()) {
try {
setAutoFormatEnabled(false);
callback.callback();
withAccessibilityDisabled(() -> callback.callback());
} finally {
setAutoFormatEnabled(true);
}
} else {
callback.callback();
withAccessibilityDisabled(() -> callback.callback());
}
}

Expand Down Expand Up @@ -213,16 +224,14 @@ private void highlightWithoutChange() {
if (MainActivity.IS_DEBUG_ENABLED) {
AppSettings.appendDebugLog("Start highlighting");
}
setAccessibilityEnabled(false);
_hl.run(getText());
withAccessibilityDisabled(() ->_hl.run(getText()));
} catch (Exception e) {
// In no case ever let highlighting crash the editor
e.printStackTrace();
} catch (Error e) {
e.printStackTrace();
} finally {
setAccessibilityEnabled(true);
}

if (MainActivity.IS_DEBUG_ENABLED) {
AppSettings.appendDebugLog(_hl._profiler.resetDebugText());
AppSettings.appendDebugLog("Finished highlighting");
Expand Down Expand Up @@ -323,12 +332,4 @@ protected void onSelectionChanged(int selStart, int selEnd) {
AppSettings.appendDebugLog("Selection changed: " + selStart + "->" + selEnd);
}
}

public void setAccessibilityEnabled(final boolean enabled) {
_accessibilityEnabled = enabled;
}

public boolean getAccessibilityEnabled() {
return _accessibilityEnabled;
}
}
21 changes: 4 additions & 17 deletions app/src/main/java/net/gsantner/markor/ui/hleditor/TextActions.java
Original file line number Diff line number Diff line change
Expand Up @@ -388,15 +388,10 @@ public static void runRegexReplaceAction(final EditText editor, final ReplacePat
* @param matchAll Whether to stop matching subsequent ReplacePatterns after first match+replace
*/
public static void runRegexReplaceAction(final EditText editor, final List<ReplacePattern> patterns, final boolean matchAll) {
try {
if (editor instanceof HighlightingEditor) {
((HighlightingEditor) editor).setAccessibilityEnabled(false);
}
if (editor instanceof HighlightingEditor) {
((HighlightingEditor) editor).withAutoFormatDisabled(() -> _runRegexReplaceAction(editor, patterns, matchAll));
} else {
_runRegexReplaceAction(editor, patterns, matchAll);
} finally {
if (editor instanceof HighlightingEditor) {
((HighlightingEditor) editor).setAccessibilityEnabled(true);
}
}
}

Expand Down Expand Up @@ -772,15 +767,7 @@ public final void runRenumberOrderedListIfRequired() {

public final void runRenumberOrderedListIfRequired(final boolean force) {
if (force || _hlEditor.getAutoFormatEnabled()) {
final boolean isAccessibilityEnabled = _hlEditor.getAccessibilityEnabled();
try {
_hlEditor.setAccessibilityEnabled(false);
_hlEditor.withAutoFormatDisabled(() -> renumberOrderedList(StringUtils.getSelection(_hlEditor)[0]));
} finally {
if (isAccessibilityEnabled) {
_hlEditor.setAccessibilityEnabled(true);
}
}
_hlEditor.withAutoFormatDisabled(() -> renumberOrderedList(StringUtils.getSelection(_hlEditor)[0]));
}
}

Expand Down
26 changes: 26 additions & 0 deletions app/src/main/java/net/gsantner/opoc/util/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -452,4 +452,30 @@ public static String interpolateEscapedDateTime(final String snip) {
interpolated.append(temp); // Remaining text
return interpolated.toString();
}

// Find the smallest single difference region { a, b, c }
// s.t. setting dest[a:b] = source[a:c] makes dest == source
public static int[] findDiff(final CharSequence dest, final CharSequence source) {

final int dl = dest.length(), sl = source.length();
final int minLength = Math.min(dl, sl);

int start = 0;
while(start < minLength && source.charAt(start) == dest.charAt(start)) start++;

// Handle several special cases
if (sl == dl && start == sl) { // Case where 2 sequences are same
return new int[] { sl, sl, sl };
} else if (sl < dl && start == sl) { // Pure crop
return new int[] { sl, dl, sl };
} else if (dl < sl && start == dl) { // Pure append
return new int[] { dl, dl, sl };
}

int end = 0;
final int maxEnd = minLength - start;
while(end < maxEnd && source.charAt(sl - end - 1) == dest.charAt(dl - end - 1)) end++;

return new int[] { start, dl - end, sl - end };
}
}
29 changes: 29 additions & 0 deletions app/src/test/java/net/gsantner/markor/StringUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.gsantner.markor;

import static org.assertj.core.api.Assertions.assertThat;

import net.gsantner.opoc.util.StringUtils;

import org.junit.Test;

public class StringUtilsTest {

@Test
public void findDiffTest() {
assertThat(StringUtils.findDiff("", "")).isEqualTo(new int[] {0, 0, 0});
assertThat(StringUtils.findDiff("abcd", "abcd")).isEqualTo(new int[] {4, 4, 4});
assertThat(StringUtils.findDiff("ab", "abcd")).isEqualTo(new int[] {2, 2, 4});
assertThat(StringUtils.findDiff("abcd", "ab")).isEqualTo(new int[] {2, 4, 2});
assertThat(StringUtils.findDiff("ab1d", "ab2d")).isEqualTo(new int[] {2, 3, 3});
assertThat(StringUtils.findDiff("ab12d", "ab34d")).isEqualTo(new int[] {2, 4, 4});
assertThat(StringUtils.findDiff("ab12d", "ab3d")).isEqualTo(new int[] {2, 4, 3});
assertThat(StringUtils.findDiff("ab12d", "abd")).isEqualTo(new int[] {2, 4, 2});
assertThat(StringUtils.findDiff("abd", "ab12d")).isEqualTo(new int[] {2, 2, 4});
assertThat(StringUtils.findDiff("abcd", "")).isEqualTo(new int[] {0, 4, 0});
assertThat(StringUtils.findDiff("", "abcd")).isEqualTo(new int[] {0, 0, 4});
assertThat(StringUtils.findDiff("ab11d", "ab1d")).isEqualTo(new int[] {3, 4, 3});
assertThat(StringUtils.findDiff("aaaaa", "aaa")).isEqualTo(new int[] {3, 5, 3});
assertThat(StringUtils.findDiff("aaa", "aaaaa")).isEqualTo(new int[] {3, 3, 5});
}
}

0 comments on commit 0142c2e

Please sign in to comment.