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

Improve performance of idea plugin #439

Merged
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,6 +1,7 @@
package com.github.takahirom.roborazzi.idea.preview

import com.github.takahirom.roborazzi.idea.settings.AppSettingsState
import com.intellij.openapi.application.readAction
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootManager
Expand Down Expand Up @@ -54,9 +55,14 @@ class PreviewViewModel {
}
}

private var caretPositionJob: Job? = null

fun onCaretPositionChanged(project: Project) {
roborazziLog("onCaretPositionChanged")
coroutineScope.launch {
caretPositionJob?.cancel()
caretPositionJob = coroutineScope.launch {
// debounce
delay(400)
roborazziLog("onCaretPositionChanged")
updateListJob?.cancel()
refreshListProcess(project)
selectListIndexByCaret(project)
Expand All @@ -72,24 +78,27 @@ class PreviewViewModel {
gradleTask.executeTaskByName(project, selectedTaskName)
}

private fun selectListIndexByCaret(project: Project) {
val editor = FileEditorManager.getInstance(project).selectedTextEditor
val offset = editor?.caretModel?.offset
if (offset != null) {
val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) as? KtFile
?: return
val kotlinFile = psiFile as? KtFile ?: return
val pe: PsiElement = kotlinFile.findElementAt(editor.caretModel.offset) ?: return
val method: KtFunction = findFunction(pe) ?: return
roborazziLog("imagesStateFlow.value = ${imagesStateFlow.value}")
imagesStateFlow.value.indexOfFirst {
it.first.substringAfterLast(File.separator).contains(method.name ?: "")
}
.let {
roborazziLog("shouldSeeIndex.value = $it")
shouldSeeImageIndex.value = it
private suspend fun selectListIndexByCaret(project: Project) {
val start = System.currentTimeMillis()
val kotlinFile = getCurrentKtFileOrNull(project) ?: return
readAction {
val editor = FileEditorManager.getInstance(project).selectedTextEditor
val offset = editor?.caretModel?.offset
if (offset != null) {
val pe: PsiElement =
kotlinFile.findElementAt(editor.caretModel.offset) ?: return@readAction
val method: KtFunction = findFunction(pe) ?: return@readAction
// roborazziLog("imagesStateFlow.value = ${imagesStateFlow.value}")
imagesStateFlow.value.indexOfFirst {
it.first.substringAfterLast(File.separator).contains(method.name ?: "")
}
.let {
roborazziLog("shouldSeeIndex.value = $it")
shouldSeeImageIndex.value = it
}
}
}
roborazziLog("selectListIndexByCaret took ${System.currentTimeMillis() - start}ms")
}

private fun findFunction(element: PsiElement): KtFunction? {
Expand Down Expand Up @@ -127,32 +136,21 @@ class PreviewViewModel {
}
}

private fun fetchTasks(project: Project) {
private suspend fun fetchTasks(project: Project) {
roborazziLog("fetchTasks...")
val startTime = System.currentTimeMillis()
_dropDownUiState.update { currentUiState ->
currentUiState.copy( tasks = gradleTask.fetchTasks(project))
currentUiState.copy(tasks = gradleTask.fetchTasks(project))
}
roborazziLog("fetchTasks took ${System.currentTimeMillis() - startTime}ms")
}

private suspend fun refreshListProcess(project: Project) {
val start = System.currentTimeMillis()
roborazziLog("refreshListProcess")
statusText.value = "Loading..."
yield()
val editor = FileEditorManager.getInstance(project).selectedTextEditor ?: return run {
statusText.value = "No editor found"
imagesStateFlow.value = emptyList()
}

val psiFile: PsiFile =
PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return run {
statusText.value = "No psi file found"
imagesStateFlow.value = emptyList()
}
val kotlinFile = psiFile as? KtFile ?: return run {
statusText.value = "No kotlin file found"
imagesStateFlow.value = emptyList()
}
val kotlinFile = getCurrentKtFileOrNull(project) ?: return
if (lastEditingFileName.value != kotlinFile.name) {
imagesStateFlow.value = emptyList()
delay(10)
Expand All @@ -167,14 +165,18 @@ class PreviewViewModel {
} || (declaration is KtFunction && declaration.name?.startsWith("test") == true)
}

val functions: List<KtFunction> = allDeclarations.filterIsInstance<KtFunction>()
.filter { hasPreviewOrTestAnnotationOrHasNameOfTestFunction(it) }
val classes: List<KtClass> = allDeclarations.filterIsInstance<KtClass>()
.filter {
it.name?.contains("Test") == true || it.declarations.any {
hasPreviewOrTestAnnotationOrHasNameOfTestFunction(it)
val functions: List<KtFunction> = readAction {
allDeclarations.filterIsInstance<KtFunction>()
.filter { hasPreviewOrTestAnnotationOrHasNameOfTestFunction(it) }
}
val classes: List<KtClass> = readAction {
allDeclarations.filterIsInstance<KtClass>()
.filter {
it.name?.contains("Test") == true || it.declarations.any {
hasPreviewOrTestAnnotationOrHasNameOfTestFunction(it)
}
}
}
}

val searchPath = project.basePath
statusText.value = "Searching images in $searchPath ..."
Expand Down Expand Up @@ -206,6 +208,24 @@ class PreviewViewModel {
imagesStateFlow.value = result
}

private suspend fun getCurrentKtFileOrNull(project: Project): KtFile? = readAction {
val editor =
FileEditorManager.getInstance(project).selectedTextEditor ?: return@readAction run {
statusText.value = "No editor found"
imagesStateFlow.value = emptyList()
}

val psiFile: PsiFile =
PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return@readAction run {
statusText.value = "No psi file found"
imagesStateFlow.value = emptyList()
}
psiFile as? KtFile ?: return@readAction run {
statusText.value = "No kotlin file found"
imagesStateFlow.value = emptyList()
}
} as? KtFile

private suspend fun List<File>.sortedByClassesAndFunctions(
classes: List<KtClass>,
functions: List<KtFunction>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.intellij.execution.RunManager
import com.intellij.execution.executors.DefaultRunExecutor
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.execution.runners.ProgramRunner
import com.intellij.openapi.application.readAction
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.ModuleData
Expand All @@ -13,6 +14,8 @@ import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.kotlin.idea.util.projectStructure.module
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType
Expand All @@ -22,18 +25,22 @@ import org.jetbrains.plugins.gradle.util.GradleUtil

class RoborazziGradleTask {

fun fetchTasks(project: Project): List<String> {
val gradleModuleData = getGradleData(project) ?: return emptyList()

return ExternalSystemApiUtil.findAll(gradleModuleData, ProjectKeys.TASK)
.filter {
it.data.name.contains("Roborazzi", true) && it.data.name.contains(
"DirRoborazzi",
true
).not() && !it.data.name.contains("finalize", true)
}
.map { gradleModuleData.data.id + ":" + it.data.name }
.sortedBy { com.github.takahirom.roborazzi.RoborazziTaskType.getOrderOfTaskName(it) }
suspend fun fetchTasks(project: Project): List<String> {
val gradleModuleData = readAction { getGradleData(project) } ?: return emptyList()
val nodes = readAction {
ExternalSystemApiUtil.findAll(gradleModuleData, ProjectKeys.TASK)
}
return withContext(Dispatchers.Default) {
nodes
.filter {
it.data.name.contains("Roborazzi", true) && it.data.name.contains(
"DirRoborazzi",
true
).not() && !it.data.name.contains("finalize", true)
}
.map { gradleModuleData.data.id + ":" + it.data.name }
.sortedBy { com.github.takahirom.roborazzi.RoborazziTaskType.getOrderOfTaskName(it) }
}
}

fun executeTaskByName(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ class ImageListCellRenderer : ListCellRenderer<Pair<String, Long>> {
): Component {
return lruCache.getOrPut(
CacheKey(
width = list.width,
// For performance, we use the rounded width to reduce the number of cache invalidations
width = list.width - (list.width % 30),
filePath = value.first,
lastModified = value.second,
isSelected = isSelected
Expand Down
Loading