Skip to content

Commit c92ff7b

Browse files
authored
Merge pull request #790 from wordpress-mobile/feature/performance-improvement
Performance improvement on parsing from html
2 parents 415ae8a + ee83fa8 commit c92ff7b

File tree

5 files changed

+43
-10
lines changed

5 files changed

+43
-10
lines changed

aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import android.content.Context
2222
import android.support.v4.text.TextDirectionHeuristicsCompat
2323
import android.text.Editable
2424
import android.text.Spannable
25+
import android.text.SpannableString
2526
import android.text.SpannableStringBuilder
2627
import android.text.Spanned
2728
import android.text.TextUtils
@@ -57,6 +58,21 @@ import java.util.Comparator
5758

5859
class AztecParser @JvmOverloads constructor(val plugins: List<IAztecPlugin> = listOf(),
5960
private val ignoredTags: List<String> = listOf("body", "html")) {
61+
/**
62+
* A faster version of fromHtml(), intended for inspecting the span structure only. It doesn't prepare the text for
63+
* visual editing.
64+
*/
65+
@Suppress("unused") // this method is used in wpandroid so, suppress the inspection
66+
fun parseHtmlForInspection(source: String, context: Context): Spanned {
67+
val tidySource = tidy(source)
68+
69+
val spanned = SpannableString(Html.fromHtml(tidySource,
70+
AztecTagHandler(context, plugins), context, plugins, ignoredTags))
71+
72+
postprocessSpans(spanned)
73+
74+
return spanned
75+
}
6076

6177
fun fromHtml(source: String, context: Context): Spanned {
6278
val tidySource = tidy(source)
@@ -117,7 +133,7 @@ class AztecParser @JvmOverloads constructor(val plugins: List<IAztecPlugin> = li
117133
return html
118134
}
119135

120-
private fun postprocessSpans(spannable: SpannableStringBuilder) {
136+
private fun postprocessSpans(spannable: Spannable) {
121137
plugins.filter { it is ISpanPostprocessor }
122138
.map { it as ISpanPostprocessor }
123139
.forEach {
@@ -236,7 +252,7 @@ class AztecParser @JvmOverloads constructor(val plugins: List<IAztecPlugin> = li
236252
}
237253

238254
// Always try to put a visual newline before block elements and only put one after if needed
239-
fun syncVisualNewlinesOfBlockElements(spanned: Editable) {
255+
fun syncVisualNewlinesOfBlockElements(spanned: Spannable) {
240256
// clear any visual newline marking. We'll mark them with a fresh set of passes
241257
spanned.getSpans(0, spanned.length, AztecVisualLinebreak::class.java).forEach {
242258
spanned.removeSpan(it)

aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ import java.util.ArrayList
5353
class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = ArrayList()) : Html.TagHandler {
5454
private val loadingDrawable: Drawable
5555

56+
// Simple LIFO stack to track the html tag nesting for easy reference when we need to handle the ending of a tag
57+
private val tagStack = mutableListOf<Any>()
58+
5659
init {
5760
val styles = context.obtainStyledAttributes(R.styleable.AztecText)
5861
loadingDrawable = ContextCompat.getDrawable(context, styles.getResourceId(R.styleable.AztecText_drawableLoading, R.drawable.ic_image_loading))!!
@@ -163,8 +166,8 @@ class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = Ar
163166
start(output, AztecMediaClickableSpan(mediaSpan))
164167
output.append(Constants.IMG_CHAR)
165168
} else {
166-
end(output, mediaSpan.javaClass)
167169
end(output, AztecMediaClickableSpan::class.java)
170+
end(output, mediaSpan.javaClass)
168171
}
169172
}
170173

@@ -177,11 +180,24 @@ class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = Ar
177180
}
178181

179182
private fun start(output: Editable, mark: Any) {
183+
tagStack.add(mark)
184+
180185
output.setSpan(mark, output.length, output.length, Spanned.SPAN_MARK_MARK)
181186
}
182187

183188
private fun end(output: Editable, kind: Class<*>) {
184-
val last = output.getLast(kind)
189+
// Get most recent tag type from the stack instead of `getLast()`. This is a speed optimization as `getLast()`
190+
// doesn't know that the tags are in fact nested and in pairs (since it's html), including empty html elements
191+
// (they are treated as pairs by tagsoup anyway).
192+
val last = if (tagStack.size > 0 && kind.equals(tagStack[tagStack.size - 1].javaClass)) {
193+
tagStack.removeAt(tagStack.size - 1) // remove and return the top mark on the stack
194+
} else {
195+
// Warning: the tags stack is apparently inconsistent at this point
196+
197+
// fall back to getting the last tag type from the Spannable
198+
output.getLast(kind)
199+
}
200+
185201
val start = output.getSpanStart(last)
186202
val end = output.length
187203

aztec/src/main/kotlin/org/wordpress/aztec/plugins/CssUnderlinePlugin.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.wordpress.aztec.plugins
22

3+
import android.text.Spannable
34
import android.text.SpannableStringBuilder
45
import android.text.Spanned
56
import org.wordpress.aztec.plugins.html2visual.ISpanPostprocessor
@@ -50,7 +51,7 @@ class CssUnderlinePlugin : ISpanPostprocessor, ISpanPreprocessor {
5051
}
5152
}
5253

53-
override fun afterSpansProcessed(spannable: SpannableStringBuilder) {
54+
override fun afterSpansProcessed(spannable: Spannable) {
5455
spannable.getSpans(0, spannable.length, HiddenHtmlSpan::class.java).forEach {
5556
if (it.TAG == SPAN_TAG && CssStyleFormatter.containsStyleAttribute(it.attributes, CssStyleFormatter.CSS_TEXT_DECORATION_ATTRIBUTE)) {
5657
CssStyleFormatter.removeStyleAttribute(it.attributes, CssStyleFormatter.CSS_TEXT_DECORATION_ATTRIBUTE)
@@ -62,4 +63,4 @@ class CssUnderlinePlugin : ISpanPostprocessor, ISpanPreprocessor {
6263
}
6364
}
6465
}
65-
}
66+
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package org.wordpress.aztec.plugins.html2visual
22

3-
import android.text.SpannableStringBuilder
3+
import android.text.Spannable
44
import org.wordpress.aztec.plugins.IAztecPlugin
55

66
interface ISpanPostprocessor : IAztecPlugin {
7-
fun afterSpansProcessed(spannable: SpannableStringBuilder)
8-
}
7+
fun afterSpansProcessed(spannable: Spannable)
8+
}

aztec/src/main/kotlin/org/wordpress/aztec/source/Format.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ object Format {
298298
}
299299

300300
@JvmStatic
301-
fun preProcessSpannedText(text: SpannableStringBuilder, isCalypsoFormat: Boolean) {
301+
fun preProcessSpannedText(text: Spannable, isCalypsoFormat: Boolean) {
302302
if (isCalypsoFormat) {
303303
text.getSpans(0, text.length, AztecVisualLinebreak::class.java).forEach {
304304
val spanStart = text.getSpanStart(it)

0 commit comments

Comments
 (0)