Skip to content

Commit b2a2e90

Browse files
committed
refactor: consolidate code completion request building; remove old code
1 parent f12656b commit b2a2e90

File tree

7 files changed

+89
-117
lines changed

7 files changed

+89
-117
lines changed

src/main/kotlin/ee/carlrobert/codegpt/codecompletions/DebouncedCodeCompletionProvider.kt

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,20 +71,19 @@ class DebouncedCodeCompletionProvider : DebouncedInlineCompletionProvider() {
7171
CompletionProgressNotifier.update(project, true)
7272

7373
var eventListener = CodeCompletionEventListener(request.editor, this)
74+
val infillRequest = InfillRequestUtil.buildInfillRequest(request)
7475

7576
if (service<ModelSelectionService>().getServiceForFeature(FeatureType.CODE_COMPLETION) == ServiceType.PROXYAI) {
76-
project.service<GrpcClientService>().cancelCodeCompletion()
77-
project.service<GrpcClientService>()
78-
.getCodeCompletionAsync(eventListener, request, this)
77+
val grpcClient = project.service<GrpcClientService>()
78+
grpcClient.cancelCodeCompletion()
79+
grpcClient.getCodeCompletionAsync(infillRequest, eventListener, this)
7980
return@channelFlow
8081
}
8182

82-
val infillRequest = InfillRequestUtil.buildInfillRequest(request)
8383
val call = service<CodeCompletionService>().getCodeCompletionAsync(
8484
infillRequest,
8585
CodeCompletionEventListener(request.editor, this)
8686
)
87-
8887
currentCallRef.set(call)
8988
} finally {
9089
awaitClose { currentCallRef.getAndSet(null)?.cancel() }
@@ -131,13 +130,8 @@ class DebouncedCodeCompletionProvider : DebouncedInlineCompletionProvider() {
131130
return false
132131
}
133132

134-
if (event is LookupInlineCompletionEvent || event is InlineCompletionEvent.DirectCall) {
135-
return true
136-
}
137-
138-
val hasActiveCompletion =
139-
REMAINING_CODE_COMPLETION.get(event.toRequest()?.editor)?.partialCompletion?.isNotEmpty() == true
140-
141-
return event is InlineCompletionEvent.DocumentChange || hasActiveCompletion
133+
return event is LookupInlineCompletionEvent
134+
|| event is InlineCompletionEvent.DirectCall
135+
|| event is InlineCompletionEvent.DocumentChange
142136
}
143137
}

src/main/kotlin/ee/carlrobert/codegpt/codecompletions/InfillRequest.kt

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package ee.carlrobert.codegpt.codecompletions
22

3+
import com.intellij.openapi.application.runReadAction
34
import com.intellij.openapi.components.service
4-
import com.intellij.openapi.editor.Document
5+
import com.intellij.openapi.editor.Editor
56
import com.intellij.openapi.util.TextRange
67
import com.intellij.psi.PsiElement
78
import ee.carlrobert.codegpt.EncodingManager
@@ -15,7 +16,9 @@ class InfillRequest private constructor(
1516
val prefix: String,
1617
val suffix: String,
1718
val caretOffset: Int,
19+
val editor: Editor?,
1820
val fileDetails: FileDetails?,
21+
val gitDiff: String?,
1922
val repositoryName: String?,
2023
val dependenciesStructure: Set<ClassStructure>?,
2124
val context: InfillContext?,
@@ -29,7 +32,8 @@ class InfillRequest private constructor(
2932
private val suffix: String
3033
private val caretOffset: Int
3134
private var fileDetails: FileDetails? = null
32-
private var additionalContext: String? = null
35+
private var editor: Editor? = null
36+
private var gitDiff: String? = null
3337
private var repositoryName: String? = null
3438
private var dependenciesStructure: Set<ClassStructure>? = null
3539
private var context: InfillContext? = null
@@ -46,10 +50,9 @@ class InfillRequest private constructor(
4650
this.stopTokens = getStopTokens()
4751
}
4852

49-
constructor(
50-
document: Document,
51-
caretOffset: Int,
52-
) {
53+
constructor(editor: Editor) {
54+
val document = editor.document
55+
val caretOffset = runReadAction { editor.caretModel.offset }
5356
prefix =
5457
document.getText(TextRange(0, caretOffset))
5558
.truncateText(MAX_PROMPT_TOKENS, false)
@@ -58,11 +61,12 @@ class InfillRequest private constructor(
5861
.truncateText(MAX_PROMPT_TOKENS)
5962
this.caretOffset = caretOffset
6063
this.stopTokens = getStopTokens()
64+
this.editor = editor
65+
this.fileDetails = FileDetails(editor.document.text, editor.virtualFile.path)
6166
}
6267

63-
fun fileDetails(fileDetails: FileDetails) = apply { this.fileDetails = fileDetails }
64-
fun additionalContext(additionalContext: String) =
65-
apply { this.additionalContext = additionalContext }
68+
fun gitDiff(gitDiff: String) =
69+
apply { this.gitDiff = gitDiff }
6670

6771
fun addRepositoryName(repositoryName: String) =
6872
apply { this.repositoryName = repositoryName }
@@ -90,16 +94,18 @@ class InfillRequest private constructor(
9094
}
9195

9296
fun build(): InfillRequest {
93-
val modifiedPrefix = if (!additionalContext.isNullOrEmpty()) {
94-
"/*\n${additionalContext}\n*/\n\n$prefix"
97+
val modifiedPrefix = if (!gitDiff.isNullOrEmpty()) {
98+
"/*\n${gitDiff}\n*/\n\n$prefix"
9599
} else {
96100
prefix
97101
}
98102
return InfillRequest(
99103
modifiedPrefix,
100104
suffix,
101105
caretOffset,
106+
editor,
102107
fileDetails,
108+
gitDiff,
103109
repositoryName,
104110
dependenciesStructure,
105111
context,
Lines changed: 7 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,34 @@
11
package ee.carlrobert.codegpt.codecompletions
22

33
import com.intellij.codeInsight.inline.completion.InlineCompletionRequest
4-
import com.intellij.openapi.application.readAction
54
import com.intellij.openapi.components.service
6-
import com.intellij.refactoring.suggested.startOffset
7-
import ee.carlrobert.codegpt.EncodingManager
8-
import ee.carlrobert.codegpt.codecompletions.psi.CompletionContextService
9-
import ee.carlrobert.codegpt.codecompletions.psi.readText
105
import ee.carlrobert.codegpt.psistructure.PsiStructureProvider
116
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings
127
import ee.carlrobert.codegpt.util.GitUtil
138

14-
159
object InfillRequestUtil {
1610

1711
suspend fun buildInfillRequest(request: InlineCompletionRequest): InfillRequest {
18-
val caretOffset = readAction { request.editor.caretModel.offset }
19-
val infillRequestBuilder = InfillRequest.Builder(request.document, caretOffset)
20-
.fileDetails(
21-
InfillRequest.FileDetails(
22-
request.document.text,
23-
request.file.virtualFile.path
24-
)
25-
)
12+
val infillRequestBuilder = InfillRequest.Builder(request.editor)
2613

2714
val project = request.editor.project ?: return infillRequestBuilder.build()
2815
if (service<ConfigurationSettings>().state.codeCompletionSettings.gitDiffEnabled) {
29-
val additionalContext = GitUtil.getCurrentChanges(project)
30-
if (!additionalContext.isNullOrEmpty()) {
31-
infillRequestBuilder.additionalContext(additionalContext)
32-
}
33-
}
34-
35-
if (service<ConfigurationSettings>().state.codeCompletionSettings.contextAwareEnabled) {
36-
getInfillContext(request, caretOffset)?.let {
37-
infillRequestBuilder.context(it)
38-
infillRequestBuilder.addRepositoryName(it.getRepoName())
16+
GitUtil.getCurrentChanges(project)?.let { diff ->
17+
if (diff.isNotEmpty()) {
18+
infillRequestBuilder.gitDiff(diff)
19+
}
3920
}
4021
}
4122

4223
if (service<ConfigurationSettings>().state.codeCompletionSettings.collectDependencyStructure) {
43-
val depth = service<ConfigurationSettings>().state.codeCompletionSettings.psiStructureAnalyzeDepth
24+
val depth =
25+
service<ConfigurationSettings>().state.codeCompletionSettings.psiStructureAnalyzeDepth
4426
val psiStructure = PsiStructureProvider().get(listOf(request.file), depth)
4527
if (psiStructure.isNotEmpty()) {
4628
infillRequestBuilder.addDependenciesStructure(psiStructure)
47-
infillRequestBuilder.addRepositoryName(psiStructure.first().repositoryName)
4829
}
4930
}
5031

5132
return infillRequestBuilder.build()
5233
}
53-
54-
private fun getInfillContext(
55-
request: InlineCompletionRequest,
56-
caretOffset: Int
57-
): InfillContext? {
58-
val infillContext =
59-
service<CompletionContextService>().findContext(request.editor, caretOffset)
60-
?: return null
61-
val caretInEnclosingElement =
62-
caretOffset - infillContext.enclosingElement.psiElement.startOffset
63-
val entireText = infillContext.enclosingElement.psiElement.readText()
64-
val prefix = entireText.take(caretInEnclosingElement)
65-
val suffix =
66-
if (entireText.length < caretInEnclosingElement) "" else entireText.takeLast(
67-
entireText.length - caretInEnclosingElement
68-
)
69-
return truncateContext(prefix + suffix, infillContext)
70-
}
71-
72-
private fun truncateContext(prompt: String, infillContext: InfillContext): InfillContext {
73-
var promptTokens = EncodingManager.getInstance().countTokens(prompt)
74-
val truncatedContextElements = infillContext.contextElements.takeWhile {
75-
promptTokens += it.tokens
76-
promptTokens <= MAX_PROMPT_TOKENS
77-
}.toSet()
78-
return InfillContext(infillContext.enclosingElement, truncatedContextElements)
79-
}
8034
}

src/main/kotlin/ee/carlrobert/codegpt/codecompletions/edit/GrpcClientService.kt

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
package ee.carlrobert.codegpt.codecompletions.edit
22

3-
import com.intellij.codeInsight.inline.completion.InlineCompletionRequest
43
import com.intellij.codeInsight.inline.completion.elements.InlineCompletionElement
54
import com.intellij.openapi.Disposable
6-
import com.intellij.openapi.application.runReadAction
75
import com.intellij.openapi.components.Service
86
import com.intellij.openapi.diagnostic.thisLogger
97
import com.intellij.openapi.editor.Editor
108
import com.intellij.openapi.project.Project
119
import com.intellij.util.net.ssl.CertificateManager
1210
import ee.carlrobert.codegpt.CodeGPTPlugin
1311
import ee.carlrobert.codegpt.codecompletions.CodeCompletionEventListener
12+
import ee.carlrobert.codegpt.codecompletions.InfillRequest
1413
import ee.carlrobert.codegpt.credentials.CredentialsStore
1514
import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.CodeGptApiKey
1615
import ee.carlrobert.codegpt.settings.service.FeatureType
@@ -48,15 +47,16 @@ class GrpcClientService(private val project: Project) : Disposable {
4847
}
4948

5049
fun getCodeCompletionAsync(
50+
request: InfillRequest,
5151
eventListener: CodeCompletionEventListener,
52-
request: InlineCompletionRequest,
5352
channel: ProducerScope<InlineCompletionElement>
5453
) {
5554
ensureCodeCompletionConnection()
5655

56+
val editor = request.editor ?: return
5757
val grpcRequest = createCodeCompletionGrpcRequest(request)
5858
codeCompletionObserver =
59-
CodeCompletionStreamObserver(request.editor, channel, eventListener)
59+
CodeCompletionStreamObserver(editor, channel, eventListener)
6060
codeCompletionContext?.cancel(null)
6161
val ctx = Context.current().withCancellation()
6262
codeCompletionContext = ctx
@@ -186,16 +186,17 @@ class GrpcClientService(private val project: Project) : Disposable {
186186
}
187187
}
188188

189-
private fun createCodeCompletionGrpcRequest(request: InlineCompletionRequest): GrpcCodeCompletionRequest {
190-
val editor = request.editor
189+
private fun createCodeCompletionGrpcRequest(request: InfillRequest): GrpcCodeCompletionRequest {
190+
val fileDetails = request.fileDetails ?: throw IllegalArgumentException("File details are required")
191+
val gitDiff = request.gitDiff ?: ""
191192
return GrpcCodeCompletionRequest.newBuilder()
192193
.setModel(
193194
ModelSelectionService.getInstance().getModelForFeature(FeatureType.CODE_COMPLETION)
194195
)
195-
.setFilePath(editor.virtualFile.path)
196-
.setFileContent(editor.document.text)
197-
.setGitDiff(GitUtil.getCurrentChanges(project) ?: "")
198-
.setCursorPosition(runReadAction { editor.caretModel.offset })
196+
.setFilePath(fileDetails.filePath)
197+
.setFileContent(fileDetails.fileContent)
198+
.setGitDiff(gitDiff)
199+
.setCursorPosition(request.caretOffset)
199200
.setPluginVersion(CodeGPTPlugin.getVersion())
200201
.build()
201202
}

src/main/kotlin/ee/carlrobert/codegpt/codecompletions/edit/NextEditStreamObserver.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import ee.carlrobert.service.NextEditResponse
1111
import io.grpc.Status
1212
import io.grpc.StatusRuntimeException
1313
import io.grpc.stub.StreamObserver
14+
import ee.carlrobert.codegpt.codecompletions.CodeCompletionFormatter
1415

1516
class NextEditStreamObserver(
1617
private val editor: Editor,

src/main/kotlin/ee/carlrobert/codegpt/nextedit/NextEditDiffViewer.kt

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class NextEditDiffViewer(
8383

8484
val size = computeCompactSize(change)
8585
myEditor.component.preferredSize = size
86-
adjustPopupSize(popup, myEditor)
86+
adjustPopupSize(popup, myEditor, mainEditor)
8787

8888
val changeOffset = change.lineFragment.startOffset1
8989
val adjustedLocation = getAdjustedPopupLocation(mainEditor, changeOffset, size)
@@ -221,7 +221,7 @@ class NextEditDiffViewer(
221221

222222
coroutineScope.launch {
223223
withContext(Dispatchers.EDT) {
224-
adjustPopupSize(popup, myEditor)
224+
adjustPopupSize(popup, myEditor, mainEditor)
225225
val adjustedLocation = getAdjustedPopupLocation(
226226
mainEditor,
227227
change.lineFragment.startOffset1,
@@ -342,31 +342,60 @@ fun getAdjustedPopupLocation(editor: Editor, changeOffset: Int, popupSize: Dimen
342342
SwingUtilities.convertPointToScreen(point, editor.component)
343343
return point
344344
}
345+
345346
val visibleArea = editor.scrollingModel.visibleArea
346347
val editorLocationOnScreen = editor.component.locationOnScreen
347-
val vsOffset = editor.scrollingModel.verticalScrollOffset
348-
val marginX = +(editor as EditorEx).gutterComponentEx.size.width
348+
val verticalOffset = editor.scrollingModel.verticalScrollOffset
349+
val gutterWidth = (editor as? EditorEx)?.gutterComponentEx?.size?.width ?: 0
350+
val margin = JBUI.scale(8)
351+
349352
val yInEditor = pointInEditor.y
353+
val visibleTopY = visibleArea.y
354+
val visibleBottomY = visibleArea.y + visibleArea.height
355+
val changeIsVisible = yInEditor in visibleTopY until visibleBottomY
356+
357+
if (changeIsVisible) {
358+
val spaceBelow = visibleBottomY - yInEditor - editor.lineHeight
359+
val fitsBelow = spaceBelow >= popupSize.height
360+
if (fitsBelow) {
361+
val rawLeft = editorLocationOnScreen.x + pointInEditor.x + gutterWidth
362+
val minLeft = editorLocationOnScreen.x + visibleArea.x + margin
363+
val maxLeft = editorLocationOnScreen.x + visibleArea.x + visibleArea.width - popupSize.width - margin
364+
val left = rawLeft.coerceIn(minLeft, maxLeft)
365+
val top = editorLocationOnScreen.y + yInEditor - verticalOffset
366+
return Point(left, top)
367+
}
350368

351-
val spaceBelow = visibleArea.y + visibleArea.height - yInEditor - editor.lineHeight
352-
val fitsBelow = spaceBelow >= popupSize.height
353-
if (fitsBelow) {
354-
val top = editorLocationOnScreen.y + yInEditor - vsOffset
355-
val left = editorLocationOnScreen.x + pointInEditor.x + marginX
369+
val screenChangeY = editorLocationOnScreen.y + yInEditor - verticalOffset
370+
val spaceAbove = screenChangeY - editorLocationOnScreen.y
371+
val canPlaceAbove = spaceAbove >= popupSize.height + margin
372+
val top = if (canPlaceAbove) {
373+
screenChangeY - popupSize.height - margin
374+
} else {
375+
val centered = screenChangeY - popupSize.height / 2
376+
val minTop = editorLocationOnScreen.y + margin
377+
val maxTop = editorLocationOnScreen.y + visibleArea.height - popupSize.height - margin
378+
centered.coerceIn(minTop, maxTop)
379+
}
380+
val left = editorLocationOnScreen.x + visibleArea.x + visibleArea.width - popupSize.width - gutterWidth
356381
return Point(left, top)
357382
}
358383

359-
val left =
360-
editorLocationOnScreen.x + visibleArea.x + visibleArea.width - popupSize.width - marginX
361-
val visibleBottom = editorLocationOnScreen.y + visibleArea.height
362-
var top = editorLocationOnScreen.y + yInEditor - vsOffset - popupSize.height / 2
363-
top = top.coerceIn(editorLocationOnScreen.y, visibleBottom - popupSize.height)
384+
val topAligned = editorLocationOnScreen.y + margin
385+
val bottomAligned = editorLocationOnScreen.y + visibleArea.height - popupSize.height - margin
386+
val top = if (yInEditor < visibleTopY) topAligned else bottomAligned
387+
val left = editorLocationOnScreen.x + visibleArea.x + visibleArea.width - popupSize.width - gutterWidth
364388
return Point(left, top)
365389
}
366390

367-
fun adjustPopupSize(popup: JBPopup, editor: Editor) {
368-
val newWidth = editor.component.preferredSize.width
369-
val newHeight = editor.component.preferredSize.height
391+
fun adjustPopupSize(popup: JBPopup, contentEditor: Editor, anchorEditor: Editor) {
392+
val preferred = contentEditor.component.preferredSize
393+
val visibleArea = anchorEditor.scrollingModel.visibleArea
394+
val margin = JBUI.scale(16)
395+
val maxWidth = (visibleArea.width - margin).coerceAtLeast(JBUI.scale(64))
396+
val maxHeight = (visibleArea.height - margin).coerceAtLeast(JBUI.scale(48))
397+
val newWidth = preferred.width.coerceAtMost(maxWidth)
398+
val newHeight = preferred.height.coerceAtMost(maxHeight)
370399
popup.size = Dimension(newWidth, newHeight)
371400
popup.content.revalidate()
372401
popup.content.repaint()

src/main/kotlin/ee/carlrobert/codegpt/util/GitUtil.kt

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,4 @@ object GitUtil {
153153
!it.startsWith("commit ")
154154
}
155155
}
156-
157-
private fun String.cleanDiff(showContext: Boolean = false): String =
158-
lineSequence()
159-
.filterNot { line ->
160-
line.startsWith("index ") ||
161-
line.startsWith("diff --git") ||
162-
line.startsWith("---") ||
163-
line.startsWith("+++") ||
164-
line.startsWith("===") ||
165-
line.contains("\\ No newline at end of file")
166-
(!showContext && line.startsWith(" "))
167-
}
168-
.joinToString("\n")
169156
}

0 commit comments

Comments
 (0)