Skip to content

Commit f65c1f4

Browse files
authored
Merge pull request #110 from jialiu-github/config
Inlay code complete
2 parents d7ffc94 + a9e8b06 commit f65c1f4

File tree

21 files changed

+1250
-19
lines changed

21 files changed

+1250
-19
lines changed

src/222/main/resources/META-INF/autodev-core.xml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
parentId="cc.unitmesh.devti.settings.AutoDevSettingsConfigurable"
2727
id="cc.unitmesh.autodevCoder"
2828
bundle="messages.AutoDevBundle" key="settings.autodev.coder"/>
29+
<applicationService
30+
serviceInterface="com.intellij.temporary.inlay.codecomplete.LLMInlayManager"
31+
serviceImplementation="com.intellij.temporary.inlay.codecomplete.LLMInlayManagerImpl"/>
2932

3033
<applicationService serviceImplementation="cc.unitmesh.devti.settings.AutoDevSettingsState"/>
3134

@@ -60,6 +63,9 @@
6063

6164
<notificationGroup id="AutoDev.notify" displayType="STICKY_BALLOON" bundle="messages.AutoDevBundle"
6265
key="name"/>
66+
<!-- <editorFactoryListener implementation="cc.unitmesh.devti.editor.inlay.AutoDevEditorListener"/>-->
67+
<!-- <typedHandler order="first, before completionAutoPopup"-->
68+
<!-- implementation="cc.unitmesh.devti.editor.inlay.TypeOverHandler"/>-->
6369

6470
<intentionAction>
6571
<className>cc.unitmesh.devti.intentions.AutoDevIntentionHelper</className>
@@ -174,6 +180,10 @@
174180
<listener topic="com.intellij.ide.plugins.DynamicPluginListener"
175181
class="cc.unitmesh.devti.AutoDevUnloadListener"/>
176182
</applicationListeners>
183+
<!-- <projectListeners>-->
184+
<!-- <listener topic="com.intellij.openapi.command.CommandListener"-->
185+
<!-- class="cc.unitmesh.devti.editor.inlay.LLMCommandListener"/>-->
186+
<!-- </projectListeners>-->
177187

178188
<extensions defaultExtensionNs="cc.unitmesh">
179189
<autoDevIntention>
@@ -186,6 +196,11 @@
186196
<bundleName>messages.AutoDevBundle</bundleName>
187197
<categoryKey>intention.category.llm</categoryKey>
188198
</autoDevIntention>
199+
<autoDevIntention>
200+
<className>cc.unitmesh.devti.intentions.action.CodeCompletionInlayIntention</className>
201+
<bundleName>messages.AutoDevBundle</bundleName>
202+
<categoryKey>intention.category.llm</categoryKey>
203+
</autoDevIntention>
189204
<autoDevIntention>
190205
<className>cc.unitmesh.devti.intentions.action.AutoTestThisBaseIntention</className>
191206
<bundleName>messages.AutoDevBundle</bundleName>
@@ -201,6 +216,22 @@
201216
</extensions>
202217

203218
<actions>
219+
<action id="llm.applyInlays"
220+
class="cc.unitmesh.devti.actions.LLMApplyInlaysAction">
221+
<keyboard-shortcut first-keystroke="TAB" keymap="$default"/>
222+
<override-text place="MainMenu" text="Apply Completions to Editor"/>
223+
<override-text place="EditorPopup" text="Accept"/>
224+
</action>
225+
<action id="cc.unitmesh.devti.inlayCompleteCode"
226+
class="cc.unitmesh.devti.actions.InlayCompleteCodeAction"
227+
text="Inlay Complete Code"
228+
description="Inlay complete code!"
229+
>
230+
<keyboard-shortcut keymap="$default" first-keystroke="control PERIOD"/>
231+
232+
<add-to-group group-id="ShowIntentionsGroup" relative-to-action="ShowIntentionActions" anchor="after"/>
233+
</action>
234+
204235
<group id="AutoDevIntentionsActionGroup" class="cc.unitmesh.devti.intentions.IntentionsActionGroup"
205236
icon="cc.unitmesh.devti.AutoDevIcons.AI_COPILOT" searchable="false">
206237

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package com.intellij.temporary.inlay.presentation
2+
3+
import com.intellij.codeInsight.codeVision.CodeVisionEntry
4+
import com.intellij.codeInsight.codeVision.ui.model.RangeCodeVisionModel
5+
import com.intellij.codeInsight.codeVision.ui.renderers.painters.ICodeVisionEntryBasePainter
6+
import com.intellij.openapi.editor.Editor
7+
import com.intellij.openapi.editor.impl.EditorImpl
8+
import com.intellij.openapi.editor.markup.TextAttributes
9+
import com.intellij.temporary.inlay.codecomplete.presentation.PresentationUtil.fontMetrics
10+
import com.intellij.temporary.inlay.codecomplete.presentation.PresentationUtil.getFont
11+
import com.intellij.temporary.inlay.codecomplete.presentation.PresentationUtil.getThemeInfoProvider
12+
import com.intellij.ui.paint.EffectPainter2D
13+
import com.intellij.util.ui.GraphicsUtil
14+
import java.awt.*
15+
import java.awt.geom.Rectangle2D
16+
import javax.swing.text.StyleConstants
17+
import kotlin.math.ceil
18+
19+
class LLMTextInlayPainter : ICodeVisionEntryBasePainter<String> {
20+
21+
override fun paint(
22+
editor: Editor,
23+
textAttributes: TextAttributes,
24+
g: Graphics,
25+
value: String,
26+
point: Point,
27+
state: RangeCodeVisionModel.InlayState,
28+
hovered: Boolean,
29+
hoveredEntry: CodeVisionEntry?
30+
) {
31+
renderCodeBlock(
32+
editor,
33+
value,
34+
value.split("\n"),
35+
g,
36+
Rectangle2D.Double(point.x.toDouble(), point.y.toDouble(), 0.0, 0.0),
37+
textAttributes,
38+
)
39+
}
40+
41+
override fun size(editor: Editor, state: RangeCodeVisionModel.InlayState, value: String): Dimension {
42+
val themeInfoProvider = getThemeInfoProvider()
43+
val fontMetrics = editor.component.getFontMetrics(themeInfoProvider.font(editor, 0))
44+
return Dimension(fontMetrics.stringWidth(value), fontMetrics.height)
45+
}
46+
47+
companion object {
48+
fun calculateWidth(editor: Editor, text: String, textLines: List<String>): Int {
49+
val metrics = fontMetrics(editor, getFont(editor, text))
50+
var maxWidth = 0
51+
for (line in textLines) {
52+
maxWidth = Math.max(maxWidth, metrics.stringWidth(line))
53+
}
54+
55+
return maxWidth
56+
}
57+
58+
private fun renderEffects(
59+
g2d: Graphics2D,
60+
x: Double,
61+
baseline: Double,
62+
width: Double,
63+
charHeight: Int,
64+
descent: Int,
65+
textAttributes: TextAttributes,
66+
font: Font?,
67+
) {
68+
val effectType = textAttributes.effectType
69+
if (effectType == null || effectType.ordinal == StyleConstants.CharacterConstants.Underline) {
70+
EffectPainter2D.LINE_UNDERSCORE.paint(
71+
g2d, x, baseline,
72+
width, 5.0, font
73+
)
74+
}
75+
}
76+
77+
private fun renderBackground(
78+
g: Graphics2D,
79+
attributes: TextAttributes,
80+
x: Double,
81+
y: Double,
82+
width: Double,
83+
height: Double,
84+
) {
85+
val color = attributes.backgroundColor
86+
if (color != null) {
87+
g.color = color
88+
g.fillRoundRect(x.toInt(), y.toInt(), width.toInt(), height.toInt(), 1, 1)
89+
}
90+
}
91+
92+
fun renderCodeBlock(
93+
editor: Editor,
94+
content: String,
95+
contentLines: List<String>,
96+
g: Graphics,
97+
region: Rectangle2D,
98+
textAttributes: TextAttributes,
99+
) {
100+
if (content.isEmpty() || contentLines.isEmpty()) return
101+
102+
val themeInfoProvider = getThemeInfoProvider()
103+
val attributes = editor.selectionModel.textAttributes
104+
105+
val inSelectedBlock = textAttributes.backgroundColor == attributes.backgroundColor
106+
val foregroundColor = textAttributes.foregroundColor ?: if (inSelectedBlock) {
107+
attributes.foregroundColor ?: editor.colorsScheme.defaultForeground
108+
} else {
109+
themeInfoProvider.foregroundColor(editor, false)
110+
}
111+
112+
val clipBounds = g.clipBounds
113+
val g2 = g.create() as Graphics2D
114+
GraphicsUtil.setupAAPainting(g2)
115+
val font = themeInfoProvider.font(editor, 0)
116+
g2.font = font
117+
118+
val metrics = fontMetrics(editor, font)
119+
val lineHeight = editor.lineHeight.toDouble()
120+
val fontBaseline = ceil(font.createGlyphVector(metrics.fontRenderContext, "Alb").visualBounds.height)
121+
val linePadding = (lineHeight - fontBaseline) / 2.0
122+
123+
val offsetX = region.x
124+
val offsetY = region.y + fontBaseline + linePadding
125+
126+
var lineOffset = 0
127+
g2.clip = if (clipBounds != null && clipBounds != region) region.createIntersection(clipBounds) else region
128+
129+
for (line in contentLines) {
130+
renderBackground(g2, attributes, offsetX, region.y + lineOffset, region.width, lineHeight)
131+
g2.color = foregroundColor
132+
g2.drawString(line, offsetX.toFloat(), (offsetY + lineOffset).toFloat())
133+
if (editor is EditorImpl) {
134+
renderEffects(
135+
g2,
136+
offsetX,
137+
offsetY + lineOffset,
138+
metrics.stringWidth(line).toDouble(),
139+
editor.charHeight,
140+
editor.descent,
141+
attributes,
142+
font,
143+
)
144+
}
145+
lineOffset = (lineOffset + lineHeight).toInt()
146+
}
147+
g2.dispose()
148+
}
149+
}
150+
151+
}
152+

src/233/main/resources/META-INF/autodev-core.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@
2929

3030
<applicationService serviceImplementation="cc.unitmesh.devti.settings.AutoDevSettingsState"/>
3131

32+
<applicationService
33+
serviceInterface=" com.intellij.temporary.inlay.codecomplete.LLMInlayManager"
34+
serviceImplementation=" com.intellij.temporary.inlay.codecomplete.LLMInlayManagerImpl"/>
35+
36+
<typedHandler order="first, before completionAutoPopup"
37+
implementation="cc.unitmesh.devti.editor.inlay.TypeOverHandler"/>
38+
39+
3240
<statusBarWidgetFactory id="AIAssistant"
3341
implementation="cc.unitmesh.devti.statusbar.AutoDevStatusBarWidgetFactory"/>
3442

@@ -175,6 +183,11 @@
175183
</applicationListeners>
176184

177185
<extensions defaultExtensionNs="cc.unitmesh">
186+
<autoDevIntention>
187+
<className>cc.unitmesh.devti.intentions.action.CodeCompletionInlayIntention</className>
188+
<bundleName>messages.AutoDevBundle</bundleName>
189+
<categoryKey>intention.category.llm</categoryKey>
190+
</autoDevIntention>
178191
<autoDevIntention>
179192
<className>cc.unitmesh.devti.intentions.action.NewChatWithCodeBaseIntention</className>
180193
<bundleName>messages.AutoDevBundle</bundleName>
@@ -200,6 +213,23 @@
200213
</extensions>
201214

202215
<actions>
216+
<action id="llm.applyInlays"
217+
class="cc.unitmesh.devti.actions.LLMApplyInlaysAction">
218+
<keyboard-shortcut first-keystroke="TAB" keymap="$default"/>
219+
<override-text place="MainMenu" text="Apply Completions to Editor"/>
220+
<override-text place="EditorPopup" text="Accept"/>
221+
</action>
222+
223+
<action id="cc.unitmesh.devti.inlayCompleteCode"
224+
class="cc.unitmesh.devti.actions.InlayCompleteCodeAction"
225+
text="Inlay Complete Code"
226+
description="Inlay complete code!"
227+
>
228+
<keyboard-shortcut keymap="$default" first-keystroke="control PERIOD"/>
229+
230+
<add-to-group group-id="ShowIntentionsGroup" relative-to-action="ShowIntentionActions" anchor="after"/>
231+
</action>
232+
203233
<group id="AutoDevIntentionsActionGroup" class="cc.unitmesh.devti.intentions.IntentionsActionGroup"
204234
icon="cc.unitmesh.devti.AutoDevIcons.AI_COPILOT" searchable="false">
205235

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package cc.unitmesh.devti.actions
2+
3+
import com.intellij.openapi.actionSystem.AnAction
4+
import com.intellij.openapi.actionSystem.AnActionEvent
5+
import com.intellij.openapi.actionSystem.CommonDataKeys
6+
import com.intellij.openapi.command.CommandProcessor
7+
import com.intellij.temporary.inlay.codecomplete.LLMInlayManager
8+
9+
/**
10+
* A quick insight action is an action that can be triggered by a user,
11+
* user can input custom text to call with LLM.
12+
*/
13+
class InlayCompleteCodeAction : AnAction() {
14+
override fun actionPerformed(e: AnActionEvent) {
15+
val dataContext = e.dataContext
16+
val editor = dataContext.getData(CommonDataKeys.EDITOR) ?: return
17+
val project = dataContext.getData(CommonDataKeys.PROJECT) ?: return
18+
if (project.isDisposed) return;
19+
20+
val commandProcessor = CommandProcessor.getInstance()
21+
if (commandProcessor.isUndoTransparentActionInProgress) return
22+
23+
val llmInlayManager = LLMInlayManager.getInstance()
24+
llmInlayManager.editorModified(editor)
25+
}
26+
}
27+

0 commit comments

Comments
 (0)