-
Notifications
You must be signed in to change notification settings - Fork 11
/
ContextFilesPanel.kt
114 lines (101 loc) · 4.2 KB
/
ContextFilesPanel.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package com.sourcegraph.cody.chat.ui
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.ui.JBInsets
import com.intellij.util.ui.JBUI
import com.sourcegraph.cody.agent.protocol.ChatMessage
import com.sourcegraph.cody.agent.protocol.ContextItem
import com.sourcegraph.cody.agent.protocol.ContextItemFile
import com.sourcegraph.cody.agent.protocol.Speaker
import com.sourcegraph.cody.chat.ChatUIConstants.ASSISTANT_MESSAGE_GRADIENT_WIDTH
import com.sourcegraph.cody.chat.ChatUIConstants.TEXT_MARGIN
import com.sourcegraph.cody.ui.AccordionSection
import com.sourcegraph.common.BrowserOpener.openInBrowser
import com.sourcegraph.common.ui.SimpleDumbAwareBGTAction
import java.awt.BorderLayout
import java.awt.Insets
import javax.swing.JPanel
import javax.swing.border.EmptyBorder
class ContextFilesPanel(
val project: Project,
chatMessage: ChatMessage,
) : PanelWithGradientBorder(ASSISTANT_MESSAGE_GRADIENT_WIDTH, Speaker.ASSISTANT) {
init {
this.layout = BorderLayout()
isVisible = false
updateContentWith(chatMessage.contextFiles)
}
fun updateContentWith(contextItems: List<ContextItem>?) {
val contextItemFiles = contextItems?.mapNotNull { it as? ContextItemFile }
if (contextItemFiles.isNullOrEmpty()) {
return
}
val title = deriveAccordionTitle(contextItemFiles)
val margin = JBInsets.create(Insets(TEXT_MARGIN, TEXT_MARGIN, TEXT_MARGIN, TEXT_MARGIN))
val accordionSection = AccordionSection(title)
accordionSection.isOpaque = false
accordionSection.border = EmptyBorder(margin)
contextItemFiles.forEachIndexed { index, contextFile: ContextItemFile ->
val filePanel = createFileWithLinkPanel(contextFile)
accordionSection.contentPanel.add(filePanel, index)
}
this.removeAll()
this.isVisible = true
add(accordionSection, BorderLayout.CENTER)
}
private fun deriveAccordionTitle(contextItemFiles: List<ContextItemFile>): String {
val (excludedFiles, includedFiles) =
contextItemFiles.partition { it.isTooLarge == true || it.isIgnored == true }
val prefix = "✨ Context: "
val excludedFileCount = excludedFiles.distinctBy { it.uri }.size
val includedFileCount = includedFiles.distinctBy { it.uri }.size
val lineCount = includedFiles.sumOf { it.range?.length() ?: 0 }
val lines = "$lineCount ${"line".pluralize(lineCount)}"
val files =
"$includedFileCount ${"file".pluralize(includedFileCount)}${if (excludedFileCount > 0) " — $excludedFileCount ${"file".pluralize(excludedFileCount)} excluded" else ""}"
val title =
if (lineCount > 0) {
"$lines from $files"
} else {
files
}
return "$prefix $title"
}
@RequiresEdt
private fun createFileWithLinkPanel(contextItemFile: ContextItemFile): JPanel {
val anAction = SimpleDumbAwareBGTAction {
if (contextItemFile.isLocal()) {
openInEditor(contextItemFile)
} else {
openInBrowser(project, contextItemFile.uri)
}
}
val goToFile = ContextFileActionLink(project, contextItemFile, anAction)
val panel = JPanel(BorderLayout())
panel.isOpaque = false
panel.border = JBUI.Borders.empty(3, 3, 0, 0)
panel.add(goToFile, BorderLayout.PAGE_START)
return panel
}
private fun openInEditor(contextItemFile: ContextItemFile) {
val logicalLine = contextItemFile.range?.start?.line ?: 0
val contextFilePath = contextItemFile.getPath() ?: return
ApplicationManager.getApplication().executeOnPooledThread {
val findFileByNioFile = LocalFileSystem.getInstance().findFileByNioFile(contextFilePath)
if (findFileByNioFile != null) {
ApplicationManager.getApplication().invokeLater {
OpenFileDescriptor(project, findFileByNioFile, logicalLine, /* logicalColumn= */ 0)
.navigate(/* requestFocus= */ true)
}
}
}
}
}
// Can pluralize "file" and "line" by adding -s
private fun String.pluralize(count: Int): String = when {
count == 1 -> this
else -> "${this}s"
}