mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-10 03:59:43 +00:00
refactor: replace inline completion diffing with editor suffix matching
This commit is contained in:
parent
25d8835db1
commit
808fdfaf7e
3 changed files with 33 additions and 113 deletions
|
|
@ -18,7 +18,6 @@ import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings
|
|||
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings
|
||||
import ee.carlrobert.codegpt.settings.service.ollama.OllamaSettings
|
||||
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings
|
||||
import ee.carlrobert.codegpt.util.StringUtil.findCompletionParts
|
||||
import kotlinx.coroutines.channels.ProducerScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
|
|
@ -26,6 +25,7 @@ import kotlinx.coroutines.flow.emptyFlow
|
|||
import kotlinx.coroutines.launch
|
||||
import okhttp3.sse.EventSource
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.math.min
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
|
|
@ -129,8 +129,8 @@ class DebouncedCodeCompletionProvider : DebouncedInlineCompletionProvider() {
|
|||
|
||||
override fun onLineReceived(completionLine: String) {
|
||||
runInEdt {
|
||||
val editorLineSuffix = editor.getLineSuffixAfterCaret()
|
||||
if (editorLineSuffix.isEmpty()) {
|
||||
var editorLineSuffix = editor.getLineSuffixAfterCaret()
|
||||
if (editorLineSuffix.isBlank()) {
|
||||
trySend(
|
||||
CodeCompletionTextElement(
|
||||
completionLine,
|
||||
|
|
@ -139,31 +139,46 @@ class DebouncedCodeCompletionProvider : DebouncedInlineCompletionProvider() {
|
|||
)
|
||||
)
|
||||
} else {
|
||||
var prevStartOffset = infillRequest.caretOffset
|
||||
val completionParts =
|
||||
findCompletionParts(editorLineSuffix, completionLine.trimEnd())
|
||||
var caretShift = 0
|
||||
|
||||
completionParts.forEach { (completionPart, offsetDelta) ->
|
||||
val element = CodeCompletionTextElement(
|
||||
completionPart,
|
||||
infillRequest.caretOffset + offsetDelta,
|
||||
TextRange.from(prevStartOffset + offsetDelta, completionPart.length),
|
||||
offsetDelta,
|
||||
// TODO: Handle other scenarios
|
||||
val processedCompletion =
|
||||
if (completionLine.startsWith(editorLineSuffix.first())) {
|
||||
caretShift++
|
||||
editorLineSuffix = editorLineSuffix.substring(1)
|
||||
completionLine.substring(1)
|
||||
} else {
|
||||
completionLine
|
||||
}
|
||||
|
||||
val completionWithRemovedSuffix =
|
||||
processedCompletion.removeSuffix(editorLineSuffix)
|
||||
|
||||
trySend(
|
||||
CodeCompletionTextElement(
|
||||
completionWithRemovedSuffix,
|
||||
infillRequest.caretOffset + caretShift,
|
||||
TextRange.from(
|
||||
infillRequest.caretOffset + caretShift,
|
||||
completionWithRemovedSuffix.length
|
||||
),
|
||||
caretShift,
|
||||
completionLine
|
||||
)
|
||||
prevStartOffset += completionPart.length
|
||||
|
||||
trySend(element)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Editor.getLineSuffixAfterCaret(): String {
|
||||
val lineEndOffset = document.getLineEndOffset(document.getLineNumber(caretModel.offset))
|
||||
return document.getText(TextRange(caretModel.offset, lineEndOffset))
|
||||
return document.getText(
|
||||
TextRange(
|
||||
caretModel.offset,
|
||||
min(lineEndOffset + 1, document.textLength)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendNextSuggestion(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package ee.carlrobert.codegpt.util
|
||||
|
||||
import ai.grazie.nlp.utils.takeWhitespaces
|
||||
import com.intellij.util.diff.Diff
|
||||
|
||||
object StringUtil {
|
||||
|
||||
|
|
@ -23,44 +22,4 @@ object StringUtil {
|
|||
|
||||
return completionLine
|
||||
}
|
||||
|
||||
fun findCompletionParts(
|
||||
editorLineSuffix: String,
|
||||
completionLine: String
|
||||
): List<Pair<String, Int>> {
|
||||
val nonOverlappingPart = findNonOverlappingPart(editorLineSuffix, completionLine)
|
||||
if (nonOverlappingPart.length == completionLine.length) {
|
||||
return listOf(Pair(completionLine, 0))
|
||||
}
|
||||
|
||||
val result = ArrayList<Pair<String, Int>>()
|
||||
val editorChars: IntArray = editorLineSuffix.chars().toArray()
|
||||
val completionChars: IntArray = completionLine.chars().toArray()
|
||||
val changes: List<Diff.Change> =
|
||||
Diff.buildChanges(editorChars, completionChars)?.toList() ?: emptyList()
|
||||
for (change in changes) {
|
||||
val part = completionLine.substring(change.line1, change.line1 + change.inserted)
|
||||
result.add(Pair(part, change.line0))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun findNonOverlappingPart(
|
||||
editorLineSuffix: String,
|
||||
completionLine: String
|
||||
): String {
|
||||
var i = editorLineSuffix.length - 1
|
||||
var j = completionLine.length - 1
|
||||
while (i >= 0 && j >= 0 && editorLineSuffix[i] == completionLine[j]) {
|
||||
i--
|
||||
j--
|
||||
}
|
||||
|
||||
if (j >= 0) {
|
||||
return completionLine.substring(0, j + 1)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
package ee.carlrobert.codegpt.util
|
||||
|
||||
import ee.carlrobert.codegpt.util.StringUtil.findCompletionParts
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
|
||||
class StringUtilTest {
|
||||
|
||||
@Test
|
||||
fun `should parse completion without brackets and braces`() {
|
||||
val completionLine = "root != null"
|
||||
val editorLineSuffix = ") {\n"
|
||||
|
||||
val result = findCompletionParts(editorLineSuffix, completionLine)
|
||||
|
||||
assertThat(result[0].second).isEqualTo(0)
|
||||
assertThat(result[0].first).isEqualTo("root != null")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse completion with closing bracket and brace into separate parts`() {
|
||||
val completionLine = "root != null) {\n"
|
||||
val editorLineSuffix = ")\n"
|
||||
|
||||
val result = findCompletionParts(editorLineSuffix, completionLine)
|
||||
|
||||
assertThat(result[0].second).isEqualTo(0)
|
||||
assertThat(result[0].first).isEqualTo("root != null")
|
||||
assertThat(result[1].second).isEqualTo(1)
|
||||
assertThat(result[1].first).isEqualTo(" {")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse completion when editor suffix contains closing bracket and brace`() {
|
||||
val completionLine = "root != null) {\n"
|
||||
val editorLineSuffix = ") {\n"
|
||||
|
||||
val result = findCompletionParts(editorLineSuffix, completionLine)
|
||||
|
||||
assertThat(result[0].second).isEqualTo(0)
|
||||
assertThat(result[0].first).isEqualTo("root != null")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse completion between opening and closing brackets`() {
|
||||
val completionLine = "(root != null) {\n"
|
||||
val editorLineSuffix = "() {\n"
|
||||
|
||||
val result = findCompletionParts(editorLineSuffix, completionLine)
|
||||
|
||||
assertThat(result[0].second).isEqualTo(1)
|
||||
assertThat(result[0].first).isEqualTo("root != null")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue