Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance improvement on parsing from html #790

Merged
merged 8 commits into from
Mar 7, 2019
20 changes: 18 additions & 2 deletions aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import android.content.Context
import android.support.v4.text.TextDirectionHeuristicsCompat
import android.text.Editable
import android.text.Spannable
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.TextUtils
Expand Down Expand Up @@ -57,6 +58,21 @@ import java.util.Comparator

class AztecParser @JvmOverloads constructor(val plugins: List<IAztecPlugin> = listOf(),
private val ignoredTags: List<String> = listOf("body", "html")) {
/**
* A faster version of fromHtml(), intended for inspecting the span structure only. It doesn't prepare the text for
* visual editing.
*/
@Suppress("unused") // this method is used in wpandroid so, suppress the inspection
fun parseHtmlForInspection(source: String, context: Context): Spanned {
val tidySource = tidy(source)

val spanned = SpannableString(Html.fromHtml(tidySource,
AztecTagHandler(context, plugins), context, plugins, ignoredTags))

postprocessSpans(spanned)

return spanned
}

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

private fun postprocessSpans(spannable: SpannableStringBuilder) {
private fun postprocessSpans(spannable: Spannable) {
plugins.filter { it is ISpanPostprocessor }
.map { it as ISpanPostprocessor }
.forEach {
Expand Down Expand Up @@ -236,7 +252,7 @@ class AztecParser @JvmOverloads constructor(val plugins: List<IAztecPlugin> = li
}

// Always try to put a visual newline before block elements and only put one after if needed
fun syncVisualNewlinesOfBlockElements(spanned: Editable) {
fun syncVisualNewlinesOfBlockElements(spanned: Spannable) {
// clear any visual newline marking. We'll mark them with a fresh set of passes
spanned.getSpans(0, spanned.length, AztecVisualLinebreak::class.java).forEach {
spanned.removeSpan(it)
Expand Down
20 changes: 18 additions & 2 deletions aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ import java.util.ArrayList
class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = ArrayList()) : Html.TagHandler {
private val loadingDrawable: Drawable

// Simple LIFO stack to track the html tag nesting for easy reference when we need to handle the ending of a tag
private val tagStack = mutableListOf<Any>()

init {
val styles = context.obtainStyledAttributes(R.styleable.AztecText)
loadingDrawable = ContextCompat.getDrawable(context, styles.getResourceId(R.styleable.AztecText_drawableLoading, R.drawable.ic_image_loading))!!
Expand Down Expand Up @@ -163,8 +166,8 @@ class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = Ar
start(output, AztecMediaClickableSpan(mediaSpan))
output.append(Constants.IMG_CHAR)
} else {
end(output, mediaSpan.javaClass)
end(output, AztecMediaClickableSpan::class.java)
end(output, mediaSpan.javaClass)
hypest marked this conversation as resolved.
Show resolved Hide resolved
}
}

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

private fun start(output: Editable, mark: Any) {
tagStack.add(mark)

output.setSpan(mark, output.length, output.length, Spanned.SPAN_MARK_MARK)
}

private fun end(output: Editable, kind: Class<*>) {
val last = output.getLast(kind)
// Get most recent tag type from the stack instead of `getLast()`. This is a speed optimization as `getLast()`
// doesn't know that the tags are in fact nested and in pairs (since it's html), including empty html elements
// (they are treated as pairs by tagsoup anyway).
val last = if (tagStack.size > 0 && kind.equals(tagStack[tagStack.size - 1].javaClass)) {
tagStack.removeAt(tagStack.size - 1) // remove and return the top mark on the stack
} else {
// Warning: the tags stack is apparently inconsistent at this point

// fall back to getting the last tag type from the Spannable
output.getLast(kind)
}

val start = output.getSpanStart(last)
val end = output.length

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.wordpress.aztec.plugins

import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.Spanned
import org.wordpress.aztec.plugins.html2visual.ISpanPostprocessor
Expand Down Expand Up @@ -50,7 +51,7 @@ class CssUnderlinePlugin : ISpanPostprocessor, ISpanPreprocessor {
}
}

override fun afterSpansProcessed(spannable: SpannableStringBuilder) {
override fun afterSpansProcessed(spannable: Spannable) {
spannable.getSpans(0, spannable.length, HiddenHtmlSpan::class.java).forEach {
if (it.TAG == SPAN_TAG && CssStyleFormatter.containsStyleAttribute(it.attributes, CssStyleFormatter.CSS_TEXT_DECORATION_ATTRIBUTE)) {
CssStyleFormatter.removeStyleAttribute(it.attributes, CssStyleFormatter.CSS_TEXT_DECORATION_ATTRIBUTE)
Expand All @@ -62,4 +63,4 @@ class CssUnderlinePlugin : ISpanPostprocessor, ISpanPreprocessor {
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.wordpress.aztec.plugins.html2visual

import android.text.SpannableStringBuilder
import android.text.Spannable
import org.wordpress.aztec.plugins.IAztecPlugin

interface ISpanPostprocessor : IAztecPlugin {
fun afterSpansProcessed(spannable: SpannableStringBuilder)
}
fun afterSpansProcessed(spannable: Spannable)
}
2 changes: 1 addition & 1 deletion aztec/src/main/kotlin/org/wordpress/aztec/source/Format.kt
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ object Format {
}

@JvmStatic
fun preProcessSpannedText(text: SpannableStringBuilder, isCalypsoFormat: Boolean) {
fun preProcessSpannedText(text: Spannable, isCalypsoFormat: Boolean) {
if (isCalypsoFormat) {
text.getSpans(0, text.length, AztecVisualLinebreak::class.java).forEach {
val spanStart = text.getSpanStart(it)
Expand Down