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

Multithreading support | Part 1: Fuzzier searches #80

Merged
merged 4 commits into from
Sep 8, 2024
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
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ intellijPlatform {
changeNotes = """
<h2>Version $currentVersion</h2>
- Improve task cancelling to avoid InterruptedException and reduce cancelling delay<br>
- Run searches with concurrency<br><br>
To have the most fluid experience it is recommended to NOT use the styled filename type and use a lower
debounce period (10 feels pretty good for me).
""".trimIndent()

ideaVersion {
Expand Down
41 changes: 34 additions & 7 deletions src/main/kotlin/com/mituuz/fuzzier/Fuzzier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,14 @@ import com.intellij.openapi.wm.WindowManager
import com.mituuz.fuzzier.components.FuzzyFinderComponent
import com.mituuz.fuzzier.entities.FuzzyMatchContainer
import com.mituuz.fuzzier.settings.FuzzierSettingsService.RecentFilesMode.NONE
import com.mituuz.fuzzier.util.FuzzierUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.apache.commons.lang3.StringUtils
import java.awt.event.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Future
import javax.swing.*
import kotlin.coroutines.cancellation.CancellationException
Expand Down Expand Up @@ -184,9 +190,9 @@ open class Fuzzier : FuzzyAction() {
component.fileList.setSelectedValue(listModel[0], true)
}
}
} catch (e: InterruptedException) {
} catch (_: InterruptedException) {
return@executeOnPooledThread
} catch (e: CancellationException) {
} catch (_: CancellationException) {
return@executeOnPooledThread
}
}
Expand Down Expand Up @@ -223,16 +229,37 @@ open class Fuzzier : FuzzyAction() {

private fun processProject(project: Project, stringEvaluator: StringEvaluator,
searchString: String, listModel: DefaultListModel<FuzzyMatchContainer>, task: Future<*>?) {
val contentIterator = stringEvaluator.getContentIterator(project.name, searchString, listModel, task)
ProjectFileIndex.getInstance(project).iterateContent(contentIterator)
val filesToIterate = ConcurrentHashMap.newKeySet<FuzzierUtil.IterationFile>()
FuzzierUtil.fileIndexToIterationFile(filesToIterate, ProjectFileIndex.getInstance(project), project.name, task)
processFiles(filesToIterate, stringEvaluator, listModel, searchString, task)
}

private fun processModules(moduleManager: ModuleManager, stringEvaluator: StringEvaluator,
searchString: String, listModel: DefaultListModel<FuzzyMatchContainer>, task: Future<*>?) {
val filesToIterate = ConcurrentHashMap.newKeySet<FuzzierUtil.IterationFile>()
for (module in moduleManager.modules) {
val moduleFileIndex = module.rootManager.fileIndex
val contentIterator = stringEvaluator.getContentIterator(module.name, searchString, listModel, task)
moduleFileIndex.iterateContent(contentIterator)
FuzzierUtil.fileIndexToIterationFile(filesToIterate, module.rootManager.fileIndex, module.name, task)
}
processFiles(filesToIterate, stringEvaluator, listModel, searchString, task)
}

/**
* Processes a set of IterationFiles concurrently
*/
private fun processFiles(
filesToIterate: ConcurrentHashMap.KeySetView<FuzzierUtil.IterationFile, Boolean>,
stringEvaluator: StringEvaluator, listModel: DefaultListModel<FuzzyMatchContainer>,
searchString: String, task: Future<*>?
) {
runBlocking {
withContext(Dispatchers.IO) {
filesToIterate.forEach { iterationFile ->
if (task?.isCancelled == true) return@forEach
launch {
stringEvaluator.evaluateFile(iterationFile, listModel, searchString)
}
}
}
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/main/kotlin/com/mituuz/fuzzier/FuzzyMover.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ import com.intellij.psi.PsiManager
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
import com.mituuz.fuzzier.components.SimpleFinderComponent
import com.mituuz.fuzzier.entities.FuzzyMatchContainer
import com.mituuz.fuzzier.util.FuzzierUtil
import org.apache.commons.lang3.StringUtils
import java.awt.event.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Future
import javax.swing.*
import kotlin.coroutines.cancellation.CancellationException
Expand Down Expand Up @@ -224,9 +226,9 @@ class FuzzyMover : FuzzyAction() {
component.fileList.setSelectedValue(listModel[0], true)
}
}
} catch (e: InterruptedException) {
} catch (_: InterruptedException) {
return@executeOnPooledThread
} catch (e: CancellationException) {
} catch (_: CancellationException) {
return@executeOnPooledThread
}
}
Expand Down
29 changes: 26 additions & 3 deletions src/main/kotlin/com/mituuz/fuzzier/StringEvaluator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.intellij.openapi.vcs.changes.ChangeListManager
import com.intellij.openapi.vfs.VirtualFile
import com.mituuz.fuzzier.entities.FuzzyMatchContainer
import com.mituuz.fuzzier.entities.ScoreCalculator
import com.mituuz.fuzzier.util.FuzzierUtil
import java.util.concurrent.Future
import javax.swing.DefaultListModel

Expand Down Expand Up @@ -58,7 +59,7 @@ class StringEvaluator(
return@ContentIterator true
}
if (filePath.isNotBlank()) {
val fuzzyMatchContainer = createFuzzyContainer(filePath, moduleName)
val fuzzyMatchContainer = createFuzzyContainer(filePath, moduleName, scoreCalculator)
if (fuzzyMatchContainer != null) {
listModel.addElement(fuzzyMatchContainer)
}
Expand All @@ -82,7 +83,7 @@ class StringEvaluator(
return@ContentIterator true
}
if (filePath.isNotBlank()) {
val fuzzyMatchContainer = createFuzzyContainer(filePath, moduleName)
val fuzzyMatchContainer = createFuzzyContainer(filePath, moduleName, scoreCalculator)
if (fuzzyMatchContainer != null) {
listModel.addElement(fuzzyMatchContainer)
}
Expand All @@ -91,6 +92,27 @@ class StringEvaluator(
true
}
}

fun evaluateFile(iterationFile: FuzzierUtil.IterationFile, listModel: DefaultListModel<FuzzyMatchContainer>,
searchString: String) {
val scoreCalculator = ScoreCalculator(searchString)
val file = iterationFile.file
val moduleName = iterationFile.module
if (!file.isDirectory) {
val moduleBasePath = modules[moduleName] ?: return

val filePath = file.path.removePrefix(moduleBasePath)
if (isExcluded(file, filePath)) {
return
}
if (filePath.isNotBlank()) {
val fuzzyMatchContainer = createFuzzyContainer(filePath, moduleName, scoreCalculator)
if (fuzzyMatchContainer != null) {
listModel.addElement(fuzzyMatchContainer)
}
}
}
}

private fun getDirPath(virtualFile: VirtualFile, basePath: String, module: String): String {
var res = virtualFile.path.removePrefix(basePath)
Expand Down Expand Up @@ -133,7 +155,8 @@ class StringEvaluator(
* @param filePath to evaluate
* @return null if no match can be found
*/
private fun createFuzzyContainer(filePath: String, module: String): FuzzyMatchContainer? {
private fun createFuzzyContainer(filePath: String, module: String,
scoreCalculator: ScoreCalculator): FuzzyMatchContainer? {
val filename = filePath.substring(filePath.lastIndexOf("/") + 1)
return when (val score = scoreCalculator.calculateScore(filePath)) {
null -> {
Expand Down
25 changes: 23 additions & 2 deletions src/main/kotlin/com/mituuz/fuzzier/util/FuzzierUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,34 @@ import com.mituuz.fuzzier.entities.FuzzyMatchContainer
import com.mituuz.fuzzier.settings.FuzzierSettingsService
import java.util.*
import javax.swing.DefaultListModel
import kotlin.collections.ArrayList
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.FileIndex
import com.intellij.openapi.vfs.VirtualFile
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Future

class FuzzierUtil {
private var settingsState = service<FuzzierSettingsService>().state
private var listLimit: Int = settingsState.fileListLimit
private var prioritizeShorterDirPaths = settingsState.prioritizeShorterDirPaths

data class IterationFile(val file: VirtualFile, val module: String)

companion object {
fun fileIndexToIterationFile(iterationFiles: ConcurrentHashMap.KeySetView<IterationFile, Boolean>,
fileIndex: FileIndex, moduleName: String, task: Future<*>?,
isDir: Boolean = false) {
fileIndex.iterateContent { file ->
if (task?.isCancelled == true) {
return@iterateContent false
}
if (file.isDirectory == isDir) {
iterationFiles.add(IterationFile(file, moduleName))
}
true
}
}
}

/**
* Process all the elements in the listModel with a priority queue to limit the size
Expand All @@ -58,7 +79,7 @@ class FuzzierUtil {

var minimumScore: Int? = null
listModel.elements().toList().forEach {
if (minimumScore == null || it.getScore() > minimumScore!!) {
if (minimumScore == null || it.getScore() > minimumScore) {
priorityQueue.add(it)
if (priorityQueue.size > listLimit) {
priorityQueue.remove()
Expand Down
Loading