Skip to content

Commit 048ccb4

Browse files
authored
Implement fuzzy search for open tabs only (#163)
* Implement fuzzy search for open tabs only * Fix tests * Update IntelliJ platform plugin * Add tests for tab collector * Add tests for base collector
1 parent 5f385e9 commit 048ccb4

20 files changed

Lines changed: 424 additions & 222 deletions

File tree

build.gradle.kts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2727
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2828

2929
// Use the same version and group for the jar and the plugin
30-
val currentVersion = "2.1.0"
30+
val currentVersion = "2.2.0"
3131
val myGroup = "com.mituuz"
3232
version = currentVersion
3333
group = myGroup
@@ -40,18 +40,12 @@ intellijPlatform {
4040
changeNotes = """
4141
<h2>Version $currentVersion</h2>
4242
<ul>
43-
<li>Add a fallback solution for file path handling on Windows
43+
<li>
44+
Implement Fuzzy File Search (Open Tabs) action
4445
<ul>
45-
<li>Thanks to <a href="https://github.com/s0ders">s0ders</a>!</li>
46+
<li><code>com.mituuz.fuzzier.search.FuzzierOpenTabs</code></li>
4647
</ul>
4748
</li>
48-
<li>Add a built-in Fuzzier grep backend
49-
<ul>
50-
<li>Replaces <code>grep</code> and <code>findstr</code> fallback implementations</li>
51-
<li>Add setting to choose between Dynamic (uses <code>rg</code> if available, otherwise Fuzzier) and Fuzzier backends</li>
52-
</ul>
53-
</li>
54-
<li>Migrate from <code>Timer</code> to coroutines for debouncing</li>
5549
</ul>
5650
""".trimIndent()
5751

changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## Version 2.2.0
4+
5+
- Implement Fuzzy File Search (Open Tabs) action
6+
- `com.mituuz.fuzzier.search.FuzzierOpenTabs`
7+
38
## Version 2.1.0
49

510
- Add a fallback solution for file path handling on Windows

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
junit5 = "6.0.1"
33
junit4 = "4.13.2"
44
kotlin = "2.2.21"
5-
intellijPlatform = "2.10.5"
5+
intellijPlatform = "2.11.0"
66
kover = "0.9.3"
77
communityVersion = "2025.1"
88
mockk = "1.14.6"

readme.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,17 @@ List movement can be remapped from settings -> keymaps, but do not support chord
5454

5555
- **Fuzzier** (`com.mituuz.fuzzier.search.Fuzzier`) - Search files using a fuzzy search
5656
- **FuzzierVCS** (`com.mituuz.fuzzier.search.FuzzierVCS`) - Search files using a fuzzy search on only VCS tracked files
57+
- **FuzzierOpenTabs** (`com.mituuz.fuzzier.search.FuzzierOpenTabs`) - Search files using a fuzzy search on currently
58+
open tabs
5759
- **FuzzyMover** (`com.mituuz.fuzzier.operation.FuzzyMover`) - Move a file using a fuzzy search
5860
- **FuzzyGrep** (`com.mituuz.fuzzier.grep.FuzzyGrep`) - Case sensitive text search from project
5961
- **FuzzyGrepCI** (`com.mituuz.fuzzier.grep.FuzzyGrepCI`) - Case insensitive text search from project
6062
- **FuzzyGrepOpenTabs** (`com.mituuz.fuzzier.grep.FuzzyGrepOpenTabs`) - Case sensitive text search from open tabs
6163
- **FuzzyGrepOpenTabsCI** (`com.mituuz.fuzzier.grep.FuzzyGrepOpenTabsCI`) - Case insensitive text search from open tabs
62-
- **FuzzyGrepCurrentBuffer** (`com.mituuz.fuzzier.grep.FuzzyGrepCurrentBuffer`) - Case sensitive text search from current buffer
63-
- **FuzzyGrepCurrentBufferCI** (`com.mituuz.fuzzier.grep.FuzzyGrepCurrentBufferCI`) - Case insensitive text search from current buffer
64+
- **FuzzyGrepCurrentBuffer** (`com.mituuz.fuzzier.grep.FuzzyGrepCurrentBuffer`) - Case sensitive text search from
65+
current buffer
66+
- **FuzzyGrepCurrentBufferCI** (`com.mituuz.fuzzier.grep.FuzzyGrepCurrentBufferCI`) - Case insensitive text search from
67+
current buffer
6468

6569
## Documentation
6670

src/main/kotlin/com/mituuz/fuzzier/actions/FuzzyAction.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
package com.mituuz.fuzzier.actions
2525

2626
import com.intellij.icons.AllIcons
27+
import com.intellij.notification.Notification
28+
import com.intellij.notification.NotificationType
29+
import com.intellij.notification.Notifications
2730
import com.intellij.openapi.actionSystem.*
2831
import com.intellij.openapi.components.service
2932
import com.intellij.openapi.editor.Caret
@@ -57,6 +60,10 @@ import java.util.concurrent.ConcurrentHashMap
5760
import javax.swing.*
5861

5962
abstract class FuzzyAction : AnAction() {
63+
companion object {
64+
const val FUZZIER_NOTIFICATION_GROUP: String = "Fuzzier Notification Group"
65+
}
66+
6067
lateinit var component: FuzzyComponent
6168
lateinit var popup: JBPopup
6269
private var originalDownHandler: EditorActionHandler? = null
@@ -71,6 +78,16 @@ abstract class FuzzyAction : AnAction() {
7178
protected open var currentUpdateListContentJob: Job? = null
7279
protected open var actionScope: CoroutineScope? = null
7380

81+
protected fun showNotification(
82+
title: String, content: String, project: Project, type: NotificationType = NotificationType.ERROR
83+
) {
84+
val grepNotification = Notification(
85+
FUZZIER_NOTIFICATION_GROUP, title, content, type
86+
)
87+
Notifications.Bus.notify(grepNotification, project)
88+
}
89+
90+
7491
protected fun getPopupProvider(): PopupProvider {
7592
return when (globalState.popupSizing) {
7693
FuzzierGlobalSettingsService.PopupSizing.AUTO_SIZE -> {

src/main/kotlin/com/mituuz/fuzzier/actions/filesystem/FilesystemAction.kt

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,10 @@ package com.mituuz.fuzzier.actions.filesystem
2626

2727
import com.intellij.openapi.actionSystem.AnActionEvent
2828
import com.intellij.openapi.application.EDT
29-
import com.intellij.openapi.module.ModuleManager
3029
import com.intellij.openapi.project.Project
31-
import com.intellij.openapi.project.rootManager
32-
import com.intellij.openapi.roots.ProjectFileIndex
3330
import com.intellij.openapi.vfs.VirtualFile
3431
import com.mituuz.fuzzier.actions.FuzzyAction
3532
import com.mituuz.fuzzier.entities.*
36-
import com.mituuz.fuzzier.intellij.iteration.IntelliJIterationFileCollector
3733
import com.mituuz.fuzzier.intellij.iteration.IterationFileCollector
3834
import com.mituuz.fuzzier.util.FuzzierUtil
3935
import kotlinx.coroutines.*
@@ -43,11 +39,10 @@ import java.util.concurrent.ConcurrentHashMap
4339
import javax.swing.DefaultListModel
4440

4541
abstract class FilesystemAction : FuzzyAction() {
46-
private var collector: IterationFileCollector = IntelliJIterationFileCollector()
42+
abstract fun createCollector(): IterationFileCollector
4743

4844
abstract override fun runAction(
49-
project: Project,
50-
actionEvent: AnActionEvent
45+
project: Project, actionEvent: AnActionEvent
5146
)
5247

5348
abstract fun buildFileFilter(project: Project): (VirtualFile) -> Boolean
@@ -58,17 +53,9 @@ abstract class FilesystemAction : FuzzyAction() {
5853
val ctx = currentCoroutineContext()
5954
val job = ctx.job
6055

61-
val indexTargets = if (projectState.isProject) {
62-
listOf(ProjectFileIndex.getInstance(project) to project.name)
63-
} else {
64-
val moduleManager = ModuleManager.getInstance(project)
65-
moduleManager.modules.map { it.rootManager.fileIndex to it.name }
66-
}
67-
68-
return collector.collectFiles(
69-
targets = indexTargets,
70-
shouldContinue = { job.isActive },
71-
fileFilter = buildFileFilter(project)
56+
val c: IterationFileCollector = createCollector()
57+
return c.collectFiles(
58+
project = project, shouldContinue = { job.isActive }, fileFilter = buildFileFilter(project)
7259
)
7360
}
7461

@@ -99,9 +86,7 @@ abstract class FilesystemAction : FuzzyAction() {
9986
val processedFiles = ConcurrentHashMap.newKeySet<String>()
10087
val listLimit = fileListLimit
10188
val priorityQueue = PriorityQueue(
102-
listLimit + 1,
103-
compareBy<FuzzyMatchContainer> { it.getScore(prioritizeShorterDirPaths) }
104-
)
89+
listLimit + 1, compareBy<FuzzyMatchContainer> { it.getScore(prioritizeShorterDirPaths) })
10590

10691
val queueLock = Any()
10792
var minimumScore: Int? = null
@@ -139,9 +124,7 @@ abstract class FilesystemAction : FuzzyAction() {
139124
}
140125
}
141126

142-
fileEntries
143-
.filter { processedFiles.add(it.path) }
144-
.forEach { ch.send(it) }
127+
fileEntries.filter { processedFiles.add(it.path) }.forEach { ch.send(it) }
145128
ch.close()
146129
}
147130

src/main/kotlin/com/mituuz/fuzzier/components/TestBenchComponent.kt

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,8 @@ import com.intellij.openapi.application.ApplicationManager
2828
import com.intellij.openapi.components.service
2929
import com.intellij.openapi.editor.event.DocumentEvent
3030
import com.intellij.openapi.editor.event.DocumentListener
31-
import com.intellij.openapi.module.ModuleManager
3231
import com.intellij.openapi.project.Project
3332
import com.intellij.openapi.project.ProjectManager
34-
import com.intellij.openapi.project.rootManager
35-
import com.intellij.openapi.roots.ProjectFileIndex
3633
import com.intellij.openapi.vfs.VirtualFile
3734
import com.intellij.ui.EditorTextField
3835
import com.intellij.ui.components.JBScrollPane
@@ -41,7 +38,6 @@ import com.intellij.uiDesigner.core.GridConstraints
4138
import com.intellij.uiDesigner.core.GridLayoutManager
4239
import com.mituuz.fuzzier.entities.*
4340
import com.mituuz.fuzzier.intellij.iteration.IntelliJIterationFileCollector
44-
import com.mituuz.fuzzier.intellij.iteration.IterationFileCollector
4541
import com.mituuz.fuzzier.settings.FuzzierSettingsService
4642
import com.mituuz.fuzzier.util.FuzzierUtil
4743
import kotlinx.coroutines.*
@@ -68,7 +64,6 @@ class TestBenchComponent : JPanel(), Disposable {
6864
private lateinit var projectState: FuzzierSettingsService.State
6965
private var currentUpdateListContentJob: Job? = null
7066
private var actionScope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
71-
private var collector: IterationFileCollector = IntelliJIterationFileCollector()
7267

7368
fun fill(settingsComponent: FuzzierGlobalSettingsComponent) {
7469
val project = ProjectManager.getInstance().openProjects[0]
@@ -222,15 +217,9 @@ class TestBenchComponent : JPanel(), Disposable {
222217
val ctx = currentCoroutineContext()
223218
val job = ctx.job
224219

225-
val indexTargets = if (projectState.isProject) {
226-
listOf(ProjectFileIndex.getInstance(project) to project.name)
227-
} else {
228-
val moduleManager = ModuleManager.getInstance(project)
229-
moduleManager.modules.map { it.rootManager.fileIndex to it.name }
230-
}
231-
220+
val collector = IntelliJIterationFileCollector(projectState)
232221
return collector.collectFiles(
233-
targets = indexTargets, shouldContinue = { job.isActive }, fileFilter = buildFileFilter()
222+
project = project, shouldContinue = { job.isActive }, fileFilter = buildFileFilter()
234223
)
235224
}
236225

src/main/kotlin/com/mituuz/fuzzier/grep/FuzzyGrep.kt

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@
2424

2525
package com.mituuz.fuzzier.grep
2626

27-
import com.intellij.notification.Notification
28-
import com.intellij.notification.NotificationType
29-
import com.intellij.notification.Notifications
3027
import com.intellij.openapi.actionSystem.AnActionEvent
3128
import com.intellij.openapi.application.ApplicationManager
3229
import com.intellij.openapi.application.EDT
@@ -59,10 +56,6 @@ import javax.swing.DefaultListModel
5956
import javax.swing.ListModel
6057

6158
open class FuzzyGrep : FuzzyAction() {
62-
companion object {
63-
const val FUZZIER_NOTIFICATION_GROUP: String = "Fuzzier Notification Group"
64-
}
65-
6659
val isWindows = System.getProperty("os.name").lowercase().contains("win")
6760
private val backendResolver = BackendResolver(isWindows)
6861
private val commandRunner = CommandRunner()
@@ -129,15 +122,6 @@ open class FuzzyGrep : FuzzyAction() {
129122
}
130123
}
131124

132-
private fun showNotification(
133-
title: String, content: String, project: Project, type: NotificationType = NotificationType.ERROR
134-
) {
135-
val grepNotification = Notification(
136-
FUZZIER_NOTIFICATION_GROUP, title, content, type
137-
)
138-
Notifications.Bus.notify(grepNotification, project)
139-
}
140-
141125
override fun onPopupClosed() {
142126
previewAlarm?.dispose()
143127
currentLaunchJob?.cancel()

src/main/kotlin/com/mituuz/fuzzier/intellij/iteration/IntelliJIterationFileCollector.kt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,24 @@
2424

2525
package com.mituuz.fuzzier.intellij.iteration
2626

27+
import com.intellij.openapi.module.ModuleManager
28+
import com.intellij.openapi.project.Project
29+
import com.intellij.openapi.project.rootManager
2730
import com.intellij.openapi.roots.FileIndex
31+
import com.intellij.openapi.roots.ProjectFileIndex
2832
import com.intellij.openapi.vfs.VirtualFile
2933
import com.mituuz.fuzzier.entities.IterationEntry
34+
import com.mituuz.fuzzier.settings.FuzzierSettingsService
3035

31-
class IntelliJIterationFileCollector : IterationFileCollector {
36+
class IntelliJIterationFileCollector(val projectState: FuzzierSettingsService.State) : IterationFileCollector {
3237
override fun collectFiles(
33-
targets: List<Pair<FileIndex, String>>,
38+
project: Project,
3439
shouldContinue: () -> Boolean,
3540
fileFilter: (VirtualFile) -> Boolean
3641
): List<IterationEntry> = buildList {
37-
for ((fileIndex, moduleName) in targets) {
42+
val targetIndexes = getTargetIndexes(project)
43+
44+
for ((fileIndex, moduleName) in targetIndexes) {
3845
fileIndex.iterateContent { vf ->
3946
if (!shouldContinue()) return@iterateContent false
4047

@@ -47,4 +54,13 @@ class IntelliJIterationFileCollector : IterationFileCollector {
4754
}
4855
}
4956
}
57+
58+
private fun getTargetIndexes(project: Project): List<Pair<FileIndex, String>> {
59+
return if (projectState.isProject) {
60+
listOf(ProjectFileIndex.getInstance(project) to project.name)
61+
} else {
62+
val moduleManager = ModuleManager.getInstance(project)
63+
moduleManager.modules.map { it.rootManager.fileIndex to it.name }
64+
}
65+
}
5066
}

src/main/kotlin/com/mituuz/fuzzier/intellij/iteration/IterationFileCollector.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@
2424

2525
package com.mituuz.fuzzier.intellij.iteration
2626

27-
import com.intellij.openapi.roots.FileIndex
27+
import com.intellij.openapi.project.Project
2828
import com.intellij.openapi.vfs.VirtualFile
2929
import com.mituuz.fuzzier.entities.IterationEntry
3030

3131
interface IterationFileCollector {
3232
fun collectFiles(
33-
targets: List<Pair<FileIndex, String>>,
33+
project: Project,
3434
shouldContinue: () -> Boolean,
3535
fileFilter: (VirtualFile) -> Boolean,
3636
): List<IterationEntry>

0 commit comments

Comments
 (0)