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

Shaped keyword swap for partial analysis #52

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group = "dev.snipme"
version = "0.9.1"
version = "0.9.2"

kotlin {
// Android
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ internal object CodeAnalyzer {
codeSnapshot.structure - newStructure.move(lengthDifference)
}

is CodeDifference.Swap -> {
val newStructure = analyzeForLanguage(difference.new, codeSnapshot.language)
codeSnapshot.structure.replace(difference.old, newStructure)
}

CodeDifference.None -> return codeSnapshot.structure
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ private const val WORDS_DELIMITER = " "
internal sealed class CodeDifference {
data class Increase(val change: String) : CodeDifference()
data class Decrease(val change: String) : CodeDifference()
data class Swap(val old: String, val new: String) : CodeDifference()
object None : CodeDifference()
}

Expand All @@ -14,7 +15,20 @@ internal object CodeComparator {
val updatedWords = updated.tokenize()

return when {
currentWords.size == updatedWords.size -> CodeDifference.None
currentWords.size == updatedWords.size -> {
if (currentWords == updatedWords) {
CodeDifference.None
} else {
val different = updatedWords.filterIndexed { index, updated ->
updated != currentWords[index]
}

CodeDifference.Swap(
old = currentWords.joinToString(WORDS_DELIMITER),
new = different.joinToString(WORDS_DELIMITER)
)
}
}

currentWords.size < updatedWords.size -> CodeDifference.Increase(
findDifference(
Expand Down
14 changes: 14 additions & 0 deletions src/commonMain/kotlin/dev/snipme/highlights/model/CodeStructure.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ data class CodeStructure(
incremental = true,
)

fun replace(old: PhraseLocation, new: CodeStructure): CodeStructure =
// TODO
CodeStructure(
marks = marks - old + new.marks,
punctuations = punctuations - old + new.punctuations,
keywords = keywords - old + new.keywords,
strings = strings - old + new.strings,
literals = literals - old + new.literals,
comments = comments - old + new.comments,
multilineComments = multilineComments - old + new.multilineComments,
annotations = annotations - old + new.annotations,
incremental = true,
)

fun printStructure(code: String) {
print("marks = ${marks.join(code)}")
print("punctuations = ${punctuations.join(code)}")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package dev.snipme.highlights.internal

import dev.snipme.highlights.Highlights
import dev.snipme.highlights.model.ColorHighlight
import dev.snipme.highlights.model.PhraseLocation
import dev.snipme.highlights.model.SyntaxLanguage
import kotlin.test.Test
Expand Down Expand Up @@ -253,4 +255,72 @@ internal class CodeAnalyzerTest {
result.annotations
)
}


@Test
fun basic_getHighlights_location() {
val highlighter = Highlights.Builder()
.language(SyntaxLanguage.JAVASCRIPT)
.build()

val code1 = "const foo = 'bar';";

highlighter.setCode(code1)
val highlights1 = highlighter.getHighlights().filterIsInstance<ColorHighlight>().sortedBy { it.location.start }
highlights1.size shouldEqual 4
highlights1[0].location shouldEqual PhraseLocation(start=0, end=5)
highlights1[1].location shouldEqual PhraseLocation(start=10, end=11)
highlights1[2].location shouldEqual PhraseLocation(start=12, end=17)
highlights1[3].location shouldEqual PhraseLocation(start=17, end=18)

highlights1.map { it.location }.printResults(code1)

val code2 = "const foo = 'barrr';"

highlighter.setCode(code2)
val highlights2 = highlighter.getHighlights().filterIsInstance<ColorHighlight>().sortedBy { it.location.start }
highlights2.map { it.location }.printResults(code2)
highlights2.size shouldEqual 4
highlights2[0].location shouldEqual PhraseLocation(start=0, end=5)
highlights2[1].location shouldEqual PhraseLocation(start=10, end=11)
// FAILS HERE:
highlights2[2].location shouldEqual PhraseLocation(start=12, end=19)
highlights2[3].location shouldEqual PhraseLocation(start=19, end=20)

}

@Test
fun basic_getHighlights_location_alt() {
var highlighter = Highlights.Builder()
.language(SyntaxLanguage.JAVASCRIPT)
.build()

highlighter = highlighter
.getBuilder()
.code("const foo = 'bar';")
.build()

val highlights1 = highlighter.getHighlights().filterIsInstance<ColorHighlight>().sortedBy { it.location.start }
highlights1.size shouldEqual 4
highlights1[0].location shouldEqual PhraseLocation(start=0, end=5)
highlights1[1].location shouldEqual PhraseLocation(start=10, end=11)
highlights1[2].location shouldEqual PhraseLocation(start=12, end=17)
highlights1[3].location shouldEqual PhraseLocation(start=17, end=18)

highlighter = highlighter
.getBuilder()
.code("const foo = 'barrr';")
.build()
val highlights2 = highlighter.getHighlights().filterIsInstance<ColorHighlight>().sortedBy { it.location.start }
highlights2.size shouldEqual 4
highlights2[0].location shouldEqual PhraseLocation(start=0, end=5)
highlights2[1].location shouldEqual PhraseLocation(start=10, end=11)
highlights2[2].location shouldEqual PhraseLocation(start=12, end=19)
highlights2[3].location shouldEqual PhraseLocation(start=19, end=20)
}

private infix fun Any?.shouldEqual(expected: Any?) {
assertEquals(expected, this)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,50 @@ internal class CodeComparatorTest {
assertEquals(CodeDifference.None, result)
}

@Test
fun `Returns difference for the char change in token`() {
val currentCode = "const foo = 'bar';"

val newCode = "const foo = 'baz';"

val result = CodeComparator.difference(currentCode, newCode)

assertEquals(CodeDifference.Swap("'bar'", "'baz'"), result)
}

@Test
fun `Returns difference for the char change in token`() {
val currentCode = "const foo = 'bar';"

val newCode = "co,st foo = 'bar';"

val result = CodeComparator.difference(currentCode, newCode)

assertEquals(CodeDifference.Swap("const", "co,st"), result)
}

@Test
fun `Returns difference for the char addition in single token`() {
val currentCode = "const foo = 'bar';"

val newCode = "const foo = 'barrr';"

val result = CodeComparator.difference(currentCode, newCode)

assertEquals(CodeDifference.Swap("'bar'", "'barrr'"), result)
}

@Test
fun `Returns difference for the char subtraction in single token`() {
val currentCode = "const foo = 'barrr';"

val newCode = "const foo = 'bar';"

val result = CodeComparator.difference(currentCode, newCode)

assertEquals(CodeDifference.Swap("'barrr'", "'bar'"), result)
}

@Test
fun `Returns only difference for complex code change`() {
val currentCode = """
Expand Down