From 4ff6cbdcd5d45272267e149e068080865abf230b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=92=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=D0=B9?= Date: Wed, 24 Jan 2024 22:23:35 +0500 Subject: [PATCH 1/5] Add RxKotlin library --- CHANGELOG.md | 8 +++++++- build.gradle.kts | 1 + gradle/libs.versions.toml | 2 ++ .../{GitGlobalHooksLocatorWindow.kt => LocatorWindow.kt} | 2 +- ...oksLocatorWindowFactory.kt => LocatorWindowFactory.kt} | 5 +++-- src/main/resources/META-INF/plugin.xml | 2 +- 6 files changed, 15 insertions(+), 5 deletions(-) rename src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/{GitGlobalHooksLocatorWindow.kt => LocatorWindow.kt} (98%) rename src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/{GitGlobalHooksLocatorWindowFactory.kt => LocatorWindowFactory.kt} (81%) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0dd2ab..bcb8c7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,12 @@ # Changelog -## [Unreleased] +## [0.1.0] +### Added +- Detect changes in the Git hooks folder + +### Changed +- Make the main tool window immediately available during project reindexing ## [0.0.2] ### Fixed @@ -13,4 +18,5 @@ - Git version handling ## [0.0.1] +### Added - Initial release \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 527285c..d16dd26 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,6 +24,7 @@ repositories { // Dependencies are managed with Gradle version catalog - read more: https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog dependencies { // implementation(libs.annotations) + implementation(libs.rxkotlin) } // Set the JVM language level used to build the project. Use Java 11 for 2020.3+, and Java 17 for 2022.2+. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index febb8cd..75c8ebf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] # libraries annotations = "24.1.0" +rxkotlin = "3.0.1" # plugins kotlin = "1.9.21" @@ -11,6 +12,7 @@ kover = "0.7.5" [libraries] annotations = { group = "org.jetbrains", name = "annotations", version.ref = "annotations" } +rxkotlin = { group = "io.reactivex.rxjava3", name = "rxkotlin", version.ref = "rxkotlin" } [plugins] changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" } diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindow.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt similarity index 98% rename from src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindow.kt rename to src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt index f34b0aa..26a5f8c 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindow.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt @@ -11,7 +11,7 @@ import com.intellij.ui.dsl.builder.Align import com.intellij.ui.dsl.builder.panel import javax.swing.JComponent -class GitGlobalHooksLocatorWindow(toolWindow: ToolWindow) { +class LocatorWindow(toolWindow: ToolWindow) { private val hooksFolder: HooksFolder = HooksFolder(Git.instance) diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindowFactory.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindowFactory.kt similarity index 81% rename from src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindowFactory.kt rename to src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindowFactory.kt index 79e8bfb..18822c2 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/GitGlobalHooksLocatorWindowFactory.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindowFactory.kt @@ -1,13 +1,14 @@ package com.github.y0ung3r.gitglobalhookslocator.ui.toolWindow +import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.Project import com.intellij.openapi.wm.ToolWindow import com.intellij.openapi.wm.ToolWindowFactory import com.intellij.ui.content.ContentFactory -class GitGlobalHooksLocatorWindowFactory : ToolWindowFactory { +class LocatorWindowFactory : ToolWindowFactory, DumbAware { override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { - val locatorWindow = GitGlobalHooksLocatorWindow(toolWindow) + val locatorWindow = LocatorWindow(toolWindow) val content = ContentFactory .getInstance() diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 848b3e5..44c19ec 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -9,6 +9,6 @@ messages.LocatorBundle - + From 4340079a31c00e39d229c4ca8d30a2ab0c41e1db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=92=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=D0=B9?= Date: Wed, 24 Jan 2024 22:24:17 +0500 Subject: [PATCH 2/5] (#10) Add a draft version of the change tracking --- .../gitglobalhookslocator/git/HookEvent.kt | 8 ++ .../git/HooksFolderVisitor.kt | 15 ++++ .../git/ObservableHooksFolder.kt | 74 +++++++++++++++++++ .../gitTests/HooksFolderTests.kt | 30 ++++++++ .../gitTests/testEngine/HookTestBase.kt | 11 +++ 5 files changed, 138 insertions(+) create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt new file mode 100644 index 0000000..a2a937c --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt @@ -0,0 +1,8 @@ +package com.github.y0ung3r.gitglobalhookslocator.git + +import java.nio.file.Path +import java.nio.file.WatchEvent + +data class HookEvent( + val kind: WatchEvent.Kind, + val entry: HookEntry) \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt new file mode 100644 index 0000000..2c3d70d --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt @@ -0,0 +1,15 @@ +package com.github.y0ung3r.gitglobalhookslocator.git + +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes + +class HooksFolderVisitor(private val watchService: WatchService) : SimpleFileVisitor() { + override fun preVisitDirectory(directory: Path, attributes: BasicFileAttributes): FileVisitResult { + directory.register( + watchService, + StandardWatchEventKinds.ENTRY_CREATE, + StandardWatchEventKinds.ENTRY_DELETE) + + return FileVisitResult.CONTINUE + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt new file mode 100644 index 0000000..c30ede4 --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt @@ -0,0 +1,74 @@ +package com.github.y0ung3r.gitglobalhookslocator.git + +import com.github.y0ung3r.gitglobalhookslocator.git.extensions.getGlobalHooksPath +import com.intellij.openapi.diagnostic.thisLogger +import io.reactivex.rxjava3.subjects.PublishSubject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.nio.file.* +import kotlin.io.path.nameWithoutExtension + +class ObservableHooksFolder(git: Git) { + companion object { + @JvmStatic + val supportedHooks = arrayOf( + "pre-commit", + "prepare-commit-msg", + "commit-msg", + "post-commit", + "applypatch-msg", + "pre-applypatch", + "post-applypatch", + "pre-rebase", + "post-rewrite", + "post-checkout", + "post-merge", + "pre-push", + "pre-auto-gc" + ) + } + + private var count: Int = 0 + val hooks: PublishSubject + val path: Path + + init { + path = git.getGlobalHooksPath() + + val files = try { + Files.list(path) + } + catch (exception: NoSuchFileException) { + thisLogger() + .info("Provided hooks path is not valid", exception) + + emptyList() + .stream() + } + + hooks = PublishSubject.create() + + files + .filter { + supportedHooks.any { hookName -> it.fileName.nameWithoutExtension.contains(hookName) } + } + .map { HookEvent(StandardWatchEventKinds.ENTRY_CREATE, HookEntry.load(it)) } + .forEach { hooks.onNext(it) } + + val watchService = FileSystems.getDefault().newWatchService() + Files.walkFileTree(path, HooksFolderVisitor(watchService)) + + val changesTrackingJob = CoroutineScope(Dispatchers.IO).launch { + while (true) { + val folderEntry = watchService.take() + + folderEntry.pollEvents().forEach { + val hookPath = Path.of(path.toString(), it.context().toString()) + val hookEntry = HookEntry.load(hookPath) + hooks.onNext(HookEvent(it.kind() as WatchEvent.Kind, hookEntry)) + } + } + } + } +} diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt index 9c2369c..495e9a6 100644 --- a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt @@ -2,12 +2,42 @@ package com.github.y0ung3r.gitglobalhookslocator.gitTests import com.github.y0ung3r.gitglobalhookslocator.git.Git import com.github.y0ung3r.gitglobalhookslocator.git.HooksFolder +import com.github.y0ung3r.gitglobalhookslocator.git.ObservableHooksFolder import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.RespondInterchangeably import org.junit.Assert.* import org.junit.Test import java.nio.file.Path +import java.nio.file.StandardWatchEventKinds + +class ObservableHooksFolderTests : HookTestBase() { + private companion object { + @JvmStatic + fun getGit(hooksPath: Path) = Git( + RespondInterchangeably( + CliResponse(Git.minRequiredVersion.toString()), + CliResponse(hooksPath.toString()) + ) + ) + } + + @Test + fun `Should observe changes`() { + // Arrange + val git = getGit(getEnabledHooksPath()) + + // Act + val sut = ObservableHooksFolder(git) + + sut.hooks.subscribe { + assertEquals(it.kind, StandardWatchEventKinds.ENTRY_CREATE) + } + + // Assert + createHook("kek") + } +} class HooksFolderTests : HookTestBase() { private companion object { diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt index 868377a..5a14efc 100644 --- a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt @@ -41,6 +41,17 @@ abstract class HookTestBase { fun getEnabledHooksPath(): Path = getHooksPath(ENABLED_HOOK) + @JvmStatic + fun createHook(hookName: String) { + val hookPath = getHookPath(ENABLED_HOOK, hookName).toFile() + + if (hookPath.exists()) { + return + } + + hookPath.createNewFile() + } + @JvmStatic private fun clearTestHooks(hookType: String) { val hooksPath = getHooksPath(hookType) From f060d54b6a8ba6d78f7e7e7a3f209e7f9a51cce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=92=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=D0=B9?= Date: Tue, 20 Feb 2024 22:15:03 +0500 Subject: [PATCH 3/5] (#10) Improve tracking of changes in hooks folder --- .../gitglobalhookslocator/git/HookEvent.kt | 8 - .../gitglobalhookslocator/git/HooksFolder.kt | 68 -------- .../git/HooksFolderVisitor.kt | 15 -- .../git/ObservableHooksFolder.kt | 74 -------- .../git/exceptions/HookNotFoundException.kt | 4 - .../git/{ => hooks}/HookEntry.kt | 23 ++- .../git/hooks/HookName.kt | 62 +++++++ .../git/hooks/HooksFolder.kt | 157 +++++++++++++++++ .../hooks/exceptions/HookNotFoundException.kt | 5 + .../UnsupportedHookNameException.kt | 4 + .../git/utils/ObservableExtensions.kt | 17 ++ .../git/utils/SystemPathUtils.kt | 19 +- .../ui/toolWindow/LocatorWindow.kt | 164 +++++++++++++----- .../ui/toolWindow/LocatorWindowFactory.kt | 1 + .../ui/toolWindow/Notifier.kt | 29 ++++ src/main/resources/META-INF/plugin.xml | 1 + .../messages/LocatorBundle.properties | 6 +- .../gitTests/HookEntryTests.kt | 6 +- .../gitTests/HooksFolderTests.kt | 122 ------------- .../hooksFolderTests/DisableAllTests.kt | 21 +++ .../hooksFolderTests/EnableAllTests.kt | 20 +++ .../hooksFolderTests/IsAllDisabledTests.kt | 27 +++ .../gitTests/hooksFolderTests/LoadTests.kt | 32 ++++ .../gitTests/hooksFolderTests/ObserveTests.kt | 63 +++++++ .../gitTests/testEngine/HookTestBase.kt | 101 +++++++---- .../testEngine/RespondInterchangeably.kt | 1 + 26 files changed, 660 insertions(+), 390 deletions(-) delete mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt delete mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolder.kt delete mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt delete mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt delete mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/HookNotFoundException.kt rename src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/{ => hooks}/HookEntry.kt (59%) create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HookName.kt create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HooksFolder.kt create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/exceptions/HookNotFoundException.kt create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/exceptions/UnsupportedHookNameException.kt create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/ObservableExtensions.kt create mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/Notifier.kt delete mode 100644 src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt create mode 100644 src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/DisableAllTests.kt create mode 100644 src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/EnableAllTests.kt create mode 100644 src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/IsAllDisabledTests.kt create mode 100644 src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/LoadTests.kt create mode 100644 src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/ObserveTests.kt diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt deleted file mode 100644 index a2a937c..0000000 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEvent.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.y0ung3r.gitglobalhookslocator.git - -import java.nio.file.Path -import java.nio.file.WatchEvent - -data class HookEvent( - val kind: WatchEvent.Kind, - val entry: HookEntry) \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolder.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolder.kt deleted file mode 100644 index ba3fde4..0000000 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolder.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.github.y0ung3r.gitglobalhookslocator.git - -import com.github.y0ung3r.gitglobalhookslocator.git.extensions.getGlobalHooksPath -import com.intellij.openapi.diagnostic.thisLogger -import java.nio.file.Files -import java.nio.file.Path -import kotlin.io.path.nameWithoutExtension -import java.nio.file.NoSuchFileException - -class HooksFolder(git: Git) { - companion object { - @JvmStatic - val supportedHooks = arrayOf( - "pre-commit", - "prepare-commit-msg", - "commit-msg", - "post-commit", - "applypatch-msg", - "pre-applypatch", - "post-applypatch", - "pre-rebase", - "post-rewrite", - "post-checkout", - "post-merge", - "pre-push", - "pre-auto-gc" - ) - } - - val hooks: List - val path: Path - - fun isEmpty(): Boolean - = hooks.isEmpty() - - init { - path = git.getGlobalHooksPath() - - val files = try { - Files.list(path) - } - catch (exception: NoSuchFileException) { - thisLogger() - .info("Provided hooks path is not valid", exception) - - emptyList() - .stream() - } - - hooks = files - .filter { - supportedHooks - .any { hookName -> - it.fileName.nameWithoutExtension.contains(hookName) } - } - .map { HookEntry.load(it) } - .toList() - } - - fun isAllDisabled() - = hooks.all { it.isDisabled() } - - fun enableAll() - = hooks.forEach { it.enable() } - - fun disableAll() - = hooks.forEach { it.disable() } -} \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt deleted file mode 100644 index 2c3d70d..0000000 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HooksFolderVisitor.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.github.y0ung3r.gitglobalhookslocator.git - -import java.nio.file.* -import java.nio.file.attribute.BasicFileAttributes - -class HooksFolderVisitor(private val watchService: WatchService) : SimpleFileVisitor() { - override fun preVisitDirectory(directory: Path, attributes: BasicFileAttributes): FileVisitResult { - directory.register( - watchService, - StandardWatchEventKinds.ENTRY_CREATE, - StandardWatchEventKinds.ENTRY_DELETE) - - return FileVisitResult.CONTINUE - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt deleted file mode 100644 index c30ede4..0000000 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/ObservableHooksFolder.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.github.y0ung3r.gitglobalhookslocator.git - -import com.github.y0ung3r.gitglobalhookslocator.git.extensions.getGlobalHooksPath -import com.intellij.openapi.diagnostic.thisLogger -import io.reactivex.rxjava3.subjects.PublishSubject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import java.nio.file.* -import kotlin.io.path.nameWithoutExtension - -class ObservableHooksFolder(git: Git) { - companion object { - @JvmStatic - val supportedHooks = arrayOf( - "pre-commit", - "prepare-commit-msg", - "commit-msg", - "post-commit", - "applypatch-msg", - "pre-applypatch", - "post-applypatch", - "pre-rebase", - "post-rewrite", - "post-checkout", - "post-merge", - "pre-push", - "pre-auto-gc" - ) - } - - private var count: Int = 0 - val hooks: PublishSubject - val path: Path - - init { - path = git.getGlobalHooksPath() - - val files = try { - Files.list(path) - } - catch (exception: NoSuchFileException) { - thisLogger() - .info("Provided hooks path is not valid", exception) - - emptyList() - .stream() - } - - hooks = PublishSubject.create() - - files - .filter { - supportedHooks.any { hookName -> it.fileName.nameWithoutExtension.contains(hookName) } - } - .map { HookEvent(StandardWatchEventKinds.ENTRY_CREATE, HookEntry.load(it)) } - .forEach { hooks.onNext(it) } - - val watchService = FileSystems.getDefault().newWatchService() - Files.walkFileTree(path, HooksFolderVisitor(watchService)) - - val changesTrackingJob = CoroutineScope(Dispatchers.IO).launch { - while (true) { - val folderEntry = watchService.take() - - folderEntry.pollEvents().forEach { - val hookPath = Path.of(path.toString(), it.context().toString()) - val hookEntry = HookEntry.load(hookPath) - hooks.onNext(HookEvent(it.kind() as WatchEvent.Kind, hookEntry)) - } - } - } - } -} diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/HookNotFoundException.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/HookNotFoundException.kt deleted file mode 100644 index aacc032..0000000 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/exceptions/HookNotFoundException.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.github.y0ung3r.gitglobalhookslocator.git.exceptions - -class HookNotFoundException(fileName: String) - : Exception("Global hook \"$fileName\" not found") \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEntry.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HookEntry.kt similarity index 59% rename from src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEntry.kt rename to src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HookEntry.kt index 023c7ef..c9782fd 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/HookEntry.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HookEntry.kt @@ -1,30 +1,28 @@ -package com.github.y0ung3r.gitglobalhookslocator.git +package com.github.y0ung3r.gitglobalhookslocator.git.hooks -import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.HookNotFoundException +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.exceptions.HookNotFoundException import java.io.File import java.nio.file.Path -class HookEntry(private var file: File) { +class HookEntry private constructor(private var file: File) { companion object { @JvmStatic fun load(filePath: Path): HookEntry { val file = filePath.toFile() if (!file.exists()) { - throw HookNotFoundException(file.name.toString()) + throw HookNotFoundException(file.nameWithoutExtension) } return HookEntry(file) } } - val name: String - = HooksFolder - .supportedHooks - .first { file.nameWithoutExtension.contains(it) } + val name: HookName + = HookName.create(file) fun isDisabled() - = HooksFolder + = HookName .supportedHooks .all { it != file.nameWithoutExtension } @@ -33,7 +31,7 @@ class HookEntry(private var file: File) { return } - renameFile(name) + renameFile(name.value) } fun disable() { @@ -48,9 +46,8 @@ class HookEntry(private var file: File) { file = Path .of(file.parent, newName) .toFile() - .let { - file.renameTo(it) - it + .apply { + file.renameTo(this) } } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HookName.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HookName.kt new file mode 100644 index 0000000..7979bb5 --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HookName.kt @@ -0,0 +1,62 @@ +package com.github.y0ung3r.gitglobalhookslocator.git.hooks + +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.exceptions.UnsupportedHookNameException +import gnu.trove.Equality +import java.io.File +import java.nio.file.Path +import kotlin.io.path.nameWithoutExtension + +class HookName private constructor(private val source: String) : Comparable { + companion object { + @JvmStatic + val supportedHooks = arrayOf( + "pre-commit", + "prepare-commit-msg", + "commit-msg", + "post-commit", + "applypatch-msg", + "pre-applypatch", + "post-applypatch", + "pre-rebase", + "post-rewrite", + "post-checkout", + "post-merge", + "pre-push", + "pre-auto-gc" + ) + + @JvmStatic + fun isSupportedHook(hookPath: Path): Boolean + = supportedHooks.any { + hookPath.nameWithoutExtension.contains(it) } + + @JvmStatic + fun create(file: File): HookName + = HookName(file.nameWithoutExtension) + + @JvmStatic + fun create(path: Path): HookName + = HookName(path.nameWithoutExtension) + } + + val value: String + = supportedHooks + .firstOrNull { source.contains(it) } + ?: throw UnsupportedHookNameException(source) + + override fun compareTo(other: HookName): Int + = other.value.compareTo(value) + + override fun equals(other: Any?): Boolean { + val name = other as? HookName + + return when { + name == null -> false + compareTo(name) == 0 -> true + else -> false + } + } + + override fun hashCode(): Int + = value.hashCode() +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HooksFolder.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HooksFolder.kt new file mode 100644 index 0000000..0aa3a6f --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HooksFolder.kt @@ -0,0 +1,157 @@ +package com.github.y0ung3r.gitglobalhookslocator.git.hooks + +import com.github.y0ung3r.gitglobalhookslocator.git.Git +import com.github.y0ung3r.gitglobalhookslocator.git.extensions.getGlobalHooksPath +import com.intellij.openapi.diagnostic.thisLogger +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.core.ObservableEmitter +import io.reactivex.rxjava3.schedulers.Schedulers +import java.nio.file.* +import java.util.stream.Collectors + +class HooksFolder(git: Git) { + private data class HooksFolderEvent(val kind: WatchEvent.Kind, private val sourcePath: Path, private val folderPath: Path) { + val hookPath: Path = Path.of(folderPath.toString(), sourcePath.toString()) + } + + val path: Path + = git.getGlobalHooksPath() + + private val cachedHooks: MutableList + = getExistsHooks() + + val addedHooks: Observable + = observeFolder(StandardWatchEventKinds.ENTRY_CREATE) + .map { HookEntry.load(it.hookPath) } + .doOnNext { + if (cachedHooks.add(it)) { + thisLogger() + .info("Hook ${it.name.value} was added") + } + } + .startWithIterable(cachedHooks) + .subscribeOn(Schedulers.io()) + + val deletedHooks: Observable + = observeFolder(StandardWatchEventKinds.ENTRY_DELETE) + .map { HookName.create(it.hookPath) } + .doOnNext { hookName -> + val index = cachedHooks.indexOfFirst { it.name == hookName } + + if (index != -1) { + cachedHooks.removeAt(index) + + thisLogger() + .info("Hook ${hookName.value} was deleted") + } + } + .subscribeOn(Schedulers.io()) + + val renamedHooks: Observable + = observeFolder(StandardWatchEventKinds.ENTRY_MODIFY) + .map { HookEntry.load(it.hookPath) } + .subscribeOn(Schedulers.io()) + + fun isEmpty(): Boolean + = cachedHooks.isEmpty() + + fun count(): Int + = cachedHooks + .count() + + fun isAllDisabled(): Boolean + = cachedHooks + .all { it.isDisabled() } + + fun enableAll(): Unit + = cachedHooks + .forEach { it.enable() } + + fun disableAll(): Unit + = cachedHooks + .forEach { it.disable() } + + fun canBeUsed(hookName: HookName): Boolean + = cachedHooks.count { hookName == it.name } <= 1 + + private fun observeFolder(kind: WatchEvent.Kind): Observable { + val watchService = FileSystems.getDefault().newWatchService() + path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE) + + return Observable.create { emitter -> + while (true) { + val folderEntry = try { + watchService.take() + } catch (exception: ClosedWatchServiceException) { + return@create emitter.onComplete() + } + + if (!folderEntry.isValid) { + return@create emitter.onComplete() + } + + emitFolderEvent(emitter, folderEntry, kind) + + if (!folderEntry.reset()) { + return@create emitter.onComplete() + } + } + } + .doFinally { + watchService.close() + + thisLogger() + .info("Watch service was closed") + } + .onErrorResumeNext { + thisLogger() + .error("An error occurred while watching the hooks folder", it) + + observeFolder(kind) + } + } + + private fun emitFolderEvent( + emitter: ObservableEmitter, + folderEntry: WatchKey, + observableKind: WatchEvent.Kind) { + val pendingEvents = folderEntry + .pollEvents() + .map { + @Suppress("UNCHECKED_CAST") + it as WatchEvent + } + + val targetKind = when { + pendingEvents.size > 1 -> StandardWatchEventKinds.ENTRY_CREATE + else -> observableKind + } + + val originalEvent = pendingEvents + .firstOrNull { it.kind() == targetKind } + ?: return + + val folderEvent = HooksFolderEvent( + observableKind, + originalEvent.context(), + path) + + if (HookName.isSupportedHook(folderEvent.hookPath)) { + emitter.onNext(folderEvent) + } + } + + private fun getExistsHooks(): MutableList + = try { + Files.list(path) + .filter { HookName.isSupportedHook(it) } + .map { HookEntry.load(it) } + .collect(Collectors.toList()) + } + catch (exception: NoSuchFileException) { + thisLogger() + .info("Provided hooks path is not valid", exception) + + mutableListOf() + } +} diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/exceptions/HookNotFoundException.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/exceptions/HookNotFoundException.kt new file mode 100644 index 0000000..c80e3ae --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/exceptions/HookNotFoundException.kt @@ -0,0 +1,5 @@ +package com.github.y0ung3r.gitglobalhookslocator.git.hooks.exceptions + +class HookNotFoundException(fileName: String) + : Exception("Global hook \"$fileName\" not found") + diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/exceptions/UnsupportedHookNameException.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/exceptions/UnsupportedHookNameException.kt new file mode 100644 index 0000000..d705e90 --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/exceptions/UnsupportedHookNameException.kt @@ -0,0 +1,4 @@ +package com.github.y0ung3r.gitglobalhookslocator.git.hooks.exceptions + +class UnsupportedHookNameException(fileName: String) + : Exception("Global hook \"$fileName\" unsupported") \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/ObservableExtensions.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/ObservableExtensions.kt new file mode 100644 index 0000000..2626717 --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/ObservableExtensions.kt @@ -0,0 +1,17 @@ +package com.github.y0ung3r.gitglobalhookslocator.git.utils + +import com.intellij.ui.layout.ComponentPredicate +import io.reactivex.rxjava3.core.Observable + +fun Observable.toComponentPredicate(): ComponentPredicate + = object : ComponentPredicate() { + override fun addListener(listener: (Boolean) -> Unit) { + subscribe(listener) + } + + override fun invoke(): Boolean + = false + } + +fun Observable.negate() + = map { !it } \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/SystemPathUtils.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/SystemPathUtils.kt index fee9695..0541028 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/SystemPathUtils.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/SystemPathUtils.kt @@ -1,5 +1,8 @@ package com.github.y0ung3r.gitglobalhookslocator.git.utils +import java.awt.Desktop +import java.nio.file.Path + object SystemPathUtils { private const val USER_HOME_KEY = "user.home" private const val USER_DIR_KEY = "user.dir" @@ -20,4 +23,18 @@ object SystemPathUtils { @JvmStatic fun getCurrentDirectoryPath(): String = getSystemPath(USER_DIR_KEY) -} + + @JvmStatic + fun tryOpen(path: Path): Boolean { + if (Desktop.isDesktopSupported()) { + val desktop = Desktop.getDesktop() + + if (desktop.isSupported(Desktop.Action.OPEN)) { + desktop.open(path.toFile()) + return true + } + } + + return false + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt index 26a5f8c..4f81e7f 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt @@ -2,54 +2,138 @@ package com.github.y0ung3r.gitglobalhookslocator.ui.toolWindow import com.github.y0ung3r.gitglobalhookslocator.LocatorBundle import com.github.y0ung3r.gitglobalhookslocator.git.Git -import com.github.y0ung3r.gitglobalhookslocator.git.HookEntry -import com.github.y0ung3r.gitglobalhookslocator.git.HooksFolder +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HookName +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HooksFolder +import com.github.y0ung3r.gitglobalhookslocator.git.utils.SystemPathUtils import com.intellij.ide.wizard.withVisualPadding import com.intellij.openapi.wm.ToolWindow import com.intellij.ui.components.JBCheckBox import com.intellij.ui.dsl.builder.Align +import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.panel import javax.swing.JComponent -class LocatorWindow(toolWindow: ToolWindow) { +class LocatorWindow(private val toolWindow: ToolWindow) { private val hooksFolder: HooksFolder = HooksFolder(Git.instance) + private lateinit var emptyHintLayout: Panel + private lateinit var checkBoxesLayout: Panel + private val disableAllCheckBox: JBCheckBox = createDisableAllCheckBox() - private val hookCheckBoxes: Array - = createHookCheckBoxes() + private val checkBoxes: Map + = HookName + .supportedHooks + .associateBy({ it }, { createHookCheckBox(it) }) fun getContent(): JComponent { - if (hooksFolder.isEmpty()) { - return panel { - row { - val text = LocatorBundle - .getMessage("locator.ui.toolWindow.hooksFolderEmpty", hooksFolder.path) + hooksFolder + .addedHooks + .subscribe { hookEntry -> + // Switching the panel visibility only when 1 item appears + if (hooksFolder.count() == 1) { + checkBoxesLayout.visible(true) + emptyHintLayout.visible(false) + } + + checkBoxes + .getValue(hookEntry.name.value) + .apply { + isVisible = true + isSelected = hookEntry.isDisabled() + isEnabled = hooksFolder.canBeUsed(hookEntry.name) + + if (isEnabled) { + addActionListener { + when (isSelected) { + true -> hookEntry.disable() + false -> hookEntry.enable() + } + + disableAllCheckBox.isSelected = hooksFolder.isAllDisabled() + } + } + } + + disableAllCheckBox.isSelected = hooksFolder.isAllDisabled() + } - label(text) - .align(Align.CENTER) + hooksFolder + .deletedHooks + .subscribe { hookName -> + disableAllCheckBox.isSelected = hooksFolder.isAllDisabled() + + checkBoxes + .getValue(hookName.value) + .apply { + isVisible = false + //isEnabled = hooksFolder.canBeUsed(hookName) + + actionListeners.forEach { + removeActionListener(it) + } + } + + if (hooksFolder.isEmpty()) { + checkBoxesLayout.visible(false) + emptyHintLayout.visible(true) } } - } + + hooksFolder + .renamedHooks + .subscribe { + checkBoxes + .getValue(it.name.value) + .apply { isSelected = it.isDisabled() } + } return panel { - val groupTitle = LocatorBundle - .getMessage("locator.ui.toolWindow.globalHooksGroup") + val isEmpty = hooksFolder.isEmpty() - group(groupTitle) { + emptyHintLayout = panel { row { - cell(disableAllCheckBox) - } + val text = LocatorBundle + .getMessage("locator.ui.toolWindow.hooksFolderEmpty", hooksFolder.path) + + comment(text) { + if (!SystemPathUtils.tryOpen(hooksFolder.path)) { + val errorTitle = LocatorBundle + .getMessage("locator.ui.notification.title") + + val errorMessage = LocatorBundle + .getMessage("locator.ui.notification.openHooksFolder.error", hooksFolder.path) - hookCheckBoxes - .map { - row { - cell(it) + Notifier.error(toolWindow.project, errorTitle, errorMessage) } } + .align(Align.CENTER) + } + } + .visible(isEmpty) + + checkBoxesLayout = panel { + val groupTitle = LocatorBundle + .getMessage("locator.ui.toolWindow.globalHooksGroup") + + group(groupTitle) { + row { + cell(disableAllCheckBox) + } + + indent { + checkBoxes + .forEach { + row { + cell(it.value) + } + } + } + } } + .visible(!isEmpty) } .withVisualPadding(true) } @@ -61,38 +145,28 @@ class LocatorWindow(toolWindow: ToolWindow) { return JBCheckBox(title, hooksFolder.isAllDisabled()) .apply { addActionListener { - when (this.isSelected) { + when (isSelected) { true -> hooksFolder.disableAll() false -> hooksFolder.enableAll() } - hookCheckBoxes.forEach { - it.isSelected = this.isSelected - } + checkBoxes + .values + .filter { it.isVisible } + .forEach { + it.isSelected = isSelected + } } } } - private fun createHookCheckBox(hook: HookEntry): JBCheckBox { + private fun createHookCheckBox(hookName: String): JBCheckBox { val hookTitle = LocatorBundle - .getMessage("locator.ui.toolWindow.disable", hook.name) - - return JBCheckBox(hookTitle, hook.isDisabled()) - .apply { - addActionListener { - when (this.isSelected) { - true -> hook.disable() - false -> hook.enable() - } + .getMessage("locator.ui.toolWindow.disable", hookName) - disableAllCheckBox.isSelected = hooksFolder.isAllDisabled() - } - } + return JBCheckBox(hookTitle) + .apply { + isVisible = false + } } - - private fun createHookCheckBoxes(): Array - = hooksFolder - .hooks - .map { createHookCheckBox(it) } - .toTypedArray() } \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindowFactory.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindowFactory.kt index 18822c2..ce81419 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindowFactory.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindowFactory.kt @@ -5,6 +5,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.wm.ToolWindow import com.intellij.openapi.wm.ToolWindowFactory import com.intellij.ui.content.ContentFactory +import com.intellij.ui.content.ContentManagerListener class LocatorWindowFactory : ToolWindowFactory, DumbAware { override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/Notifier.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/Notifier.kt new file mode 100644 index 0000000..b5be8fa --- /dev/null +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/Notifier.kt @@ -0,0 +1,29 @@ +package com.github.y0ung3r.gitglobalhookslocator.ui.toolWindow + +import com.intellij.notification.NotificationGroupManager +import com.intellij.notification.NotificationType +import com.intellij.openapi.project.Project + +object Notifier { + private const val GROUP_ID = "GitGlobalHooksLocator" + + @JvmStatic + fun error(project: Project, title: String, content: String) + = notify(project, title, content, NotificationType.ERROR) + + @JvmStatic + fun warning(project: Project, title: String, content: String) + = notify(project, title, content, NotificationType.WARNING) + + @JvmStatic + fun info(project: Project, title: String, content: String) + = notify(project, title, content, NotificationType.INFORMATION) + + @JvmStatic + private fun notify(project: Project, title: String, content: String, type: NotificationType) + = NotificationGroupManager + .getInstance() + .getNotificationGroup(GROUP_ID) + .createNotification(title, content, type) + .notify(project) +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 44c19ec..a5aee30 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -10,5 +10,6 @@ + diff --git a/src/main/resources/messages/LocatorBundle.properties b/src/main/resources/messages/LocatorBundle.properties index 4eb8f0d..be3b161 100644 --- a/src/main/resources/messages/LocatorBundle.properties +++ b/src/main/resources/messages/LocatorBundle.properties @@ -1,4 +1,6 @@ locator.ui.toolWindow.globalHooksGroup=Global Hooks -locator.ui.toolWindow.hooksFolderEmpty=Git global hooks from "{0}" not found +locator.ui.toolWindow.hooksFolderEmpty=Git global hooks from "{0}" not found locator.ui.toolWindow.disableAll=Disable all Git global hooks -locator.ui.toolWindow.disable=Disable {0} \ No newline at end of file +locator.ui.toolWindow.disable=Disable {0} +locator.ui.notification.title=Git global hooks locator +locator.ui.notification.openHooksFolder.error=Failed to open folder at specified path "{0}" \ No newline at end of file diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HookEntryTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HookEntryTests.kt index 10e517f..3ffac25 100644 --- a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HookEntryTests.kt +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HookEntryTests.kt @@ -1,10 +1,8 @@ package com.github.y0ung3r.gitglobalhookslocator.gitTests +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HookEntry +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.exceptions.HookNotFoundException import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase -import com.github.y0ung3r.gitglobalhookslocator.git.HookEntry -import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.HookNotFoundException -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt deleted file mode 100644 index 495e9a6..0000000 --- a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/HooksFolderTests.kt +++ /dev/null @@ -1,122 +0,0 @@ -package com.github.y0ung3r.gitglobalhookslocator.gitTests - -import com.github.y0ung3r.gitglobalhookslocator.git.Git -import com.github.y0ung3r.gitglobalhookslocator.git.HooksFolder -import com.github.y0ung3r.gitglobalhookslocator.git.ObservableHooksFolder -import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse -import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase -import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.RespondInterchangeably -import org.junit.Assert.* -import org.junit.Test -import java.nio.file.Path -import java.nio.file.StandardWatchEventKinds - -class ObservableHooksFolderTests : HookTestBase() { - private companion object { - @JvmStatic - fun getGit(hooksPath: Path) = Git( - RespondInterchangeably( - CliResponse(Git.minRequiredVersion.toString()), - CliResponse(hooksPath.toString()) - ) - ) - } - - @Test - fun `Should observe changes`() { - // Arrange - val git = getGit(getEnabledHooksPath()) - - // Act - val sut = ObservableHooksFolder(git) - - sut.hooks.subscribe { - assertEquals(it.kind, StandardWatchEventKinds.ENTRY_CREATE) - } - - // Assert - createHook("kek") - } -} - -class HooksFolderTests : HookTestBase() { - private companion object { - @JvmStatic - fun getGit(hooksPath: Path) = Git( - RespondInterchangeably( - CliResponse(Git.minRequiredVersion.toString()), - CliResponse(hooksPath.toString()) - ) - ) - } - - @Test - fun `Should load all hooks from disabled hooks path`() { - // Arrange - val git = getGit(getDisabledHooksPath()) - - // Act - val sut = HooksFolder(git) - - // Assert - assertEquals(HooksFolder.supportedHooks.size, sut.hooks.size) - } - - @Test - fun `Should load all hooks from enabled hooks path`() { - // Arrange - val git = getGit(getEnabledHooksPath()) - - // Act - val sut = HooksFolder(git) - - // Assert - assertEquals(HooksFolder.supportedHooks.size, sut.hooks.size) - } - - @Test - fun `All hooks should be disabled`() { - // Arrange - val git = getGit(getDisabledHooksPath()) - val sut = HooksFolder(git) - - // Act & Assert - assertTrue(sut.isAllDisabled()) - } - - @Test - fun `All hooks should be enabled`() { - // Arrange - val git = getGit(getEnabledHooksPath()) - val sut = HooksFolder(git) - - // Act & Assert - assertFalse(sut.isAllDisabled()) - } - - @Test - fun `Should disable all hooks`() { - // Arrange - val git = getGit(getEnabledHooksPath()) - val sut = HooksFolder(git) - - // Act - sut.disableAll() - - // Assert - assertTrue(sut.isAllDisabled()) - } - - @Test - fun `Should enable all hooks`() { - // Arrange - val git = getGit(getDisabledHooksPath()) - val sut = HooksFolder(git) - - // Act - sut.enableAll() - - // Assert - assertFalse(sut.isAllDisabled()) - } -} \ No newline at end of file diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/DisableAllTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/DisableAllTests.kt new file mode 100644 index 0000000..3928c09 --- /dev/null +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/DisableAllTests.kt @@ -0,0 +1,21 @@ +package com.github.y0ung3r.gitglobalhookslocator.gitTests.hooksFolderTests + +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HooksFolder +import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase +import org.junit.Assert +import org.junit.Test + +class DisableAllTests : HookTestBase() { + @Test + fun `Should disable all hooks`() { + // Arrange + val git = getMockGit(getEnabledHooksPath()) + val sut = HooksFolder(git) + + // Act + sut.disableAll() + + // Assert + Assert.assertTrue(sut.isAllDisabled()) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/EnableAllTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/EnableAllTests.kt new file mode 100644 index 0000000..07cfd80 --- /dev/null +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/EnableAllTests.kt @@ -0,0 +1,20 @@ +package com.github.y0ung3r.gitglobalhookslocator.gitTests.hooksFolderTests + +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HooksFolder +import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase +import org.junit.Test + +class EnableAllTests : HookTestBase() { + @Test + fun `Should enable all hooks`() { + // Arrange + val git = getMockGit(getDisabledHooksPath()) + val sut = HooksFolder(git) + + // Act + sut.enableAll() + + // Assert + assertFalse(sut.isAllDisabled()) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/IsAllDisabledTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/IsAllDisabledTests.kt new file mode 100644 index 0000000..2d88ffb --- /dev/null +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/IsAllDisabledTests.kt @@ -0,0 +1,27 @@ +package com.github.y0ung3r.gitglobalhookslocator.gitTests.hooksFolderTests + +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HooksFolder +import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase +import org.junit.Test + +class IsAllDisabledTests : HookTestBase() { + @Test + fun `All hooks should be disabled`() { + // Arrange + val git = getMockGit(getDisabledHooksPath()) + val sut = HooksFolder(git) + + // Act & Assert + assertTrue(sut.isAllDisabled()) + } + + @Test + fun `All hooks should be enabled`() { + // Arrange + val git = getMockGit(getEnabledHooksPath()) + val sut = HooksFolder(git) + + // Act & Assert + assertFalse(sut.isAllDisabled()) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/LoadTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/LoadTests.kt new file mode 100644 index 0000000..dcb01e0 --- /dev/null +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/LoadTests.kt @@ -0,0 +1,32 @@ +package com.github.y0ung3r.gitglobalhookslocator.gitTests.hooksFolderTests + +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HookName +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HooksFolder +import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase +import org.junit.Test + +class LoadTests : HookTestBase() { + @Test + fun `Should load all hooks from disabled hooks path`() { + // Arrange + val git = getMockGit(getDisabledHooksPath()) + + // Act + val sut = HooksFolder(git) + + // Assert + assertEquals(HookName.supportedHooks.size, sut.count()) + } + + @Test + fun `Should load all hooks from enabled hooks path`() { + // Arrange + val git = getMockGit(getEnabledHooksPath()) + + // Act + val sut = HooksFolder(git) + + // Assert + assertEquals(HookName.supportedHooks.size, sut.count()) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/ObserveTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/ObserveTests.kt new file mode 100644 index 0000000..a35adaf --- /dev/null +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/hooksFolderTests/ObserveTests.kt @@ -0,0 +1,63 @@ +package com.github.y0ung3r.gitglobalhookslocator.gitTests.hooksFolderTests + +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HookEntry +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HookName +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HooksFolder +import com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine.HookTestBase +import io.reactivex.rxjava3.observers.TestObserver +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class ObserveTests(private val hookName: String) : HookTestBase() { + @Test + fun `Should observe added hooks`() { + // Arrange + val git = getMockGit(getCustomHooksPath()) + val sut = HooksFolder(git) + val testObserver = TestObserver() + + sut.addedHooks + .subscribe { + testObserver.onNext(it) + testObserver.onComplete() + } + + // Act + createHook(hookName) + + // Assert + testObserver + .await() + .assertValue { + it.name.value == hookName + } + } + + @Test + fun `Should observe deleted hooks`() { + // Arrange + val git = getMockGit(getCustomHooksPath()) + val sut = HooksFolder(git) + val testObserver = TestObserver() + + sut.deletedHooks + .subscribe { + testObserver.onNext(it) + testObserver.onComplete() + } + + createHook(hookName) + + // Act + deleteHook(hookName) + + // Assert + testObserver + .await() + .assertValue { + it.value == hookName + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt index 5a14efc..5b9bb03 100644 --- a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/HookTestBase.kt @@ -1,15 +1,26 @@ package com.github.y0ung3r.gitglobalhookslocator.gitTests.testEngine -import com.github.y0ung3r.gitglobalhookslocator.git.HooksFolder +import com.github.y0ung3r.gitglobalhookslocator.git.Git +import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse +import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HookName +import org.junit.After +import org.junit.Assert +import org.junit.Before import org.junit.runners.Parameterized import java.nio.file.Files import java.nio.file.Path -abstract class HookTestBase { +abstract class HookTestBase: Assert() { companion object { private const val BASE_PATH = "src/test/testData/hooks" private const val DISABLED_HOOK = "disabled" private const val ENABLED_HOOK = "enabled" + private const val CUSTOM_HOOK = "custom" + + @JvmStatic + @Parameterized.Parameters + fun hookNames() + = HookName.supportedHooks @JvmStatic private fun getHookPath(hookType: String, hookName: String): Path { @@ -25,33 +36,6 @@ abstract class HookTestBase { private fun getHooksPath(hookType: String): Path = Path.of(BASE_PATH, hookType) - @JvmStatic - fun getDisabledHookPath(hookName: String): Path - = getHookPath(DISABLED_HOOK, hookName) - - @JvmStatic - fun getEnabledHookPath(hookName: String): Path - = getHookPath(ENABLED_HOOK, hookName) - - @JvmStatic - fun getDisabledHooksPath(): Path - = getHooksPath(DISABLED_HOOK) - - @JvmStatic - fun getEnabledHooksPath(): Path - = getHooksPath(ENABLED_HOOK) - - @JvmStatic - fun createHook(hookName: String) { - val hookPath = getHookPath(ENABLED_HOOK, hookName).toFile() - - if (hookPath.exists()) { - return - } - - hookPath.createNewFile() - } - @JvmStatic private fun clearTestHooks(hookType: String) { val hooksPath = getHooksPath(hookType) @@ -72,15 +56,64 @@ abstract class HookTestBase { .filter { !it.exists() } .forEach { it.createNewFile() } } + } - @JvmStatic - @Parameterized.Parameters - fun hookNames() - = HooksFolder.supportedHooks + fun getDisabledHookPath(hookName: String): Path + = getHookPath(DISABLED_HOOK, hookName) + + fun getEnabledHookPath(hookName: String): Path + = getHookPath(ENABLED_HOOK, hookName) + + fun getCustomHookPath(hookName: String): Path + = getHookPath(CUSTOM_HOOK, hookName) + + fun getDisabledHooksPath(): Path + = getHooksPath(DISABLED_HOOK) + + fun getEnabledHooksPath(): Path + = getHooksPath(ENABLED_HOOK) + + fun getCustomHooksPath(): Path + = getHooksPath(CUSTOM_HOOK) + + fun createHook(hookName: String) { + val hookPath = getHookPath(CUSTOM_HOOK, hookName).toFile() + + if (hookPath.exists()) { + return + } + + hookPath.createNewFile() } - init { + fun deleteHook(hookName: String) { + val hookPath = getHookPath(CUSTOM_HOOK, hookName).toFile() + + if (!hookPath.exists()) { + return + } + + hookPath.delete() + } + + fun getMockGit(hooksPath: Path) = Git( + RespondInterchangeably( + CliResponse(Git.minRequiredVersion.toString()), + CliResponse(hooksPath.toString()) + ) + ) + + @Before + fun setUp() { generateTestHooks(DISABLED_HOOK) generateTestHooks(ENABLED_HOOK) + clearTestHooks(CUSTOM_HOOK) + } + + @After + fun tearDown() { + clearTestHooks(DISABLED_HOOK) + clearTestHooks(ENABLED_HOOK) + clearTestHooks(CUSTOM_HOOK) } } \ No newline at end of file diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/RespondInterchangeably.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/RespondInterchangeably.kt index 920272a..ae8e6ff 100644 --- a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/RespondInterchangeably.kt +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/testEngine/RespondInterchangeably.kt @@ -5,6 +5,7 @@ import com.github.y0ung3r.gitglobalhookslocator.git.cli.interfaces.CliCommandExe class RespondInterchangeably(vararg responses: CliResponse) : CliCommandExecutor { private val iterator = responses.iterator() + override fun execute(processBuilder: ProcessBuilder): CliResponse = iterator.next() } \ No newline at end of file From d10b73d8f5ef2654e95ce684c95542ce3c42eca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=92=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=D0=B9?= Date: Tue, 20 Feb 2024 22:27:18 +0500 Subject: [PATCH 4/5] Move some folders --- .../git/extensions/GitExtensions.kt | 2 +- .../git/utils/ObservableExtensions.kt | 17 ----------------- .../ui/{toolWindow => }/Notifier.kt | 2 +- .../ui/toolWindow/LocatorWindow.kt | 3 ++- .../{git => }/utils/SystemPathUtils.kt | 2 +- .../gitTests/GetGlobalHooksPathTests.kt | 2 +- 6 files changed, 6 insertions(+), 22 deletions(-) delete mode 100644 src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/ObservableExtensions.kt rename src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/{toolWindow => }/Notifier.kt (93%) rename src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/{git => }/utils/SystemPathUtils.kt (93%) diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/extensions/GitExtensions.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/extensions/GitExtensions.kt index 612b50c..2a4480b 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/extensions/GitExtensions.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/extensions/GitExtensions.kt @@ -4,7 +4,7 @@ import com.github.y0ung3r.gitglobalhookslocator.git.Git import com.github.y0ung3r.gitglobalhookslocator.git.GitResponse import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse import com.github.y0ung3r.gitglobalhookslocator.git.exceptions.GitCommandNotFoundException -import com.github.y0ung3r.gitglobalhookslocator.git.utils.SystemPathUtils +import com.github.y0ung3r.gitglobalhookslocator.utils.SystemPathUtils import java.nio.file.Path private const val SLASHES_PATTERN = "[/\\\\]" diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/ObservableExtensions.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/ObservableExtensions.kt deleted file mode 100644 index 2626717..0000000 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/ObservableExtensions.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.y0ung3r.gitglobalhookslocator.git.utils - -import com.intellij.ui.layout.ComponentPredicate -import io.reactivex.rxjava3.core.Observable - -fun Observable.toComponentPredicate(): ComponentPredicate - = object : ComponentPredicate() { - override fun addListener(listener: (Boolean) -> Unit) { - subscribe(listener) - } - - override fun invoke(): Boolean - = false - } - -fun Observable.negate() - = map { !it } \ No newline at end of file diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/Notifier.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/Notifier.kt similarity index 93% rename from src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/Notifier.kt rename to src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/Notifier.kt index b5be8fa..032a814 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/Notifier.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/Notifier.kt @@ -1,4 +1,4 @@ -package com.github.y0ung3r.gitglobalhookslocator.ui.toolWindow +package com.github.y0ung3r.gitglobalhookslocator.ui import com.intellij.notification.NotificationGroupManager import com.intellij.notification.NotificationType diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt index 4f81e7f..e2373b4 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/ui/toolWindow/LocatorWindow.kt @@ -4,7 +4,8 @@ import com.github.y0ung3r.gitglobalhookslocator.LocatorBundle import com.github.y0ung3r.gitglobalhookslocator.git.Git import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HookName import com.github.y0ung3r.gitglobalhookslocator.git.hooks.HooksFolder -import com.github.y0ung3r.gitglobalhookslocator.git.utils.SystemPathUtils +import com.github.y0ung3r.gitglobalhookslocator.ui.Notifier +import com.github.y0ung3r.gitglobalhookslocator.utils.SystemPathUtils import com.intellij.ide.wizard.withVisualPadding import com.intellij.openapi.wm.ToolWindow import com.intellij.ui.components.JBCheckBox diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/SystemPathUtils.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/utils/SystemPathUtils.kt similarity index 93% rename from src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/SystemPathUtils.kt rename to src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/utils/SystemPathUtils.kt index 0541028..e28288e 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/utils/SystemPathUtils.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/utils/SystemPathUtils.kt @@ -1,4 +1,4 @@ -package com.github.y0ung3r.gitglobalhookslocator.git.utils +package com.github.y0ung3r.gitglobalhookslocator.utils import java.awt.Desktop import java.nio.file.Path diff --git a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/GetGlobalHooksPathTests.kt b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/GetGlobalHooksPathTests.kt index 13c736c..47a3c2e 100644 --- a/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/GetGlobalHooksPathTests.kt +++ b/src/test/kotlin/com/github/y0ung3r/gitglobalhookslocator/gitTests/GetGlobalHooksPathTests.kt @@ -6,7 +6,7 @@ import com.github.y0ung3r.gitglobalhookslocator.git.cli.CliResponse import com.github.y0ung3r.gitglobalhookslocator.git.cli.EmptyCliResponse import com.github.y0ung3r.gitglobalhookslocator.git.cli.NotFoundCliResponse import com.github.y0ung3r.gitglobalhookslocator.git.extensions.getGlobalHooksPath -import com.github.y0ung3r.gitglobalhookslocator.git.utils.SystemPathUtils +import com.github.y0ung3r.gitglobalhookslocator.utils.SystemPathUtils import org.junit.Assert.assertEquals import org.junit.Test import java.nio.file.Path From ef9e8ac722eeeeb61ebd0fcdfacb4a37844deacd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=92=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=D0=B9?= Date: Tue, 20 Feb 2024 22:54:58 +0500 Subject: [PATCH 5/5] Refactor folder event emitting --- .../git/hooks/HooksFolder.kt | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HooksFolder.kt b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HooksFolder.kt index 0aa3a6f..a176c2d 100644 --- a/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HooksFolder.kt +++ b/src/main/kotlin/com/github/y0ung3r/gitglobalhookslocator/git/hooks/HooksFolder.kt @@ -90,7 +90,34 @@ class HooksFolder(git: Git) { return@create emitter.onComplete() } - emitFolderEvent(emitter, folderEntry, kind) + val pendingEvents = folderEntry + .pollEvents() + .map { + @Suppress("UNCHECKED_CAST") + it as WatchEvent + } + + // if a rename event is received + val isRenameEventReceived = pendingEvents.size > 1 && + kind == StandardWatchEventKinds.ENTRY_MODIFY + + val targetKind = if (isRenameEventReceived) + StandardWatchEventKinds.ENTRY_CREATE + else kind + + val originalEvent = pendingEvents + .firstOrNull { it.kind() == targetKind } + + if (originalEvent != null) { + val folderEvent = HooksFolderEvent( + kind, + originalEvent.context(), + path) + + if (HookName.isSupportedHook(folderEvent.hookPath)) { + emitter.onNext(folderEvent) + } + } if (!folderEntry.reset()) { return@create emitter.onComplete() @@ -111,36 +138,6 @@ class HooksFolder(git: Git) { } } - private fun emitFolderEvent( - emitter: ObservableEmitter, - folderEntry: WatchKey, - observableKind: WatchEvent.Kind) { - val pendingEvents = folderEntry - .pollEvents() - .map { - @Suppress("UNCHECKED_CAST") - it as WatchEvent - } - - val targetKind = when { - pendingEvents.size > 1 -> StandardWatchEventKinds.ENTRY_CREATE - else -> observableKind - } - - val originalEvent = pendingEvents - .firstOrNull { it.kind() == targetKind } - ?: return - - val folderEvent = HooksFolderEvent( - observableKind, - originalEvent.context(), - path) - - if (HookName.isSupportedHook(folderEvent.hookPath)) { - emitter.onNext(folderEvent) - } - } - private fun getExistsHooks(): MutableList = try { Files.list(path)