From 9ee9356e5f0025a7358a8dadc967fd11068042e5 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Tue, 27 Jun 2023 14:50:30 -0700 Subject: [PATCH 01/27] Add ChangelogParser --- skate-plugin/build.gradle.kts | 5 ++ .../com/slack/sgp/intellij/ChangelogParser.kt | 41 ++++++++++++ .../slack/sgp/intellij/ChangeLogParserTest.kt | 66 +++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt create mode 100644 skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt diff --git a/skate-plugin/build.gradle.kts b/skate-plugin/build.gradle.kts index 9d9857483..a8b7673bc 100644 --- a/skate-plugin/build.gradle.kts +++ b/skate-plugin/build.gradle.kts @@ -33,3 +33,8 @@ tasks { publishPlugin { token.set(System.getenv("PUBLISH_TOKEN")) } } + +dependencies { + testImplementation(libs.junit) + testImplementation(libs.truth) +} diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt new file mode 100644 index 000000000..cf652164d --- /dev/null +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt @@ -0,0 +1,41 @@ +package com.slack.sgp.intellij + +import java.time.LocalDate + +private val LOCAL_DATE_REGEX = "^\\d{4}-\\d{2}-\\d{2}\$".toRegex() +private val String.isLocalDate: Boolean + get() { + return LOCAL_DATE_REGEX.matches(this) + } + +object ChangelogParser { + fun readFile(changeLogString: String, previousEntry: LocalDate? = null): ParseResult { + /* + date format: yyyy-mm-dd + */ + + // no previous entry in the changelog + // previous entry is found, if found, check if it's the first entry or if there's other content + // since + var previous = LocalDate.now() // var means mutable + var entryCount = 0 + val changeLogSubstring = buildString { + for (line in changeLogString.lines()) { + if (line.isLocalDate) { + val localDate = LocalDate.parse(line) + if (entryCount == 0) { + previous = localDate + } + entryCount++ + if (previousEntry == previous) { + break + } else { + appendLine(line) + } + } + } + } + return ParseResult(changeLogSubstring.takeIf { entryCount > 1 }, previous) + } + data class ParseResult(val changeLogString: String?, val latestEntry: LocalDate) +} diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt new file mode 100644 index 000000000..e0bae9cad --- /dev/null +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -0,0 +1,66 @@ +package com.slack.sgp.intellij + +import com.google.common.truth.Truth.assertThat +import java.time.LocalDate +import org.junit.Test + +class ChangeLogParserTest { + @Test + fun testNoEntries() { + val (changeLogString, latestEntry) = ChangelogParser.readFile("", null) + assertThat(changeLogString).isNull() + assertThat(latestEntry).isEqualTo(LocalDate.now()) + } + + @Test + fun testNullPreviousEntry() { + val initialChangeLogString = "2023-06-27" + val (changeLogString, latestEntry) = ChangelogParser.readFile(initialChangeLogString, null) + assertThat(changeLogString).isNull() + assertThat(latestEntry).isEqualTo(LocalDate.of(2023, 6, 27)) + } + + @Test + fun testChangeLogStringIsNotNull() { + val initialChangeLogString = + """ + Changelog + ========= + + 0.9.14 + ------ + + _2023-06-25_ + + * Fix compose compiler config not applying to android projects. + + 0.9.13 + ------ + + _2023-06-24_ + + * Fix wrong map key name being used in exclusion. + + 0.9.12 + ------ + + _2023-06-24_ + + * Fix wrong dependency being used for compose-compiler in new Compose configuration overhaul. + """ + .trimIndent() + + val previous = LocalDate.of(2023, 6, 24) // This date is prior to the latest entry date + + val (changeLogString, latestEntry) = ChangelogParser.readFile(initialChangeLogString, previous) + + assertThat(changeLogString).isNull() + assertThat(latestEntry).isEqualTo(LocalDate.of(2023, 6, 25)) + } + + // TODO: + // ChangeLogString with an entry but null previous entry + // ChangeLogString with latest entry = to previous entry + // ChangeLogString where previous entry is not null, but not present in ChangeLog + // ChangeLogString where previous entry is not null, not present in ChangeLog, and +} From f9a0fcf6226bc2d93efe101d588f3f1c3a9a5eb3 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Tue, 27 Jun 2023 16:28:26 -0700 Subject: [PATCH 02/27] Write tests for ChangelogParser --- .../com/slack/sgp/intellij/ChangelogParser.kt | 14 ++++--- .../slack/sgp/intellij/ChangeLogParserTest.kt | 41 ++++++++++++++++++- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt index cf652164d..8864fc3c8 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt @@ -20,22 +20,24 @@ object ChangelogParser { var previous = LocalDate.now() // var means mutable var entryCount = 0 val changeLogSubstring = buildString { + var shouldAppend = previousEntry == null for (line in changeLogString.lines()) { if (line.isLocalDate) { - val localDate = LocalDate.parse(line) - if (entryCount == 0) { + val localDate = LocalDate.parse(line.trim()) + if (localDate == previousEntry) { + shouldAppend = true + } + if (entryCount == 0 || (previous != null && localDate.isAfter(previous))) { previous = localDate } entryCount++ - if (previousEntry == previous) { - break - } else { + if (shouldAppend) { appendLine(line) } } } } - return ParseResult(changeLogSubstring.takeIf { entryCount > 1 }, previous) + return ParseResult(changeLogSubstring.takeIf { it.isNotBlank() }, previous) } data class ParseResult(val changeLogString: String?, val latestEntry: LocalDate) } diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index e0bae9cad..e85448d33 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -30,7 +30,7 @@ class ChangeLogParserTest { 0.9.14 ------ - _2023-06-25_ + _2023-06-25_ * Fix compose compiler config not applying to android projects. @@ -58,6 +58,45 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(LocalDate.of(2023, 6, 25)) } + @Test + fun testChangeLogStringWithNonexistentPreviousEntry() { + val initialChangeLogString = + """ + Changelog + ========= + + 0.9.14 + ------ + + _2023-06-25_ + + * Fix compose compiler config not applying to android projects. + + 0.9.13 + ------ + + _2023-06-24_ + + * Fix wrong map key name being used in exclusion. + + 0.9.12 + ------ + + _2023-06-24_ + + * Fix wrong dependency being used for compose-compiler in new Compose configuration overhaul. + """ + .trimIndent() + + val previousEntry = LocalDate.of(2023, 6, 24) + + val (changeLogString, latestEntry) = + ChangelogParser.readFile(initialChangeLogString, previousEntry) + + assertThat(changeLogString).isNotNull() + assertThat(latestEntry).isEqualTo(LocalDate.of(2023, 6, 25)) + } + // TODO: // ChangeLogString with an entry but null previous entry // ChangeLogString with latest entry = to previous entry From cb8dab5b04b636faa50212ae92feebdea8da047f Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 28 Jun 2023 12:58:03 -0700 Subject: [PATCH 03/27] More test cases with changelog and previous latest entry components. --- .../com/slack/sgp/intellij/ChangelogParser.kt | 49 +++++---- .../slack/sgp/intellij/ChangeLogParserTest.kt | 99 +++++-------------- 2 files changed, 58 insertions(+), 90 deletions(-) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt index 8864fc3c8..cb9c538b0 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt @@ -17,27 +17,42 @@ object ChangelogParser { // no previous entry in the changelog // previous entry is found, if found, check if it's the first entry or if there's other content // since - var previous = LocalDate.now() // var means mutable + var previous: LocalDate? = null + var breakOnNextDate = false var entryCount = 0 - val changeLogSubstring = buildString { - var shouldAppend = previousEntry == null - for (line in changeLogString.lines()) { - if (line.isLocalDate) { - val localDate = LocalDate.parse(line.trim()) - if (localDate == previousEntry) { - shouldAppend = true + val changeLogSubstring = + buildString { + var currentBlock = StringBuilder() + for (line in changeLogString.lines()) { + if (line.isLocalDate) { + val localDate = LocalDate.parse(line) + if (breakOnNextDate) { + break + } + if (localDate == previousEntry) { + break + } + if (previous != null) { + append(currentBlock.toString()) + } + currentBlock = StringBuilder() + if (previous == null) { + previous = localDate + } + if (localDate == previousEntry) { + breakOnNextDate = true + } else { + entryCount++ + } + } + currentBlock.appendLine(line) } - if (entryCount == 0 || (previous != null && localDate.isAfter(previous))) { - previous = localDate - } - entryCount++ - if (shouldAppend) { - appendLine(line) + if (entryCount == 0) { + append(currentBlock.toString()) } } - } - } - return ParseResult(changeLogSubstring.takeIf { it.isNotBlank() }, previous) + .trimEnd() + return ParseResult(changeLogSubstring.takeIf { it.isNotBlank() }, previous ?: LocalDate.now()) } data class ParseResult(val changeLogString: String?, val latestEntry: LocalDate) } diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index e85448d33..0f05f826e 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -13,88 +13,41 @@ class ChangeLogParserTest { } @Test - fun testNullPreviousEntry() { - val initialChangeLogString = "2023-06-27" - val (changeLogString, latestEntry) = ChangelogParser.readFile(initialChangeLogString, null) + fun testSingleEntryNullPreviousEntry() { + val input = "2023-06-28\nBug fixes\nNew features" + val expectedDate = LocalDate.of(2023, 6, 28) + val (changeLogString, latestEntry) = ChangelogParser.readFile(input, null) assertThat(changeLogString).isNull() - assertThat(latestEntry).isEqualTo(LocalDate.of(2023, 6, 27)) + assertThat(latestEntry).isEqualTo(expectedDate) } @Test - fun testChangeLogStringIsNotNull() { - val initialChangeLogString = - """ - Changelog - ========= - - 0.9.14 - ------ - - _2023-06-25_ - - * Fix compose compiler config not applying to android projects. - - 0.9.13 - ------ - - _2023-06-24_ - - * Fix wrong map key name being used in exclusion. - - 0.9.12 - ------ - - _2023-06-24_ - - * Fix wrong dependency being used for compose-compiler in new Compose configuration overhaul. - """ - .trimIndent() - - val previous = LocalDate.of(2023, 6, 24) // This date is prior to the latest entry date - - val (changeLogString, latestEntry) = ChangelogParser.readFile(initialChangeLogString, previous) + fun testMultipleEntriesNullPreviousEntry() { + val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" + val expectedDate = LocalDate.of(2023, 6, 28) + val expectedString = "2023-06-28\nBug fixes\nNew features" + val (changeLogString, latestEntry) = ChangelogParser.readFile(input, null) + assertThat(changeLogString).isEqualTo(expectedString) + assertThat(latestEntry).isEqualTo(expectedDate) + } + @Test + fun testPreviousEntrySameAsLatest() { + val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" + val expectedDate = LocalDate.of(2023, 6, 28) + val (changeLogString, latestEntry) = ChangelogParser.readFile(input, LocalDate.of(2023, 6, 28)) assertThat(changeLogString).isNull() - assertThat(latestEntry).isEqualTo(LocalDate.of(2023, 6, 25)) + assertThat(latestEntry).isEqualTo(expectedDate) } @Test - fun testChangeLogStringWithNonexistentPreviousEntry() { - val initialChangeLogString = - """ - Changelog - ========= - - 0.9.14 - ------ - - _2023-06-25_ - - * Fix compose compiler config not applying to android projects. - - 0.9.13 - ------ - - _2023-06-24_ - - * Fix wrong map key name being used in exclusion. - - 0.9.12 - ------ - - _2023-06-24_ - - * Fix wrong dependency being used for compose-compiler in new Compose configuration overhaul. - """ - .trimIndent() - - val previousEntry = LocalDate.of(2023, 6, 24) - - val (changeLogString, latestEntry) = - ChangelogParser.readFile(initialChangeLogString, previousEntry) - - assertThat(changeLogString).isNotNull() - assertThat(latestEntry).isEqualTo(LocalDate.of(2023, 6, 25)) + fun testPreviousEntryNotInChangeLog() { + val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" + val expectedDate = LocalDate.of(2023, 6, 28) + val expectedString = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" + val (changeLogString, latestEntry) = ChangelogParser.readFile(input, LocalDate.of(2023, 6, 29)) + assertThat(changeLogString).isEqualTo(expectedString) + assertThat(latestEntry).isEqualTo(expectedDate) } // TODO: From a8bdc6ba7493cba8a4e3a2b02886a7afa85bb79d Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 28 Jun 2023 14:30:49 -0700 Subject: [PATCH 04/27] Test case for if previous entry is not null but present in the changelog --- .../com/slack/sgp/intellij/ChangelogParser.kt | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt index cb9c538b0..756c037c0 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt @@ -17,8 +17,13 @@ object ChangelogParser { // no previous entry in the changelog // previous entry is found, if found, check if it's the first entry or if there's other content // since + if (previousEntry != null && !changeLogString.contains(previousEntry.toString())) { + return ParseResult( + changeLogString, + LocalDate.parse(changeLogString.lines().firstOrNull { it.isLocalDate }) + ) + } var previous: LocalDate? = null - var breakOnNextDate = false var entryCount = 0 val changeLogSubstring = buildString { @@ -26,9 +31,6 @@ object ChangelogParser { for (line in changeLogString.lines()) { if (line.isLocalDate) { val localDate = LocalDate.parse(line) - if (breakOnNextDate) { - break - } if (localDate == previousEntry) { break } @@ -39,11 +41,7 @@ object ChangelogParser { if (previous == null) { previous = localDate } - if (localDate == previousEntry) { - breakOnNextDate = true - } else { - entryCount++ - } + entryCount++ } currentBlock.appendLine(line) } From 9a0490633c2ef360bf4336a051de1de2d203632f Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 28 Jun 2023 14:57:32 -0700 Subject: [PATCH 05/27] Add comments --- .../kotlin/com/slack/sgp/intellij/ChangelogParser.kt | 3 --- .../com/slack/sgp/intellij/ChangeLogParserTest.kt | 11 +++++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt index 756c037c0..abc7caf6b 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt @@ -14,9 +14,6 @@ object ChangelogParser { date format: yyyy-mm-dd */ - // no previous entry in the changelog - // previous entry is found, if found, check if it's the first entry or if there's other content - // since if (previousEntry != null && !changeLogString.contains(previousEntry.toString())) { return ParseResult( changeLogString, diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index 0f05f826e..fbdad4c1d 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -5,6 +5,7 @@ import java.time.LocalDate import org.junit.Test class ChangeLogParserTest { + // test with no entries and null changelogstring @Test fun testNoEntries() { val (changeLogString, latestEntry) = ChangelogParser.readFile("", null) @@ -12,6 +13,7 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(LocalDate.now()) } + // test with one entry, no previous entry @Test fun testSingleEntryNullPreviousEntry() { val input = "2023-06-28\nBug fixes\nNew features" @@ -21,6 +23,7 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(expectedDate) } + // test with mutliple entries, and no previous entry @Test fun testMultipleEntriesNullPreviousEntry() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" @@ -31,6 +34,7 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(expectedDate) } + // test with multiple entries, where the previous is the same as the latest @Test fun testPreviousEntrySameAsLatest() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" @@ -40,6 +44,7 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(expectedDate) } + // test with a previous entry not in the change log @Test fun testPreviousEntryNotInChangeLog() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" @@ -49,10 +54,4 @@ class ChangeLogParserTest { assertThat(changeLogString).isEqualTo(expectedString) assertThat(latestEntry).isEqualTo(expectedDate) } - - // TODO: - // ChangeLogString with an entry but null previous entry - // ChangeLogString with latest entry = to previous entry - // ChangeLogString where previous entry is not null, but not present in ChangeLog - // ChangeLogString where previous entry is not null, not present in ChangeLog, and } From 4ff0d3c11ec8f8b5c94b071fe7b66b136f37a4f3 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 28 Jun 2023 14:57:56 -0700 Subject: [PATCH 06/27] spotless Apply --- .../com/slack/sgp/intellij/ChangelogParser.kt | 16 ++++++++++++++++ .../slack/sgp/intellij/ChangeLogParserTest.kt | 15 +++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt index abc7caf6b..723afef6f 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2023 Slack Technologies, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.slack.sgp.intellij import java.time.LocalDate @@ -49,5 +64,6 @@ object ChangelogParser { .trimEnd() return ParseResult(changeLogSubstring.takeIf { it.isNotBlank() }, previous ?: LocalDate.now()) } + data class ParseResult(val changeLogString: String?, val latestEntry: LocalDate) } diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index fbdad4c1d..ce9822cec 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2023 Slack Technologies, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.slack.sgp.intellij import com.google.common.truth.Truth.assertThat From 7839e035b67b683094bfa4dcf9fb15cef58fd676 Mon Sep 17 00:00:00 2001 From: Katherine Liu <67719108+kateliu20@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:10:35 -0700 Subject: [PATCH 07/27] Update ChangeLogParser.kt readability. Co-authored-by: Zac Sweers --- .../src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt index 723afef6f..f85a03de0 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt @@ -35,6 +35,7 @@ object ChangelogParser { LocalDate.parse(changeLogString.lines().firstOrNull { it.isLocalDate }) ) } + var previous: LocalDate? = null var entryCount = 0 val changeLogSubstring = From e88b058c696153f8fd6b9284b84e460aa77a3716 Mon Sep 17 00:00:00 2001 From: Katherine Liu <67719108+kateliu20@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:15:29 -0700 Subject: [PATCH 08/27] Using Raw Strings for readability Co-authored-by: Zac Sweers --- .../kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index ce9822cec..2f9dc8b6c 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -31,7 +31,13 @@ class ChangeLogParserTest { // test with one entry, no previous entry @Test fun testSingleEntryNullPreviousEntry() { - val input = "2023-06-28\nBug fixes\nNew features" + val input = """ + 2023-06-28 + ---------- + + - Bug fixes + - New features + """.trimIndent() val expectedDate = LocalDate.of(2023, 6, 28) val (changeLogString, latestEntry) = ChangelogParser.readFile(input, null) assertThat(changeLogString).isNull() From c7bcdaf192b9c9aba864d8e294b7e92b4f613d0b Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 28 Jun 2023 15:25:49 -0700 Subject: [PATCH 09/27] merge comments and function names --- .../com/slack/sgp/intellij/ChangeLogParserTest.kt | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index ce9822cec..8666cd779 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -20,17 +20,15 @@ import java.time.LocalDate import org.junit.Test class ChangeLogParserTest { - // test with no entries and null changelogstring @Test - fun testNoEntries() { + fun `test with no entries and null changelogstring`() { val (changeLogString, latestEntry) = ChangelogParser.readFile("", null) assertThat(changeLogString).isNull() assertThat(latestEntry).isEqualTo(LocalDate.now()) } - // test with one entry, no previous entry @Test - fun testSingleEntryNullPreviousEntry() { + fun `test with one entry, no previous entry`() { val input = "2023-06-28\nBug fixes\nNew features" val expectedDate = LocalDate.of(2023, 6, 28) val (changeLogString, latestEntry) = ChangelogParser.readFile(input, null) @@ -38,9 +36,8 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(expectedDate) } - // test with mutliple entries, and no previous entry @Test - fun testMultipleEntriesNullPreviousEntry() { + fun `test with mutliple entries, and no previous entry`() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" val expectedDate = LocalDate.of(2023, 6, 28) val expectedString = "2023-06-28\nBug fixes\nNew features" @@ -49,9 +46,8 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(expectedDate) } - // test with multiple entries, where the previous is the same as the latest @Test - fun testPreviousEntrySameAsLatest() { + fun `test with multiple entries, where the previous is the same as the latest`() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" val expectedDate = LocalDate.of(2023, 6, 28) val (changeLogString, latestEntry) = ChangelogParser.readFile(input, LocalDate.of(2023, 6, 28)) @@ -59,9 +55,8 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(expectedDate) } - // test with a previous entry not in the change log @Test - fun testPreviousEntryNotInChangeLog() { + fun `test with a previous entry not in the change log`() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" val expectedDate = LocalDate.of(2023, 6, 28) val expectedString = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" From 0e4f3bedf9c7d6b8bec5eef44b846d536c791d88 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 28 Jun 2023 15:27:37 -0700 Subject: [PATCH 10/27] merge comments and function names --- .../com/slack/sgp/intellij/ChangeLogParserTest.kt | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index ce9822cec..8666cd779 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -20,17 +20,15 @@ import java.time.LocalDate import org.junit.Test class ChangeLogParserTest { - // test with no entries and null changelogstring @Test - fun testNoEntries() { + fun `test with no entries and null changelogstring`() { val (changeLogString, latestEntry) = ChangelogParser.readFile("", null) assertThat(changeLogString).isNull() assertThat(latestEntry).isEqualTo(LocalDate.now()) } - // test with one entry, no previous entry @Test - fun testSingleEntryNullPreviousEntry() { + fun `test with one entry, no previous entry`() { val input = "2023-06-28\nBug fixes\nNew features" val expectedDate = LocalDate.of(2023, 6, 28) val (changeLogString, latestEntry) = ChangelogParser.readFile(input, null) @@ -38,9 +36,8 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(expectedDate) } - // test with mutliple entries, and no previous entry @Test - fun testMultipleEntriesNullPreviousEntry() { + fun `test with mutliple entries, and no previous entry`() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" val expectedDate = LocalDate.of(2023, 6, 28) val expectedString = "2023-06-28\nBug fixes\nNew features" @@ -49,9 +46,8 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(expectedDate) } - // test with multiple entries, where the previous is the same as the latest @Test - fun testPreviousEntrySameAsLatest() { + fun `test with multiple entries, where the previous is the same as the latest`() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" val expectedDate = LocalDate.of(2023, 6, 28) val (changeLogString, latestEntry) = ChangelogParser.readFile(input, LocalDate.of(2023, 6, 28)) @@ -59,9 +55,8 @@ class ChangeLogParserTest { assertThat(latestEntry).isEqualTo(expectedDate) } - // test with a previous entry not in the change log @Test - fun testPreviousEntryNotInChangeLog() { + fun `test with a previous entry not in the change log`() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" val expectedDate = LocalDate.of(2023, 6, 28) val expectedString = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" From 06f3b2a13a569913c74020cb88ebd3897c0df6b8 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 28 Jun 2023 15:30:03 -0700 Subject: [PATCH 11/27] remove the word "test" in all the test functions --- .../kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index 8666cd779..be6736e88 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -21,14 +21,14 @@ import org.junit.Test class ChangeLogParserTest { @Test - fun `test with no entries and null changelogstring`() { + fun `no entries and null changelogstring`() { val (changeLogString, latestEntry) = ChangelogParser.readFile("", null) assertThat(changeLogString).isNull() assertThat(latestEntry).isEqualTo(LocalDate.now()) } @Test - fun `test with one entry, no previous entry`() { + fun `one entry, no previous entries`() { val input = "2023-06-28\nBug fixes\nNew features" val expectedDate = LocalDate.of(2023, 6, 28) val (changeLogString, latestEntry) = ChangelogParser.readFile(input, null) @@ -37,7 +37,7 @@ class ChangeLogParserTest { } @Test - fun `test with mutliple entries, and no previous entry`() { + fun `mutliple entries, and no previous entries`() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" val expectedDate = LocalDate.of(2023, 6, 28) val expectedString = "2023-06-28\nBug fixes\nNew features" @@ -47,7 +47,7 @@ class ChangeLogParserTest { } @Test - fun `test with multiple entries, where the previous is the same as the latest`() { + fun `multiple entries, where the previous is the same as the latest`() { val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" val expectedDate = LocalDate.of(2023, 6, 28) val (changeLogString, latestEntry) = ChangelogParser.readFile(input, LocalDate.of(2023, 6, 28)) From 37f1b004893743472769c57b51e495f7f8c109da Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 28 Jun 2023 15:36:42 -0700 Subject: [PATCH 12/27] Documentation added for ChangelogParser --- .../com/slack/sgp/intellij/ChangelogParser.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt index f85a03de0..72554d6d4 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ChangelogParser.kt @@ -17,6 +17,20 @@ package com.slack.sgp.intellij import java.time.LocalDate +/** + * Reads a CHANGELOG.md file and produces a subset of it, based on a specified previous entry date. + * + * If a previous entry date is supplied and is found within the changelog, this function will return + * a new changelog that contains entries only up to and including this date. If the previous entry + * date is not found, the entire changelog is returned. + * + * @param changeLogString The entire changelog, as a string, where each entry is expected to start + * with a date line. + * @param previousEntry The date of the previous entry. Can be null, in which case the entire + * changelog is returned. + * @return A ParseResult object containing the filtered changelog string and the date of the latest + * entry. + */ private val LOCAL_DATE_REGEX = "^\\d{4}-\\d{2}-\\d{2}\$".toRegex() private val String.isLocalDate: Boolean get() { From 8bd4eea9b633d312d18d8a05c4c8eb10f3df7dfb Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 28 Jun 2023 15:57:20 -0700 Subject: [PATCH 13/27] Raw Strings --- .../slack/sgp/intellij/ChangeLogParserTest.kt | 75 +++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index be6736e88..74d1251cb 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -29,7 +29,19 @@ class ChangeLogParserTest { @Test fun `one entry, no previous entries`() { - val input = "2023-06-28\nBug fixes\nNew features" + val input = + """ + 2023-06-28 + + * Bug fixes + * New features + + 2023-06-27 + + * Other changes + """ + .trimIndent() + val expectedDate = LocalDate.of(2023, 6, 28) val (changeLogString, latestEntry) = ChangelogParser.readFile(input, null) assertThat(changeLogString).isNull() @@ -38,9 +50,27 @@ class ChangeLogParserTest { @Test fun `mutliple entries, and no previous entries`() { - val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" + val input = + """ + 2023-06-28 + + * Bug fixes + * New features + + 2023-06-27 + + * Other changes + """ + .trimIndent() val expectedDate = LocalDate.of(2023, 6, 28) - val expectedString = "2023-06-28\nBug fixes\nNew features" + val expectedString = + """ + 2023-06-28 + + * Bug fixes + * New features + """ + .trimIndent() val (changeLogString, latestEntry) = ChangelogParser.readFile(input, null) assertThat(changeLogString).isEqualTo(expectedString) assertThat(latestEntry).isEqualTo(expectedDate) @@ -48,7 +78,18 @@ class ChangeLogParserTest { @Test fun `multiple entries, where the previous is the same as the latest`() { - val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" + val input = + """ + 2023-06-28 + + * Bug fixes + * New features + + 2023-06-27 + + * Other changes + """ + .trimIndent() val expectedDate = LocalDate.of(2023, 6, 28) val (changeLogString, latestEntry) = ChangelogParser.readFile(input, LocalDate.of(2023, 6, 28)) assertThat(changeLogString).isNull() @@ -57,9 +98,31 @@ class ChangeLogParserTest { @Test fun `test with a previous entry not in the change log`() { - val input = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" + val input = + """ + 2023-06-28 + + * Bug fixes + * New features + + 2023-06-27 + + * Other changes + """ + .trimIndent() val expectedDate = LocalDate.of(2023, 6, 28) - val expectedString = "2023-06-28\nBug fixes\nNew features\n2023-06-27\nOther changes" + val expectedString = + """ + 2023-06-28 + + * Bug fixes + * New features + + 2023-06-27 + + * Other changes + """ + .trimIndent() val (changeLogString, latestEntry) = ChangelogParser.readFile(input, LocalDate.of(2023, 6, 29)) assertThat(changeLogString).isEqualTo(expectedString) assertThat(latestEntry).isEqualTo(expectedDate) From af7b9b5e7c56c60083b613de466f5b233aa7dbe7 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 28 Jun 2023 16:23:11 -0700 Subject: [PATCH 14/27] Change test for one entry but no previous entries, original input had two entries --- .../test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index 74d1251cb..24eee344c 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -35,10 +35,6 @@ class ChangeLogParserTest { * Bug fixes * New features - - 2023-06-27 - - * Other changes """ .trimIndent() From 25b11bd263e33fc3d1a6c0db747415c21e3d2232 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Thu, 29 Jun 2023 15:35:31 -0700 Subject: [PATCH 15/27] allow for html parsing --- skate-plugin/build.gradle.kts | 7 ++++- .../com/slack/sgp/intellij/SkateService.kt | 5 ++-- .../sgp/intellij/WhatsNewPanelFactory.kt | 30 ++++++++++++++----- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/skate-plugin/build.gradle.kts b/skate-plugin/build.gradle.kts index a8b7673bc..42b4610dc 100644 --- a/skate-plugin/build.gradle.kts +++ b/skate-plugin/build.gradle.kts @@ -16,7 +16,7 @@ intellij { version.set("2022.2.5") type.set("IC") // Target IDE Platform - plugins.set(listOf(/* Plugin Dependencies */ )) + plugins.set(listOf("org.intellij.plugins.markdown")) } tasks { @@ -37,4 +37,9 @@ tasks { dependencies { testImplementation(libs.junit) testImplementation(libs.truth) + implementation("com.vladsch.flexmark:flexmark-all:0.64.8") +} +java { + sourceCompatibility = JavaVersion.VERSION_19 + targetCompatibility = JavaVersion.VERSION_19 } diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt index 0679fff2d..fadb17fde 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt @@ -31,14 +31,13 @@ class SkateProjectServiceImpl(private val project: Project) : SkateProjectServic override fun showWhatsNewWindow() { // TODO - // Make the file configurable? // Only show when changed // Only show latest changes val settings = project.service() if (!settings.isWhatsNewEnabled) return val projectDir = project.guessProjectDir() ?: return val changeLogFile = VfsUtil.findRelativeFile(projectDir, settings.whatsNewFilePath) ?: return - val changeLogString = VfsUtil.loadText(changeLogFile) + // val changeLogString = VfsUtil.loadText(changeLogFile) val toolWindowManager = ToolWindowManager.getInstance(project) toolWindowManager.invokeLater { val toolWindow = @@ -46,7 +45,7 @@ class SkateProjectServiceImpl(private val project: Project) : SkateProjectServic stripeTitle = Supplier { "What's New in Slack!" } anchor = ToolWindowAnchor.RIGHT } - WhatsNewPanelFactory().createToolWindowContent(toolWindow, changeLogString) + WhatsNewPanelFactory().createToolWindowContent(toolWindow, changeLogFile) toolWindow.show() } } diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt index 63a52a2aa..c95b884fa 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt @@ -16,36 +16,50 @@ package com.slack.sgp.intellij import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.wm.ToolWindow import com.intellij.ui.content.ContentFactory +import com.vladsch.flexmark.html.HtmlRenderer +import com.vladsch.flexmark.parser.Parser +import com.vladsch.flexmark.util.data.MutableDataSet import java.awt.BorderLayout import javax.swing.BorderFactory import javax.swing.JButton -import javax.swing.JLabel +import javax.swing.JEditorPane import javax.swing.JPanel +import javax.swing.JScrollPane class WhatsNewPanelFactory : DumbAware { - fun createToolWindowContent(toolWindow: ToolWindow, projectName: String) { - val toolWindowContent = WhatsNewPanelContent(toolWindow, projectName) + fun createToolWindowContent(toolWindow: ToolWindow, markdownFile: VirtualFile) { + val toolWindowContent = WhatsNewPanelContent(toolWindow, markdownFile) val content = ContentFactory.getInstance().createContent(toolWindowContent.contentPanel, "", false) toolWindow.contentManager.addContent(content) } - private class WhatsNewPanelContent(toolWindow: ToolWindow, projectName: String) { + private class WhatsNewPanelContent(toolWindow: ToolWindow, markdownFile: VirtualFile) { val contentPanel: JPanel = JPanel().apply { layout = BorderLayout(0, 20) border = BorderFactory.createEmptyBorder(40, 0, 0, 0) - add(createControlsPanel(toolWindow, projectName), BorderLayout.CENTER) + add(createControlsPanel(toolWindow, markdownFile), BorderLayout.CENTER) } - private fun createControlsPanel(toolWindow: ToolWindow, projectName: String): JPanel { + private fun createControlsPanel(toolWindow: ToolWindow, markdownFile: VirtualFile): JPanel { + val options = MutableDataSet() + val parser = Parser.builder(options).build() + val renderer = HtmlRenderer.builder(options).build() + val markdownContent = String(markdownFile.contentsToByteArray(), Charsets.UTF_8) + val document = parser.parse(markdownContent) + val htmlContent = renderer.render(document) + println(htmlContent) return JPanel().apply { - val helloWorldLabel = JLabel(projectName) - add(helloWorldLabel) + val markdownDisplay = JEditorPane("text/html", htmlContent) + markdownDisplay.isEditable = false + val scrollPane = JScrollPane(markdownDisplay) + add(scrollPane) val hideToolWindowButton = JButton("Hide") hideToolWindowButton.addActionListener { toolWindow.hide(null) } From b1bd591fc3d5fa630b013f9f734428638d7fe4ff Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Thu, 29 Jun 2023 16:40:52 -0700 Subject: [PATCH 16/27] change css for markdown file --- .../sgp/intellij/WhatsNewPanelFactory.kt | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt index c95b884fa..239c36743 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt @@ -23,11 +23,15 @@ import com.vladsch.flexmark.html.HtmlRenderer import com.vladsch.flexmark.parser.Parser import com.vladsch.flexmark.util.data.MutableDataSet import java.awt.BorderLayout +import java.awt.Color import javax.swing.BorderFactory import javax.swing.JButton import javax.swing.JEditorPane import javax.swing.JPanel import javax.swing.JScrollPane +import javax.swing.ScrollPaneConstants +import javax.swing.text.html.HTMLEditorKit +import javax.swing.text.html.StyleSheet class WhatsNewPanelFactory : DumbAware { fun createToolWindowContent(toolWindow: ToolWindow, markdownFile: VirtualFile) { @@ -56,14 +60,51 @@ class WhatsNewPanelFactory : DumbAware { val htmlContent = renderer.render(document) println(htmlContent) return JPanel().apply { - val markdownDisplay = JEditorPane("text/html", htmlContent) + layout = BorderLayout() + val markdownDisplay = + JEditorPane("text/html", htmlContent).apply { + isEditable = false + background = Color(0x333333) // set to a dark color + contentType = "text/html" + editorKit = HTMLEditorKit().apply { styleSheet = styleSheet } + } + markdownDisplay.contentType = "text/html" + + // Enable text wrapping + // Enable text wrapping + val styleSheet = StyleSheet() + styleSheet.addRule( + "body { width: fit-content; font-family: Arial; line-height: 1.6; color: #FFF; }" + ) + styleSheet.addRule("h1, h2, h3, h4, h5, h6 { color: #FFF; margin-top: 20px; }") + styleSheet.addRule("h1 { font-size: 1.6em; }") + styleSheet.addRule("h2 { font-size: 1.4em; }") + styleSheet.addRule("h3 { font-size: 1.2em; }") + styleSheet.addRule("li { margin: 5px 0; }") + + val htmlEditorKit = HTMLEditorKit() + htmlEditorKit.styleSheet = styleSheet + markdownDisplay.editorKit = htmlEditorKit + + markdownDisplay.text = htmlContent markdownDisplay.isEditable = false - val scrollPane = JScrollPane(markdownDisplay) - add(scrollPane) + val scrollPane = + JScrollPane(markdownDisplay).apply { + horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER + } + add(scrollPane, BorderLayout.CENTER) val hideToolWindowButton = JButton("Hide") hideToolWindowButton.addActionListener { toolWindow.hide(null) } - add(hideToolWindowButton) + add(hideToolWindowButton, BorderLayout.PAGE_END) + // val markdownDisplay = JEditorPane("text/html", htmlContent).apply { isEditable = + // false } + // val scrollPane = JScrollPane(markdownDisplay) + // add(scrollPane, BorderLayout.CENTER) + // + // val hideToolWindowButton = JButton("Hide") + // hideToolWindowButton.addActionListener { toolWindow.hide(null) } + // add(hideToolWindowButton, BorderLayout.PAGE_END) } } } From 4068600ad5a2a97c48263ad766ac12a7e2fefad0 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Thu, 29 Jun 2023 16:58:38 -0700 Subject: [PATCH 17/27] add italics --- .../main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt index 239c36743..c85f02822 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt @@ -57,6 +57,7 @@ class WhatsNewPanelFactory : DumbAware { val markdownContent = String(markdownFile.contentsToByteArray(), Charsets.UTF_8) val document = parser.parse(markdownContent) + val htmlContent = renderer.render(document) println(htmlContent) return JPanel().apply { @@ -81,6 +82,7 @@ class WhatsNewPanelFactory : DumbAware { styleSheet.addRule("h2 { font-size: 1.4em; }") styleSheet.addRule("h3 { font-size: 1.2em; }") styleSheet.addRule("li { margin: 5px 0; }") + styleSheet.addRule("em { font-style: italic; }") val htmlEditorKit = HTMLEditorKit() htmlEditorKit.styleSheet = styleSheet From cad5bbac45607767a9b4087ea00dc08046323d35 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 5 Jul 2023 11:06:44 -0700 Subject: [PATCH 18/27] markdown style with css style sheet rules --- .../com/slack/sgp/intellij/SkateService.kt | 1 + .../slack/sgp/intellij/ui/MarkdownStyle.kt | 64 +++++++++++++++++++ .../intellij/{ => ui}/WhatsNewPanelFactory.kt | 38 ++++------- 3 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt rename skate-plugin/src/main/kotlin/com/slack/sgp/intellij/{ => ui}/WhatsNewPanelFactory.kt (68%) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt index fadb17fde..8e5dc4043 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.project.guessProjectDir import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.wm.ToolWindowAnchor import com.intellij.openapi.wm.ToolWindowManager +import com.slack.sgp.intellij.ui.WhatsNewPanelFactory import java.util.function.Supplier interface SkateProjectService { diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt new file mode 100644 index 000000000..e9d3ca502 --- /dev/null +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 Slack Technologies, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.slack.sgp.intellij.ui + +import javax.swing.text.html.StyleSheet + +object MarkdownStyle { + fun createStyleSheet(): StyleSheet { + val rules = + listOf( + /* General body text */ + "body { width: fit-content; font-family: Arial, sans-serif; line-height: 1.6; color: #FFF; padding: 10px; }", + + /* Headings */ + "h1, h2, h3, h4, h5, h6 { margin-top: 20px; margin-bottom: 10px; }", + "h1 { font-size: 2.5em; font-weight: bold}", + "h2 { font-size: 1.5em; font-weight: bold}", + "h3 { font-size: 1.17em; }", + "h4 { font-size: 1.12em; }", + "h5 { font-size: .83em; }", + "h6 { font-size: .75em; }", + + /* Links */ + "a { color: #0366d6; }", + + /* Blockquotes */ + "blockquote { padding: 0 1em; color: #6a737d; border-left: .25em solid #dfe2e5; }", + + /* Code blocks and inline code */ + "pre, code { font-family: monospace; }", + "pre { padding: 16px; overflow: auto; line-height: 1.45; background-color: #f6f8fa; border-radius: 6px; }", + + /* Lists - not rendering properly */ + "ul li, ol li { margin-left: 2em; }", + + /* Images */ + "img { max-width: 100%; }", + + /*Italics*/ + "em { font-style: italic; }", + + /* Tables */ + "table { border-collapse: collapse; width: 100%; }", + "table th, table td { padding: 6px 13px; border: 1px solid #dfe2e5; }", + "table th { font-weight: bold; }", + "table tr:nth-child(2n) { background-color: #f6f8fa; }", + ) + + return StyleSheet().apply { rules.forEach { rule -> addRule(rule) } } + } +} diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt similarity index 68% rename from skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt rename to skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt index c85f02822..a0a535aeb 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/WhatsNewPanelFactory.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.slack.sgp.intellij +package com.slack.sgp.intellij.ui import com.intellij.openapi.project.DumbAware import com.intellij.openapi.vfs.VirtualFile @@ -25,32 +25,30 @@ import com.vladsch.flexmark.util.data.MutableDataSet import java.awt.BorderLayout import java.awt.Color import javax.swing.BorderFactory -import javax.swing.JButton import javax.swing.JEditorPane import javax.swing.JPanel import javax.swing.JScrollPane import javax.swing.ScrollPaneConstants import javax.swing.text.html.HTMLEditorKit -import javax.swing.text.html.StyleSheet class WhatsNewPanelFactory : DumbAware { fun createToolWindowContent(toolWindow: ToolWindow, markdownFile: VirtualFile) { - val toolWindowContent = WhatsNewPanelContent(toolWindow, markdownFile) + val toolWindowContent = WhatsNewPanelContent(markdownFile) val content = ContentFactory.getInstance().createContent(toolWindowContent.contentPanel, "", false) toolWindow.contentManager.addContent(content) } - private class WhatsNewPanelContent(toolWindow: ToolWindow, markdownFile: VirtualFile) { + private class WhatsNewPanelContent(markdownFile: VirtualFile) { val contentPanel: JPanel = JPanel().apply { layout = BorderLayout(0, 20) border = BorderFactory.createEmptyBorder(40, 0, 0, 0) - add(createControlsPanel(toolWindow, markdownFile), BorderLayout.CENTER) + add(createControlsPanel(markdownFile), BorderLayout.CENTER) } - private fun createControlsPanel(toolWindow: ToolWindow, markdownFile: VirtualFile): JPanel { + private fun createControlsPanel(markdownFile: VirtualFile): JPanel { val options = MutableDataSet() val parser = Parser.builder(options).build() val renderer = HtmlRenderer.builder(options).build() @@ -67,38 +65,24 @@ class WhatsNewPanelFactory : DumbAware { isEditable = false background = Color(0x333333) // set to a dark color contentType = "text/html" - editorKit = HTMLEditorKit().apply { styleSheet = styleSheet } } - markdownDisplay.contentType = "text/html" - - // Enable text wrapping - // Enable text wrapping - val styleSheet = StyleSheet() - styleSheet.addRule( - "body { width: fit-content; font-family: Arial; line-height: 1.6; color: #FFF; }" - ) - styleSheet.addRule("h1, h2, h3, h4, h5, h6 { color: #FFF; margin-top: 20px; }") - styleSheet.addRule("h1 { font-size: 1.6em; }") - styleSheet.addRule("h2 { font-size: 1.4em; }") - styleSheet.addRule("h3 { font-size: 1.2em; }") - styleSheet.addRule("li { margin: 5px 0; }") - styleSheet.addRule("em { font-style: italic; }") val htmlEditorKit = HTMLEditorKit() - htmlEditorKit.styleSheet = styleSheet + htmlEditorKit.styleSheet = MarkdownStyle.createStyleSheet() markdownDisplay.editorKit = htmlEditorKit markdownDisplay.text = htmlContent - markdownDisplay.isEditable = false val scrollPane = JScrollPane(markdownDisplay).apply { horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER } add(scrollPane, BorderLayout.CENTER) - val hideToolWindowButton = JButton("Hide") - hideToolWindowButton.addActionListener { toolWindow.hide(null) } - add(hideToolWindowButton, BorderLayout.PAGE_END) + print(htmlContent) + + // val hideToolWindowButton = JButton("Hide") + // hideToolWindowButton.addActionListener { toolWindow.hide(null) } + // add(hideToolWindowButton, BorderLayout.PAGE_END) // val markdownDisplay = JEditorPane("text/html", htmlContent).apply { isEditable = // false } // val scrollPane = JScrollPane(markdownDisplay) From 7caf5b13164df38730ad4e094d5b911b9fde4b8a Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 5 Jul 2023 14:25:52 -0700 Subject: [PATCH 19/27] add codeblock formatting --- .../slack/sgp/intellij/ui/MarkdownStyle.kt | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt index e9d3ca502..2487d1b6d 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt @@ -27,8 +27,8 @@ object MarkdownStyle { /* Headings */ "h1, h2, h3, h4, h5, h6 { margin-top: 20px; margin-bottom: 10px; }", "h1 { font-size: 2.5em; font-weight: bold}", - "h2 { font-size: 1.5em; font-weight: bold}", - "h3 { font-size: 1.17em; }", + "h2 { font-size: 2.0em; font-weight: bold}", + "h3 { font-size: 1.5em; font-weight: bold}", "h4 { font-size: 1.12em; }", "h5 { font-size: .83em; }", "h6 { font-size: .75em; }", @@ -37,26 +37,23 @@ object MarkdownStyle { "a { color: #0366d6; }", /* Blockquotes */ - "blockquote { padding: 0 1em; color: #6a737d; border-left: .25em solid #dfe2e5; }", + "blockquote { padding: 0 1em; color: #FFF; background-color: #464140; border-left: .25em solid #dfe2e5; }", /* Code blocks and inline code */ - "pre, code { font-family: monospace; }", - "pre { padding: 16px; overflow: auto; line-height: 1.45; background-color: #f6f8fa; border-radius: 6px; }", + "pre, code { display: block; background-color: #5A5A5A; font-family: monospace; padding-bottom: 10px; word-warp: normal; overflow: auto; }", + "pre { padding: 8px; line-height: 1; margin-bottom: 10px}", - /* Lists - not rendering properly */ - "ul li, ol li { margin-left: 2em; }", + /* Lists */ + "ul, ol { list-style-type: disc; padding-left: 15px; margin-bottom: 5px; }", /* Images */ "img { max-width: 100%; }", /*Italics*/ - "em { font-style: italic; }", + "em { font-style: italic; font-size: 1.2em; }", - /* Tables */ - "table { border-collapse: collapse; width: 100%; }", - "table th, table td { padding: 6px 13px; border: 1px solid #dfe2e5; }", - "table th { font-weight: bold; }", - "table tr:nth-child(2n) { background-color: #f6f8fa; }", + /* Paragraphs */ + "p { word-wrap: break-word; padding-bottom: 5px; }" ) return StyleSheet().apply { rules.forEach { rule -> addRule(rule) } } From d45568a149466fb0f11c8f9262fdd61685d14ea3 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Wed, 5 Jul 2023 15:24:17 -0700 Subject: [PATCH 20/27] fix padding / margins --- .../src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt index 2487d1b6d..870b2db04 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt @@ -44,7 +44,7 @@ object MarkdownStyle { "pre { padding: 8px; line-height: 1; margin-bottom: 10px}", /* Lists */ - "ul, ol { list-style-type: disc; padding-left: 15px; margin-bottom: 5px; }", + "ul, ol { display: inline-block; list-style-type: disc; padding-left: 15px; margin-bottom: 5px; }", /* Images */ "img { max-width: 100%; }", From 0a62c709efff4b3cc55f4a12e43f5b9993eb29a8 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Thu, 6 Jul 2023 11:26:36 -0700 Subject: [PATCH 21/27] run spotless --- skate-plugin/build.gradle.kts | 4 ++++ .../test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt | 1 + 2 files changed, 5 insertions(+) diff --git a/skate-plugin/build.gradle.kts b/skate-plugin/build.gradle.kts index 02333ef35..09d527b8d 100644 --- a/skate-plugin/build.gradle.kts +++ b/skate-plugin/build.gradle.kts @@ -38,3 +38,7 @@ dependencies { testImplementation(libs.junit) testImplementation(libs.truth) } +java { + sourceCompatibility = JavaVersion.VERSION_19 + targetCompatibility = JavaVersion.VERSION_19 +} diff --git a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt index 9d1716c81..e3a99dcec 100644 --- a/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt +++ b/skate-plugin/src/test/kotlin/com/slack/sgp/intellij/ChangeLogParserTest.kt @@ -123,6 +123,7 @@ class ChangeLogParserTest { assertThat(changeLogString).isEqualTo(expectedString) assertThat(latestEntry).isEqualTo(expectedDate) } + @Test fun `multiple entries, previous entry matches but not the latest`() { val input = From e26d91e39177bd75d3d7abc820de11f8edd82e33 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Thu, 6 Jul 2023 17:39:21 -0700 Subject: [PATCH 22/27] extended the Markdown plugin extension using MarkdownJCEFHtmlPanel --- .../com/slack/sgp/intellij/SkateService.kt | 13 ++- .../sgp/intellij/ui/WhatsNewPanelFactory.kt | 98 ++++++++----------- .../src/main/resources/META-INF/plugin.xml | 1 + 3 files changed, 52 insertions(+), 60 deletions(-) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt index 8e5dc4043..38c7a46bb 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt @@ -18,6 +18,7 @@ package com.slack.sgp.intellij import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.openapi.project.guessProjectDir +import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.wm.ToolWindowAnchor import com.intellij.openapi.wm.ToolWindowManager @@ -28,6 +29,10 @@ interface SkateProjectService { fun showWhatsNewWindow() } +/** + * This file's intended purpose is to pass in the changelog file from the file path into the What's + * New UI of the Skate Plugin + */ class SkateProjectServiceImpl(private val project: Project) : SkateProjectService { override fun showWhatsNewWindow() { @@ -38,7 +43,7 @@ class SkateProjectServiceImpl(private val project: Project) : SkateProjectServic if (!settings.isWhatsNewEnabled) return val projectDir = project.guessProjectDir() ?: return val changeLogFile = VfsUtil.findRelativeFile(projectDir, settings.whatsNewFilePath) ?: return - // val changeLogString = VfsUtil.loadText(changeLogFile) + val changeLogString = VfsUtil.loadText(changeLogFile) val toolWindowManager = ToolWindowManager.getInstance(project) toolWindowManager.invokeLater { val toolWindow = @@ -46,7 +51,11 @@ class SkateProjectServiceImpl(private val project: Project) : SkateProjectServic stripeTitle = Supplier { "What's New in Slack!" } anchor = ToolWindowAnchor.RIGHT } - WhatsNewPanelFactory().createToolWindowContent(toolWindow, changeLogFile) + + val parentDisposable = Disposer.newDisposable() // Creating a new Disposable + + WhatsNewPanelFactory() + .createToolWindowContent(toolWindow, project, changeLogString, parentDisposable) toolWindow.show() } } diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt index a0a535aeb..b120a3357 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt @@ -15,83 +15,65 @@ */ package com.slack.sgp.intellij.ui +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.runReadAction import com.intellij.openapi.project.DumbAware -import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer import com.intellij.openapi.wm.ToolWindow +import com.intellij.testFramework.LightVirtualFile import com.intellij.ui.content.ContentFactory -import com.vladsch.flexmark.html.HtmlRenderer -import com.vladsch.flexmark.parser.Parser -import com.vladsch.flexmark.util.data.MutableDataSet import java.awt.BorderLayout -import java.awt.Color -import javax.swing.BorderFactory -import javax.swing.JEditorPane +import javax.swing.JComponent import javax.swing.JPanel -import javax.swing.JScrollPane -import javax.swing.ScrollPaneConstants -import javax.swing.text.html.HTMLEditorKit +import org.intellij.lang.annotations.Language +import org.intellij.plugins.markdown.ui.preview.html.MarkdownUtil +import org.intellij.plugins.markdown.ui.preview.jcef.MarkdownJCEFHtmlPanel class WhatsNewPanelFactory : DumbAware { - fun createToolWindowContent(toolWindow: ToolWindow, markdownFile: VirtualFile) { - val toolWindowContent = WhatsNewPanelContent(markdownFile) + + // Function that creates the tool window + fun createToolWindowContent( + toolWindow: ToolWindow, + project: Project, + markdownFileString: String, + parentDisposable: Disposable + ) { + + val toolWindowContent = WhatsNewPanelContent(project, markdownFileString, parentDisposable) val content = ContentFactory.getInstance().createContent(toolWindowContent.contentPanel, "", false) toolWindow.contentManager.addContent(content) } - private class WhatsNewPanelContent(markdownFile: VirtualFile) { + private class WhatsNewPanelContent( + project: Project, + @Language("Markdown") markdownFileString: String, + parentDisposable: Disposable + ) { + // Actual panel box for What's New at Slack val contentPanel: JPanel = JPanel().apply { layout = BorderLayout(0, 20) - border = BorderFactory.createEmptyBorder(40, 0, 0, 0) - add(createControlsPanel(markdownFile), BorderLayout.CENTER) + add(createControlsPanel(project, markdownFileString, parentDisposable), BorderLayout.CENTER) } - private fun createControlsPanel(markdownFile: VirtualFile): JPanel { - val options = MutableDataSet() - val parser = Parser.builder(options).build() - val renderer = HtmlRenderer.builder(options).build() - - val markdownContent = String(markdownFile.contentsToByteArray(), Charsets.UTF_8) - val document = parser.parse(markdownContent) - - val htmlContent = renderer.render(document) - println(htmlContent) - return JPanel().apply { - layout = BorderLayout() - val markdownDisplay = - JEditorPane("text/html", htmlContent).apply { - isEditable = false - background = Color(0x333333) // set to a dark color - contentType = "text/html" - } - - val htmlEditorKit = HTMLEditorKit() - htmlEditorKit.styleSheet = MarkdownStyle.createStyleSheet() - markdownDisplay.editorKit = htmlEditorKit - - markdownDisplay.text = htmlContent - val scrollPane = - JScrollPane(markdownDisplay).apply { - horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER - } - add(scrollPane, BorderLayout.CENTER) - - print(htmlContent) - - // val hideToolWindowButton = JButton("Hide") - // hideToolWindowButton.addActionListener { toolWindow.hide(null) } - // add(hideToolWindowButton, BorderLayout.PAGE_END) - // val markdownDisplay = JEditorPane("text/html", htmlContent).apply { isEditable = - // false } - // val scrollPane = JScrollPane(markdownDisplay) - // add(scrollPane, BorderLayout.CENTER) - // - // val hideToolWindowButton = JButton("Hide") - // hideToolWindowButton.addActionListener { toolWindow.hide(null) } - // add(hideToolWindowButton, BorderLayout.PAGE_END) + private fun createControlsPanel( + project: Project, + @Language("Markdown") markdownFileString: String, + parentDisposable: Disposable + ): JComponent { + println(markdownFileString) + val file = LightVirtualFile("changelog.md", markdownFileString) + val panel = MarkdownJCEFHtmlPanel(project, file) + Disposer.register(parentDisposable, panel) + val html = runReadAction { + MarkdownUtil.generateMarkdownHtml(file, markdownFileString, project) } + + panel.setHtml(html, 0) + return panel.component } } } diff --git a/skate-plugin/src/main/resources/META-INF/plugin.xml b/skate-plugin/src/main/resources/META-INF/plugin.xml index 9e38059c1..57ce37fa3 100644 --- a/skate-plugin/src/main/resources/META-INF/plugin.xml +++ b/skate-plugin/src/main/resources/META-INF/plugin.xml @@ -20,6 +20,7 @@ com.intellij.modules.platform + org.intellij.plugins.markdown From 2a4348d7cdc809287bf8a3fc1dab92be0f5df9e9 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Thu, 6 Jul 2023 17:47:57 -0700 Subject: [PATCH 23/27] Deleted MarkdownStyle.kt because it is now unnecessary, ran spotless and added docs --- .../slack/sgp/intellij/ui/MarkdownStyle.kt | 61 ------------------- .../sgp/intellij/ui/WhatsNewPanelFactory.kt | 8 +++ 2 files changed, 8 insertions(+), 61 deletions(-) delete mode 100644 skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt deleted file mode 100644 index 870b2db04..000000000 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/MarkdownStyle.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2023 Slack Technologies, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.slack.sgp.intellij.ui - -import javax.swing.text.html.StyleSheet - -object MarkdownStyle { - fun createStyleSheet(): StyleSheet { - val rules = - listOf( - /* General body text */ - "body { width: fit-content; font-family: Arial, sans-serif; line-height: 1.6; color: #FFF; padding: 10px; }", - - /* Headings */ - "h1, h2, h3, h4, h5, h6 { margin-top: 20px; margin-bottom: 10px; }", - "h1 { font-size: 2.5em; font-weight: bold}", - "h2 { font-size: 2.0em; font-weight: bold}", - "h3 { font-size: 1.5em; font-weight: bold}", - "h4 { font-size: 1.12em; }", - "h5 { font-size: .83em; }", - "h6 { font-size: .75em; }", - - /* Links */ - "a { color: #0366d6; }", - - /* Blockquotes */ - "blockquote { padding: 0 1em; color: #FFF; background-color: #464140; border-left: .25em solid #dfe2e5; }", - - /* Code blocks and inline code */ - "pre, code { display: block; background-color: #5A5A5A; font-family: monospace; padding-bottom: 10px; word-warp: normal; overflow: auto; }", - "pre { padding: 8px; line-height: 1; margin-bottom: 10px}", - - /* Lists */ - "ul, ol { display: inline-block; list-style-type: disc; padding-left: 15px; margin-bottom: 5px; }", - - /* Images */ - "img { max-width: 100%; }", - - /*Italics*/ - "em { font-style: italic; font-size: 1.2em; }", - - /* Paragraphs */ - "p { word-wrap: break-word; padding-bottom: 5px; }" - ) - - return StyleSheet().apply { rules.forEach { rule -> addRule(rule) } } - } -} diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt index b120a3357..76c8effa2 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt @@ -30,6 +30,11 @@ import org.intellij.lang.annotations.Language import org.intellij.plugins.markdown.ui.preview.html.MarkdownUtil import org.intellij.plugins.markdown.ui.preview.jcef.MarkdownJCEFHtmlPanel +/** + * The WhatsNewPanelFactory class takes the markdown file string from SkateService and displays it + * in a tool window. It uses MarkdownJCEFHtmlPanel and has dependency on intellij markdown plugin to + * properly format the markdown file and its contents + */ class WhatsNewPanelFactory : DumbAware { // Function that creates the tool window @@ -59,6 +64,9 @@ class WhatsNewPanelFactory : DumbAware { add(createControlsPanel(project, markdownFileString, parentDisposable), BorderLayout.CENTER) } + // Control Panel that takes in the current project, markdown string, and a Disposable. + // The Disposable is necessary to prevent a substantial memory leak while working with + // MarkdownJCEFHtmlPanel private fun createControlsPanel( project: Project, @Language("Markdown") markdownFileString: String, From 1580f11ef9a02912b82e66005119715b0f101046 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Thu, 6 Jul 2023 18:10:35 -0700 Subject: [PATCH 24/27] Remove java versions --- skate-plugin/build.gradle.kts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/skate-plugin/build.gradle.kts b/skate-plugin/build.gradle.kts index 09d527b8d..02333ef35 100644 --- a/skate-plugin/build.gradle.kts +++ b/skate-plugin/build.gradle.kts @@ -38,7 +38,3 @@ dependencies { testImplementation(libs.junit) testImplementation(libs.truth) } -java { - sourceCompatibility = JavaVersion.VERSION_19 - targetCompatibility = JavaVersion.VERSION_19 -} From 728a44af5471a31284436bc848a9b95b7ef33b3d Mon Sep 17 00:00:00 2001 From: Katherine Liu <67719108+kateliu20@users.noreply.github.com> Date: Thu, 6 Jul 2023 22:30:14 -0700 Subject: [PATCH 25/27] Update skate-plugin/build.gradle.kts to .add Co-authored-by: Zac Sweers --- skate-plugin/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skate-plugin/build.gradle.kts b/skate-plugin/build.gradle.kts index 02333ef35..e41f50a02 100644 --- a/skate-plugin/build.gradle.kts +++ b/skate-plugin/build.gradle.kts @@ -16,7 +16,7 @@ intellij { version.set("2022.2.5") type.set("IC") // Target IDE Platform - plugins.set(listOf("org.intellij.plugins.markdown")) + plugins.add("org.intellij.plugins.markdown") } tasks { From ef81fb1e30983db32d66a35bdaf08df2bd0a5e5a Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Thu, 6 Jul 2023 23:01:16 -0700 Subject: [PATCH 26/27] Added clearer comments, removed nits --- .../src/main/kotlin/com/slack/sgp/intellij/SkateService.kt | 4 +++- .../com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt | 7 ++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt index 38c7a46bb..0ca6cd78f 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt @@ -52,7 +52,9 @@ class SkateProjectServiceImpl(private val project: Project) : SkateProjectServic anchor = ToolWindowAnchor.RIGHT } - val parentDisposable = Disposer.newDisposable() // Creating a new Disposable + // The Disposable is necessary to prevent a substantial memory leak while working with + // MarkdownJCEFHtmlPanel + val parentDisposable = Disposer.newDisposable() WhatsNewPanelFactory() .createToolWindowContent(toolWindow, project, changeLogString, parentDisposable) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt index 76c8effa2..57ebb7841 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt @@ -61,18 +61,15 @@ class WhatsNewPanelFactory : DumbAware { val contentPanel: JPanel = JPanel().apply { layout = BorderLayout(0, 20) - add(createControlsPanel(project, markdownFileString, parentDisposable), BorderLayout.CENTER) + add(createWhatsNewPanel(project, markdownFileString, parentDisposable), BorderLayout.CENTER) } // Control Panel that takes in the current project, markdown string, and a Disposable. - // The Disposable is necessary to prevent a substantial memory leak while working with - // MarkdownJCEFHtmlPanel - private fun createControlsPanel( + private fun createWhatsNewPanel( project: Project, @Language("Markdown") markdownFileString: String, parentDisposable: Disposable ): JComponent { - println(markdownFileString) val file = LightVirtualFile("changelog.md", markdownFileString) val panel = MarkdownJCEFHtmlPanel(project, file) Disposer.register(parentDisposable, panel) From 89e89928f567a974cbb4656f164e46d2813b68b5 Mon Sep 17 00:00:00 2001 From: Katherine Liu Date: Fri, 7 Jul 2023 13:36:20 -0700 Subject: [PATCH 27/27] Add disposable for when tool window is closed --- .../com/slack/sgp/intellij/SkateService.kt | 2 ++ .../sgp/intellij/ui/WhatsNewPanelFactory.kt | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt index 0ca6cd78f..b8c530d2f 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/SkateService.kt @@ -45,6 +45,7 @@ class SkateProjectServiceImpl(private val project: Project) : SkateProjectServic val changeLogFile = VfsUtil.findRelativeFile(projectDir, settings.whatsNewFilePath) ?: return val changeLogString = VfsUtil.loadText(changeLogFile) val toolWindowManager = ToolWindowManager.getInstance(project) + toolWindowManager.invokeLater { val toolWindow = toolWindowManager.registerToolWindow("skate-whats-new") { @@ -58,6 +59,7 @@ class SkateProjectServiceImpl(private val project: Project) : SkateProjectServic WhatsNewPanelFactory() .createToolWindowContent(toolWindow, project, changeLogString, parentDisposable) + toolWindow.show() } } diff --git a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt index 57ebb7841..771e88e83 100644 --- a/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt +++ b/skate-plugin/src/main/kotlin/com/slack/sgp/intellij/ui/WhatsNewPanelFactory.kt @@ -23,6 +23,8 @@ import com.intellij.openapi.util.Disposer import com.intellij.openapi.wm.ToolWindow import com.intellij.testFramework.LightVirtualFile import com.intellij.ui.content.ContentFactory +import com.intellij.ui.content.ContentManagerEvent +import com.intellij.ui.content.ContentManagerListener import java.awt.BorderLayout import javax.swing.JComponent import javax.swing.JPanel @@ -49,6 +51,16 @@ class WhatsNewPanelFactory : DumbAware { val content = ContentFactory.getInstance().createContent(toolWindowContent.contentPanel, "", false) toolWindow.contentManager.addContent(content) + toolWindow.contentManager.addContentManagerListener( + object : ContentManagerListener { + override fun contentRemoved(event: ContentManagerEvent) { + if (event.content.component == toolWindowContent.contentPanel) { + Disposer.dispose(parentDisposable) + toolWindow.contentManager.removeContentManagerListener(this) + } + } + } + ) } private class WhatsNewPanelContent( @@ -70,7 +82,12 @@ class WhatsNewPanelFactory : DumbAware { @Language("Markdown") markdownFileString: String, parentDisposable: Disposable ): JComponent { + // to take in the parsed Changelog: + // val parsedChangelog = ChangelogParser.readFile(markdownFileString) + // then, pass in parsedChangelog instead of markdownFileString + val file = LightVirtualFile("changelog.md", markdownFileString) + val panel = MarkdownJCEFHtmlPanel(project, file) Disposer.register(parentDisposable, panel) val html = runReadAction {