Skip to content

Commit 2b87bc9

Browse files
committed
Merge branch 'dev' into 2017.2
# Conflicts: # gradle.properties # readme.md # src/main/resources/META-INF/plugin.xml
2 parents aa80801 + 10e377b commit 2b87bc9

28 files changed

+244
-121
lines changed

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ plugins {
3232
id("org.jetbrains.kotlin.jvm") version "1.1.1" // kept in sync with IntelliJ's bundled dep
3333
groovy
3434
idea
35-
id("org.jetbrains.intellij") version "0.2.7"
35+
id("org.jetbrains.intellij") version "0.2.13"
3636
id("net.minecrell.licenser") version "0.3"
3737
}
3838

@@ -91,7 +91,7 @@ java {
9191
}
9292
}
9393

94-
val gradleToolingExtension = java().sourceSets["gradle-tooling-extension"]
94+
val gradleToolingExtension = java().sourceSets["gradle-tooling-extension"]!!
9595
val gradleToolingExtensionJar = task<Jar>(gradleToolingExtension.jarTaskName) {
9696
from(gradleToolingExtension.output)
9797
classifier = "gradle-tooling-extension"

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ javaVersion = 1.8
1313
kotlinVersion = 1.1.1
1414

1515
group = com.demonwav.minecraft-dev
16-
version = 2017.2-0.7.2
16+
version = 2017.2-0.7.3
1717
downloadIdeaSources = true
1818

1919
org.gradle.script.lang.kotlin.accessors.auto=true

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Minecraft Development for IntelliJ
1010
|**CircleCI**|[![Travis Build Status](https://img.shields.io/circleci/project/github/minecraft-dev/MinecraftDev/dev.svg?style=flat-square)](https://circleci.com/gh/minecraft-dev/MinecraftDev)|
1111
| **Travis** |[![CircleCI Build Status](https://img.shields.io/travis/minecraft-dev/MinecraftDev/dev.svg?style=flat-square)](https://travis-ci.org/minecraft-dev/MinecraftDev/)|
1212

13-
Info and Documentation [![Current Release](https://img.shields.io/badge/release-2017.2--0.7.2-orange.svg?style=flat-square)](https://plugins.jetbrains.com/plugin/8327)
13+
Info and Documentation [![Current Release](https://img.shields.io/badge/release-2017.2--0.7.3-orange.svg?style=flat-square)](https://plugins.jetbrains.com/plugin/8327)
1414
----------------------
1515

1616
Visit [https://minecraftdev.org](https://minecraftdev.org) for information about the project, change logs, features, FAQs, and chat.

src/main/kotlin/com/demonwav/mcdev/buildsystem/gradle/GradleBuildSystem.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,15 +220,15 @@ class GradleBuildSystem : BuildSystem() {
220220
}
221221

222222
private fun createRepositoriesOrDependencies(project: Project, file: GroovyFile, name: String, expressions: List<String>) {
223-
// Ge the block so we can start working with it
223+
// Get the block so we can start working with it
224224
val block = getClosableBlockByName(file, name) ?: return
225225

226226
// Create a super expression with all the expressions tied together
227227
val expressionText = expressions.joinToString("\n")
228228

229229
// We can't create each expression and add them to the file...that won't work. Groovy requires a new line
230230
// from one method call expression to another, and there's no way to (easily) put whitespace in Psi because Psi is
231-
// stupid. So instead we make hte whole thing as one big clump and insert it into the block.
231+
// stupid. So instead we make the whole thing as one big clump and insert it into the block.
232232
val fakeFile = GroovyPsiElementFactory.getInstance(project).createGroovyFile(expressionText, false, null)
233233
val last = block.children.last()
234234
block.addBefore(fakeFile, last)

src/main/kotlin/com/demonwav/mcdev/creator/MinecraftModuleBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class MinecraftModuleBuilder : JavaModuleBuilder() {
5555
creator.root = root
5656
creator.module = modifiableRootModel.module
5757

58-
val r = DumbAwareRunnable { creator.create() }
58+
val r = DumbAwareRunnable(creator::create)
5959

6060
if (project.isDisposed) {
6161
return

src/main/kotlin/com/demonwav/mcdev/creator/MinecraftModuleWizardStep.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package com.demonwav.mcdev.creator
1212

1313
import com.demonwav.mcdev.exception.BadListSetupException
1414
import com.demonwav.mcdev.exception.EmptyInputSetupException
15+
import com.demonwav.mcdev.exception.InvalidMainClassNameException
1516
import com.demonwav.mcdev.exception.SetupException
1617
import com.intellij.ide.util.projectWizard.ModuleWizardStep
1718
import com.intellij.openapi.ui.MessageType
@@ -37,9 +38,30 @@ abstract class MinecraftModuleWizardStep : ModuleWizardStep() {
3738
throw EmptyInputSetupException(pluginVersionField)
3839
}
3940

41+
// empty
4042
if (mainClassField.text.trim { it <= ' ' }.isEmpty()) {
4143
throw EmptyInputSetupException(mainClassField)
4244
}
45+
// default package
46+
if (!mainClassField.text.contains('.')) {
47+
throw InvalidMainClassNameException(mainClassField)
48+
}
49+
// crazy dots
50+
if (mainClassField.text.split('.').any { it.isEmpty() } ||
51+
mainClassField.text.first() == '.' || mainClassField.text.last() == '.') {
52+
throw InvalidMainClassNameException(mainClassField)
53+
}
54+
// invalid character
55+
if (mainClassField.text.split('.').any {
56+
!it.first().isJavaIdentifierStart() || !it.asSequence().drop(1).all { it.isJavaIdentifierPart() }
57+
}) {
58+
throw InvalidMainClassNameException(mainClassField)
59+
}
60+
// keyword identifier
61+
if (mainClassField.text.split('.').any { keywords.contains(it) }) {
62+
throw InvalidMainClassNameException(mainClassField)
63+
}
64+
4365
if (!authorsField.text.matches(pattern)) {
4466
throw BadListSetupException(authorsField)
4567
}
@@ -60,5 +82,11 @@ abstract class MinecraftModuleWizardStep : ModuleWizardStep() {
6082

6183
companion object {
6284
val pattern = "(\\s*(\\w+)\\s*(,\\s*\\w+\\s*)*,?|\\[?\\s*(\\w+)\\s*(,\\s*\\w+\\s*)*])?".toRegex()
85+
val keywords = setOf(
86+
"abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", "boolean", "do", "if",
87+
"private", "this", "break", "double", "implements", "protected", "throw", "byte", "else", "import", "public", "throws", "case",
88+
"enum", "instanceof", "return", "transient", "catch", "extends", "int", "short", "try", "char", "final", "interface", "static",
89+
"void", "class", "finally", "long", "strictfp", "volatile", "const", "float", "native", "super", "while"
90+
)
6391
}
6492
}

src/main/kotlin/com/demonwav/mcdev/creator/PlatformVersion.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
package com.demonwav.mcdev.creator
1212

1313
import com.demonwav.mcdev.platform.PlatformType
14+
import com.demonwav.mcdev.util.fromJson
1415
import com.google.gson.Gson
1516
import org.jetbrains.concurrency.runAsync
1617
import java.net.URL
@@ -44,7 +45,7 @@ fun getVersionSelector(type: PlatformType) = runAsync {
4445
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2"
4546
)
4647
val text = connection.getInputStream().use { it.reader().use { it.readText() } }
47-
Gson().fromJson(text, PlatformVersion::class.java)
48+
Gson().fromJson<PlatformVersion>(text)
4849
}
4950

5051
data class PlatformVersion(var versions: Array<String>, var selectedIndex: Int) {

src/main/kotlin/com/demonwav/mcdev/error/AnonymousFeedback.kt

Lines changed: 82 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
package com.demonwav.mcdev.error
1212

1313
import com.demonwav.mcdev.update.PluginUtil
14+
import com.demonwav.mcdev.util.fromJson
1415
import com.google.gson.Gson
1516
import com.intellij.ide.plugins.PluginManager
1617
import org.apache.commons.io.IOUtils
@@ -19,8 +20,20 @@ import java.net.URL
1920

2021
object AnonymousFeedback {
2122

22-
fun sendFeedback(factory: HttpConnectionFactory, envDetails: LinkedHashMap<String, String?>): Int {
23-
return sendFeedback(factory, convertToGitHubIssueFormat(envDetails))
23+
data class FeedbackData(val url: String, val token: Int, val isDuplicate: Boolean)
24+
25+
const val url = "https://www.demonwav.com/errorReport"
26+
27+
fun sendFeedback(factory: HttpConnectionFactory, envDetails: LinkedHashMap<String, String?>): FeedbackData {
28+
val duplicateId = findDuplicateIssue(envDetails)
29+
if (duplicateId != null) {
30+
// This is a duplicate
31+
val commentUrl = sendCommentOnDuplicateIssue(duplicateId, factory, convertToGitHubIssueFormat(envDetails))
32+
return FeedbackData(commentUrl, duplicateId, true)
33+
}
34+
35+
val (htmlUrl, token) = sendFeedback(factory, convertToGitHubIssueFormat(envDetails))
36+
return FeedbackData(htmlUrl, token, false)
2437
}
2538

2639
private fun convertToGitHubIssueFormat(envDetails: LinkedHashMap<String, String?>): ByteArray {
@@ -62,22 +75,8 @@ object AnonymousFeedback {
6275
return sb.toString()
6376
}
6477

65-
private fun sendFeedback(factory: HttpConnectionFactory, payload: ByteArray): Int {
66-
val url = "https://www.demonwav.com/errorReport"
67-
var userAgent = "Minecraft Development IntelliJ IDEA plugin"
68-
69-
val pluginDescriptor = PluginManager.getPlugin(PluginUtil.PLUGIN_ID)
70-
if (pluginDescriptor != null) {
71-
val name = pluginDescriptor.name
72-
val version = pluginDescriptor.version
73-
userAgent = "$name ($version)"
74-
}
75-
76-
val connection = connect(factory, url)
77-
connection.doOutput = true
78-
connection.requestMethod = "POST"
79-
connection.setRequestProperty("User-Agent", userAgent)
80-
connection.setRequestProperty("Content-Type", "application/json")
78+
private fun sendFeedback(factory: HttpConnectionFactory, payload: ByteArray): Pair<String, Int> {
79+
val connection = getConnection(factory, url)
8180
connection.outputStream.use {
8281
it.write(payload)
8382
}
@@ -92,9 +91,8 @@ object AnonymousFeedback {
9291
IOUtils.toString(it, contentEncoding)
9392
}
9493

95-
val json = Gson().fromJson(body, HashMap::class.java)
96-
val issueNum = json["number"].toString().toDouble().toInt()
97-
return issueNum
94+
val json = Gson().fromJson<Map<*, *>>(body)
95+
return json["html_url"] as String to (json["number"] as Double).toInt()
9896
}
9997

10098
private fun connect(factory: HttpConnectionFactory, url: String): HttpURLConnection {
@@ -104,9 +102,69 @@ object AnonymousFeedback {
104102
return connection
105103
}
106104

107-
open class HttpConnectionFactory {
108-
open fun openHttpConnection(url: String): HttpURLConnection {
109-
return URL(url).openConnection() as HttpURLConnection
105+
private fun findDuplicateIssue(envDetails: LinkedHashMap<String, String?>): Int? {
106+
val stack = envDetails["error.stacktrace"]?.replace("\\d+".toRegex(), "") ?: return null
107+
108+
val text = URL("https://api.github.com/repos/minecraft-dev/MinecraftDev/issues?state=all&creator=minecraft-dev-autoreporter").readText()
109+
val list = Gson().fromJson<List<Map<*, *>>>(text)
110+
val block = list.firstOrNull {
111+
val body = it["body"] as? String ?: return@firstOrNull false
112+
113+
// We can't comment on locked issues
114+
if (it["locked"] as Boolean) {
115+
return@firstOrNull false
116+
}
117+
118+
val first = body.indexOf("\n```\n", startIndex = 0) + 5
119+
val second = body.indexOf("\n```\n", startIndex = first)
120+
val stackText = body.substring(first, second)
121+
122+
stackText.replace("\\d+".toRegex(), "") == stack
123+
} ?: return null
124+
return (block["number"] as Double).toInt()
125+
}
126+
127+
private fun sendCommentOnDuplicateIssue(id: Int, factory: HttpConnectionFactory, payload: ByteArray): String {
128+
val commentUrl = "$url/$id/comments"
129+
val connection = getConnection(factory, commentUrl)
130+
connection.outputStream.use {
131+
it.write(payload)
110132
}
133+
134+
val responseCode = connection.responseCode
135+
if (responseCode != 201) {
136+
throw RuntimeException("Expected HTTP_CREATED (201), obtained $responseCode instead.")
137+
}
138+
139+
val contentEncoding = connection.contentEncoding ?: "UTF-8"
140+
val body = connection.inputStream.use {
141+
IOUtils.toString(it, contentEncoding)
142+
}
143+
144+
val json = Gson().fromJson<Map<*, *>>(body)
145+
return json["html_url"] as String
146+
}
147+
148+
private fun getConnection(factory: HttpConnectionFactory, url: String): HttpURLConnection {
149+
var userAgent = "Minecraft Development IntelliJ IDEA plugin"
150+
151+
val pluginDescription = PluginManager.getPlugin(PluginUtil.PLUGIN_ID)
152+
if (pluginDescription != null) {
153+
val name = pluginDescription.name
154+
val version = pluginDescription.version
155+
userAgent = "$name ($version)"
156+
}
157+
158+
val connection = connect(factory, url)
159+
connection.doOutput = true
160+
connection.requestMethod = "POST"
161+
connection.setRequestProperty("User-Agent", userAgent)
162+
connection.setRequestProperty("Content-Type", "application/json")
163+
164+
return connection
165+
}
166+
167+
open class HttpConnectionFactory {
168+
open fun openHttpConnection(url: String) = URL(url).openConnection() as HttpURLConnection
111169
}
112170
}

src/main/kotlin/com/demonwav/mcdev/error/AnonymousFeedbackTask.kt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,28 @@ import com.intellij.openapi.progress.ProgressIndicator
1414
import com.intellij.openapi.progress.Task
1515
import com.intellij.openapi.project.Project
1616
import com.intellij.util.net.HttpConfigurable
17-
import java.net.HttpURLConnection
1817

1918
class AnonymousFeedbackTask(
2019
project: Project?,
2120
title: String,
2221
canBeCancelled: Boolean,
2322
private val params: LinkedHashMap<String, String?>,
24-
private val callback: (Int) -> Unit,
23+
private val callback: (String, Int, Boolean) -> Unit,
2524
private val errorCallback: (Exception) -> Unit
2625
) : Task.Backgroundable(project, title, canBeCancelled) {
2726

2827
override fun run(indicator: ProgressIndicator) {
2928
indicator.isIndeterminate = true
3029

3130
try {
32-
val token = AnonymousFeedback.sendFeedback(ProxyHttpConnectionFactory(), params)
33-
callback(token)
31+
val (url, token, isDuplicate) = AnonymousFeedback.sendFeedback(ProxyHttpConnectionFactory(), params)
32+
callback(url, token, isDuplicate)
3433
} catch (e: Exception) {
3534
errorCallback(e)
3635
}
3736
}
3837

3938
private inner class ProxyHttpConnectionFactory : AnonymousFeedback.HttpConnectionFactory() {
40-
override fun openHttpConnection(url: String): HttpURLConnection {
41-
return HttpConfigurable.getInstance().openHttpConnection(url)
42-
}
39+
override fun openHttpConnection(url: String) = HttpConfigurable.getInstance().openHttpConnection(url)
4340
}
4441
}

src/main/kotlin/com/demonwav/mcdev/error/ErrorReporter.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import com.intellij.util.Consumer
3131
import java.awt.Component
3232

3333
class ErrorReporter : ErrorReportSubmitter() {
34-
val baseUrl = "https://github.com/minecraft-dev/MinecraftDev/issues"
34+
private val baseUrl = "https://github.com/minecraft-dev/MinecraftDev/issues"
3535
override fun getReportActionText() = "Report to Minecraft Dev GitHub Issue Tracker"
3636

3737
override fun submit(events: Array<out IdeaLoggingEvent>,
@@ -68,13 +68,17 @@ class ErrorReporter : ErrorReportSubmitter() {
6868

6969
val project = CommonDataKeys.PROJECT.getData(dataContext)
7070

71-
val task = AnonymousFeedbackTask(project, "Submitting error report", true, reportValues, { token ->
72-
val url = "$baseUrl/$token"
73-
val reportInfo = SubmittedReportInfo(url, "Issue #$token", SubmittedReportInfo.SubmissionStatus.NEW_ISSUE)
71+
val task = AnonymousFeedbackTask(project, "Submitting error report", true, reportValues, { htmlUrl, token, isDuplicate ->
72+
val reportInfo = SubmittedReportInfo(htmlUrl, "Issue #$token", SubmittedReportInfo.SubmissionStatus.NEW_ISSUE)
7473
consumer.consume(reportInfo)
7574

76-
val message = "<html>Created Issue #$token successfully.<br>" +
77-
"<a href=\"$url\">View issue.</a></html>"
75+
val message = if (!isDuplicate) {
76+
"<html>Created Issue #$token successfully. " +
77+
"<a href=\"$htmlUrl\">View issue.</a></html>"
78+
} else {
79+
"<html>Commented on existing Issue #$token successfully. " +
80+
"<a href=\"$htmlUrl\">View comment.</a></html>"
81+
}
7882

7983
ReportMessages.GROUP.createNotification(
8084
ReportMessages.ERROR_REPORT,

0 commit comments

Comments
 (0)