From 9bc3f0ab4ac75e8e60eade785bd82bd009a212c4 Mon Sep 17 00:00:00 2001 From: Abdourahamane Boinaidi Date: Thu, 24 Sep 2026 17:09:13 +0200 Subject: [PATCH 1/5] feat: Add extension to clean event descriptions by removing marked blocks --- .../extensions/EventDescriptionExtensions.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensions.kt diff --git a/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensions.kt b/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensions.kt new file mode 100644 index 000000000..e544e62fb --- /dev/null +++ b/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensions.kt @@ -0,0 +1,26 @@ +/* + * Infomaniak Calendar - Multiplatform + * Copyright (C) 2026 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.multiplatform_calendar.core.extensions + +private const val MARKED_BLOCKS = "/\\*----------------------------------------\\*/" +private val MARKED_BLOCKS_REGEX = Regex("$MARKED_BLOCKS[\\s\\S]*?$MARKED_BLOCKS") + +internal fun String.removeMarkedBlocks(): String { + val regex = MARKED_BLOCKS_REGEX + return this.replace(regex, "").trim() +} From b4b04b16e242e8b66fc55b1dec1811956822f0f0 Mon Sep 17 00:00:00 2001 From: Abdourahamane Boinaidi Date: Thu, 24 Sep 2026 17:10:18 +0200 Subject: [PATCH 2/5] chore: Add unit tests for removeMarkedBlocks extension function --- .../EventDescriptionExtensionsTest.kt | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 CalendarCore/src/commonTest/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensionsTest.kt diff --git a/CalendarCore/src/commonTest/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensionsTest.kt b/CalendarCore/src/commonTest/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensionsTest.kt new file mode 100644 index 000000000..187c7a71f --- /dev/null +++ b/CalendarCore/src/commonTest/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensionsTest.kt @@ -0,0 +1,106 @@ +/* + * Infomaniak Calendar - Multiplatform + * Copyright (C) 2026 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.multiplatform_calendar.core.extensions + +import kotlin.test.Test +import kotlin.test.assertEquals + +class EventDescriptionExtensionsTest { + + private val marker = "/*----------------------------------------*/" + + @Test + fun removeMarkedBlocks_removesOneBlockAndPreservesSurroundingText() { + val input = "avant${marker}contenu à supprimer${marker}après" + + assertEquals("avantaprès", input.removeMarkedBlocks()) + } + + @Test + fun removeMarkedBlocks_removesMultipleBlocks() { + val input = "avant${marker}premier${marker}milieu${marker}second${marker}après" + + assertEquals("avantmilieuaprès", input.removeMarkedBlocks()) + } + + @Test + fun removeMarkedBlocks_removesMultilineBlockAndPreservesLineBreaksOutsideIt() { + val input = "avant\n${marker}\npremière ligne\ndeuxième ligne\n${marker}\naprès" + + assertEquals("avant\n\naprès", input.removeMarkedBlocks()) + } + + @Test + fun removeMarkedBlocks_removesSeveralMultilineBlocks() { + val input = "avant\n${marker}\npremier bloc\nsur deux lignes\n${marker}\nentre\n" + + "${marker}\nsecond bloc\nsur deux lignes\n${marker}\naprès" + + assertEquals("avant\n\nentre\n\naprès", input.removeMarkedBlocks()) + } + + @Test + fun removeMarkedBlocks_removesMultilineEmptyBlock() { + val input = "avant\n${marker}\n${marker}\naprès" + + assertEquals("avant\n\naprès", input.removeMarkedBlocks()) + } + + @Test + fun removeMarkedBlocks_removesBlockWithEmptyContent() { + val input = "avant${marker}${marker}après" + + assertEquals("avantaprès", input.removeMarkedBlocks()) + } + + @Test + fun removeMarkedBlocks_removesSeveralEmptyBlocks() { + val input = "avant${marker}${marker}${marker}${marker}après" + + assertEquals("avantaprès", input.removeMarkedBlocks()) + } + + @Test + fun removeMarkedBlocks_removesEmptyAndNonEmptyBlocksTogether() { + val input = "avant${marker}contenu${marker}${marker}${marker}après" + + assertEquals("avantaprès", input.removeMarkedBlocks()) + } + + @Test + fun removeMarkedBlocks_returnsEmptyStringWhenOnlyAnEmptyBlockIsPresent() { + val input = "${marker}${marker}" + + assertEquals("", input.removeMarkedBlocks()) + } + + @Test + fun removeMarkedBlocks_leavesTextWithoutBlocksUntouched() { + val input = "description normale" + + assertEquals(input, input.removeMarkedBlocks()) + } + + @Test + fun removeMarkedBlocks_leavesUnmatchedMarkerUntouched() { + val input = "avant${marker}contenu sans fermeture" + + assertEquals(input, input.removeMarkedBlocks()) + } +} + + From 32aa298f6086d2711d2306d1ce174b86e8bf8c1b Mon Sep 17 00:00:00 2001 From: Abdourahamane Boinaidi Date: Thu, 24 Sep 2026 17:10:47 +0200 Subject: [PATCH 3/5] feat: Add cleanedDescription to Event for removing marked blocks --- .../multiplatform_calendar/core/domain/model/event/Event.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/domain/model/event/Event.kt b/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/domain/model/event/Event.kt index b83d41a14..546ea3cb6 100644 --- a/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/domain/model/event/Event.kt +++ b/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/domain/model/event/Event.kt @@ -23,6 +23,7 @@ import com.infomaniak.multiplatform_calendar.core.domain.model.calendar.Calendar import com.infomaniak.multiplatform_calendar.core.domain.model.event.alarm.EventAlarm import com.infomaniak.multiplatform_calendar.core.domain.model.event.recurrence.RecurrenceScope import com.infomaniak.multiplatform_calendar.core.domain.model.event.recurrence.recurrenceScopesFor +import com.infomaniak.multiplatform_calendar.core.extensions.removeMarkedBlocks import kotlin.experimental.ExperimentalObjCRefinement import kotlin.native.HiddenFromObjC import kotlin.time.ExperimentalTime @@ -67,4 +68,9 @@ public data class Event( /** What a mutation of this event may be asked to reach, empty when there is nothing to ask. */ val recurrenceScopes: Set get() = recurrenceScopesFor(isOccurrence, canEdit) + + /** The description with all Infomaniak marked blocks removed. */ + val cleanedDescription: String + get() = description?.removeMarkedBlocks() ?: "" + } From 7b26a0a4580235db8e06304d42342ea87ba805a7 Mon Sep 17 00:00:00 2001 From: Abdourahamane Boinaidi Date: Thu, 24 Sep 2026 17:29:33 +0200 Subject: [PATCH 4/5] refactor: Simplify removeMarkedBlocks function by using the regex directly --- .../core/extensions/EventDescriptionExtensions.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensions.kt b/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensions.kt index e544e62fb..168e2417d 100644 --- a/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensions.kt +++ b/CalendarCore/src/commonMain/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensions.kt @@ -21,6 +21,5 @@ private const val MARKED_BLOCKS = "/\\*----------------------------------------\ private val MARKED_BLOCKS_REGEX = Regex("$MARKED_BLOCKS[\\s\\S]*?$MARKED_BLOCKS") internal fun String.removeMarkedBlocks(): String { - val regex = MARKED_BLOCKS_REGEX - return this.replace(regex, "").trim() + return this.replace(MARKED_BLOCKS_REGEX, "").trim() } From b47486f71b48399d8ba67135e9053a2981eb0ce5 Mon Sep 17 00:00:00 2001 From: Abdourahamane Boinaidi Date: Thu, 24 Sep 2026 17:36:45 +0200 Subject: [PATCH 5/5] refactor: Update test inputs in removeMarkedBlocks tests for clarity --- .../EventDescriptionExtensionsTest.kt | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/CalendarCore/src/commonTest/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensionsTest.kt b/CalendarCore/src/commonTest/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensionsTest.kt index 187c7a71f..1c55d788c 100644 --- a/CalendarCore/src/commonTest/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensionsTest.kt +++ b/CalendarCore/src/commonTest/kotlin/com/infomaniak/multiplatform_calendar/core/extensions/EventDescriptionExtensionsTest.kt @@ -26,59 +26,59 @@ class EventDescriptionExtensionsTest { @Test fun removeMarkedBlocks_removesOneBlockAndPreservesSurroundingText() { - val input = "avant${marker}contenu à supprimer${marker}après" + val input = "before${marker}content to remove${marker}after" - assertEquals("avantaprès", input.removeMarkedBlocks()) + assertEquals("beforeafter", input.removeMarkedBlocks()) } @Test fun removeMarkedBlocks_removesMultipleBlocks() { - val input = "avant${marker}premier${marker}milieu${marker}second${marker}après" + val input = "before${marker}first${marker}middle${marker}second${marker}after" - assertEquals("avantmilieuaprès", input.removeMarkedBlocks()) + assertEquals("beforemiddleafter", input.removeMarkedBlocks()) } @Test fun removeMarkedBlocks_removesMultilineBlockAndPreservesLineBreaksOutsideIt() { - val input = "avant\n${marker}\npremière ligne\ndeuxième ligne\n${marker}\naprès" + val input = "before\n${marker}\nfirst line\nsecond line\n${marker}\nafter" - assertEquals("avant\n\naprès", input.removeMarkedBlocks()) + assertEquals("before\n\nafter", input.removeMarkedBlocks()) } @Test fun removeMarkedBlocks_removesSeveralMultilineBlocks() { - val input = "avant\n${marker}\npremier bloc\nsur deux lignes\n${marker}\nentre\n" + - "${marker}\nsecond bloc\nsur deux lignes\n${marker}\naprès" + val input = "before\n${marker}\nfirst block\nspanning two lines\n${marker}\nbetween\n" + + "${marker}\nsecond block\nspanning two lines\n${marker}\nafter" - assertEquals("avant\n\nentre\n\naprès", input.removeMarkedBlocks()) + assertEquals("before\n\nbetween\n\nafter", input.removeMarkedBlocks()) } @Test fun removeMarkedBlocks_removesMultilineEmptyBlock() { - val input = "avant\n${marker}\n${marker}\naprès" + val input = "before\n${marker}\n${marker}\nafter" - assertEquals("avant\n\naprès", input.removeMarkedBlocks()) + assertEquals("before\n\nafter", input.removeMarkedBlocks()) } @Test fun removeMarkedBlocks_removesBlockWithEmptyContent() { - val input = "avant${marker}${marker}après" + val input = "before${marker}${marker}after" - assertEquals("avantaprès", input.removeMarkedBlocks()) + assertEquals("beforeafter", input.removeMarkedBlocks()) } @Test fun removeMarkedBlocks_removesSeveralEmptyBlocks() { - val input = "avant${marker}${marker}${marker}${marker}après" + val input = "before${marker}${marker}${marker}${marker}after" - assertEquals("avantaprès", input.removeMarkedBlocks()) + assertEquals("beforeafter", input.removeMarkedBlocks()) } @Test fun removeMarkedBlocks_removesEmptyAndNonEmptyBlocksTogether() { - val input = "avant${marker}contenu${marker}${marker}${marker}après" + val input = "before${marker}content${marker}${marker}${marker}after" - assertEquals("avantaprès", input.removeMarkedBlocks()) + assertEquals("beforeafter", input.removeMarkedBlocks()) } @Test @@ -90,14 +90,14 @@ class EventDescriptionExtensionsTest { @Test fun removeMarkedBlocks_leavesTextWithoutBlocksUntouched() { - val input = "description normale" + val input = "normal description" assertEquals(input, input.removeMarkedBlocks()) } @Test fun removeMarkedBlocks_leavesUnmatchedMarkerUntouched() { - val input = "avant${marker}contenu sans fermeture" + val input = "before${marker}content without closing marker" assertEquals(input, input.removeMarkedBlocks()) }