@@ -16,9 +16,11 @@ import com.demonwav.mcdev.util.fromJson
1616import com.google.gson.Gson
1717import com.intellij.ide.plugins.PluginManagerCore
1818import com.intellij.openapi.diagnostic.Attachment
19+ import com.intellij.openapi.util.text.StringUtil
1920import java.net.HttpURLConnection
2021import java.nio.ByteBuffer
2122import java.nio.charset.CodingErrorAction
23+ import org.apache.commons.httpclient.HttpStatus
2224import org.apache.commons.io.IOUtils
2325import org.apache.http.HttpHeaders
2426import org.apache.http.entity.ContentType
@@ -65,12 +67,8 @@ object AnonymousFeedback {
6567 errorMessage = " no error"
6668 }
6769
68- var stackTrace = body.remove(" error.stacktrace" )
69- stackTrace = if (stackTrace.isNullOrEmpty()) {
70- " no stacktrace"
71- } else {
72- linkStacktrace(stackTrace)
73- }
70+ val rawStackTrace = body.remove(" error.raw_stacktrace" )?.takeIf { it.isNotBlank() } ? : " no stacktrace"
71+ val stackTrace = body.remove(" error.stacktrace" )?.takeIf { it.isNotBlank() } ? : " no stacktrace"
7472
7573 val sb = StringBuilder ()
7674
@@ -84,13 +82,20 @@ object AnonymousFeedback {
8482 sb.append(" </table></td><td><table>\n " )
8583 }
8684 val (key, value) = entry
87- sb.append(" <tr><td><b>" ).append(key).append(" </b></td><td><code>" ).append(value).append(
88- " </code></td></tr>\n "
89- )
85+ sb.append(" <tr><td><b>" )
86+ .append(key)
87+ .append(" </b></td><td><code>" )
88+ .append(value)
89+ .append(" </code></td></tr>\n " )
9090 }
9191 sb.append(" </table></td></tr></table>\n " )
9292
93- sb.append(" \n <pre>\n " ).append(stackTrace).append(" \n </pre>\n " )
93+ sb.append(" \n <pre><code>" ).append(stackTrace).append(" </code></pre>\n " )
94+
95+ sb.append(" \n <details><summary>Original stack trace</summary>\n\n ```\n " )
96+ .append(rawStackTrace)
97+ .append(" \n ```\n </details>\n " )
98+
9499 sb.append(" \n ```\n " ).append(errorMessage).append(" \n ```\n " )
95100
96101 if (attachments.isNotEmpty()) {
@@ -149,11 +154,16 @@ object AnonymousFeedback {
149154 val numberRegex = Regex (" \\ d+" )
150155 val newLineRegex = Regex (" [\r\n ]+" )
151156
152- val stack = envDetails[" error.stacktrace " ]?.replace(numberRegex, " " ) ? : return null
157+ val stack = envDetails[" error.raw_stacktrace " ]?.replace(numberRegex, " " ) ? : return null
153158
154- val stackMcdevParts = stack.lineSequence()
159+ val ourMcdevParts = stack.lineSequence()
155160 .filter { line -> line.startsWith(packagePrefix) }
156- .joinToString(" \n " )
161+ .map { it.trim() }
162+ .toList()
163+
164+ if (ourMcdevParts.isEmpty()) {
165+ return null
166+ }
157167
158168 val predicate = fun (map : Map <* , * >): Boolean {
159169 val body = (map[" body" ] as ? String ? : return false )
@@ -167,23 +177,36 @@ object AnonymousFeedback {
167177
168178 val first = body.indexOf(" \n ```\n " , startIndex = 0 ) + 5
169179 val second = body.indexOf(" \n ```\n " , startIndex = first)
180+ if (first == 4 || second == - 1 ) {
181+ return false
182+ }
183+
170184 val stackText = body.substring(first, second)
171185
172- val mcdevParts = stackText.lineSequence()
186+ val theirMcdevParts = stackText.lineSequence()
173187 .filter { line -> line.startsWith(packagePrefix) }
174- .joinToString(" \n " )
188+ .map { it.trim() }
189+ .toList()
175190
176- return stackMcdevParts == mcdevParts
191+ return ourMcdevParts == theirMcdevParts
177192 }
178193
179194 // Look first for an open issue, then for a closed issue if one isn't found
180195 val block = getAllIssues(openIssueUrl, factory)?.firstOrNull(predicate)
181- ? : getAllIssues(closedIssueUrl, factory)?.firstOrNull(predicate)
196+ ? : getAllIssues(closedIssueUrl, factory, limit = 300 )?.firstOrNull(predicate)
182197 ? : return null
183198 return (block[" number" ] as Double ).toInt()
184199 }
185200
186- private fun getAllIssues (url : String , factory : HttpConnectionFactory ): List <Map <* , * >>? {
201+ private fun getMcdevStackElementLines (stack : String , numberRegex : Regex , linkRegex : Regex ): List <String > {
202+ return stack.lineSequence()
203+ .mapNotNull { line -> linkRegex.matchEntire(line)?.groups?.get(" content" )?.value }
204+ .map { line -> StringUtil .unescapeXmlEntities(line) }
205+ .map { line -> line.replace(numberRegex, " " ) }
206+ .toList()
207+ }
208+
209+ private fun getAllIssues (url : String , factory : HttpConnectionFactory , limit : Int = -1): List <Map <* , * >>? {
187210 var useAuthed = false
188211
189212 var next: String? = url
@@ -197,13 +220,13 @@ object AnonymousFeedback {
197220
198221 connection.connect()
199222
200- if (connection.responseCode == 403 && ! useAuthed) {
223+ if (connection.responseCode == HttpStatus . SC_FORBIDDEN && ! useAuthed) {
201224 useAuthed = true
202225 next = replaceWithAuth(next)
203226 continue
204227 }
205228
206- if (connection.responseCode != 200 ) {
229+ if (connection.responseCode != HttpStatus . SC_OK ) {
207230 return null
208231 }
209232
@@ -216,6 +239,10 @@ object AnonymousFeedback {
216239 val response = Gson ().fromJson<List <Map <* , * >>>(data)
217240 list.addAll(response)
218241
242+ if (limit > 0 && list.size >= limit) {
243+ return list
244+ }
245+
219246 val link = connection.getHeaderField(" Link" )
220247
221248 next = getNextLink(link, useAuthed)
@@ -277,7 +304,7 @@ object AnonymousFeedback {
277304 }
278305
279306 val responseCode = connection.responseCode
280- if (responseCode != 201 ) {
307+ if (responseCode != HttpStatus . SC_CREATED ) {
281308 throw RuntimeException (" Expected HTTP_CREATED (201), obtained $responseCode instead." )
282309 }
283310
@@ -287,7 +314,7 @@ object AnonymousFeedback {
287314 }
288315 connection.disconnect()
289316
290- return Gson ().fromJson< Map < * , * >> (body)
317+ return Gson ().fromJson(body)
291318 }
292319
293320 private fun getConnection (factory : HttpConnectionFactory , url : String ): HttpURLConnection {
@@ -300,74 +327,6 @@ object AnonymousFeedback {
300327 return connection
301328 }
302329
303- private fun linkStacktrace (stacktrace : String ): String {
304- val versionRegex = Regex (""" (?<intellijVersion>\d{4}\.\d)-(?<pluginVersion>\d+\.\d+\.\d+)""" )
305-
306- val version = PluginUtil .pluginVersion
307- val match = versionRegex.matchEntire(version) ? : return stacktrace
308-
309- val intellijVersion = match.groups[" intellijVersion" ]?.value ? : return stacktrace
310- val pluginVersion = match.groups[" pluginVersion" ]?.value ? : return stacktrace
311-
312- val tag = " $pluginVersion -$intellijVersion "
313-
314- // v stack element text v
315- // at com.demonwav.mcdev.facet.MinecraftFacet.shouldShowPluginIcon(MinecraftFacet.kt:185)
316- // prefix ^ class path ^ ^ file name ^ ^ ^ line number
317- val stackElementRegex = Regex (
318- """ (?<prefix>\s+at\s+)""" +
319- """ (?<stackElementText>""" +
320- """ (?<className>com\.demonwav\.mcdev(?:\.\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*)+)""" +
321- """ (?:\.\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*|<(?:cl)?init>)""" +
322- """ \((?<fileName>.*\.\w+):(?<lineNumber>\d+)\)""" +
323- """ )\s*"""
324- )
325-
326- val baseTagUrl = " https://github.com/minecraft-dev/MinecraftDev/blob/$tag /src/main/kotlin/"
327-
328- val sb = StringBuilder (stacktrace.length * 2 )
329-
330- for (line in stacktrace.lineSequence()) {
331- val lineMatch = stackElementRegex.matchEntire(line)
332- if (lineMatch == null ) {
333- sb.append(line).append(' \n ' )
334- continue
335- }
336-
337- val prefix = lineMatch.groups[" prefix" ]?.value
338- val className = lineMatch.groups[" className" ]?.value
339- val fileName = lineMatch.groups[" fileName" ]?.value
340- val lineNumber = lineMatch.groups[" lineNumber" ]?.value
341- val stackElementText = lineMatch.groups[" stackElementText" ]?.value
342-
343- if (prefix == null || className == null || fileName == null ||
344- lineNumber == null || stackElementText == null
345- ) {
346- sb.append(line).append(' \n ' )
347- continue
348- }
349-
350- val path = className.substringAfter(" com.demonwav.mcdev." )
351- .substringBeforeLast(' .' )
352- .replace(' .' , ' /' )
353- sb.apply {
354- append(prefix)
355- append(" <a href=\" " )
356- append(baseTagUrl)
357- append(path)
358- append(' /' )
359- append(fileName)
360- append(" #L" )
361- append(lineNumber)
362- append(" \" >" )
363- append(stackElementText)
364- append(" </a>\n " )
365- }
366- }
367-
368- return sb.toString()
369- }
370-
371330 private val userAgent by lazy {
372331 var agent = " Minecraft Development IntelliJ IDEA plugin"
373332
0 commit comments