Skip to content

Commit

Permalink
refactored the main text area into a standalone class (#1422)
Browse files Browse the repository at this point in the history
I had been planning to try to add @-mentions of context files into the
Inline Edits instructions dialog. This necessitates pulling the TextArea
class out of EditCommandPrompt.

I've done that in this PR, but did not get the @-mentions done because
it's a larger project than I had anticipated.

## Test plan

No user-visible changes, and all automated & manual tests pass.
  • Loading branch information
steveyegge authored Apr 30, 2024
1 parent fd06855 commit dd7eeaf
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 90 deletions.
10 changes: 8 additions & 2 deletions src/main/java/com/sourcegraph/cody/PromptPanel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ import com.sourcegraph.cody.vscode.CancellationToken
import com.sourcegraph.common.CodyBundle
import com.sourcegraph.common.ui.SimpleDumbAwareBGTAction
import java.awt.Dimension
import java.awt.event.*
import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import javax.swing.DefaultListModel
import javax.swing.JLayeredPane
import javax.swing.KeyStroke
Expand All @@ -40,7 +45,8 @@ class PromptPanel(project: Project, private val chatSession: ChatSession) : JLay
private val contextFilesListView = JBList(contextFilesListViewModel)
private val contextFilesContainer = JBScrollPane(contextFilesListView)
// When "history mode" is enabled, pressing Up/Down arrow keys replaces the chat input with the
// previous/next chat message (via CodyChatMessageHistory). History mode can only activated when
// previous/next chat message (via CodyChatMessageHistory). History mode can only be activated
// when
// the chat input is empty, and it's deactivated on the first key action that is not an Up/Down
// arrow key.
private var isInHistoryMode = true
Expand Down
89 changes: 1 addition & 88 deletions src/main/kotlin/com/sourcegraph/cody/edit/EditCommandPrompt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import com.intellij.openapi.fileEditor.FileEditorManagerListener
import com.intellij.openapi.keymap.KeymapUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
Expand All @@ -42,7 +41,6 @@ import java.awt.Dimension
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.Point
import java.awt.RenderingHints
import java.awt.Toolkit
import java.awt.event.ActionEvent
import java.awt.event.FocusEvent
Expand All @@ -68,7 +66,6 @@ import javax.swing.JList
import javax.swing.JPanel
import javax.swing.JRootPane
import javax.swing.JScrollPane
import javax.swing.JTextArea
import javax.swing.KeyStroke
import javax.swing.ListCellRenderer
import javax.swing.SwingUtilities
Expand Down Expand Up @@ -125,7 +122,7 @@ class EditCommandPrompt(val controller: FixupService, val editor: Editor, dialog
}

private val instructionsField =
GhostTextField().apply {
InstructionsInputTextArea(this).apply {
text = lastPrompt
if (text.isBlank() && promptHistory.isNotEmpty()) {
text = promptHistory.getPrevious()
Expand Down Expand Up @@ -449,7 +446,6 @@ class EditCommandPrompt(val controller: FixupService, val editor: Editor, dialog
private fun getProjectRootPath(project: Project, file: VirtualFile?): String? {
val projectRootManager = ProjectRootManager.getInstance(project)
val contentRoots = projectRootManager.contentRoots
// Find the content root that contains the given file
val contentRoot =
file?.let { nonNullFile ->
contentRoots.firstOrNull { VfsUtilCore.isAncestor(it, nonNullFile, false) }
Expand Down Expand Up @@ -545,85 +541,6 @@ class EditCommandPrompt(val controller: FixupService, val editor: Editor, dialog
clearActivePrompt()
}

// TODO: Refactor this into a standalone class.
private inner class GhostTextField : JTextArea(), FocusListener, Disposable {

private inner class GhostTextDocumentListener : DocumentListener {
private var previousTextEmpty = true

override fun insertUpdate(e: DocumentEvent) {
handleDocumentChange(e)
}

override fun removeUpdate(e: DocumentEvent) {
handleDocumentChange(e)
}

override fun changedUpdate(e: DocumentEvent) {
// Ignore changedUpdate events
}

private fun handleDocumentChange(e: DocumentEvent) {
val currentTextEmpty = e.document.getText(0, e.document.length).isNullOrBlank()
if (currentTextEmpty != previousTextEmpty) {
previousTextEmpty = currentTextEmpty
repaint()
}
}
}

private val ghostTextDocumentListener = GhostTextDocumentListener()

init {
Disposer.register(this@EditCommandPrompt, this@GhostTextField)

addFocusListener(this)
document.addDocumentListener(ghostTextDocumentListener)

lineWrap = true
wrapStyleWord = true
border = JBUI.Borders.empty(JBUI.insets(5))
}

override fun paintComponent(g: Graphics) {
background = textFieldBackground()
(g as Graphics2D).background = textFieldBackground()
super.paintComponent(g)

if (text.isNullOrBlank()) {
g.apply {
setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
color = EditUtil.getThemeColor("Component.infoForeground")
val leftMargin = 15
drawString(GHOST_TEXT, leftMargin, (fontMetrics.height * 1.5).toInt())
}
}
}

// This is used by the up/down arrow keys to insert a history item.
fun setTextAndSelectAll(newContents: String?) {
if (newContents != null) {
text = newContents
selectAll()
}
}

// Focus tracking ensures the ghost text is hidden or shown on focus change.
// The superclass has a tendency to hide the text when we lose the focus.
override fun focusGained(e: FocusEvent?) {
repaint()
}

override fun focusLost(e: FocusEvent?) {
repaint()
}

override fun dispose() {
removeFocusListener(this)
document.removeDocumentListener(ghostTextDocumentListener)
}
} // GhostTextField

private fun makeCornerShape(width: Int, height: Int): RoundRectangle2D {
return RoundRectangle2D.Double(
0.0, 0.0, width.toDouble(), height.toDouble(), CORNER_RADIUS, CORNER_RADIUS)
Expand Down Expand Up @@ -673,10 +590,6 @@ class EditCommandPrompt(val controller: FixupService, val editor: Editor, dialog

const val DIALOG_MINIMUM_HEIGHT = 200

// TODO: Put this back when @-includes are in
// const val GHOST_TEXT = "Instructions (@ to include code)"
const val GHOST_TEXT = "Type what changes you want to make to this file..."

private const val CORNER_RADIUS = 16.0

// Used when the Editor/Document does not have an associated filename.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.sourcegraph.cody.edit

import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Disposer
import com.intellij.util.ui.JBUI
import com.sourcegraph.cody.edit.EditCommandPrompt.Companion.textFieldBackground
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.RenderingHints
import java.awt.event.FocusEvent
import java.awt.event.FocusListener
import javax.swing.JTextArea
import javax.swing.event.DocumentEvent
import javax.swing.event.DocumentListener

class InstructionsInputTextArea(parentDisposable: Disposable) :
JTextArea(), FocusListener, Disposable {

private inner class GhostTextDocumentListener : DocumentListener {
private var previousTextEmpty = true

override fun insertUpdate(e: DocumentEvent) {
handleDocumentChange(e)
}

override fun removeUpdate(e: DocumentEvent) {
handleDocumentChange(e)
}

override fun changedUpdate(e: DocumentEvent) {
// Ignore changedUpdate events
}

private fun handleDocumentChange(e: DocumentEvent) {
val currentTextEmpty = e.document.getText(0, e.document.length).isNullOrBlank()
if (currentTextEmpty != previousTextEmpty) {
previousTextEmpty = currentTextEmpty
repaint()
}
}
}

private val ghostTextDocumentListener = GhostTextDocumentListener()

init {
Disposer.register(parentDisposable, this)

addFocusListener(this)
document.addDocumentListener(ghostTextDocumentListener)

lineWrap = true
wrapStyleWord = true
border = JBUI.Borders.empty(JBUI.insets(5))
}

override fun paintComponent(g: Graphics) {
background = textFieldBackground()
(g as Graphics2D).background = textFieldBackground()
super.paintComponent(g)

if (text.isNullOrBlank()) {
g.apply {
setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
color = EditUtil.getThemeColor("Component.infoForeground")
val leftMargin = 15
drawString(GHOST_TEXT, leftMargin, (fontMetrics.height * 1.5).toInt())
}
}
}

// This is used by the up/down arrow keys to insert a history item.
fun setTextAndSelectAll(newContents: String?) {
if (newContents != null) {
text = newContents
selectAll()
}
}

// Focus tracking ensures the ghost text is hidden or shown on focus change.
// The superclass has a tendency to hide the text when we lose the focus.
override fun focusGained(e: FocusEvent?) {
repaint()
}

override fun focusLost(e: FocusEvent?) {
repaint()
}

override fun dispose() {
removeFocusListener(this)
document.removeDocumentListener(ghostTextDocumentListener)
}

companion object {
// TODO: Put this back when @-includes are in
// const val GHOST_TEXT = "Instructions (@ to include code)"
const val GHOST_TEXT = "Type what changes you want to make to this file..."
}
}

0 comments on commit dd7eeaf

Please sign in to comment.