diff --git a/.gitignore b/.gitignore index df6966a..6c11680 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .kotlin .qodana build +.idea/ \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 05eca02..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/main/kotlin/com/github/yeoli/devlog/domain/memo/domain/Memo.kt b/src/main/kotlin/com/github/yeoli/devlog/domain/memo/domain/Memo.kt index 626b566..e820a7f 100644 --- a/src/main/kotlin/com/github/yeoli/devlog/domain/memo/domain/Memo.kt +++ b/src/main/kotlin/com/github/yeoli/devlog/domain/memo/domain/Memo.kt @@ -1,8 +1,13 @@ package com.github.yeoli.devlog.domain.memo.domain +import com.github.yeoli.devlog.domain.memo.repository.MemoState import java.time.LocalDateTime class Memo( + val id: Long, + val createdAt: LocalDateTime, + val updatedAt: LocalDateTime, + val content: String, val commitHash: String? = null, @@ -15,14 +20,33 @@ class Memo( val visibleStart: Int? = null, val visibleEnd: Int? = null ) { - val id: Long = generateId() - val createdAt: LocalDateTime = LocalDateTime.now() - val updatedAt: LocalDateTime = LocalDateTime.now() - init { validate() } + constructor( + content: String, + commitHash: String?, + filePath: String?, + selectedCodeSnippet: String?, + selectionStart: Int?, + selectionEnd: Int?, + visibleStart: Int?, + visibleEnd: Int? + ) : this( + id = System.currentTimeMillis(), + createdAt = LocalDateTime.now(), + updatedAt = LocalDateTime.now(), + content = content, + commitHash = commitHash, + filePath = filePath, + selectedCodeSnippet = selectedCodeSnippet, + selectionStart = selectionStart, + selectionEnd = selectionEnd, + visibleStart = visibleStart, + visibleEnd = visibleEnd + ) + private fun validate() { if (selectionStart != null && selectionEnd != null) { require(selectionStart <= selectionEnd) { @@ -37,7 +61,18 @@ class Memo( } } - private fun generateId(): Long { - return System.currentTimeMillis() - } -} \ No newline at end of file + fun toState(): MemoState = + MemoState( + id = this.id, + createdAt = this.createdAt.toString(), + updatedAt = this.updatedAt.toString(), + content = this.content, + commitHash = this.commitHash, + filePath = this.filePath, + selectedCodeSnippet = this.selectedCodeSnippet, + selectionStart = this.selectionStart, + selectionEnd = this.selectionEnd, + visibleStart = this.visibleStart, + visibleEnd = this.visibleEnd + ) +} diff --git a/src/main/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoRepository.kt b/src/main/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoRepository.kt new file mode 100644 index 0000000..1bb9ee8 --- /dev/null +++ b/src/main/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoRepository.kt @@ -0,0 +1,29 @@ +package com.github.yeoli.devlog.domain.memo.repository + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage + +@State( + name = "DevLogMemoStorage", + storages = [Storage("devlog-memos.xml")] +) +@Service(Service.Level.PROJECT) +class MemoRepository : PersistentStateComponent { + private var state = MemoStorageState() + + override fun getState(): MemoStorageState = state + + override fun loadState(state: MemoStorageState) { + this.state = state + } + + fun save(memo: MemoState) { + state.memos.add(memo) + } + + fun getAll(): List { + return state.memos + } +} diff --git a/src/main/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoState.kt b/src/main/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoState.kt new file mode 100644 index 0000000..272f360 --- /dev/null +++ b/src/main/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoState.kt @@ -0,0 +1,33 @@ +package com.github.yeoli.devlog.domain.memo.repository + +import com.github.yeoli.devlog.domain.memo.domain.Memo +import java.time.LocalDateTime + +data class MemoState( + var id: Long = 0L, + var createdAt: String, + var updatedAt: String, + var content: String = "", + var commitHash: String? = null, + var filePath: String? = null, + var selectedCodeSnippet: String? = null, + var selectionStart: Int? = null, + var selectionEnd: Int? = null, + var visibleStart: Int? = null, + var visibleEnd: Int? = null +) { + fun toDomain(): Memo = + Memo( + id = this.id, + createdAt = this.createdAt.let { LocalDateTime.parse(it) }, + updatedAt = this.updatedAt.let { LocalDateTime.parse(it) }, + content = content, + commitHash = commitHash, + filePath = filePath, + selectedCodeSnippet = selectedCodeSnippet, + selectionStart = selectionStart, + selectionEnd = selectionEnd, + visibleStart = visibleStart, + visibleEnd = visibleEnd + ) +} diff --git a/src/main/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoStorageState.kt b/src/main/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoStorageState.kt new file mode 100644 index 0000000..9a555f2 --- /dev/null +++ b/src/main/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoStorageState.kt @@ -0,0 +1,6 @@ +package com.github.yeoli.devlog.domain.memo.repository + +class MemoStorageState( + var memos: MutableList = mutableListOf() +) { +} diff --git a/src/main/kotlin/com/github/yeoli/devlog/domain/memo/service/MemoService.kt b/src/main/kotlin/com/github/yeoli/devlog/domain/memo/service/MemoService.kt index 438465b..497471b 100644 --- a/src/main/kotlin/com/github/yeoli/devlog/domain/memo/service/MemoService.kt +++ b/src/main/kotlin/com/github/yeoli/devlog/domain/memo/service/MemoService.kt @@ -1,7 +1,9 @@ package com.github.yeoli.devlog.domain.memo.service import com.github.yeoli.devlog.domain.memo.domain.Memo +import com.github.yeoli.devlog.domain.memo.repository.MemoRepository import com.ibm.icu.impl.IllegalIcuArgumentException +import com.intellij.openapi.components.Service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Editor import com.intellij.openapi.fileEditor.FileDocumentManager @@ -10,11 +12,14 @@ import com.intellij.openapi.project.Project import git4idea.repo.GitRepositoryManager import java.awt.Point -class MemoService { +@Service(Service.Level.PROJECT) +class MemoService(private val project: Project) { + + private val memoRepository = project.getService(MemoRepository::class.java) private val logger = Logger.getInstance(MemoService::class.java) - fun createMemo(content: String, project: Project): Memo? { + fun createMemo(content: String): Memo? { val editor = getActiveEditor(project) if (editor == null) { logger.warn("[createMemo] editor가 null이므로 null을 반환합니다.") @@ -74,4 +79,12 @@ class MemoService { val repo = repoManager.repositories.firstOrNull() ?: return null return repo.currentRevision } + + fun saveMemo(memo: Memo) { + try { + memoRepository.save(memo.toState()) + } catch (e: Exception) { + logger.warn("[saveMemo] 메모 저장 중 알 수 없는 에러가 발생했습니다. ${e.message}", e) + } + } } \ No newline at end of file diff --git a/src/test/kotlin/com/github/yeoli/devlog/domain/memo/domain/MemoTest.kt b/src/test/kotlin/com/github/yeoli/devlog/domain/memo/domain/MemoTest.kt index 01ef4c5..4533a23 100644 --- a/src/test/kotlin/com/github/yeoli/devlog/domain/memo/domain/MemoTest.kt +++ b/src/test/kotlin/com/github/yeoli/devlog/domain/memo/domain/MemoTest.kt @@ -1,15 +1,13 @@ package com.github.yeoli.devlog.domain.memo.domain -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue +import com.github.yeoli.devlog.domain.memo.repository.MemoState +import kotlin.test.* class MemoTest { @Test - fun test_Memo_생성_성공() { + fun `test Memo 생성 성공`() { + // given & then val memo = Memo( content = "테스트 메모", commitHash = "abc123", @@ -21,6 +19,7 @@ class MemoTest { visibleEnd = 20 ) + // then assertEquals("테스트 메모", memo.content) assertEquals("abc123", memo.commitHash) assertEquals("/path/SampleFile.kt", memo.filePath) @@ -35,24 +34,69 @@ class MemoTest { } @Test - fun test_Memo_생성_실패_selection_범위() { + fun `test Memo 생성 실패 selection 범위`() { assertFailsWith { Memo( content = "잘못된 메모", + commitHash = "abc123", + filePath = "/path/SampleFile.kt", + selectedCodeSnippet = "val selected = 42", selectionStart = 10, - selectionEnd = 5 + selectionEnd = 5, + visibleStart = null, + visibleEnd = null ) } } @Test - fun test_Memo_생성_실패_visible_범위() { + fun `test Memo 생성 실패 visible 범위`() { + // when & then assertFailsWith { Memo( - content = "보이는 영역 오류", + content = "잘못된 메모", + commitHash = "abc123", + filePath = "/path/SampleFile.kt", + selectedCodeSnippet = "val selected = 42", + selectionStart = 5, + selectionEnd = 10, visibleStart = 20, visibleEnd = 10 ) } } + + @Test + fun `test MemoState 변환 성공`() { + // given + val memo = Memo( + content = "테스트 메모", + commitHash = "abc123", + filePath = "/path/SampleFile.kt", + selectedCodeSnippet = "val selected = 42", + selectionStart = 5, + selectionEnd = 10, + visibleStart = 1, + visibleEnd = 20 + ) + + // when + val memoState: MemoState = memo.toState() + + // then + assertEquals(memo.id, memoState.id) + assertEquals(memo.createdAt.toString(), memoState.createdAt) + assertEquals(memo.updatedAt.toString(), memoState.updatedAt) + assertEquals("테스트 메모", memoState.content) + assertEquals("abc123", memoState.commitHash) + assertEquals("/path/SampleFile.kt", memoState.filePath) + assertEquals("val selected = 42", memoState.selectedCodeSnippet) + assertEquals(5, memoState.selectionStart) + assertEquals(10, memoState.selectionEnd) + assertEquals(1, memoState.visibleStart) + assertEquals(20, memoState.visibleEnd) + assertTrue(memoState.id > 0) + assertNotNull(memoState.createdAt) + assertNotNull(memoState.updatedAt) + } } diff --git a/src/test/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoStateTest.kt b/src/test/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoStateTest.kt new file mode 100644 index 0000000..9d232a9 --- /dev/null +++ b/src/test/kotlin/com/github/yeoli/devlog/domain/memo/repository/MemoStateTest.kt @@ -0,0 +1,48 @@ +package com.github.yeoli.devlog.domain.memo.repository + +import com.github.yeoli.devlog.domain.memo.domain.Memo +import org.junit.jupiter.api.Test +import java.time.LocalDateTime +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +class MemoStateTest { + + @Test + fun `test 도메인 변환 성공`() { + // given + val memoState = MemoState( + id = 0L, + createdAt = LocalDateTime.now().toString(), + updatedAt = LocalDateTime.now().toString(), + content = "테스트 메모", + commitHash = "abc123", + filePath = "/path/SampleFile.kt", + selectedCodeSnippet = "val selected = 42", + selectionStart = 5, + selectionEnd = 10, + visibleStart = 1, + visibleEnd = 20 + ) + + // when + val memo: Memo = memoState.toDomain() + + // then + assertEquals(memo.id, memoState.id) + assertEquals(memo.createdAt.toString(), memoState.createdAt) + assertEquals(memo.updatedAt.toString(), memoState.updatedAt) + assertEquals("테스트 메모", memoState.content) + assertEquals("abc123", memoState.commitHash) + assertEquals("/path/SampleFile.kt", memoState.filePath) + assertEquals("val selected = 42", memoState.selectedCodeSnippet) + assertEquals(5, memoState.selectionStart) + assertEquals(10, memoState.selectionEnd) + assertEquals(1, memoState.visibleStart) + assertEquals(20, memoState.visibleEnd) + assertTrue(memoState.id > 0) + assertNotNull(memoState.createdAt) + assertNotNull(memoState.updatedAt) + } +} diff --git a/src/test/kotlin/com/github/yeoli/devlog/domain/memo/service/MemoServiceTest.kt b/src/test/kotlin/com/github/yeoli/devlog/domain/memo/service/MemoServiceTest.kt index 816b289..6ee0d2c 100644 --- a/src/test/kotlin/com/github/yeoli/devlog/domain/memo/service/MemoServiceTest.kt +++ b/src/test/kotlin/com/github/yeoli/devlog/domain/memo/service/MemoServiceTest.kt @@ -14,7 +14,7 @@ import java.awt.Point class MemoServiceTest : BasePlatformTestCase() { - fun test_메모_생성_성공() { + fun `test 메모 생성 성공`() { // given val psiFile = myFixture.configureByText( "SampleFile.kt", @@ -41,7 +41,7 @@ class MemoServiceTest : BasePlatformTestCase() { ).line // when - val memo: Memo? = MemoService().createMemo(memoContent, project) + val memo: Memo? = MemoService(project).createMemo(memoContent) // then if (memo != null) { assertEquals(memoContent, memo.content) @@ -58,7 +58,7 @@ class MemoServiceTest : BasePlatformTestCase() { } - fun test_메모_생성_선택없음() { + fun `test 메모 생성 선택없음`() { // given val psiFile = myFixture.configureByText( "SampleFile.kt", @@ -84,7 +84,7 @@ class MemoServiceTest : BasePlatformTestCase() { ).line // when - val memo: Memo? = MemoService().createMemo(memoContent, project) + val memo: Memo? = MemoService(project).createMemo(memoContent) // then if (memo != null) { assertEquals(memoContent, memo.content) @@ -100,7 +100,7 @@ class MemoServiceTest : BasePlatformTestCase() { } } - fun test_메모_생성_에디터없음_예외() { + fun `test 메모 생성 에디터 없음 예외`() { // given val psiFile = myFixture.configureByText( "SampleFile.kt", @@ -113,11 +113,11 @@ class MemoServiceTest : BasePlatformTestCase() { assertNull("선택된 에디터가 없어야 합니다.", fileEditorManager.selectedTextEditor) // expect - val memo: Memo? = MemoService().createMemo("에디터 없음", project) + val memo: Memo? = MemoService(project).createMemo("에디터 없음") assertNull(memo); } - fun test_메모_생성_파일경로없음() { + fun `test 메모 생성 파일경로 없음`() { // given myFixture.configureByText( "SampleFile.kt", @@ -149,7 +149,7 @@ class MemoServiceTest : BasePlatformTestCase() { try { // when - val memo: Memo? = MemoService().createMemo(memoContent, project) + val memo: Memo? = MemoService(project).createMemo(memoContent) // then if (memo != null) { @@ -166,4 +166,5 @@ class MemoServiceTest : BasePlatformTestCase() { Disposer.dispose(mockDisposable) } } + }