Skip to content

Commit 1feb136

Browse files
authored
Merge pull request #21 from yeo-li/feat/save-memo
[feat] 메모 저장 기능 구현
2 parents cbdb5b5 + e0cbf4b commit 1feb136

10 files changed

Lines changed: 238 additions & 45 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
.kotlin
66
.qodana
77
build
8+
.idea/

.idea/gradle.xml

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/main/kotlin/com/github/yeoli/devlog/domain/memo/domain/Memo.kt

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package com.github.yeoli.devlog.domain.memo.domain
22

3+
import com.github.yeoli.devlog.domain.memo.repository.MemoState
34
import java.time.LocalDateTime
45

56
class Memo(
7+
val id: Long,
8+
val createdAt: LocalDateTime,
9+
val updatedAt: LocalDateTime,
10+
611
val content: String,
712

813
val commitHash: String? = null,
@@ -15,14 +20,33 @@ class Memo(
1520
val visibleStart: Int? = null,
1621
val visibleEnd: Int? = null
1722
) {
18-
val id: Long = generateId()
19-
val createdAt: LocalDateTime = LocalDateTime.now()
20-
val updatedAt: LocalDateTime = LocalDateTime.now()
21-
2223
init {
2324
validate()
2425
}
2526

27+
constructor(
28+
content: String,
29+
commitHash: String?,
30+
filePath: String?,
31+
selectedCodeSnippet: String?,
32+
selectionStart: Int?,
33+
selectionEnd: Int?,
34+
visibleStart: Int?,
35+
visibleEnd: Int?
36+
) : this(
37+
id = System.currentTimeMillis(),
38+
createdAt = LocalDateTime.now(),
39+
updatedAt = LocalDateTime.now(),
40+
content = content,
41+
commitHash = commitHash,
42+
filePath = filePath,
43+
selectedCodeSnippet = selectedCodeSnippet,
44+
selectionStart = selectionStart,
45+
selectionEnd = selectionEnd,
46+
visibleStart = visibleStart,
47+
visibleEnd = visibleEnd
48+
)
49+
2650
private fun validate() {
2751
if (selectionStart != null && selectionEnd != null) {
2852
require(selectionStart <= selectionEnd) {
@@ -37,7 +61,18 @@ class Memo(
3761
}
3862
}
3963

40-
private fun generateId(): Long {
41-
return System.currentTimeMillis()
42-
}
43-
}
64+
fun toState(): MemoState =
65+
MemoState(
66+
id = this.id,
67+
createdAt = this.createdAt.toString(),
68+
updatedAt = this.updatedAt.toString(),
69+
content = this.content,
70+
commitHash = this.commitHash,
71+
filePath = this.filePath,
72+
selectedCodeSnippet = this.selectedCodeSnippet,
73+
selectionStart = this.selectionStart,
74+
selectionEnd = this.selectionEnd,
75+
visibleStart = this.visibleStart,
76+
visibleEnd = this.visibleEnd
77+
)
78+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.github.yeoli.devlog.domain.memo.repository
2+
3+
import com.intellij.openapi.components.PersistentStateComponent
4+
import com.intellij.openapi.components.Service
5+
import com.intellij.openapi.components.State
6+
import com.intellij.openapi.components.Storage
7+
8+
@State(
9+
name = "DevLogMemoStorage",
10+
storages = [Storage("devlog-memos.xml")]
11+
)
12+
@Service(Service.Level.PROJECT)
13+
class MemoRepository : PersistentStateComponent<MemoStorageState> {
14+
private var state = MemoStorageState()
15+
16+
override fun getState(): MemoStorageState = state
17+
18+
override fun loadState(state: MemoStorageState) {
19+
this.state = state
20+
}
21+
22+
fun save(memo: MemoState) {
23+
state.memos.add(memo)
24+
}
25+
26+
fun getAll(): List<MemoState> {
27+
return state.memos
28+
}
29+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.github.yeoli.devlog.domain.memo.repository
2+
3+
import com.github.yeoli.devlog.domain.memo.domain.Memo
4+
import java.time.LocalDateTime
5+
6+
data class MemoState(
7+
var id: Long = 0L,
8+
var createdAt: String,
9+
var updatedAt: String,
10+
var content: String = "",
11+
var commitHash: String? = null,
12+
var filePath: String? = null,
13+
var selectedCodeSnippet: String? = null,
14+
var selectionStart: Int? = null,
15+
var selectionEnd: Int? = null,
16+
var visibleStart: Int? = null,
17+
var visibleEnd: Int? = null
18+
) {
19+
fun toDomain(): Memo =
20+
Memo(
21+
id = this.id,
22+
createdAt = this.createdAt.let { LocalDateTime.parse(it) },
23+
updatedAt = this.updatedAt.let { LocalDateTime.parse(it) },
24+
content = content,
25+
commitHash = commitHash,
26+
filePath = filePath,
27+
selectedCodeSnippet = selectedCodeSnippet,
28+
selectionStart = selectionStart,
29+
selectionEnd = selectionEnd,
30+
visibleStart = visibleStart,
31+
visibleEnd = visibleEnd
32+
)
33+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.github.yeoli.devlog.domain.memo.repository
2+
3+
class MemoStorageState(
4+
var memos: MutableList<MemoState> = mutableListOf()
5+
) {
6+
}

src/main/kotlin/com/github/yeoli/devlog/domain/memo/service/MemoService.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.github.yeoli.devlog.domain.memo.service
22

33
import com.github.yeoli.devlog.domain.memo.domain.Memo
4+
import com.github.yeoli.devlog.domain.memo.repository.MemoRepository
45
import com.ibm.icu.impl.IllegalIcuArgumentException
6+
import com.intellij.openapi.components.Service
57
import com.intellij.openapi.diagnostic.Logger
68
import com.intellij.openapi.editor.Editor
79
import com.intellij.openapi.fileEditor.FileDocumentManager
@@ -10,11 +12,14 @@ import com.intellij.openapi.project.Project
1012
import git4idea.repo.GitRepositoryManager
1113
import java.awt.Point
1214

13-
class MemoService {
15+
@Service(Service.Level.PROJECT)
16+
class MemoService(private val project: Project) {
17+
18+
private val memoRepository = project.getService(MemoRepository::class.java)
1419

1520
private val logger = Logger.getInstance(MemoService::class.java)
1621

17-
fun createMemo(content: String, project: Project): Memo? {
22+
fun createMemo(content: String): Memo? {
1823
val editor = getActiveEditor(project)
1924
if (editor == null) {
2025
logger.warn("[createMemo] editor가 null이므로 null을 반환합니다.")
@@ -74,4 +79,12 @@ class MemoService {
7479
val repo = repoManager.repositories.firstOrNull() ?: return null
7580
return repo.currentRevision
7681
}
82+
83+
fun saveMemo(memo: Memo) {
84+
try {
85+
memoRepository.save(memo.toState())
86+
} catch (e: Exception) {
87+
logger.warn("[saveMemo] 메모 저장 중 알 수 없는 에러가 발생했습니다. ${e.message}", e)
88+
}
89+
}
7790
}

src/test/kotlin/com/github/yeoli/devlog/domain/memo/domain/MemoTest.kt

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
package com.github.yeoli.devlog.domain.memo.domain
22

3-
import kotlin.test.Test
4-
import kotlin.test.assertEquals
5-
import kotlin.test.assertFailsWith
6-
import kotlin.test.assertNotNull
7-
import kotlin.test.assertTrue
3+
import com.github.yeoli.devlog.domain.memo.repository.MemoState
4+
import kotlin.test.*
85

96
class MemoTest {
107

118
@Test
12-
fun test_Memo_생성_성공() {
9+
fun `test Memo 생성 성공`() {
10+
// given & then
1311
val memo = Memo(
1412
content = "테스트 메모",
1513
commitHash = "abc123",
@@ -21,6 +19,7 @@ class MemoTest {
2119
visibleEnd = 20
2220
)
2321

22+
// then
2423
assertEquals("테스트 메모", memo.content)
2524
assertEquals("abc123", memo.commitHash)
2625
assertEquals("/path/SampleFile.kt", memo.filePath)
@@ -35,24 +34,69 @@ class MemoTest {
3534
}
3635

3736
@Test
38-
fun test_Memo_생성_실패_selection_범위() {
37+
fun `test Memo 생성 실패 selection 범위`() {
3938
assertFailsWith<IllegalArgumentException> {
4039
Memo(
4140
content = "잘못된 메모",
41+
commitHash = "abc123",
42+
filePath = "/path/SampleFile.kt",
43+
selectedCodeSnippet = "val selected = 42",
4244
selectionStart = 10,
43-
selectionEnd = 5
45+
selectionEnd = 5,
46+
visibleStart = null,
47+
visibleEnd = null
4448
)
4549
}
4650
}
4751

4852
@Test
49-
fun test_Memo_생성_실패_visible_범위() {
53+
fun `test Memo 생성 실패 visible 범위`() {
54+
// when & then
5055
assertFailsWith<IllegalArgumentException> {
5156
Memo(
52-
content = "보이는 영역 오류",
57+
content = "잘못된 메모",
58+
commitHash = "abc123",
59+
filePath = "/path/SampleFile.kt",
60+
selectedCodeSnippet = "val selected = 42",
61+
selectionStart = 5,
62+
selectionEnd = 10,
5363
visibleStart = 20,
5464
visibleEnd = 10
5565
)
5666
}
5767
}
68+
69+
@Test
70+
fun `test MemoState 변환 성공`() {
71+
// given
72+
val memo = Memo(
73+
content = "테스트 메모",
74+
commitHash = "abc123",
75+
filePath = "/path/SampleFile.kt",
76+
selectedCodeSnippet = "val selected = 42",
77+
selectionStart = 5,
78+
selectionEnd = 10,
79+
visibleStart = 1,
80+
visibleEnd = 20
81+
)
82+
83+
// when
84+
val memoState: MemoState = memo.toState()
85+
86+
// then
87+
assertEquals(memo.id, memoState.id)
88+
assertEquals(memo.createdAt.toString(), memoState.createdAt)
89+
assertEquals(memo.updatedAt.toString(), memoState.updatedAt)
90+
assertEquals("테스트 메모", memoState.content)
91+
assertEquals("abc123", memoState.commitHash)
92+
assertEquals("/path/SampleFile.kt", memoState.filePath)
93+
assertEquals("val selected = 42", memoState.selectedCodeSnippet)
94+
assertEquals(5, memoState.selectionStart)
95+
assertEquals(10, memoState.selectionEnd)
96+
assertEquals(1, memoState.visibleStart)
97+
assertEquals(20, memoState.visibleEnd)
98+
assertTrue(memoState.id > 0)
99+
assertNotNull(memoState.createdAt)
100+
assertNotNull(memoState.updatedAt)
101+
}
58102
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.github.yeoli.devlog.domain.memo.repository
2+
3+
import com.github.yeoli.devlog.domain.memo.domain.Memo
4+
import org.junit.jupiter.api.Test
5+
import java.time.LocalDateTime
6+
import kotlin.test.assertEquals
7+
import kotlin.test.assertNotNull
8+
import kotlin.test.assertTrue
9+
10+
class MemoStateTest {
11+
12+
@Test
13+
fun `test 도메인 변환 성공`() {
14+
// given
15+
val memoState = MemoState(
16+
id = 0L,
17+
createdAt = LocalDateTime.now().toString(),
18+
updatedAt = LocalDateTime.now().toString(),
19+
content = "테스트 메모",
20+
commitHash = "abc123",
21+
filePath = "/path/SampleFile.kt",
22+
selectedCodeSnippet = "val selected = 42",
23+
selectionStart = 5,
24+
selectionEnd = 10,
25+
visibleStart = 1,
26+
visibleEnd = 20
27+
)
28+
29+
// when
30+
val memo: Memo = memoState.toDomain()
31+
32+
// then
33+
assertEquals(memo.id, memoState.id)
34+
assertEquals(memo.createdAt.toString(), memoState.createdAt)
35+
assertEquals(memo.updatedAt.toString(), memoState.updatedAt)
36+
assertEquals("테스트 메모", memoState.content)
37+
assertEquals("abc123", memoState.commitHash)
38+
assertEquals("/path/SampleFile.kt", memoState.filePath)
39+
assertEquals("val selected = 42", memoState.selectedCodeSnippet)
40+
assertEquals(5, memoState.selectionStart)
41+
assertEquals(10, memoState.selectionEnd)
42+
assertEquals(1, memoState.visibleStart)
43+
assertEquals(20, memoState.visibleEnd)
44+
assertTrue(memoState.id > 0)
45+
assertNotNull(memoState.createdAt)
46+
assertNotNull(memoState.updatedAt)
47+
}
48+
}

0 commit comments

Comments
 (0)