Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util

import com.intellij.diff.comparison.ComparisonManager
import com.intellij.diff.comparison.ComparisonPolicy
import com.intellij.diff.fragments.LineFragment
import com.intellij.openapi.progress.EmptyProgressIndicator

data class DiffMetrics(
val insertedLines: Int,
val insertedCharacters: Int,
)

fun lineEnding(content: String, curr: Int, end: Int): Int {
require(curr <= end) { "curr must be within end of range" }
require(end <= content.length) { "end must be within content" }

return if (curr == end) {
-1
} else if (content[curr] == '\r') {
if ((curr + 1 < end) && (content[curr + 1] == '\n')) {
2
} else {
1
}
} else if (content[curr] == '\n') {
1
} else {
-1
}
}

fun getDiffMetrics(before: String, after: String): DiffMetrics {
val comparisonManager = ComparisonManager.getInstance()
val fragments = comparisonManager.compareLines(
before,
after,
ComparisonPolicy.DEFAULT,
EmptyProgressIndicator()
)

var accLineCount = 0
var accCharCount = 0

fragments.forEach { fragment: LineFragment ->
var curr = fragment.startOffset2
val end = fragment.endOffset2

while (curr < end) {
accLineCount += 1

// Consume leading whitespace:
while (curr < end && lineEnding(after, curr, end) == -1 && after[curr].isWhitespace()) curr++

// Consume through EOL:
val lineContentStart = curr
while (curr < end && lineEnding(after, curr, end) == -1) curr++
var lineContentEnd = curr
curr += maxOf(lineEnding(after, curr, end), 0)

// Walk back trailing whitespace and record character count before continuing to next line:
while (lineContentEnd > lineContentStart && after[lineContentEnd - 1].isWhitespace()) lineContentEnd--
accCharCount += lineContentEnd - lineContentStart
}
}

return DiffMetrics(
insertedLines = accLineCount,
insertedCharacters = accCharCount,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util

import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.testFramework.TestApplicationManager

class DiffMetricsTest : LightPlatformTestCase() {
override fun setUp() {
super.setUp()
TestApplicationManager.getInstance()
}

fun `test empty input`() {
val metrics = getDiffMetrics("", "")
assertEquals(0, metrics.insertedLines)
assertEquals(0, metrics.insertedCharacters)
}

fun `test insertions are counted`() {
val before = """
line1
line2
""".trimIndent()

val after = """
line1
inserted
line2
""".trimIndent()

val metrics = getDiffMetrics(before, after)
assertEquals(1, metrics.insertedLines)
assertEquals(8, metrics.insertedCharacters)
}

fun `test modifications are counted`() {
val before = """
line1
line2
line3
""".trimIndent()

val after = """
line1
modified
line3
""".trimIndent()

val metrics = getDiffMetrics(before, after)
assertEquals(1, metrics.insertedLines)
assertEquals(8, metrics.insertedCharacters)
}

fun `test deletions are counted`() {
val before = """
line1
line2
line3
""".trimIndent()

val after = """
line1
line3
""".trimIndent()

val metrics = getDiffMetrics(before, after)
assertEquals(0, metrics.insertedLines)
assertEquals(0, metrics.insertedCharacters)
}

fun `test multiline and multiple hunks are counted`() {
val before = """
line1
line2
line3
""".trimIndent()

val after = """
inserted1
line1
inserted2
inserted3
line3
inserted4
""".trimIndent()

val metrics = getDiffMetrics(before, after)
assertEquals(4, metrics.insertedLines)
assertEquals(36, metrics.insertedCharacters)
}

fun `test empty lines are counted`() {
val before = "line1"
val after = "line1\n\nline2"
val metrics = getDiffMetrics(before, after)
assertEquals(2, metrics.insertedLines)
assertEquals(5, metrics.insertedCharacters)
}

fun `test trailing newline is not counted`() {
val before = "line1"
val after = "line1\nline2\n"
val metrics = getDiffMetrics(before, after)
assertEquals(1, metrics.insertedLines)
assertEquals(5, metrics.insertedCharacters)
}

fun `test newline sequences are counted`() {
val before = "line1"
val after = "line1\nline2\rline3\r\nline4"
val metrics = getDiffMetrics(before, after)
assertEquals(3, metrics.insertedLines)
assertEquals(15, metrics.insertedCharacters)
}

fun `test leading and trailing whitespace are not counted as characters`() {
val before = "line1\nline2"
val after = "line1\n line2"

val metrics = getDiffMetrics(before, after)
assertEquals(1, metrics.insertedLines)
assertEquals(5, metrics.insertedCharacters)
}
}
Loading