Skip to content

Commit e7ca900

Browse files
Improvements
- Fix too much notifications for Vue with JS - Cache config - Better logs for RequestSender - Port crash reporter improvements from NUD, fixing crash - Upgrade dependencies
1 parent bb51d83 commit e7ca900

File tree

8 files changed

+139
-61
lines changed

8 files changed

+139
-61
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
- Consequently, the plugin now depends
1919
on [Svelte](https://plugins.jetbrains.com/plugin/12375-svelte) & [Vue](https://plugins.jetbrains.com/plugin/9442-vue-js)
2020
extensions and only works on WebStorm or IntelliJ IDEA Ultimate
21+
- Improve crash reporter to include more relevant information
22+
23+
### Fixed
24+
25+
- Fix a potential crash with 2024.1+ IDEs
26+
- Fix JS users with Vue getting notified too often about the unavailability of the JS option
2127

2228
## [0.7.7] - 2024-03-29
2329

build.gradle.kts

+6-4
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ changelog {
4949
}
5050

5151
// Configure Gradle Kover Plugin - read more: https://github.com/Kotlin/kotlinx-kover#configuration
52-
koverReport {
53-
defaults {
54-
xml {
55-
onCheck = true
52+
kover {
53+
reports {
54+
total {
55+
xml {
56+
onCheck = true
57+
}
5658
}
5759
}
5860
}

gradle/libs.versions.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[versions]
2-
kotlin = "1.9.23"
2+
kotlin = "1.9.24"
33
changelog = "2.2.0"
44
gradleIntelliJPlugin = "1.17.3"
5-
qodana = "2024.1.3"
6-
kover = "0.7.6"
5+
qodana = "2024.1.5"
6+
kover = "0.8.0"
77

88
[plugins]
99
changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" }

src/main/kotlin/com/github/warningimhack3r/intellijshadcnplugin/backend/http/RequestSender.kt

+12-2
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,24 @@ object RequestSender {
3333
log.debug(
3434
"Request method: ${request.method()}, headers: ${
3535
request.headers().map()
36-
}, body: ${body?.take(100)}${if ((body?.length ?: 0) > 100) "..." else ""}"
36+
}, body:${
37+
if (body != null) {
38+
"\n"
39+
} else ""
40+
}${body?.take(100)}${if ((body?.length ?: 0) > 100) "..." else ""}"
3741
)
3842
HttpClient.newBuilder()
3943
.followRedirects(HttpClient.Redirect.NORMAL)
4044
.build()
4145
.send(request, HttpResponse.BodyHandlers.ofString()).let { response ->
4246
return Response(response.statusCode(), response.headers().map(), response.body()).also {
43-
log.debug("Request to $url returned ${it.statusCode} (${it.body.length} bytes): ${it.body.take(100)}${if (it.body.length > 100) "..." else ""}")
47+
log.debug(
48+
"Request to $url returned ${it.statusCode} (${it.body.length} bytes):${
49+
if (it.body.isNotEmpty()) {
50+
"\n"
51+
} else ""
52+
}${it.body.take(100)}${if (it.body.length > 100) "..." else ""}"
53+
)
4454
}
4555
}
4656
}

src/main/kotlin/com/github/warningimhack3r/intellijshadcnplugin/backend/sources/Source.kt

+11-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import java.nio.file.NoSuchFileException
2424
abstract class Source<C : Config>(val project: Project, private val serializer: KSerializer<C>) {
2525
private val log = logger<Source<C>>()
2626
abstract var framework: String
27+
private var config: C? = null
2728
protected val domain: String
2829
get() = URI(getLocalConfig().`$schema`).let { uri ->
2930
"${uri.scheme}://${uri.host}".also {
@@ -34,15 +35,22 @@ abstract class Source<C : Config>(val project: Project, private val serializer:
3435
// Utility methods
3536
protected fun getLocalConfig(): C {
3637
val file = "components.json"
37-
return FileManager(project).getFileContentsAtPath(file)?.let {
38+
return config?.also {
39+
log.debug("Returning cached config")
40+
} ?: FileManager(project).getFileContentsAtPath(file)?.let {
3841
log.debug("Parsing config from $file")
3942
try {
40-
Json.decodeFromString(serializer, it).also { config ->
41-
log.debug("Parsed config: ${config.javaClass.name}")
43+
Json.decodeFromString(serializer, it).also {
44+
log.debug("Parsed config")
4245
}
4346
} catch (e: Exception) {
4447
throw UnparseableConfigException(project, "Unable to parse $file", e)
4548
}
49+
}?.also {
50+
if (config == null) {
51+
log.debug("Caching config")
52+
config = it
53+
}
4654
} ?: throw NoSuchFileException("$file not found")
4755
}
4856

src/main/kotlin/com/github/warningimhack3r/intellijshadcnplugin/backend/sources/impl/VueSource.kt

+9-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import java.nio.file.NoSuchFileException
2121
class VueSource(project: Project) : Source<VueConfig>(project, VueConfig.serializer()) {
2222
companion object {
2323
private val log = logger<VueSource>()
24+
private var jsVueNotified = false
2425
}
2526

2627
override var framework = "Vue"
@@ -74,11 +75,14 @@ class VueSource(project: Project) : Source<VueConfig>(project, VueConfig.seriali
7475
override fun adaptFileToConfig(file: PsiFile) {
7576
val config = getLocalConfig()
7677
if (!config.typescript) {
77-
NotificationManager(project).sendNotification(
78-
"TypeScript option for Vue",
79-
"You have TypeScript disabled in your shadcn/ui config. This feature is not supported yet. Please install/update your components with the CLI for now.",
80-
NotificationType.WARNING
81-
)
78+
if (!jsVueNotified) {
79+
NotificationManager(project).sendNotification(
80+
"TypeScript option for Vue",
81+
"You have TypeScript disabled in your shadcn/ui config. This feature is not supported yet. Please install/update your components with the CLI for now.",
82+
NotificationType.WARNING
83+
)
84+
jsVueNotified = true
85+
}
8286
// TODO: detype Vue file
8387
}
8488

src/main/kotlin/com/github/warningimhack3r/intellijshadcnplugin/errorsubmitter/ErrorThrowingAction.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.github.warningimhack3r.intellijshadcnplugin.errorsubmitter
22

33
import com.intellij.openapi.actionSystem.AnActionEvent
4+
import com.intellij.openapi.actionSystem.UpdateInBackground
45
import com.intellij.openapi.diagnostic.logger
56
import com.intellij.openapi.project.DumbAwareAction
67

@@ -13,7 +14,7 @@ import com.intellij.openapi.project.DumbAwareAction
1314
* #com.github.warningimhack3r.intellijshadcnplugin.errorreport.ErrorThrowingAction
1415
* ```
1516
*/
16-
class ErrorThrowingAction : DumbAwareAction() {
17+
class ErrorThrowingAction : DumbAwareAction(), UpdateInBackground {
1718
companion object {
1819
private val LOG = logger<ErrorThrowingAction>()
1920
}

src/main/kotlin/com/github/warningimhack3r/intellijshadcnplugin/errorsubmitter/GitHubErrorReportSubmitter.kt

+90-43
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ import java.nio.charset.StandardCharsets
2222
// https://github.com/SonarSource/sonarlint-intellij/blob/master/src/main/java/org/sonarlint/intellij/errorsubmitter/BlameSonarSource.java
2323
class GitHubErrorReportSubmitter : ErrorReportSubmitter() {
2424
companion object {
25+
private const val REPO_URL = "https://github.com/WarningImHack3r/intellij-shadcn-plugin"
2526
private const val MAX_URL_LENGTH = 2083
2627
private const val BUG_LOGS_KEY = "bug-logs"
2728
private const val TRIMMED_STACKTRACE_MARKER = "\n\n<TRIMMED STACKTRACE>"
28-
private const val WORM_UNICODE = "\uD83D\uDC1B"
29+
private const val UNICODE_WORM = "\uD83D\uDC1B"
2930
}
3031

31-
override fun getReportActionText() = "$WORM_UNICODE Open an Issue on GitHub"
32+
override fun getReportActionText() = "$UNICODE_WORM Open an Issue on GitHub"
3233

3334
override fun submit(
3435
events: Array<out IdeaLoggingEvent>,
@@ -37,6 +38,7 @@ class GitHubErrorReportSubmitter : ErrorReportSubmitter() {
3738
consumer: Consumer<in SubmittedReportInfo>
3839
): Boolean {
3940
return try {
41+
// Base data
4042
val event = if (events.isNotEmpty()) events.first() else null
4143

4244
val stackTrace = event?.throwableText ?: ""
@@ -48,13 +50,32 @@ class GitHubErrorReportSubmitter : ErrorReportSubmitter() {
4850
DataManager.getInstance().getDataContext(parentComponent)
4951
) ?: getLastFocusedOrOpenedProject()
5052

51-
BrowserUtil.browse(
52-
buildAbbreviatedUrl(mapOf(
53+
// Computed data
54+
var causedByLastIndex = -1
55+
val splitStackTrace = stackTrace.split("\n")
56+
splitStackTrace.reversed().forEachIndexed { index, s ->
57+
if (s.lowercase().startsWith("caused by")) {
58+
causedByLastIndex = splitStackTrace.size - index
59+
return@forEachIndexed
60+
}
61+
}
62+
63+
// Build URL and content
64+
BrowserUtil.browse(buildAbbreviatedUrl(
65+
mapOf(
5366
"title" to "[crash] $simpleErrorMessage",
5467
"bug-explanation" to (additionalInfo ?: ""),
55-
BUG_LOGS_KEY to stackTrace.split("\n").filter {
56-
!it.trim().startsWith("at java.desktop/")
57-
&& !it.trim().startsWith("at java.base/")
68+
BUG_LOGS_KEY to splitStackTrace.filterIndexed { index, s ->
69+
if (index == 0) return@filterIndexed true
70+
val line = s.trim()
71+
if (causedByLastIndex > 0 && line.startsWith("at ") && index < causedByLastIndex) {
72+
return@filterIndexed false
73+
}
74+
!line.startsWith("at java.desktop/")
75+
&& !line.startsWith("at java.base/")
76+
&& !line.startsWith("at kotlin.")
77+
&& !line.startsWith("at kotlinx.")
78+
&& !line.startsWith("at com.intellij.")
5879
}.joinToString("\n"),
5980
/*"device-os" to with(System.getProperty("os.name").lowercase()) {
6081
when { // Windows, macOS or Linux
@@ -63,9 +84,9 @@ class GitHubErrorReportSubmitter : ErrorReportSubmitter() {
6384
else -> "Linux"
6485
}
6586
},*/ // currently cannot be set (https://github.com/orgs/community/discussions/44983)
66-
"additional-device-info" to getDefaultHelpBlock(project)
67-
).filterValues { it.isNotEmpty() })
68-
)
87+
"additional-device-info" to getPlatformAndPluginsInfo(project)
88+
).filterValues { it.isNotEmpty() }
89+
))
6990
consumer.consume(SubmittedReportInfo(SubmittedReportInfo.SubmissionStatus.NEW_ISSUE))
7091
true
7192
} catch (e: Exception) {
@@ -74,54 +95,82 @@ class GitHubErrorReportSubmitter : ErrorReportSubmitter() {
7495
}
7596
}
7697

98+
/**
99+
* Build the URL for the GitHub issue from the given fields, abbreviating the URL if necessary
100+
* to fit within the maximum URL length.
101+
*
102+
* @param fields the fields to include in the URL.
103+
* @return the URL for the GitHub issue.
104+
*/
77105
private fun buildAbbreviatedUrl(fields: Map<String, String>): URI {
78106
val url = buildUrl(fields)
79-
return URI(if (url.length > MAX_URL_LENGTH) {
80-
val newMap = fields.toMutableMap()
81-
newMap[BUG_LOGS_KEY]?.let { fullLog ->
82-
val logLessUrlLength = buildUrl(fields.mapValues { (key, value) ->
83-
if (key == BUG_LOGS_KEY) "" else value
84-
}).length
85-
val encodedLogDiff = URLEncoder.encode(fullLog, StandardCharsets.UTF_8).length - fullLog.length
86-
newMap[BUG_LOGS_KEY] = fullLog.take(
87-
(MAX_URL_LENGTH - logLessUrlLength - encodedLogDiff).coerceAtLeast(fullLog.substringBefore("\n").length)
88-
).run {
89-
if (length > fullLog.substringBefore("\n").length + TRIMMED_STACKTRACE_MARKER.length) {
90-
"${take(length - TRIMMED_STACKTRACE_MARKER.length)}$TRIMMED_STACKTRACE_MARKER"
91-
} else this
107+
return URI(
108+
if (url.length > MAX_URL_LENGTH) {
109+
val newMap = fields.toMutableMap()
110+
newMap[BUG_LOGS_KEY]?.let { fullLog ->
111+
val logLessUrlLength = buildUrl(fields.mapValues { (key, value) ->
112+
if (key == BUG_LOGS_KEY) "" else value
113+
}).length
114+
val encodedLogDiff = URLEncoder.encode(fullLog, StandardCharsets.UTF_8).length - fullLog.length
115+
newMap[BUG_LOGS_KEY] = fullLog.take(
116+
(MAX_URL_LENGTH - logLessUrlLength - encodedLogDiff).coerceAtLeast(fullLog.substringBefore("\n").length)
117+
).run {
118+
if (length > fullLog.substringBefore("\n").length + TRIMMED_STACKTRACE_MARKER.length) {
119+
"${take(length - TRIMMED_STACKTRACE_MARKER.length)}$TRIMMED_STACKTRACE_MARKER"
120+
} else this
121+
}
92122
}
93-
}
94-
val shorterLogUrl = buildUrl(newMap)
95-
if (shorterLogUrl.length > MAX_URL_LENGTH) {
96-
buildUrl(fields.filter { (key, _) ->
97-
key == "title" || key == "additional-device-info"
98-
})
99-
} else shorterLogUrl
100-
} else url
123+
val shorterLogUrl = buildUrl(newMap)
124+
if (shorterLogUrl.length > MAX_URL_LENGTH) {
125+
buildUrl(fields.filter { (key, _) ->
126+
key == "title" || key == "additional-device-info"
127+
})
128+
} else shorterLogUrl
129+
} else url
101130
)
102131
}
103132

133+
/**
134+
* Build the URL for the GitHub issue from the given fields.
135+
*
136+
* @param fields the fields to include in the URL.
137+
* @return the URL for the GitHub issue.
138+
*/
104139
private fun buildUrl(fields: Map<String, String>) = buildString {
105-
append("https://github.com/WarningImHack3r/intellij-shadcn-plugin/issues/new?labels=bug&template=bug_report.yml")
140+
append("$REPO_URL/issues/new?labels=bug&template=bug_report.yml")
106141
fields.forEach { (key, value) ->
107142
append("&$key=${URLEncoder.encode(value, StandardCharsets.UTF_8)}")
108143
}
109144
}
110145

111-
private fun getDefaultHelpBlock(project: Project): String {
146+
/**
147+
* Get the platform and plugins information for the given project.
148+
* Used in the "Additional platform info" section of the GitHub issue.
149+
*
150+
* @param project the [Project][com.intellij.openapi.project.Project] to get the platform and plugins information from.
151+
* @return the platform and plugins information for the given project.
152+
*/
153+
private fun getPlatformAndPluginsInfo(project: Project): String {
112154
return CompositeGeneralTroubleInfoCollector().collectInfo(project).run {
113155
val trimmedAndCleaned = split("\n".toRegex()).filter { trim().isNotEmpty() }
114156
// Build, JRE, JVM, OS
115-
trimmedAndCleaned
116-
.dropWhile { s -> s == "=== About ===" }
117-
.takeWhile { s -> s != "=== System ===" }
118-
.filter { s -> !s.startsWith("idea.") && !s.startsWith("Theme") }
119-
.joinToString("\n") + "\n" +
120-
// Plugins
157+
buildString {
158+
append(
159+
trimmedAndCleaned
160+
.dropWhile { s -> s == "=== About ===" }
161+
.takeWhile { s -> s != "=== System ===" }
162+
.filter { s -> !s.startsWith("idea.") && !s.startsWith("Theme") }
163+
.joinToString("\n")
164+
)
165+
append("\n")
166+
// Plugins
167+
append(
121168
trimmedAndCleaned
122169
.dropWhile { s -> s != "=== Plugins ===" }
123170
.takeWhile { s -> s.isNotBlank() && s.isNotEmpty() }
124171
.joinToString("\n")
172+
)
173+
}
125174
}
126175
}
127176

@@ -132,12 +181,10 @@ class GitHubErrorReportSubmitter : ErrorReportSubmitter() {
132181
* @return the [Project][com.intellij.openapi.project.Project] that was last in focus or open.
133182
*/
134183
private fun getLastFocusedOrOpenedProject(): Project {
135-
val project = IdeFocusManager.getGlobalInstance().lastFocusedFrame?.project
136-
if (project == null) {
184+
return IdeFocusManager.getGlobalInstance().lastFocusedFrame?.project ?: run {
137185
val projectManager = ProjectManager.getInstance()
138186
val openProjects = projectManager.openProjects
139-
return if (openProjects.isNotEmpty()) openProjects.first() else projectManager.defaultProject
187+
if (openProjects.isNotEmpty()) openProjects.first() else projectManager.defaultProject
140188
}
141-
return project
142189
}
143190
}

0 commit comments

Comments
 (0)