Skip to content

Commit 30b9757

Browse files
author
Jia Liu
committed
[liujia] revert inlay dependencies
1 parent 93aa5a8 commit 30b9757

File tree

4 files changed

+295
-18
lines changed

4 files changed

+295
-18
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package cc.unitmesh.devti.editor.inlay
2+
3+
import com.intellij.codeInsight.editorActions.TypedHandlerDelegate
4+
import com.intellij.openapi.command.CommandProcessor
5+
import com.intellij.openapi.editor.Editor
6+
import com.intellij.openapi.fileTypes.FileType
7+
import com.intellij.openapi.project.Project
8+
import com.intellij.openapi.util.Key
9+
import com.intellij.psi.PsiFile
10+
11+
class TypeOverHandler : TypedHandlerDelegate() {
12+
override fun beforeCharTyped(c: Char, project: Project, editor: Editor, file: PsiFile, fileType: FileType): Result {
13+
val validTypeOver = c == ')' || c == ']' || c == '}' || c == '"' || c == '\'' || c == '>' || c == ';'
14+
if (validTypeOver && CommandProcessor.getInstance().currentCommand != null) {
15+
TYPE_OVER_STAMP[editor] = editor.document.modificationStamp
16+
} else {
17+
TYPE_OVER_STAMP[editor] = null
18+
}
19+
20+
return Result.CONTINUE
21+
}
22+
23+
companion object {
24+
private val TYPE_OVER_STAMP = Key.create<Long>("copilot.typeOverStamp")
25+
fun getPendingTypeOverAndReset(editor: Editor): Boolean {
26+
val stamp = TYPE_OVER_STAMP[editor] ?: return false
27+
TYPE_OVER_STAMP[editor] = null
28+
return stamp == editor.document.modificationStamp
29+
}
30+
}
31+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package cc.unitmesh.devti.intentions.action.base
2+
3+
import cc.unitmesh.devti.AutoDevBundle
4+
import com.intellij.temporary.calculateFrontendElementToExplain
5+
import com.intellij.temporary.getElementToAction
6+
import cc.unitmesh.devti.gui.chat.ChatActionType
7+
import cc.unitmesh.devti.provider.ContextPrompter
8+
import cc.unitmesh.devti.gui.sendToChatPanel
9+
import com.intellij.codeInsight.intention.IntentionAction
10+
import com.intellij.lang.injection.InjectedLanguageManager
11+
import com.intellij.openapi.editor.Editor
12+
import com.intellij.openapi.project.Project
13+
import com.intellij.openapi.util.NlsSafe
14+
import com.intellij.openapi.util.TextRange
15+
import com.intellij.psi.ElementDescriptionUtil
16+
import com.intellij.psi.PsiElement
17+
import com.intellij.psi.PsiFile
18+
import com.intellij.psi.PsiNameIdentifierOwner
19+
import com.intellij.usageView.UsageViewTypeLocation
20+
21+
abstract class AbstractChatIntention : IntentionAction {
22+
abstract fun priority(): Int
23+
open fun getActionType() = ChatActionType.CODE_COMPLETE
24+
25+
open val prompt: String = "Code completion"
26+
27+
override fun startInWriteAction(): Boolean = false
28+
29+
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile?): Boolean =
30+
editor != null && file != null
31+
32+
/**
33+
* Invokes the given method with the specified parameters.
34+
*
35+
* @param project The current project.
36+
* @param editor The editor in which the method is invoked.
37+
* @param file The file in which the method is invoked.
38+
*/
39+
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
40+
if (editor == null || file == null) return
41+
val withRange = elementWithRange(editor, file, project) ?: return
42+
43+
val selectedText = withRange.first
44+
val psiElement = withRange.second
45+
46+
val actionType = getActionType()
47+
48+
val prompter = ContextPrompter.prompter(file.language.displayName)
49+
prompter.initContext(actionType, selectedText, file, project, editor.caretModel.offset, psiElement)
50+
sendToChatPanel(project, actionType, prompter)
51+
}
52+
53+
fun elementWithRange(
54+
editor: Editor,
55+
file: PsiFile,
56+
project: Project,
57+
): Pair<@NlsSafe String, PsiElement?>? {
58+
var selectedText = editor.selectionModel.selectedText
59+
val psiElement = getElementToAction(project, editor)
60+
61+
if (selectedText == null) {
62+
if (psiElement == null) {
63+
return null
64+
}
65+
selectElement(psiElement, editor)
66+
selectedText = editor.selectionModel.selectedText
67+
}
68+
69+
if (selectedText == null) {
70+
return null
71+
}
72+
73+
return Pair(selectedText, psiElement)
74+
}
75+
76+
protected fun selectElement(elementToExplain: PsiElement, editor: Editor) {
77+
val startOffset = elementToExplain.textRange.startOffset
78+
val endOffset = elementToExplain.textRange.endOffset
79+
80+
editor.selectionModel.setSelection(startOffset, endOffset)
81+
}
82+
83+
fun getCurrentSelectionAsRange(editor: Editor): TextRange {
84+
val currentCaret = editor.caretModel.currentCaret
85+
return TextRange(currentCaret.selectionStart, currentCaret.selectionEnd)
86+
}
87+
88+
fun computeTitle(project: Project, psiFile: PsiFile, range: TextRange): String {
89+
val defaultTitle = AutoDevBundle.message("intentions.chat.selected.code.name")
90+
if (!range.isEmpty) {
91+
return defaultTitle
92+
}
93+
val element: PsiElement = calculateFrontendElementToExplain(project, psiFile, range) ?: return defaultTitle
94+
95+
return when {
96+
element is PsiFile -> {
97+
if (InjectedLanguageManager.getInstance(project).isInjectedFragment(element)) {
98+
val displayName = element.getLanguage().displayName
99+
return AutoDevBundle.message("intentions.chat.selected.fragment.name", displayName)
100+
}
101+
102+
val name: String = element.name
103+
return AutoDevBundle.message("intentions.chat.selected.element.name", name, getDescription(element))
104+
}
105+
106+
element is PsiNameIdentifierOwner && element.name != null -> {
107+
AutoDevBundle.message("intentions.chat.selected.element.name", element.name!!, getDescription(element))
108+
}
109+
110+
else -> {
111+
defaultTitle
112+
}
113+
}
114+
}
115+
116+
private fun getDescription(element: PsiElement): String {
117+
return ElementDescriptionUtil.getElementDescription(element, UsageViewTypeLocation.INSTANCE)
118+
}
119+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package cc.unitmesh.devti.intentions.action.task
2+
3+
import cc.unitmesh.devti.AutoDevBundle
4+
import com.intellij.temporary.similar.chunks.SimilarChunksWithPaths
5+
import cc.unitmesh.devti.llms.LlmFactory
6+
import cc.unitmesh.devti.util.LLMCoroutineScope
7+
import cc.unitmesh.devti.intentions.action.CodeCompletionBaseIntention
8+
import com.intellij.lang.LanguageCommenters
9+
import com.intellij.openapi.application.invokeLater
10+
import com.intellij.openapi.command.WriteCommandAction
11+
import com.intellij.openapi.diagnostic.logger
12+
import com.intellij.openapi.editor.Document
13+
import com.intellij.openapi.editor.ScrollType
14+
import com.intellij.openapi.progress.ProgressIndicator
15+
import com.intellij.openapi.progress.Task
16+
import com.intellij.openapi.project.Project
17+
import com.intellij.openapi.util.TextRange
18+
import com.intellij.psi.PsiDocumentManager
19+
import com.intellij.psi.codeStyle.CodeStyleManager
20+
import kotlinx.coroutines.flow.Flow
21+
import kotlinx.coroutines.flow.collect
22+
import kotlinx.coroutines.launch
23+
import java.util.function.Consumer
24+
import kotlin.jvm.internal.Ref
25+
26+
class CodeCompletionTask(private val request: CodeCompletionRequest) :
27+
Task.Backgroundable(request.project, AutoDevBundle.message("intentions.chat.code.complete.name")) {
28+
29+
private val llmFactory = LlmFactory()
30+
31+
private val writeActionGroupId = "code.complete.intention.write.action"
32+
private val codeMessage = AutoDevBundle.message("intentions.chat.code.complete.name")
33+
34+
private val chunksString = request.element?.let { SimilarChunksWithPaths.createQuery(it, 60) }
35+
private val commenter = request.element?.let { LanguageCommenters.INSTANCE.forLanguage(it.language) }
36+
private val commentPrefix = commenter?.lineCommentPrefix
37+
38+
override fun run(indicator: ProgressIndicator) {
39+
val prompt = promptText()
40+
41+
val flow: Flow<String> = llmFactory.create(request.project).stream(prompt, "")
42+
logger.info("Prompt: $prompt")
43+
44+
val editor = request.editor
45+
LLMCoroutineScope.scope(request.project).launch {
46+
val currentOffset = Ref.IntRef()
47+
currentOffset.element = request.offset
48+
49+
val project = request.project
50+
val suggestion = StringBuilder()
51+
52+
flow.collect {
53+
suggestion.append(it)
54+
invokeLater {
55+
WriteCommandAction.runWriteCommandAction(project, codeMessage, writeActionGroupId, {
56+
insertStringAndSaveChange(project, it, editor.document, currentOffset.element, false)
57+
})
58+
59+
currentOffset.element += it.length
60+
editor.caretModel.moveToOffset(currentOffset.element)
61+
editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
62+
}
63+
}
64+
65+
logger.info("Suggestion: $suggestion")
66+
}
67+
}
68+
69+
private fun promptText(): String {
70+
val documentLength = request.editor.document.textLength
71+
val prefix = if (request.offset > documentLength) {
72+
request.prefixText
73+
} else {
74+
val text = request.editor.document.text
75+
text.substring(0, request.offset)
76+
}
77+
78+
val prompt = if (chunksString == null) {
79+
"complete code for given code: \n$prefix"
80+
} else {
81+
"complete code for given code: \n$commentPrefix\n$chunksString\n$prefix"
82+
}
83+
84+
return prompt
85+
}
86+
87+
fun execute(onFirstCompletion: Consumer<String>?) {
88+
val prompt = promptText()
89+
90+
logger.warn("Prompt: $prompt")
91+
LLMCoroutineScope.scope(project).launch {
92+
val flow: Flow<String> = llmFactory.create(project).stream(prompt, "")
93+
val suggestion = StringBuilder()
94+
flow.collect {
95+
suggestion.append(it)
96+
}
97+
98+
onFirstCompletion?.accept(suggestion.toString())
99+
}
100+
}
101+
102+
companion object {
103+
val logger = logger<CodeCompletionBaseIntention>()
104+
105+
fun insertStringAndSaveChange(
106+
project: Project,
107+
suggestion: String,
108+
document: Document,
109+
startOffset: Int,
110+
withReformat: Boolean
111+
) {
112+
document.insertString(startOffset, suggestion)
113+
PsiDocumentManager.getInstance(project).commitDocument(document)
114+
115+
if (!withReformat) return
116+
117+
val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document)
118+
psiFile?.let { file ->
119+
val reformatRange = TextRange(startOffset, startOffset + suggestion.length)
120+
CodeStyleManager.getInstance(project).reformatText(file, listOf(reformatRange))
121+
}
122+
}
123+
}
124+
}

src/main/kotlin/com/intellij/temporary/inlay/presentation/LLMTextInlayPainter.kt

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.intellij.temporary.inlay.presentation
22

3+
import com.intellij.codeInsight.codeVision.CodeVisionEntry
34
import com.intellij.temporary.inlay.presentation.PresentationUtil.fontMetrics
45
import com.intellij.temporary.inlay.presentation.PresentationUtil.getFont
56
import com.intellij.temporary.inlay.presentation.PresentationUtil.getThemeInfoProvider
@@ -17,14 +18,16 @@ import javax.swing.text.StyleConstants
1718
import kotlin.math.ceil
1819

1920
class LLMTextInlayPainter : ICodeVisionEntryBasePainter<String> {
21+
2022
override fun paint(
2123
editor: Editor,
2224
textAttributes: TextAttributes,
2325
g: Graphics,
2426
value: String,
2527
point: Point,
2628
state: RangeCodeVisionModel.InlayState,
27-
hovered: Boolean
29+
hovered: Boolean,
30+
// hoveredEntry: CodeVisionEntry?
2831
) {
2932
renderCodeBlock(
3033
editor,
@@ -42,23 +45,23 @@ class LLMTextInlayPainter : ICodeVisionEntryBasePainter<String> {
4245
return Dimension(fontMetrics.stringWidth(value), fontMetrics.height)
4346
}
4447

45-
override fun toIcon(
46-
editor: Editor,
47-
textAttributes: TextAttributes,
48-
value: String,
49-
state: RangeCodeVisionModel.InlayState,
50-
hovered: Boolean
51-
) = object : Icon {
52-
var size = size(editor, state, value)
53-
54-
override fun getIconHeight(): Int = size.height
55-
56-
override fun paintIcon(c: Component, g: Graphics, x: Int, y: Int) {
57-
paint(editor, textAttributes, g, value, Point(x, y + (editor as EditorImpl).ascent), state, hovered)
58-
}
59-
60-
override fun getIconWidth(): Int = size.width
61-
}
48+
// fun toIcon(
49+
// editor: Editor,
50+
// textAttributes: TextAttributes,
51+
// value: String,
52+
// state: RangeCodeVisionModel.InlayState,
53+
// hovered: Boolean
54+
// ) = object : Icon {
55+
// var size = size(editor, state, value)
56+
//
57+
// override fun getIconHeight(): Int = size.height
58+
//
59+
// override fun paintIcon(c: Component, g: Graphics, x: Int, y: Int) {
60+
// paint(editor, textAttributes, g, value, Point(x, y + (editor as EditorImpl).ascent), state, hovered)
61+
// }
62+
//
63+
// override fun getIconWidth(): Int = size.width
64+
// }
6265

6366
companion object {
6467
fun calculateWidth(editor: Editor, text: String, textLines: List<String>): Int {

0 commit comments

Comments
 (0)