mirror of
				https://codeberg.org/Bazsalanszky/Infinity-For-Lemmy.git
				synced 2025-10-30 00:18:07 +01:00 
			
		
		
		
	Optimize spoiler parser
Should take 3-4x less milliseconds, on average
This commit is contained in:
		| @@ -3,6 +3,7 @@ package ml.docilealligator.infinityforreddit.markdown; | ||||
| import android.graphics.Color; | ||||
| import android.text.SpannableStringBuilder; | ||||
| import android.text.Spanned; | ||||
| import android.util.Pair; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| @@ -13,8 +14,7 @@ import org.commonmark.node.HtmlBlock; | ||||
| import org.commonmark.node.HtmlInline; | ||||
| import org.commonmark.parser.Parser; | ||||
|  | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.Map; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Set; | ||||
| import java.util.Stack; | ||||
|  | ||||
| @@ -52,21 +52,21 @@ public class SpoilerParserPlugin extends AbstractMarkwonPlugin { | ||||
|     public void afterSetText(@NonNull TextView textView) { | ||||
|         textView.setHighlightColor(Color.TRANSPARENT); | ||||
|  | ||||
|         if(textView.getText().length() < 5) { | ||||
|         if (textView.getText().length() < 5) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         SpannableStringBuilder markdownStringBuilder = new SpannableStringBuilder(textView.getText()); | ||||
|  | ||||
|         LinkedHashMap<Integer, Integer> spoilers = parse(markdownStringBuilder); | ||||
|         if(spoilers.size() == 0) { | ||||
|         ArrayList<Pair<Integer, Integer>> spoilers = parse(markdownStringBuilder); | ||||
|         if (spoilers.size() == 0) { | ||||
|             return; | ||||
|         } | ||||
|         int offset = 2; | ||||
|  | ||||
|         for (Map.Entry<Integer, Integer> entry : spoilers.entrySet()) { | ||||
|             int spoilerStart = entry.getKey() - offset; | ||||
|             int spoilerEnd = entry.getValue() - offset; | ||||
|         int offset = 2; | ||||
|         for (Pair<Integer, Integer> spoiler : spoilers) { | ||||
|             int spoilerStart = spoiler.first - offset; | ||||
|             int spoilerEnd = spoiler.second - offset; | ||||
|  | ||||
|             // Try not to set a spoiler span if it's inside a CodeSpan | ||||
|             CodeSpan[] codeSpans = markdownStringBuilder.getSpans(spoilerStart, spoilerEnd, CodeSpan.class); | ||||
| @@ -78,6 +78,7 @@ public class SpoilerParserPlugin extends AbstractMarkwonPlugin { | ||||
|                 SpoilerSpan spoilerSpan = new SpoilerSpan(textColor, backgroundColor); | ||||
|                 markdownStringBuilder.setSpan(spoilerSpan, spoilerStart, spoilerEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); | ||||
|                 offset += 4; | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             for (CodeSpan codeSpan : codeSpans) { | ||||
| @@ -108,7 +109,7 @@ public class SpoilerParserPlugin extends AbstractMarkwonPlugin { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if(offset > 2) { | ||||
|         if (offset > 2) { | ||||
|             textView.setText(markdownStringBuilder); | ||||
|         } | ||||
|     } | ||||
| @@ -117,86 +118,44 @@ public class SpoilerParserPlugin extends AbstractMarkwonPlugin { | ||||
|     // Don't allow more than one new line after every non-blank line | ||||
|     // Try not to care about recursing spoilers, we just want the outermost spoiler because | ||||
|     // spoiler revealing-hiding breaks with recursing spoilers | ||||
|     private LinkedHashMap<Integer, Integer> parse(SpannableStringBuilder markdown) { | ||||
|     private ArrayList<Pair<Integer, Integer>> parse(SpannableStringBuilder markdown) { | ||||
|         final int MAX_NEW_LINE = 1; | ||||
|         var openSpoilerStack = new Stack<Integer>(); | ||||
|         var closedSpoilerMap = new LinkedHashMap<Integer, Integer>(); | ||||
|         int variable_max_depth = calculateBalance(0, markdown) + 1; | ||||
|         int length = markdown.length(); | ||||
|         Stack<Integer> openSpoilerStack = new Stack<>(); | ||||
|         ArrayList<Pair<Integer, Integer>> closedSpoilers = new ArrayList<>(); | ||||
|         int new_lines = 0; | ||||
|         int depth = 0; | ||||
|         for (int i = 0; i < markdown.length(); i++) { | ||||
|             if (markdown.charAt(i) == '\u2000' || markdown.charAt(i) == '\t') { | ||||
|                 continue; | ||||
|             } else if (markdown.charAt(i) == '>' && (i + 1) < markdown.length() && markdown.charAt(i + 1) == '!') { | ||||
|                 openSpoilerStack.push(i + 1); | ||||
|                 depth++; | ||||
|             } else if (openSpoilerStack.size() > 0 | ||||
|                     && markdown.charAt(i) == '!' && (i + 1) < markdown.length() | ||||
|                     && markdown.charAt(i + 1) == '<') { | ||||
|                 var pos = i + 1; | ||||
|                 for (int j = 0; j < depth; j++) { | ||||
|                     if (!openSpoilerStack.isEmpty()) pos = openSpoilerStack.peek(); | ||||
|                     if (pos + 1 <= i) { | ||||
|                         if (!openSpoilerStack.isEmpty()) pos = openSpoilerStack.peek(); | ||||
|                         break; | ||||
|                     } else { | ||||
|                         if (!openSpoilerStack.isEmpty()) pos = openSpoilerStack.pop(); | ||||
|                     } | ||||
|                 } | ||||
|                 if (depth <= variable_max_depth && pos + 1 <= i) //Spoiler content cannot be zero or less length | ||||
|                 { | ||||
|                     openSpoilerStack.clear(); | ||||
|                     closedSpoilerMap.put(pos + 1, i); | ||||
|                 } | ||||
|                 depth--; | ||||
|             } else if (markdown.charAt(i) == '\n') { | ||||
|         for (int i = 0; i < length; i++) { | ||||
|             if (markdown.charAt(i) == '\n') { | ||||
|                 new_lines++; | ||||
|                 if (openSpoilerStack.size() >= 1 && new_lines > MAX_NEW_LINE) { | ||||
|                     openSpoilerStack.clear(); | ||||
|                     new_lines = 0; | ||||
|                     depth = 0; | ||||
|                     variable_max_depth = calculateBalance(i, markdown) + 1; | ||||
|                 } | ||||
|             } else { | ||||
|                 new_lines = 0; | ||||
|             } | ||||
|  | ||||
|             if (openSpoilerStack.size() >= 32) // No | ||||
|             { | ||||
|                 openSpoilerStack.clear(); | ||||
|                 closedSpoilerMap.clear(); | ||||
|                 continue; | ||||
|             } | ||||
|             if ((markdown.charAt(i) != '>') && (markdown.charAt(i) != '<') && (markdown.charAt(i) != '!')) { | ||||
|                 continue; | ||||
|             } | ||||
|             if ((i + 1 < length) | ||||
|                     && markdown.charAt(i) == '>' | ||||
|                     && markdown.charAt(i + 1) == '!') { | ||||
|                 openSpoilerStack.push(i + 2); | ||||
|                 continue; | ||||
|             } | ||||
|             if ((i + 1 < length) && (i - 1 >= 0) | ||||
|                     && openSpoilerStack.size() > 0 | ||||
|                     && markdown.charAt(i - 1) != '>' | ||||
|                     && markdown.charAt(i) == '!' | ||||
|                     && markdown.charAt(i + 1) == '<') { | ||||
|                 var pos = openSpoilerStack.pop(); | ||||
|                 if (!closedSpoilers.isEmpty() | ||||
|                         && closedSpoilers.get(closedSpoilers.size() - 1).first > pos | ||||
|                         && closedSpoilers.get(closedSpoilers.size() - 1).second < i) { | ||||
|                     closedSpoilers.remove(closedSpoilers.size() - 1); | ||||
|                 } | ||||
|                 closedSpoilers.add(Pair.create(pos, i)); | ||||
|             } | ||||
|         } | ||||
|         return closedSpoilerMap; | ||||
|         return closedSpoilers; | ||||
|     } | ||||
|  | ||||
|     private int calculateBalance(int index, SpannableStringBuilder line) { | ||||
|         final int MAX_NEW_LINE = 1; | ||||
|         int new_lines = 0; | ||||
|         int opening = 0; | ||||
|         int closing = 0; | ||||
|         for (int i = index; i < line.length(); i++) { | ||||
|             if (line.charAt(i) == '\u0020' || line.charAt(i) == '\t') { | ||||
|                 continue; | ||||
|             } else if (line.charAt(i) == '>' | ||||
|                     && (i + 1) < line.length() | ||||
|                     && line.charAt(i + 1) == '!') { | ||||
|                 opening++; | ||||
|             } else if (line.charAt(i) == '!' && (i + 1) < line.length() | ||||
|                     && line.charAt(i + 1) == '<') { | ||||
|                 closing++; | ||||
|             } else if (line.charAt(i) == '\n') { | ||||
|                 new_lines++; | ||||
|                 if (new_lines > MAX_NEW_LINE) { | ||||
|                     break; | ||||
|                 } | ||||
|             } else { | ||||
|                 new_lines = 0; | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
|         return Math.abs(opening - closing); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user