0
0
mirror of https://github.com/ankidroid/Anki-Android.git synced 2024-09-20 20:03:05 +02:00

[Kotlin Cleanup] LaTeX (#11980)

* Cleanup of LaTeX.kt

* Modified tests to use string literals, new hashes, functions to public

* Renamed functions to be more accurate, params/output to String, comment style
This commit is contained in:
Raymond Zeng 2022-08-10 02:12:40 -07:00 committed by GitHub
parent 3752358577
commit f83ce04675
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 91 additions and 48 deletions

View File

@ -19,7 +19,6 @@ package com.ichi2.libanki
import androidx.annotation.VisibleForTesting
import com.ichi2.utils.HtmlUtils.escape
import com.ichi2.utils.KotlinCleanup
import java.util.regex.Matcher
import java.util.regex.Pattern
@ -37,20 +36,19 @@ import java.util.regex.Pattern
* Unlike the original python implementation of this class, the AnkiDroid version does not support
* the generation of LaTeX images.
*/
@KotlinCleanup("fix IDE lint issues")
object LaTeX {
/**
* Patterns used to identify LaTeX tags
*/
val STANDARD_PATTERN = Pattern.compile(
private val STANDARD_PATTERN = Pattern.compile(
"\\[latex](.+?)\\[/latex]",
Pattern.DOTALL or Pattern.CASE_INSENSITIVE
)
val EXPRESSION_PATTERN = Pattern.compile(
private val EXPRESSION_PATTERN = Pattern.compile(
"\\[\\$](.+?)\\[/\\$]",
Pattern.DOTALL or Pattern.CASE_INSENSITIVE
)
val MATH_PATTERN = Pattern.compile(
private val MATH_PATTERN = Pattern.compile(
"\\[\\$\\$](.+?)\\[/\\$\\$]",
Pattern.DOTALL or Pattern.CASE_INSENSITIVE
)
@ -64,49 +62,61 @@ object LaTeX {
return mungeQA(html, col.media, model)
}
@JvmStatic
fun convertHTML(html: String, media: Media, model: Model): String {
val stringBuffer = StringBuffer()
STANDARD_PATTERN.matcher(html).run {
while (find()) {
appendReplacement(stringBuffer, imgLink(group(1)!!, model, media))
}
appendTail(stringBuffer)
}
return stringBuffer.toString()
}
@JvmStatic
fun convertExpression(input: String, media: Media, model: Model): String {
val stringBuffer = StringBuffer()
EXPRESSION_PATTERN.matcher(input).run {
while (find()) {
appendReplacement(stringBuffer, imgLink("$" + group(1) + "$", model, media))
}
appendTail(stringBuffer)
}
return stringBuffer.toString()
}
@JvmStatic
fun convertMath(input: String, media: Media, model: Model): String {
val stringBuffer = StringBuffer()
MATH_PATTERN.matcher(input).run {
while (find()) {
appendReplacement(
stringBuffer,
imgLink("\\begin{displaymath}" + group(1) + "\\end{displaymath}", model, media)
)
}
appendTail(stringBuffer)
}
return stringBuffer.toString()
}
// It's only goal is to allow testing with a different media manager.
@VisibleForTesting
@JvmStatic
@KotlinCleanup("refactor each matcher/sb code group into a standalone function")
fun mungeQA(html: String, m: Media, model: Model): String {
@KotlinCleanup("declare val variables for sb and matcher for each instantiation instead of using a single var variable")
var sb = StringBuffer()
@KotlinCleanup("use a scope function like run/with to have matcher in scope to simplify its usage")
var matcher = STANDARD_PATTERN.matcher(html)
while (matcher.find()) {
matcher.appendReplacement(sb, _imgLink(matcher.group(1)!!, model, m))
fun mungeQA(html: String, m: Media, model: Model): String =
arrayOf(::convertHTML, ::convertExpression, ::convertMath).fold(html) { input, transformer ->
transformer(input, m, model)
}
matcher.appendTail(sb)
matcher = EXPRESSION_PATTERN.matcher(sb.toString())
sb = StringBuffer()
while (matcher.find()) {
matcher.appendReplacement(sb, _imgLink("$" + matcher.group(1) + "$", model, m))
}
matcher.appendTail(sb)
matcher = MATH_PATTERN.matcher(sb.toString())
sb = StringBuffer()
while (matcher.find()) {
matcher.appendReplacement(
sb,
_imgLink("\\begin{displaymath}" + matcher.group(1) + "\\end{displaymath}", model, m)
)
}
matcher.appendTail(sb)
return sb.toString()
}
/**
* Return an img link for LATEX.
*/
@VisibleForTesting
@JvmStatic
internal fun _imgLink(latex: String, model: Model, m: Media): String {
val txt = _latexFromHtml(latex)
@KotlinCleanup("use an if expression to determine extension type and make ext a val")
var ext = "png"
if (model.optBoolean("latexsvg", false)) {
ext = "svg"
}
internal fun imgLink(latex: String, model: Model, m: Media): String {
val txt = latexFromHtml(latex)
val ext = if (model.optBoolean("latexsvg", false)) "svg" else "png"
val fname = "latex-" + Utils.checksum(txt) + "." + ext
return if (m.have(fname)) {
Matcher.quoteReplacement("<img class=latex alt=\"" + escape(latex) + "\" src=\"" + fname + "\">")
@ -119,10 +129,5 @@ object LaTeX {
* Convert entities and fix newlines.
*/
@JvmStatic
@KotlinCleanup("remove the intermediary var, reduce function body to single line by inlining the method calls")
private fun _latexFromHtml(latex: String): String {
var l = latex.replace("<br( /)?>|<div>".toRegex(), "\n")
l = Utils.stripHTML(l)
return l
}
private fun latexFromHtml(latex: String): String = Utils.stripHTML(latex.replace("<br( /)?>|<div>".toRegex(), "\n"))
}

View File

@ -40,26 +40,64 @@ class LaTeXTest : RobolectricTest() {
val m: Media = MockMedia(col)
val model = col.models.byName("Basic")!!
// The hashing function should never change, as it would broke link. So hard coding the expected hash value is valid
//  Test with media access
// Test with media access
assertThat(
LaTeX._imgLink("$\\sqrt[3]{2} + \\text{\"var\"}$", model, m),
LaTeX.imgLink("$\\sqrt[3]{2} + \\text{\"var\"}$", model, m),
equalTo("<img class=latex alt=\"\\$\\\\sqrt[3]{2} + \\\\text{&quot;var&quot;}\\$\" src=\"latex-dd84e5d506179a137f7924d0960609a8c89d491e.png\">")
)
// Test without access to media
assertThat(
LaTeX._imgLink("$\\sqrt[3]{2} + \\text{\"var\"}$", model, col.media),
LaTeX.imgLink("$\\sqrt[3]{2} + \\text{\"var\"}$", model, col.media),
equalTo("\\$\\\\sqrt[3]{2} + \\\\text{\"var\"}\\$")
)
}
@Test
fun htmlMatchTest() {
val col = col
val media: Media = MockMedia(col)
val model = col.models.byName("Basic")!!
// The hashing function should never change, as it would broke link. So hard coding the expected hash value is valid
// Test with media access
assertThat(
LaTeX.convertHTML("""[latex]\sqrt[3]{2} + \text{"var"}[/latex]""", media, model),
equalTo("""<img class=latex alt="\sqrt[3]{2} + \text{&quot;var&quot;}" src="latex-def68dc5a5ada07529f673b6493464e94f88c3df.png">""")
)
// Test without access to media
assertThat(
LaTeX.convertHTML("""[latex]\sqrt[3]{2} + \text{"var"}[/latex]""", col.media, model),
equalTo("""\sqrt[3]{2} + \text{"var"}""")
)
}
@Test
fun mathMatchTest() {
val col = col
val media: Media = MockMedia(col)
val model = col.models.byName("Basic")!!
// The hashing function should never change, as it would broke link. So hard coding the expected hash value is valid
// Test with media access
assertThat(
LaTeX.convertMath("""[$$]\sqrt[3]{2} + \text{"var"}[/$$]""", media, model),
equalTo("""<img class=latex alt="\begin{displaymath}\sqrt[3]{2} + \text{&quot;var&quot;}\end{displaymath}" src="latex-ac92a31b0e2dc842ac2b3542a68f81d89438793a.png">""")
)
// Test without access to media
assertThat(
LaTeX.convertMath("""[$$]\sqrt[3]{2} + \text{"var"}[/$$]""", col.media, model),
equalTo("""\begin{displaymath}\sqrt[3]{2} + \text{"var"}\end{displaymath}""")
)
}
@Test
fun mungeQATest() {
val col = col
val m: Media = MockMedia(col)
val model = col.models.byName("Basic")!!
//  Test with media access
// Test with media access
assertThat(
LaTeX.mungeQA("[$]\\sqrt[3]{2} + \\text{\"var\"}[/$]", m, model),
equalTo("<img class=latex alt=\"$\\sqrt[3]{2} + \\text{&quot;var&quot;}$\" src=\"latex-dd84e5d506179a137f7924d0960609a8c89d491e.png\">")