diff --git a/build.gradle.kts b/build.gradle.kts
index a56e96da..48ecd49a 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -64,6 +64,7 @@ intellijPlatform {
changeNotes = """
Version $currentVersion
+ - Add option to ignore characters from search
- Update dependencies
""".trimIndent()
diff --git a/changelog.md b/changelog.md
index 1df50eab..08cd5a0a 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,6 @@
# Changelog
## Version 1.4.0
+- Add option to ignore characters from search
- Update dependencies
## Version 1.3.0
diff --git a/src/main/kotlin/com/mituuz/fuzzier/Fuzzier.kt b/src/main/kotlin/com/mituuz/fuzzier/Fuzzier.kt
index d615ed39..3295e853 100644
--- a/src/main/kotlin/com/mituuz/fuzzier/Fuzzier.kt
+++ b/src/main/kotlin/com/mituuz/fuzzier/Fuzzier.kt
@@ -265,12 +265,13 @@ open class Fuzzier : FuzzyAction() {
stringEvaluator: StringEvaluator, listModel: DefaultListModel,
searchString: String, task: Future<*>?
) {
+ val ss = FuzzierUtil.cleanSearchString(searchString, fuzzierSettingsService.state.ignoredCharacters)
runBlocking {
withContext(Dispatchers.IO) {
filesToIterate.forEach { iterationFile ->
if (task?.isCancelled == true) return@forEach
launch {
- stringEvaluator.evaluateFile(iterationFile, listModel, searchString)
+ stringEvaluator.evaluateFile(iterationFile, listModel, ss)
}
}
}
diff --git a/src/main/kotlin/com/mituuz/fuzzier/FuzzyMover.kt b/src/main/kotlin/com/mituuz/fuzzier/FuzzyMover.kt
index 76c4ab0d..b5e7a4f6 100644
--- a/src/main/kotlin/com/mituuz/fuzzier/FuzzyMover.kt
+++ b/src/main/kotlin/com/mituuz/fuzzier/FuzzyMover.kt
@@ -49,6 +49,7 @@ import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectori
import com.mituuz.fuzzier.components.SimpleFinderComponent
import com.mituuz.fuzzier.entities.FuzzyMatchContainer
import com.mituuz.fuzzier.entities.StringEvaluator
+import com.mituuz.fuzzier.util.FuzzierUtil
import com.mituuz.fuzzier.util.FuzzierUtil.Companion.createDimensionKey
import org.apache.commons.lang3.StringUtils
import java.awt.Point
@@ -250,10 +251,11 @@ class FuzzyMover : FuzzyAction() {
private fun process(project: Project, stringEvaluator: StringEvaluator, searchString: String,
listModel: DefaultListModel, task: Future<*>?) {
val moduleManager = ModuleManager.getInstance(project)
+ val ss = FuzzierUtil.cleanSearchString(searchString, fuzzierSettingsService.state.ignoredCharacters)
if (fuzzierSettingsService.state.isProject) {
- processProject(project, stringEvaluator, searchString, listModel, task)
+ processProject(project, stringEvaluator, ss, listModel, task)
} else {
- processModules(moduleManager, stringEvaluator, searchString, listModel, task)
+ processModules(moduleManager, stringEvaluator, ss, listModel, task)
}
}
diff --git a/src/main/kotlin/com/mituuz/fuzzier/components/FuzzierSettingsComponent.kt b/src/main/kotlin/com/mituuz/fuzzier/components/FuzzierSettingsComponent.kt
index bc8a8594..6f2a6b74 100644
--- a/src/main/kotlin/com/mituuz/fuzzier/components/FuzzierSettingsComponent.kt
+++ b/src/main/kotlin/com/mituuz/fuzzier/components/FuzzierSettingsComponent.kt
@@ -32,6 +32,7 @@ import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBTextArea
import com.intellij.util.ui.FormBuilder
import com.intellij.openapi.ui.ComboBox
+import com.intellij.ui.components.JBTextField
import com.mituuz.fuzzier.components.FuzzierSettingsComponent.SettingsComponent
import com.mituuz.fuzzier.entities.FuzzyMatchContainer.FilenameType
import com.mituuz.fuzzier.settings.FuzzierSettingsService
@@ -54,6 +55,16 @@ class FuzzierSettingsComponent {
e.g. "kt" excludes all files/file paths that contain the "kt" string. (main.kt, ktlin.java)
""".trimIndent())
+ val ignoredCharacters = SettingsComponent(JBTextField(), "Ignored characters",
+ """
+ Exclude characters from affecting the search. Any character added here will be skipped during the search.
+ This could be useful for example when copy pasting similar file paths.
+ Note! This is case insensitive, everything is considered as lowercase
+
+ e.g. "%" would transform a search string like "%%%kot%%lin" to "kotlin"
+ """.trimIndent(),
+ false)
+
val newTabSelect = SettingsComponent(JBCheckBox(), "Open files in a new tab")
val recentFileModeSelector = SettingsComponent(ComboBox(), "Show recent files on start", """
@@ -96,7 +107,7 @@ class FuzzierSettingsComponent {
file (path/to/file)
- Note!This is more performance intensive, you should not use too high file list limit with this option.
+ Note! This is more performance intensive, you should not use too high file list limit with this option.
""".trimIndent(),
false)
@@ -196,6 +207,7 @@ class FuzzierSettingsComponent {
jPanel = FormBuilder.createFormBuilder()
.addComponent(JBLabel("General settings"))
.addComponent(exclusionSet)
+ .addComponent(ignoredCharacters)
.addSeparator()
.addComponent(newTabSelect)
@@ -309,6 +321,10 @@ class FuzzierSettingsComponent {
return component as JBTextArea
}
+ fun getJBTextField(): JBTextField {
+ return component as JBTextField
+ }
+
fun getCheckBox(): JBCheckBox {
return component as JBCheckBox
}
diff --git a/src/main/kotlin/com/mituuz/fuzzier/components/TestBenchComponent.kt b/src/main/kotlin/com/mituuz/fuzzier/components/TestBenchComponent.kt
index 864c47a6..a4315f26 100644
--- a/src/main/kotlin/com/mituuz/fuzzier/components/TestBenchComponent.kt
+++ b/src/main/kotlin/com/mituuz/fuzzier/components/TestBenchComponent.kt
@@ -171,7 +171,8 @@ class TestBenchComponent : JPanel() {
private fun processProject(project: Project, stringEvaluator: StringEvaluator,
searchString: String, listModel: DefaultListModel) {
- val contentIterator = stringEvaluator.getContentIterator(project.name, searchString, listModel, null)
+ val ss = FuzzierUtil.cleanSearchString(searchString, liveSettingsComponent.ignoredCharacters.getJBTextField().text)
+ val contentIterator = stringEvaluator.getContentIterator(project.name, ss, listModel, null)
val scoreCalculator = stringEvaluator.scoreCalculator
scoreCalculator.setMultiMatch(liveSettingsComponent.multiMatchActive.getCheckBox().isSelected)
@@ -186,7 +187,8 @@ class TestBenchComponent : JPanel() {
searchString: String, listModel: DefaultListModel) {
for (module in moduleManager.modules) {
val moduleFileIndex = module.rootManager.fileIndex
- val contentIterator = stringEvaluator.getContentIterator(module.name, searchString, listModel, null)
+ val ss = FuzzierUtil.cleanSearchString(searchString, liveSettingsComponent.ignoredCharacters.getJBTextField().text)
+ val contentIterator = stringEvaluator.getContentIterator(module.name, ss, listModel, null)
val scoreCalculator = stringEvaluator.scoreCalculator
scoreCalculator.setMultiMatch(liveSettingsComponent.multiMatchActive.getCheckBox().isSelected)
diff --git a/src/main/kotlin/com/mituuz/fuzzier/entities/ScoreCalculator.kt b/src/main/kotlin/com/mituuz/fuzzier/entities/ScoreCalculator.kt
index 18049376..f88c03c3 100644
--- a/src/main/kotlin/com/mituuz/fuzzier/entities/ScoreCalculator.kt
+++ b/src/main/kotlin/com/mituuz/fuzzier/entities/ScoreCalculator.kt
@@ -47,6 +47,7 @@ class ScoreCalculator(searchString: String) {
private var matchWeightStreakModifier = settings.matchWeightStreakModifier
private var matchWeightPartialPath = settings.matchWeightPartialPath
private var matchWeightFilename = settings.matchWeightFilename
+ private var ignoredCharacters: Set = settings.ignoredCharacters.toSet()
var currentFilePath = ""
private var longestStreak: Int = 0
@@ -222,4 +223,8 @@ class ScoreCalculator(searchString: String) {
fun setTolerance(value: Int) {
tolerance = value
}
+
+ fun setIgnoredCharacters(characterString: String) {
+ ignoredCharacters = characterString.toSet()
+ }
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsConfigurable.kt b/src/main/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsConfigurable.kt
index eab5fac7..87b400cc 100644
--- a/src/main/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsConfigurable.kt
+++ b/src/main/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsConfigurable.kt
@@ -42,6 +42,7 @@ class FuzzierSettingsConfigurable : Configurable {
val combinedString = state.exclusionSet.joinToString("\n")
component.exclusionSet.getJBTextArea().text = combinedString
+ component.ignoredCharacters.getJBTextField().text = state.ignoredCharacters
component.newTabSelect.getCheckBox().isSelected = state.newTab
component.recentFileModeSelector.getRecentFilesTypeComboBox().selectedIndex = state.recentFilesMode.ordinal
component.prioritizeShortDirs.getCheckBox().isSelected = state.prioritizeShorterDirPaths
@@ -71,6 +72,7 @@ class FuzzierSettingsConfigurable : Configurable {
.toSet()
return state.exclusionSet != newSet
+ || state.ignoredCharacters != component.ignoredCharacters.getJBTextField().text
|| state.newTab != component.newTabSelect.getCheckBox().isSelected
|| state.recentFilesMode != component.recentFileModeSelector.getRecentFilesTypeComboBox().selectedItem
|| state.prioritizeShorterDirPaths != component.prioritizeShortDirs.getCheckBox().isSelected
@@ -97,6 +99,7 @@ class FuzzierSettingsConfigurable : Configurable {
.filter { it.isNotBlank() }
.toSet()
state.exclusionSet = newSet as MutableSet
+ state.ignoredCharacters = component.ignoredCharacters.getJBTextField().text
state.newTab = component.newTabSelect.getCheckBox().isSelected
state.recentFilesMode = RecentFilesMode.entries.toTypedArray()[component.recentFileModeSelector.getRecentFilesTypeComboBox().selectedIndex]
state.prioritizeShorterDirPaths = component.prioritizeShortDirs.getCheckBox().isSelected
diff --git a/src/main/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsService.kt b/src/main/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsService.kt
index 35808061..d28e0b2d 100644
--- a/src/main/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsService.kt
+++ b/src/main/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsService.kt
@@ -47,6 +47,7 @@ class FuzzierSettingsService : PersistentStateComponent = setOf("/.idea/*", "/.git/*", "/target/*", "/build/*", "/.gradle/*", "/.run/*")
+ var ignoredCharacters: String = ""
var newTab: Boolean = false
var prioritizeShorterDirPaths = true
var debouncePeriod: Int = 80
diff --git a/src/main/kotlin/com/mituuz/fuzzier/util/FuzzierUtil.kt b/src/main/kotlin/com/mituuz/fuzzier/util/FuzzierUtil.kt
index 7a96ff9a..3b5e0557 100644
--- a/src/main/kotlin/com/mituuz/fuzzier/util/FuzzierUtil.kt
+++ b/src/main/kotlin/com/mituuz/fuzzier/util/FuzzierUtil.kt
@@ -54,9 +54,11 @@ class FuzzierUtil {
return "${baseDimensionKey}_${screenBounds.width}_${screenBounds.height}_${screenBounds.x}_${screenBounds.y}"
}
- fun fileIndexToIterationFile(iterationFiles: ConcurrentHashMap.KeySetView,
- fileIndex: FileIndex, moduleName: String, task: Future<*>?,
- isDir: Boolean = false) {
+ fun fileIndexToIterationFile(
+ iterationFiles: ConcurrentHashMap.KeySetView,
+ fileIndex: FileIndex, moduleName: String, task: Future<*>?,
+ isDir: Boolean = false
+ ) {
fileIndex.iterateContent { file ->
if (task?.isCancelled == true) {
return@iterateContent false
@@ -67,6 +69,15 @@ class FuzzierUtil {
true
}
}
+
+ fun cleanSearchString(ss: String, ignoredChars: String): String {
+ var ret = ss.lowercase()
+ for (i in ignoredChars.toSet()) {
+ ret = ret.filterNot { it == i.lowercaseChar() }
+ }
+
+ return ret;
+ }
}
/**
@@ -81,14 +92,17 @@ class FuzzierUtil {
*
* @return a sorted and sized list model
*/
- fun sortAndLimit(listModel: DefaultListModel, isDirSort: Boolean = false): DefaultListModel {
+ fun sortAndLimit(
+ listModel: DefaultListModel,
+ isDirSort: Boolean = false
+ ): DefaultListModel {
val useShortDirPath = isDirSort && prioritizeShorterDirPaths
var comparator = getComparator(useShortDirPath, false)
val priorityQueue = PriorityQueue(listLimit + 1, comparator)
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()
@@ -154,7 +168,8 @@ class FuzzierUtil {
var prevModule: ModuleContainer? = null
for (currentModule in moduleList.sortedBy { it.basePath }) {
if (prevModule != null && (currentModule.basePath.startsWith(prevModule.basePath)
- || prevModule.basePath.startsWith(currentModule.basePath))) {
+ || prevModule.basePath.startsWith(currentModule.basePath))
+ ) {
if (currentModule.basePath.length > prevModule.basePath.length) {
currentModule.basePath = prevModule.basePath;
} else {
@@ -192,7 +207,7 @@ class FuzzierUtil {
return contentRoots.firstOrNull()?.path
}
- data class ModuleContainer(val name:String, var basePath:String)
+ data class ModuleContainer(val name: String, var basePath: String)
private fun listToMap(modules: List): Map {
return modules.associateBy({ it.name }, { it.basePath })
diff --git a/src/test/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsConfigurableTest.kt b/src/test/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsConfigurableTest.kt
index f4e53354..467347cd 100644
--- a/src/test/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsConfigurableTest.kt
+++ b/src/test/kotlin/com/mituuz/fuzzier/settings/FuzzierSettingsConfigurableTest.kt
@@ -77,6 +77,13 @@ class FuzzierSettingsConfigurableTest {
assertTrue(settingsConfigurable.isModified())
}
+ @Test
+ fun excludedCharacters() {
+ pre()
+ state.ignoredCharacters = "abc"
+ assertTrue(settingsConfigurable.isModified())
+ }
+
@Test
fun newTab() {
pre()
diff --git a/src/test/kotlin/com/mituuz/fuzzier/util/FuzzierUtilTest.kt b/src/test/kotlin/com/mituuz/fuzzier/util/FuzzierUtilTest.kt
index 199cdf54..cd865904 100644
--- a/src/test/kotlin/com/mituuz/fuzzier/util/FuzzierUtilTest.kt
+++ b/src/test/kotlin/com/mituuz/fuzzier/util/FuzzierUtilTest.kt
@@ -258,6 +258,20 @@ class FuzzierUtilTest {
assertEquals("file3", result[3].filename)
}
+ @Test
+ fun `Test ignored characters`() {
+ val searchString = "HELLO/THERE/GENERAL/KENOBI"
+ val ignoredChars = "H/"
+ assertEquals("elloteregeneralkenobi", FuzzierUtil.cleanSearchString(searchString, ignoredChars))
+ }
+
+ @Test
+ fun `No ignored characters`() {
+ val searchString = "!#ยค%(&`Soqwe'"
+ val ignoredChars = ""
+ assertEquals(searchString.lowercase(), FuzzierUtil.cleanSearchString(searchString, ignoredChars))
+ }
+
private fun addElement(score: Int, fileName: String) {
val fuzzyScore = FuzzyScore()
fuzzyScore.streakScore = score