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