Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement caching for diff in IntelliJ extension #4753

Merged
merged 6 commits into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.github.continuedev.continueintellijextension.activities
import IntelliJIDE
import com.intellij.openapi.fileEditor.FileEditorManagerListener
import com.github.continuedev.continueintellijextension.auth.AuthListener
import com.github.continuedev.continueintellijextension.auth.ContinueAuthService
Expand Down Expand Up @@ -185,6 +186,8 @@ class ContinuePluginStartupActivity : StartupActivity, DumbAware {

// Notify core of content changes
if (changedURIs.isNotEmpty()) {
continuePluginService.updateLastFileSaveTimestamp()

val data = mapOf("uris" to changedURIs)
continuePluginService.coreMessenger?.request("files/changed", data, null) { _ -> }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.github.continuedev.continueintellijextension.`continue`

import com.github.continuedev.continueintellijextension.services.ContinuePluginService
import com.github.continuedev.continueintellijextension.utils.toUriOrNull
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
import java.net.URI

class GitService(
private val project: Project,
private val continuePluginService: ContinuePluginService
) {
// Add a simple cache for diff results
private data class DiffCache(
val timestamp: Long,
val diffs: List<String>
)

// Cache the last diff result
private var diffCache: DiffCache? = null
private var lastFileSaveTimestamp: Long = System.currentTimeMillis()

/**
* Updates the timestamp when a file is saved
*/
fun updateLastFileSaveTimestamp() {
lastFileSaveTimestamp = System.currentTimeMillis()
}

/**
* Returns the git diff for all workspace directories
*/
suspend fun getDiff(includeUnstaged: Boolean): List<String> {
// Check if we have a valid cache entry
if (diffCache != null && diffCache!!.timestamp == lastFileSaveTimestamp) {
return diffCache!!.diffs
}

// If no cache hit, compute the diff
val workspaceDirs = workspaceDirectories()
val diffs = mutableListOf<String>()

for (workspaceDir in workspaceDirs) {
val output = StringBuilder()
val builder = if (includeUnstaged) {
ProcessBuilder("git", "diff")
} else {
ProcessBuilder("git", "diff", "--cached")
}
builder.directory(File(URI(workspaceDir)))
val process = withContext(Dispatchers.IO) {
builder.start()
}

val reader = BufferedReader(InputStreamReader(process.inputStream))
var line: String? = withContext(Dispatchers.IO) {
reader.readLine()
}
while (line != null) {
output.append(line)
output.append("\n")
line = withContext(Dispatchers.IO) {
reader.readLine()
}
}

withContext(Dispatchers.IO) {
process.waitFor()
}

diffs.add(output.toString())
}

// Cache the result
diffCache = DiffCache(lastFileSaveTimestamp, diffs)
return diffs
}

private fun workspaceDirectories(): Array<String> {
val dirs = this.continuePluginService.workspacePaths

if (dirs?.isNotEmpty() == true) {
return dirs
}

return listOfNotNull(project.guessProjectDir()?.toUriOrNull()).toTypedArray()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class IdeProtocolClient(
)
}

fun updateLastFileSaveTimestamp() {
(ide as IntelliJIDE).updateLastFileSaveTimestamp()
}

fun handleMessage(msg: String, respond: (Any?) -> Unit) {
coroutineScope.launch(Dispatchers.IO) {
val message = Gson().fromJson(msg, Message::class.java)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import com.github.continuedev.continueintellijextension.*
import com.github.continuedev.continueintellijextension.constants.getContinueGlobalPath
import com.github.continuedev.continueintellijextension.`continue`.GitService
import com.github.continuedev.continueintellijextension.services.ContinueExtensionSettings
import com.github.continuedev.continueintellijextension.services.ContinuePluginService
import com.github.continuedev.continueintellijextension.utils.OS
import com.github.continuedev.continueintellijextension.utils.getMachineUniqueID
import com.github.continuedev.continueintellijextension.utils.getOS
import com.github.continuedev.continueintellijextension.utils.toUriOrNull
import com.github.continuedev.continueintellijextension.utils.Desktop
import com.google.gson.Gson
import com.github.continuedev.continueintellijextension.utils.*
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.util.ExecUtil
Expand All @@ -31,28 +27,26 @@ import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiDocumentManager
import com.intellij.testFramework.LightVirtualFile
import com.intellij.util.containers.toArray
import kotlinx.coroutines.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.awt.Toolkit
import java.awt.datatransfer.DataFlavor
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.InputStreamReader
import java.net.URI
import java.net.URL
import java.nio.charset.Charset
import java.nio.file.Paths

class IntelliJIDE(
private val project: Project,
private val continuePluginService: ContinuePluginService,

) : IDE {
) : IDE {

private val gitService = GitService(project, continuePluginService)


private val ripgrep: String

init {
Expand All @@ -66,6 +60,13 @@ class IntelliJIDE(
Paths.get(pluginPath.toString(), "ripgrep", "bin", "rg" + if (os == OS.WINDOWS) ".exe" else "").toString()
}

/**
* Updates the timestamp when a file is saved
*/
override fun updateLastFileSaveTimestamp() {
gitService.updateLastFileSaveTimestamp()
}

override suspend fun getIdeInfo(): IdeInfo {
val applicationInfo = ApplicationInfo.getInstance()
val ideName: String = applicationInfo.fullApplicationName
Expand Down Expand Up @@ -110,41 +111,7 @@ class IntelliJIDE(
}

override suspend fun getDiff(includeUnstaged: Boolean): List<String> {
val workspaceDirs = workspaceDirectories()
val diffs = mutableListOf<String>()

for (workspaceDir in workspaceDirs) {
val output = StringBuilder()
val builder = if (includeUnstaged) {
ProcessBuilder("git", "diff")
} else {
ProcessBuilder("git", "diff", "--cached")
}
builder.directory(File(URI(workspaceDir)))
val process = withContext(Dispatchers.IO) {
builder.start()
}

val reader = BufferedReader(InputStreamReader(process.inputStream))
var line: String? = withContext(Dispatchers.IO) {
reader.readLine()
}
while (line != null) {
output.append(line)
output.append("\n")
line = withContext(Dispatchers.IO) {
reader.readLine()
}
}

withContext(Dispatchers.IO) {
process.waitFor()
}

diffs.add(output.toString())
}

return diffs
return gitService.getDiff(includeUnstaged)
}

override suspend fun getClipboardContent(): Map<String, String> {
Expand Down Expand Up @@ -185,7 +152,7 @@ class IntelliJIDE(
}

override suspend fun getWorkspaceConfigs(): List<ContinueRcJson> {
val workspaceDirs = workspaceDirectories()
val workspaceDirs = this.getWorkspaceDirs()

val configs = mutableListOf<String>()

Expand Down Expand Up @@ -434,7 +401,7 @@ class IntelliJIDE(
}

override suspend fun getTags(artifactId: String): List<IndexTag> {
val workspaceDirs = workspaceDirectories()
val workspaceDirs = this.getWorkspaceDirs()

// Collect branches concurrently using Kotlin coroutines
val branches = withContext(Dispatchers.IO) {
Expand Down Expand Up @@ -577,4 +544,5 @@ class IntelliJIDE(

return listOfNotNull(project.guessProjectDir()?.toUriOrNull()).toTypedArray()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@ class ContinuePluginService : Disposable, DumbAware {
) {
continuePluginWindow?.browser?.sendToWebview(messageType, data, messageId)
}

fun updateLastFileSaveTimestamp() {
ideProtocolClient?.updateLastFileSaveTimestamp()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ interface IDE {

// Callbacks
fun onDidChangeActiveTextEditor(callback: (filepath: String) -> Unit)

fun updateLastFileSaveTimestamp() {
// Default implementation does nothing
}
}

data class GetGhTokenArgs(
Expand Down
Loading