From ff3cf02869dc5a5ab7f84c3db149a2951421801b Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Tue, 10 Dec 2019 23:56:15 +0000
Subject: [PATCH 01/14] Handle project path containing backslashes
---
.../com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt
index ea6a500ec..50b5ac2a8 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt
@@ -114,7 +114,7 @@ object UnityRunUtil {
while (tokenizer.hasMoreTokens()) {
val token = tokenizer.nextToken()
if (token.equals("-projectPath", true) || token.equals("-createPath", true)) {
- return getProjectNameFromPath(StringUtil.unescapeStringCharacters(tokenizer.nextToken()))
+ return getProjectNameFromPath(StringUtil.unquoteString(tokenizer.nextToken()))
}
}
}
From e6c3882436e0e4097ae7c4c06d0ee8fc9c635de9 Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Wed, 11 Dec 2019 17:15:16 +0000
Subject: [PATCH 02/14] Make Attach to Unity Process dialog resizable
Fixes #1446
---
.../rider/plugins/unity/run/UnityProcessPickerDialog.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityProcessPickerDialog.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityProcessPickerDialog.kt
index decf46cf7..f9e9111df 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityProcessPickerDialog.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityProcessPickerDialog.kt
@@ -62,12 +62,12 @@ class UnityProcessPickerDialog(private val project: Project) : DialogWrapper(pro
}
commentRow("Please ensure both the Development Build and Script Debugging options are checked in Unity's Build Settings dialog. " +
"Standalone players must be visible to the current network.")
- }.apply { minimumSize = Dimension(650, 300) }
+ }.apply { preferredSize = Dimension(600, 450) }
isOKActionEnabled = false
cancelAction.putValue(FOCUSED_ACTION, true)
init()
- setResizable(false)
+ setResizable(true)
}
// DialogWrapper only lets the Mac set the preferred component via FOCUSED_ACTION because reasons
From 88fd4a4bef6c2ae702beab523c6f7657307e7e05 Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Thu, 12 Dec 2019 11:39:16 +0000
Subject: [PATCH 03/14] Show Unity child process role in debug list
Also fixes issue showing the correct project name in the Attach to Process popup list.
Fixes #1328, #1456
---
.../rider/plugins/unity/run/UnityPlayer.kt | 7 +-
.../plugins/unity/run/UnityPlayerListener.kt | 7 +-
.../unity/run/UnityProcessPickerDialog.kt | 5 +-
.../rider/plugins/unity/run/UnityRunUtil.kt | 153 +++++++++++-------
...UnityLocalAttachProcessDebuggerProvider.kt | 12 +-
...nityLocalAttachProcessPresentationGroup.kt | 11 +-
.../UnityAttachToEditorViewModel.kt | 4 +-
7 files changed, 124 insertions(+), 75 deletions(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityPlayer.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityPlayer.kt
index 4f6476f9d..c1ba25014 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityPlayer.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityPlayer.kt
@@ -3,11 +3,12 @@ package com.jetbrains.rider.plugins.unity.run
data class UnityPlayer(val host: String, val port: Int, val debuggerPort: Int,
val flags: Long, val guid: Long, val editorId: Long, val version: Int,
val id: String, val allowDebugging: Boolean, val packageName: String? = null,
- val projectName: String? = null, val pid: Int? = null, val isEditor: Boolean = false) {
+ val projectName: String? = null, val pid: Int? = null, val roleName: String? = null,
+ val isEditor: Boolean = false) {
companion object {
- fun createEditorPlayer(host: String, port: Int, id: String, pid: Int, projectName: String?): UnityPlayer {
+ fun createEditorPlayer(host: String, port: Int, id: String, pid: Int, projectName: String?, roleName: String?): UnityPlayer {
return UnityPlayer(host, port, port, flags = 0, guid = port.toLong(), editorId = port.toLong(), version = 0,
- id = id, allowDebugging = true, pid = pid, projectName = projectName, isEditor = true)
+ id = id, allowDebugging = true, pid = pid, projectName = projectName, isEditor = true, roleName = roleName)
}
fun createRemotePlayer(host: String, port: Int): UnityPlayer {
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityPlayerListener.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityPlayerListener.kt
index a663e76fe..a20c7db3a 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityPlayerListener.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityPlayerListener.kt
@@ -102,11 +102,12 @@ class UnityPlayerListener(private val project: Project,
private fun addLocalProcesses() {
val unityProcesses = OSProcessUtil.getProcessList().filter { UnityRunUtil.isUnityEditorProcess(it) }
- val projectNames = UnityRunUtil.getUnityProcessProjectNames(unityProcesses, project)
+ val unityProcessInfoMap = UnityRunUtil.getAllUnityProcessInfo(unityProcesses, project)
unityProcesses.map { processInfo ->
- val projectName = projectNames[processInfo.pid]
+ val unityProcessInfo = unityProcessInfoMap[processInfo.pid]
val port = convertPidToDebuggerPort(processInfo.pid)
- UnityPlayer.createEditorPlayer("127.0.0.1", port, processInfo.executableName, processInfo.pid, projectName)
+ UnityPlayer.createEditorPlayer("127.0.0.1", port, processInfo.executableName, processInfo.pid,
+ unityProcessInfo?.projectName, unityProcessInfo?.roleName)
}.forEach {
onPlayerAdded(it)
}
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityProcessPickerDialog.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityProcessPickerDialog.kt
index f9e9111df..fe1013cad 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityProcessPickerDialog.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityProcessPickerDialog.kt
@@ -62,7 +62,7 @@ class UnityProcessPickerDialog(private val project: Project) : DialogWrapper(pro
}
commentRow("Please ensure both the Development Build and Script Debugging options are checked in Unity's Build Settings dialog. " +
"Standalone players must be visible to the current network.")
- }.apply { preferredSize = Dimension(600, 450) }
+ }.apply { preferredSize = Dimension(650, 450) }
isOKActionEnabled = false
cancelAction.putValue(FOCUSED_ACTION, true)
@@ -159,6 +159,9 @@ class UnityProcessPickerDialog(private val project: Project) : DialogWrapper(pro
val debug = player.allowDebugging && !UnityRunUtil.isDebuggerAttached(player.host, player.port, project)
val attributes = if (debug) SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES else SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES
append(player.id, attributes)
+ if (player.roleName != null) {
+ append(" ${player.roleName}", attributes)
+ }
if (player.projectName != null) {
append(" - ${player.projectName}", attributes)
}
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt
index 50b5ac2a8..5aa243000 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt
@@ -23,6 +23,8 @@ import java.io.File
import java.nio.charset.StandardCharsets
import java.nio.file.Paths
+data class UnityProcessInfo(val projectName: String?, val roleName: String?)
+
object UnityRunUtil {
private val logger = Logger.getInstance(UnityRunUtil::class.java)
@@ -59,12 +61,15 @@ object UnityRunUtil {
return processList.any { it.pid == pid && isUnityEditorProcess(it) }
}
- fun getUnityProcessProjectName(processInfo: ProcessInfo, project: Project): String? {
- return getUnityProcessProjectNames(listOf(processInfo), project)[processInfo.pid]
+ fun getUnityProcessInfo(processInfo: ProcessInfo, project: Project): UnityProcessInfo? {
+ return getAllUnityProcessInfo(listOf(processInfo), project)[processInfo.pid]
}
- fun getUnityProcessProjectNames(processList: List, project: Project): Map {
- // We have several options to get the project name (and directory, for future use):
+ fun getAllUnityProcessInfo(processList: List, project: Project): Map {
+ // We might have to call external processes. Make sure we're running in the background
+ assertNotDispatchThread()
+
+ // We have several options to get the project name (and maybe directory, for future use):
// 1) Match pid with EditorInstance.json - it's the current project
// 2) If the editor was started from Unity Hub, use the -projectPath or -createProject parameters
// 3) If we're on Mac/Linux, use `lsof -a -p {pid},{pid},{pid} -d cwd -Fn` to get the current working directory
@@ -73,22 +78,19 @@ object UnityRunUtil {
// E.g. https://stackoverflow.com/questions/16110936/read-other-process-current-directory-in-c-sharp
// 4) Scrape the main window title. This is fragile, as the format changes, and can easily break with hyphens in
// project or scene names. It also doesn't give us the project path. And it doesn't work on Mac/Linux
-
- // We might have to call external processes. Make sure we're running in the background
- assertNotDispatchThread()
- val projectNames = mutableMapOf()
+ val processInfoMap = mutableMapOf()
processList.forEach {
- val projectName = getProjectNameFromEditorInstanceJson(it, project) ?:
- getProjectNameFromCommandLine(it)
- projectName?.let { name -> projectNames[it.pid] = name }
+ val projectName = getProjectNameFromEditorInstanceJson(it, project)
+ parseProcessInfoFromCommandLine(it, projectName)?.let { n -> processInfoMap[it.pid] = n }
}
- if (projectNames.size != processList.size) {
- fillProjectNamesFromWorkingDirectory(processList, projectNames)
+ // If we failed to get project name from the command line, try and get it from the working directory
+ if (processInfoMap.size != processList.size || processInfoMap.any { it.value.projectName == null }) {
+ fillProjectNamesFromWorkingDirectory(processList, processInfoMap)
}
- return projectNames
+ return processInfoMap
}
private fun assertNotDispatchThread() {
@@ -107,63 +109,102 @@ object UnityRunUtil {
} else null
}
- private fun getProjectNameFromCommandLine(processInfo: ProcessInfo): String? {
- // Make sure the command line we're using is properly quoted, if possible
- getQuotedCommandLine(processInfo)?.let {
- val tokenizer = CommandLineTokenizer(processInfo.commandLine)
- while (tokenizer.hasMoreTokens()) {
- val token = tokenizer.nextToken()
- if (token.equals("-projectPath", true) || token.equals("-createPath", true)) {
- return getProjectNameFromPath(StringUtil.unquoteString(tokenizer.nextToken()))
- }
- }
- }
+ private fun parseProcessInfoFromCommandLine(processInfo: ProcessInfo, canonicalProjectName: String?): UnityProcessInfo? {
+ var projectName = canonicalProjectName
+ var name: String? = null
+ var umpProcessRole: String? = null
+ var umpWindowTitle: String? = null
- // Try to parse the unquoted command line, coping with a -projectPath or -createPath that might contain spaces
- // and/or hyphens. Split the command line at argument boundaries, e.g. a hyphen followed by a non-whitespace
- // char, with leading whitespace, or at the start of the string. Each split string should be an arg and an
- // argvalue (lookahead means we keep the delimiter). If the path contains an embedded space-hyphen-nonspace
- // sequence, we need to join segments until we're sure we've got the longest possible path.
- // There is a pathological edge case of two directories with the same name but one with a suffix that matches
- // the next command line argument. If we take the longest path, we can get the wrong one. E.g.
- // "/home/Unity/project1" and "/home/Unity/project1 -useHub" would confuse things. I think this is so unlikely
- // as to be happily ignored
- // This assumes that all arguments begin with a hyphen, and there are no standalone arguments. Empirically, this
- // is true
- val commandLineArgs = processInfo.commandLine.split("(^|\\s)(?=-[^\\s])".toRegex())
+ val tokens = tokenizeCommandLine(processInfo)
var i = 0
- do {
- if (commandLineArgs[i].startsWith("-projectPath", ignoreCase = true)
- || commandLineArgs[i].startsWith("-createProject", ignoreCase = true)) {
- val whitespace = commandLineArgs[i].indexOf(' ')
- if (whitespace == -1) continue // Weird if true
- var path = commandLineArgs[i].substring(whitespace + 1)
+ while (i < tokens.size - 1) { // -1 for the argument + argument value
+ val token = tokens[i++]
+ if (projectName == null && (token.equals("-projectPath", true) || token.equals("-createProject", true))) {
+ // For an unquoted command line, the next token isn't guaranteed to be the whole path. If the path
+ // contains a space-hyphen-char (e.g. `-projectPath /Users/matt/Projects/space game -is great -yeah`)
+ // they will be split as multiple tokens. Concatenate subsequent tokens until we have the longest valid
+ // path. Note that the arguments and values are all separated by a single space. Any other whitespace
+ // is still part of the string
+ var path = tokens[i++]
var lastValid = if (File(path).isDirectory) path else ""
- while (i < commandLineArgs.size - 1) {
- path += " " + commandLineArgs[++i]
+ var j = i
+ while (j < tokens.size) {
+ path += " " + tokens[j++]
if (File(path).isDirectory) {
lastValid = path
+ i = j
}
}
- return getProjectNameFromPath(lastValid)
+ projectName = getProjectNameFromPath(StringUtil.unquoteString(lastValid))
+ }
+ else if (token.equals("-name", true)) {
+ name = StringUtil.unquoteString(tokens[i++])
+ }
+ else if (token.equals("-ump-process-role", true)) {
+ umpProcessRole = StringUtil.unquoteString(tokens[i++])
+ }
+ else if (token.equals("-ump-window-title", true)) {
+ umpWindowTitle = StringUtil.unquoteString(tokens[i++])
}
+ }
+
+ if (projectName == null && name == null && umpWindowTitle == null && umpProcessRole == null) {
+ return null
+ }
- i++
- } while (i < commandLineArgs.size)
+ return UnityProcessInfo(projectName, name ?: umpWindowTitle ?: umpProcessRole)
+ }
- return null
+ private fun tokenizeCommandLine(processInfo: ProcessInfo): List {
+ return tokenizeQuotedCommandLine(processInfo)
+ ?: tokenizeUnquotedCommandLine(processInfo)
+ }
+
+ private fun tokenizeQuotedCommandLine(processInfo: ProcessInfo): List? {
+ return getQuotedCommandLine(processInfo)?.let {
+ val tokens = mutableListOf()
+ val tokenizer = CommandLineTokenizer(it)
+ while(tokenizer.hasMoreTokens())
+ tokens.add(tokenizer.nextToken())
+ tokens
+ }
+ }
+
+ private fun tokenizeUnquotedCommandLine(processInfo: ProcessInfo): List {
+ // Split the command line into arguments
+ // We assume an argument starts with a hyphen and has no whitespace in the name. Empirically, this is true
+ // So split on ^- or \s-
+ // Each chunk should now be an arg and an argvalue, e.g. `-name Foo`
+ // Split on the first whitespace. The argument value should be correct, but might require concatenating if
+ // the value pathologically contains a \s-[^\s] sequence
+ // E.g. `-createProject /Users/matt/my interesting -project` would split into the following tokens:
+ // "-createProject" "/Users/matt/my interesting" "-project"
+ // The single whitespace between arguments and between the argument and value is not captured, so must be added
+ // back if concatenating
+ val tokens = mutableListOf()
+ processInfo.commandLine.split("(^|\\s)(?=-[^\\s])".toRegex()).forEach {
+ val whitespace = it.indexOf(' ')
+ if (whitespace == -1) {
+ tokens.add(it)
+ }
+ else {
+ tokens.add(it.substring(0, whitespace))
+ tokens.add(it.substring(whitespace + 1))
+ }
+ }
+ return tokens
}
private fun getQuotedCommandLine(processInfo: ProcessInfo): String? {
return when {
- SystemInfo.isWindows -> processInfo.commandLine
- SystemInfo.isMac -> null
+ SystemInfo.isWindows -> processInfo.commandLine // Already quoted correctly
+ SystemInfo.isMac -> null // We can't add quotes, and can't easily get an unquoted version
SystemInfo.isUnix -> {
try {
// ProcessListUtil.getProcessListOnUnix already reads /proc/{pid}/cmdline, but doesn't quote
- // arguments that contain spaces, which makes it much harder to parse
+ // arguments that contain spaces. https://youtrack.jetbrains.com/issue/IDEA-229022
val procfsCmdline = File("/proc/${processInfo.pid}/cmdline")
val cmdlineString = String(FileUtil.loadFileBytes(procfsCmdline), StandardCharsets.UTF_8)
val cmdlineParts = StringUtil.split(cmdlineString, "\u0000")
@@ -193,8 +234,10 @@ object UnityRunUtil {
private fun getProjectNameFromPath(projectPath: String): String = Paths.get(projectPath).fileName.toString()
- private fun fillProjectNamesFromWorkingDirectory(processList: List, projectNames: MutableMap) {
+ private fun fillProjectNamesFromWorkingDirectory(processList: List, projectNames: MutableMap) {
+ // Windows requires reading process memory. Unix is so much nicer.
if (SystemInfo.isWindows) return
+
try {
val processIds = processList.joinToString(",") { it.pid.toString() }
val command = when {
@@ -212,7 +255,7 @@ object UnityRunUtil {
val pid = stdout[i].substring(1).toInt()
val cwd = getProjectNameFromPath(stdout[i + 2].substring(1))
- projectNames[pid] = cwd
+ projectNames[pid] = UnityProcessInfo(cwd, projectNames[pid]?.roleName)
}
}
}
@@ -245,4 +288,4 @@ object UnityRunUtil {
.build()
ProgramRunnerUtil.executeConfiguration(environment, false, true)
}
-}
\ No newline at end of file
+}
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/attach/UnityLocalAttachProcessDebuggerProvider.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/attach/UnityLocalAttachProcessDebuggerProvider.kt
index 8679f98c5..8c02eb667 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/attach/UnityLocalAttachProcessDebuggerProvider.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/attach/UnityLocalAttachProcessDebuggerProvider.kt
@@ -5,20 +5,24 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.UserDataHolder
import com.intellij.xdebugger.attach.*
+import com.jetbrains.rdclient.util.idea.getOrCreateUserData
+import com.jetbrains.rider.plugins.unity.run.UnityProcessInfo
import com.jetbrains.rider.plugins.unity.run.UnityRunUtil
@Suppress("UnstableApiUsage")
class UnityLocalAttachProcessDebuggerProvider : XAttachDebuggerProvider {
companion object {
- val PROJECT_NAME_KEY: Key = Key("UnityProcess::ProjectName")
+ val PROCESS_INFO_KEY: Key> = Key("UnityProcess::Info")
}
override fun getAvailableDebuggers(project: Project, host: XAttachHost, process: ProcessInfo, userData: UserDataHolder): MutableList {
if (UnityRunUtil.isUnityEditorProcess(process)) {
- // Cache the project name. When we're asked for display name, we're on the EDT thread, and can't call this
- UnityRunUtil.getUnityProcessProjectName(process, project)?.let {
- userData.putUserData(PROJECT_NAME_KEY, it)
+ // Cache the processes display names. When we're asked for the display text for the menu, we're on the EDT
+ // thread, and can't call this
+ UnityRunUtil.getUnityProcessInfo(process, project)?.let {
+ val map = userData.getOrCreateUserData(PROCESS_INFO_KEY) { mutableMapOf() }
+ map[process.pid]= it
}
return mutableListOf(UnityLocalAttachDebugger())
}
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/attach/UnityLocalAttachProcessPresentationGroup.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/attach/UnityLocalAttachProcessPresentationGroup.kt
index 00707685f..e6383ea30 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/attach/UnityLocalAttachProcessPresentationGroup.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/attach/UnityLocalAttachProcessPresentationGroup.kt
@@ -14,13 +14,10 @@ object UnityLocalAttachProcessPresentationGroup : XAttachProcessPresentationGrou
override fun getItemIcon(project: Project, process: ProcessInfo, userData: UserDataHolder) = UnityIcons.Icons.UnityLogo
override fun getItemDisplayText(project: Project, process: ProcessInfo, userData: UserDataHolder): String {
- val projectName = userData.getUserData(UnityLocalAttachProcessDebuggerProvider.PROJECT_NAME_KEY)
- return if (projectName != null) {
- "${process.executableDisplayName} ($projectName)"
- }
- else {
- process.executableDisplayName
- }
+ val displayNames = userData.getUserData(UnityLocalAttachProcessDebuggerProvider.PROCESS_INFO_KEY)?.get(process.pid)
+ val projectName = if (displayNames?.projectName != null) " (${displayNames.projectName})" else ""
+ val roleName = if (displayNames?.roleName != null) " ${displayNames.roleName}" else ""
+ return process.executableDisplayName + roleName + projectName
}
override fun compare(p1: ProcessInfo, p2: ProcessInfo) = p1.pid.compareTo(p2.pid)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
index d4ba4ba41..aa5c4fafb 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
@@ -47,9 +47,9 @@ class UnityAttachToEditorViewModel(val lifetime: Lifetime, private val project:
private fun getEditorProcessInfos(processList: Array): List {
val unityProcesses = processList.filter { UnityRunUtil.isUnityEditorProcess(it) }
- val projectNames = UnityRunUtil.getUnityProcessProjectNames(unityProcesses, project)
+ val unityProcessInfoMap = UnityRunUtil.getAllUnityProcessInfo(unityProcesses, project)
return unityProcesses.map {
- EditorProcessInfo(it.executableName, it.pid, projectNames[it.pid])
+ EditorProcessInfo(it.executableName, it.pid, unityProcessInfoMap[it.pid]?.projectName)
}
}
}
\ No newline at end of file
From 316f4dbac57783b88f0710988783aca5b27d1e31 Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Thu, 12 Dec 2019 11:57:32 +0000
Subject: [PATCH 04/14] Stop any exception killing the Unity process list
Fixes #1454
---
.../jetbrains/rider/plugins/unity/run/UnityRunUtil.kt | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt
index 5aa243000..2ed58d76b 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/UnityRunUtil.kt
@@ -81,8 +81,13 @@ object UnityRunUtil {
val processInfoMap = mutableMapOf()
processList.forEach {
- val projectName = getProjectNameFromEditorInstanceJson(it, project)
- parseProcessInfoFromCommandLine(it, projectName)?.let { n -> processInfoMap[it.pid] = n }
+ try {
+ val projectName = getProjectNameFromEditorInstanceJson(it, project)
+ parseProcessInfoFromCommandLine(it, projectName)?.let { n -> processInfoMap[it.pid] = n }
+ }
+ catch (t: Throwable) {
+ logger.warn("Error fetching Unity process info: ${it.commandLine}", t)
+ }
}
// If we failed to get project name from the command line, try and get it from the working directory
From dc4b4f0b97be302cc770ddc46bb4e8ac7f067013 Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Sun, 15 Dec 2019 15:46:47 +0000
Subject: [PATCH 05/14] Show dialog if we don't know which process to debug
Fixes #1445
---
.../UnityAttachToEditorRunConfiguration.kt | 42 ++++++++++++++++---
1 file changed, 36 insertions(+), 6 deletions(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
index eee42e4e6..478bd1229 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
@@ -10,6 +10,7 @@ import com.intellij.execution.runners.RunConfigurationWithSuppressedDefaultRunAc
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.options.SettingsEditor
import com.intellij.openapi.project.Project
+import com.intellij.util.xmlb.annotations.Transient
import com.jetbrains.rider.plugins.unity.run.UnityRunUtil
import com.jetbrains.rider.plugins.unity.util.*
import com.jetbrains.rider.run.configurations.remote.DotNetRemoteConfiguration
@@ -29,9 +30,9 @@ class UnityAttachToEditorRunConfiguration(project: Project, factory: Configurati
}
// Note that we don't serialise these - they will change between sessions, possibly during a session
- override var port: Int = -1
- override var address: String = "127.0.0.1"
- var pid: Int? = null
+ @Transient override var port: Int = -1
+ @Transient override var address: String = "127.0.0.1"
+ @Transient var pid: Int? = null
override fun clone(): RunConfiguration {
val configuration = super.clone() as UnityAttachToEditorRunConfiguration
@@ -68,6 +69,25 @@ class UnityAttachToEditorRunConfiguration(project: Project, factory: Configurati
override var listenPortForConnections: Boolean = false
+ override fun checkSettingsBeforeRun() {
+ // This method lets us check settings before run. If we throw an instance of RuntimeConfigurationError, the Run
+ // Configuration editor is displayed. It's called on the EDT, so theres' not a lot we can do - e.g. we can't get
+ // a process list.
+
+ // If we already have a pid, that means this run configuration has been launched before, and we've successfully
+ // attached to a process. Use it again.
+ if (pid != null) {
+ return
+ }
+
+ // Verify that we have an EditorInstance.json. If we don't, we can't easily tell that any running instances are
+ // for our project.
+ val editorInstanceJson = EditorInstanceJson.getInstance(project)
+ if (editorInstanceJson.status != EditorInstanceJsonStatus.Valid) {
+ throw RuntimeConfigurationError("Unable to automatically discover correct Unity Editor to debug")
+ }
+ }
+
fun updatePidAndPort() : Boolean {
val processList = OSProcessUtil.getProcessList()
@@ -100,12 +120,22 @@ class UnityAttachToEditorRunConfiguration(project: Project, factory: Configurati
// Too expensive to check here?
}
+ override fun readExternal(element: Element) {
+ super.readExternal(element)
+ // Reset pid, address + port to defaults. It makes no sense to persist the pid across sessions. Unfortunately,
+ // the base class has been serialising them for years...
+ pid = null
+ port = -1
+ address = "127.0.0.1"
+ }
+
override fun writeExternal(element: Element) {
super.writeExternal(element)
- // Write it, but don't read it. We need to write it so that the modified check
- // works, but we're not interested in reading it as we will recalculate it
+ // Write it, but don't read it. We need to write it so that the modified check works, but we're not interested
+ // in reading it as we will recalculate it.
+ // TODO: Explain the comment above - what modified check?
if (pid != null) {
- element.setAttribute("pid", pid.toString())
+ element.setAttribute("ignored-value-for-modified-check", pid.toString())
}
}
}
From efa00cd9357304dffdafffda3d594be152ffb9df Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Sun, 15 Dec 2019 15:47:11 +0000
Subject: [PATCH 06/14] Refresh dialog correctly on initial load
---
.../UnityAttachToEditorViewModel.kt | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
index aa5c4fafb..6b1e814f4 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
@@ -2,6 +2,7 @@ package com.jetbrains.rider.plugins.unity.run.configurations
import com.intellij.execution.process.OSProcessUtil
import com.intellij.execution.process.ProcessInfo
+import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.project.Project
import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rd.util.reactive.IProperty
@@ -27,21 +28,20 @@ class UnityAttachToEditorViewModel(val lifetime: Lifetime, private val project:
fun refreshProcessList() {
editorProcesses.clear()
- val currentModalityState = application.currentModalityState
application.executeOnPooledThread {
val processList = OSProcessUtil.getProcessList()
val editors = getEditorProcessInfos(processList)
- application.invokeLater({ editors.forEach { editorProcesses.add(it) } }, currentModalityState)
-
- editorInstanceJsonStatus.set(editorInstanceJson.validateStatus(processList))
-
- this.pid.value = if (editorInstanceJsonStatus.value != EditorInstanceJsonStatus.Valid && editorProcesses.count() == 1) {
- editorProcesses[0].pid
- } else {
- editorInstanceJson.contents?.process_id
- }
+ application.invokeLater({
+ editorProcesses.addAll(editors)
+ editorInstanceJsonStatus.set(editorInstanceJson.validateStatus(processList))
+ pid.value = if (editorInstanceJsonStatus.value != EditorInstanceJsonStatus.Valid && editors.count() == 1) {
+ editors[0].pid
+ } else {
+ editorInstanceJson.contents?.process_id
+ }
+ }, ModalityState.any())
}
}
From 7a18e9a28d20c7d85e0127ecda10cc28a4616f5e Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Mon, 16 Dec 2019 08:54:56 +0000
Subject: [PATCH 07/14] Reset pid if we can't find a valid Unity process
---
.../configurations/UnityAttachToEditorRunConfiguration.kt | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
index 478bd1229..3833e122f 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
@@ -95,7 +95,11 @@ class UnityAttachToEditorRunConfiguration(project: Project, factory: Configurati
// Try to reuse the previous process ID, if it's still valid, then fall back to finding the process
// automatically. Theoretically, there is a tiny chance the previous process has died, and the process ID has
// been recycled for a new process that just happens to be a Unity process. Practically, this is not likely
- pid = checkValidEditorInstance(pid, processList) ?: findUnityEditorInstanceFromEditorInstanceJson(processList) ?: return false
+ port = -1
+ pid = checkValidEditorInstance(pid, processList) ?: findUnityEditorInstanceFromEditorInstanceJson(processList)
+ if (pid == null) {
+ return false
+ }
port = convertPidToDebuggerPort(pid!!)
return true
}
From 88488fd5c1c3dd9c67ed67e2c79bcb8e4055a499 Mon Sep 17 00:00:00 2001
From: Ivan Shakhov
Date: Mon, 16 Dec 2019 10:42:49 +0100
Subject: [PATCH 08/14] correct logging
---
.../run/configurations/UnityAttachToEditorProfileState.kt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorProfileState.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorProfileState.kt
index 512f70175..78db1bc26 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorProfileState.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorProfileState.kt
@@ -86,7 +86,8 @@ class UnityAttachToEditorProfileState(private val remoteConfiguration: UnityAtta
Thread.sleep(2000)
}
UIUtil.invokeLaterIfNeeded {
- logger.trace("Connecting to Unity Editor with port: $port")
+ logger.trace("DebuggerWorker port: $port")
+ logger.trace("Connecting to Unity Editor with port: ${remoteConfiguration.port}")
super.createWorkerRunCmd(lifetime, helper, port).onSuccess { result.setResult(it) }.onError { result.setError(it) }
}
}
From dfe387022ff4173f38453f3fa68b39d8b5118223 Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Mon, 16 Dec 2019 10:22:04 +0000
Subject: [PATCH 09/14] Improve finding Unity editor to debug
If the previously cached pid is no longer valid, will now check project name as well as EditorInstance.json
---
.../jetbrains/rider/UnityProjectDiscoverer.kt | 2 +-
.../UnityAttachToEditorProfileState.kt | 6 +-
.../UnityAttachToEditorRunConfiguration.kt | 81 ++++++++++++++-----
3 files changed, 67 insertions(+), 22 deletions(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt b/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt
index 6bad0c45a..8b4b5da61 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt
@@ -19,7 +19,7 @@ class UnityProjectDiscoverer(project: Project, unityHost: UnityHost) : Lifetimed
// anywhere)
val isUnityProject = isUnityProjectFolder && isCorrectlyLoadedSolution(project)
val isUnityGeneratedProject = isUnityProject && solutionNameMatchesUnityProjectName(project)
- val isUnitySidecarProject = isUnityProject && !solutionNameMatchesUnityProjectName(project)
+ val isUnityClassLibraryProject = isUnityProject && !solutionNameMatchesUnityProjectName(project)
companion object {
fun getInstance(project: Project) = project.getComponent()
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorProfileState.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorProfileState.kt
index 512f70175..c4dd96d97 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorProfileState.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorProfileState.kt
@@ -11,11 +11,11 @@ import com.intellij.util.ui.UIUtil
import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rd.util.reactive.AddRemove
import com.jetbrains.rd.util.reactive.hasTrueValue
-import com.jetbrains.rider.UnityProjectDiscoverer
import com.jetbrains.rider.debugger.DebuggerHelperHost
import com.jetbrains.rider.debugger.DebuggerInitializingState
import com.jetbrains.rider.debugger.DebuggerWorkerProcessHandler
import com.jetbrains.rider.debugger.RiderDebugActiveDotNetSessionsTracker
+import com.jetbrains.rider.isUnityProject
import com.jetbrains.rider.model.rdUnityModel
import com.jetbrains.rider.plugins.unity.UnityHost
import com.jetbrains.rider.plugins.unity.run.UnityDebuggerOutputListener
@@ -68,10 +68,12 @@ class UnityAttachToEditorProfileState(private val remoteConfiguration: UnityAtta
try {
if (!remoteConfiguration.updatePidAndPort()) {
logger.trace("Do not found Unity, starting new Unity Editor")
+
val model = UnityHost.getInstance(project).model
if (UnityInstallationFinder.getInstance(project).getApplicationPath() == null ||
- model.hasUnityReference.hasTrueValue && !UnityProjectDiscoverer.getInstance(project).isUnityProjectFolder)
+ model.hasUnityReference.hasTrueValue && !project.isUnityProject()) {
throw RuntimeConfigurationError("Cannot automatically determine Unity Editor instance. Please open the project in Unity and try again.")
+ }
val args = getUnityWithProjectArgs(project)
if (remoteConfiguration.play) {
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
index 3833e122f..2ff7a6446 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
@@ -11,6 +11,7 @@ import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.options.SettingsEditor
import com.intellij.openapi.project.Project
import com.intellij.util.xmlb.annotations.Transient
+import com.jetbrains.rider.*
import com.jetbrains.rider.plugins.unity.run.UnityRunUtil
import com.jetbrains.rider.plugins.unity.util.*
import com.jetbrains.rider.run.configurations.remote.DotNetRemoteConfiguration
@@ -71,17 +72,22 @@ class UnityAttachToEditorRunConfiguration(project: Project, factory: Configurati
override fun checkSettingsBeforeRun() {
// This method lets us check settings before run. If we throw an instance of RuntimeConfigurationError, the Run
- // Configuration editor is displayed. It's called on the EDT, so theres' not a lot we can do - e.g. we can't get
+ // Configuration editor is displayed. It's called on the EDT, so there's not a lot we can do - e.g. we can't get
// a process list.
// If we already have a pid, that means this run configuration has been launched before, and we've successfully
- // attached to a process. Use it again.
+ // attached to a process. Use it again. If the pid is out of date (highly unlikely), we'll do our best to find
+ // the process again
if (pid != null) {
return
}
// Verify that we have an EditorInstance.json. If we don't, we can't easily tell that any running instances are
// for our project.
+ // This means:
+ // * If Unity isn't running, we show the dialog. We never try to launch Unity ourselves
+ // * If EditorInstance.json doesn't exist (for some reason), we show the dialog
+ // * All other scenarios, we have EditorInstance.json and know exactly which instance to attach to
val editorInstanceJson = EditorInstanceJson.getInstance(project)
if (editorInstanceJson.status != EditorInstanceJsonStatus.Valid) {
throw RuntimeConfigurationError("Unable to automatically discover correct Unity Editor to debug")
@@ -92,19 +98,36 @@ class UnityAttachToEditorRunConfiguration(project: Project, factory: Configurati
val processList = OSProcessUtil.getProcessList()
- // Try to reuse the previous process ID, if it's still valid, then fall back to finding the process
- // automatically. Theoretically, there is a tiny chance the previous process has died, and the process ID has
- // been recycled for a new process that just happens to be a Unity process. Practically, this is not likely
port = -1
- pid = checkValidEditorInstance(pid, processList) ?: findUnityEditorInstanceFromEditorInstanceJson(processList)
- if (pid == null) {
- return false
+
+ try {
+ // Try to reuse the previously attached process ID, if it's still valid. If we don't have a previous pid, or
+ // the process is no longer valid, try to find the best match, via EditorInstance.json or project name.
+ // The only way we'll match on project name is if a previously cached pid turns out to be invalid, and the
+ // new process hasn't created an EditorInstance.json for some reason.
+ pid = checkValidEditorProcess(pid, processList)
+ ?: findUnityEditorProcessFromEditorInstanceJson(processList)
+ ?: findUnityEditorProcessFromProjectName(processList)
+ if (pid == null) {
+ return false
+ }
+ port = convertPidToDebuggerPort(pid!!)
+ return true
+ }
+ catch(t: Throwable) {
+ pid = null
+ throw t
}
- port = convertPidToDebuggerPort(pid!!)
- return true
}
- private fun findUnityEditorInstanceFromEditorInstanceJson(processList: Array): Int? {
+ private fun checkValidEditorProcess(pid: Int?, processList: Array): Int? {
+ if (pid != null && UnityRunUtil.isValidUnityEditorProcess(pid, processList)) {
+ return pid
+ }
+ return null
+ }
+
+ private fun findUnityEditorProcessFromEditorInstanceJson(processList: Array): Int? {
val editorInstanceJson = EditorInstanceJson.getInstance(project)
if (editorInstanceJson.validateStatus(processList) == EditorInstanceJsonStatus.Valid) {
return editorInstanceJson.contents!!.process_id
@@ -113,15 +136,35 @@ class UnityAttachToEditorRunConfiguration(project: Project, factory: Configurati
return null
}
- private fun checkValidEditorInstance(pid: Int?, processList: Array): Int? {
- if (pid != null && UnityRunUtil.isValidUnityEditorProcess(pid, processList)) {
- return pid
- }
- return null
- }
+ private fun findUnityEditorProcessFromProjectName(processList: Array): Int? {
+ // This only works if we can figure out the project name for a running process. This might not succeed on
+ // Windows, if the process is started without appropriate command line args.
+ val unityProcesses = processList.filter { UnityRunUtil.isUnityEditorProcess(it) }
+ val map = UnityRunUtil.getAllUnityProcessInfo(unityProcesses, project)
+
+ // If we're a generated project, or a class library project that lives in the root of a Unity project alongside
+ // a generated project, we can use the project dir as the expected project name.
+ if (project.isUnityProject()) {
+ val expectedProjectName = project.projectDir.name
+ val entry = map.entries.firstOrNull { expectedProjectName.equals(it.value.projectName, true) }
+ if (entry != null) {
+ return entry.key
+ }
- override fun checkConfiguration() {
- // Too expensive to check here?
+ // We don't have a cached pid from a previous debug session, we don't have EditorInstance.json, we can't
+ // find a process with a matching project name. Best guess fallback is to attach to an unnamed project
+ val noNameProjects = map.entries.filter { it.value.projectName == null }
+ if (noNameProjects.count() == 1) {
+ return noNameProjects[0].key
+ }
+
+ return null
+ }
+ else {
+ // We're a class library project in a standalone directory. We can't guess the project name, and it's best
+ // not to attach to a random editor
+ throw RuntimeConfigurationError("Unable to automatically discover correct Unity Editor to debug")
+ }
}
override fun readExternal(element: Element) {
From f6e24c1586d1a00565b50a6bc433396d110a09fa Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Mon, 16 Dec 2019 10:26:18 +0000
Subject: [PATCH 10/14] Select Unity instance based on project name
---
.../run/configurations/UnityAttachToEditorViewModel.kt | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
index 6b1e814f4..a1dbd0664 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
@@ -8,9 +8,11 @@ import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rd.util.reactive.IProperty
import com.jetbrains.rd.util.reactive.Property
import com.jetbrains.rd.util.reactive.ViewableList
+import com.jetbrains.rider.isUnityProject
import com.jetbrains.rider.plugins.unity.run.UnityRunUtil
import com.jetbrains.rider.plugins.unity.util.EditorInstanceJson
import com.jetbrains.rider.plugins.unity.util.EditorInstanceJsonStatus
+import com.jetbrains.rider.projectDir
import com.jetbrains.rider.util.idea.application
class UnityAttachToEditorViewModel(val lifetime: Lifetime, private val project: Project) {
@@ -38,8 +40,11 @@ class UnityAttachToEditorViewModel(val lifetime: Lifetime, private val project:
editorInstanceJsonStatus.set(editorInstanceJson.validateStatus(processList))
pid.value = if (editorInstanceJsonStatus.value != EditorInstanceJsonStatus.Valid && editors.count() == 1) {
editors[0].pid
- } else {
+ } else if (editorInstanceJson.status == EditorInstanceJsonStatus.Valid) {
editorInstanceJson.contents?.process_id
+ } else {
+ // If we're a class library project in the same folder as a Unity project, we can still guess the name
+ editors.firstOrNull { project.projectDir.name.equals(it.projectName, true) }?.pid
}
}, ModalityState.any())
}
From 1ba9bc79123d70559cde87eb2712a657dd08f340 Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Mon, 16 Dec 2019 11:04:58 +0000
Subject: [PATCH 11/14] Only show dialog for class library projects
---
.../com/jetbrains/rider/UnityProjectDiscoverer.kt | 5 ++++-
.../UnityAttachToEditorRunConfiguration.kt | 13 +++----------
.../configurations/UnityAttachToEditorViewModel.kt | 1 -
3 files changed, 7 insertions(+), 12 deletions(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt b/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt
index 8b4b5da61..7f7ea246f 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt
@@ -1,6 +1,7 @@
package com.jetbrains.rider
import com.intellij.openapi.project.Project
+import com.jetbrains.rd.util.reactive.valueOrDefault
import com.jetbrains.rdclient.util.idea.LifetimedProjectComponent
import com.jetbrains.rider.model.RdExistingSolution
import com.jetbrains.rider.plugins.unity.UnityHost
@@ -19,7 +20,8 @@ class UnityProjectDiscoverer(project: Project, unityHost: UnityHost) : Lifetimed
// anywhere)
val isUnityProject = isUnityProjectFolder && isCorrectlyLoadedSolution(project)
val isUnityGeneratedProject = isUnityProject && solutionNameMatchesUnityProjectName(project)
- val isUnityClassLibraryProject = isUnityProject && !solutionNameMatchesUnityProjectName(project)
+ val isUnitySidecarProject = isUnityProject && !solutionNameMatchesUnityProjectName(project)
+ val isUnityClassLibraryProject = hasUnityReference.valueOrDefault(false) && isCorrectlyLoadedSolution(project)
companion object {
fun getInstance(project: Project) = project.getComponent()
@@ -58,5 +60,6 @@ class UnityProjectDiscoverer(project: Project, unityHost: UnityHost) : Lifetimed
}
fun Project.isUnityGeneratedProject() = UnityProjectDiscoverer.getInstance(this).isUnityGeneratedProject
+fun Project.isUnityClassLibraryProject() = UnityProjectDiscoverer.getInstance(this).isUnityClassLibraryProject
fun Project.isUnityProject()= UnityProjectDiscoverer.getInstance(this).isUnityProject
fun Project.isUnityProjectFolder()= UnityProjectDiscoverer.getInstance(this).isUnityProjectFolder
\ No newline at end of file
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
index 2ff7a6446..c13da77ee 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
@@ -82,14 +82,9 @@ class UnityAttachToEditorRunConfiguration(project: Project, factory: Configurati
return
}
- // Verify that we have an EditorInstance.json. If we don't, we can't easily tell that any running instances are
- // for our project.
- // This means:
- // * If Unity isn't running, we show the dialog. We never try to launch Unity ourselves
- // * If EditorInstance.json doesn't exist (for some reason), we show the dialog
- // * All other scenarios, we have EditorInstance.json and know exactly which instance to attach to
- val editorInstanceJson = EditorInstanceJson.getInstance(project)
- if (editorInstanceJson.status != EditorInstanceJsonStatus.Valid) {
+ // If we're a class library project that isn't in a Unity project folder, we can't guess at the correct project
+ // to attach to, so throw an error and show the dialog
+ if (project.isUnityClassLibraryProject() && !project.isUnityProjectFolder()) {
throw RuntimeConfigurationError("Unable to automatically discover correct Unity Editor to debug")
}
}
@@ -103,8 +98,6 @@ class UnityAttachToEditorRunConfiguration(project: Project, factory: Configurati
try {
// Try to reuse the previously attached process ID, if it's still valid. If we don't have a previous pid, or
// the process is no longer valid, try to find the best match, via EditorInstance.json or project name.
- // The only way we'll match on project name is if a previously cached pid turns out to be invalid, and the
- // new process hasn't created an EditorInstance.json for some reason.
pid = checkValidEditorProcess(pid, processList)
?: findUnityEditorProcessFromEditorInstanceJson(processList)
?: findUnityEditorProcessFromProjectName(processList)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
index a1dbd0664..e4d263148 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorViewModel.kt
@@ -8,7 +8,6 @@ import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rd.util.reactive.IProperty
import com.jetbrains.rd.util.reactive.Property
import com.jetbrains.rd.util.reactive.ViewableList
-import com.jetbrains.rider.isUnityProject
import com.jetbrains.rider.plugins.unity.run.UnityRunUtil
import com.jetbrains.rider.plugins.unity.util.EditorInstanceJson
import com.jetbrains.rider.plugins.unity.util.EditorInstanceJsonStatus
From 429569b89ababcfe6b6c33bae37f500cbd00ddde Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Mon, 16 Dec 2019 11:20:24 +0000
Subject: [PATCH 12/14] Fix showing dialog for class library projects
---
.../kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt | 8 +++++++-
.../configurations/UnityAttachToEditorRunConfiguration.kt | 8 ++++++--
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt b/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt
index 7f7ea246f..fabe86f19 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/UnityProjectDiscoverer.kt
@@ -21,7 +21,13 @@ class UnityProjectDiscoverer(project: Project, unityHost: UnityHost) : Lifetimed
val isUnityProject = isUnityProjectFolder && isCorrectlyLoadedSolution(project)
val isUnityGeneratedProject = isUnityProject && solutionNameMatchesUnityProjectName(project)
val isUnitySidecarProject = isUnityProject && !solutionNameMatchesUnityProjectName(project)
- val isUnityClassLibraryProject = hasUnityReference.valueOrDefault(false) && isCorrectlyLoadedSolution(project)
+
+ // Note that this will only return a sensible value once the solution + backend have finished loading
+ val isUnityClassLibraryProject: Boolean?
+ get() {
+ val hasReference = hasUnityReference.valueOrNull ?: return null
+ return hasReference && isCorrectlyLoadedSolution(project)
+ }
companion object {
fun getInstance(project: Project) = project.getComponent()
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
index c13da77ee..1e78eb11b 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/run/configurations/UnityAttachToEditorRunConfiguration.kt
@@ -83,8 +83,12 @@ class UnityAttachToEditorRunConfiguration(project: Project, factory: Configurati
}
// If we're a class library project that isn't in a Unity project folder, we can't guess at the correct project
- // to attach to, so throw an error and show the dialog
- if (project.isUnityClassLibraryProject() && !project.isUnityProjectFolder()) {
+ // to attach to, so throw an error and show the dialog. This value will be null until the backend has finished
+ // loading. However, because we're a Unity run configuration, we can safely assume we're a Unity project, and if
+ // we're not inside a Unity project folder, then we can't automatically attach, so throw an error and show the
+ // dialog
+ val isClassLibraryProject = project.isUnityClassLibraryProject()
+ if (!project.isUnityProjectFolder() && (isClassLibraryProject == null || isClassLibraryProject)) {
throw RuntimeConfigurationError("Unable to automatically discover correct Unity Editor to debug")
}
}
From 648a5bd95af0d899a6b64af7333a9edd94d095a5 Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Mon, 16 Dec 2019 11:30:37 +0000
Subject: [PATCH 13/14] Ensure EditorInstance isn't locked after reading
---
.../rider/plugins/unity/util/EditorInstanceJson.kt | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/util/EditorInstanceJson.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/util/EditorInstanceJson.kt
index f3c4ddb3c..c51e8a718 100644
--- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/util/EditorInstanceJson.kt
+++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/util/EditorInstanceJson.kt
@@ -49,8 +49,10 @@ data class EditorInstanceJson(val status: EditorInstanceJsonStatus, val contents
}
return try {
- val contents = Gson().fromJson(FileReader(file), EditorInstanceJsonContents::class.java)
- EditorInstanceJson(EditorInstanceJsonStatus.Valid, contents)
+ FileReader(file).use {
+ val contents = Gson().fromJson(it, EditorInstanceJsonContents::class.java)
+ EditorInstanceJson(EditorInstanceJsonStatus.Valid, contents)
+ }
} catch (e: IOException) {
logger.error("Error reading EditorInstance.json", e)
empty(EditorInstanceJsonStatus.Error)
From 42001c540d97eecbc0686f49cba597a12ef0c974 Mon Sep 17 00:00:00 2001
From: Matt Ellis
Date: Mon, 16 Dec 2019 11:43:26 +0000
Subject: [PATCH 14/14] Update CHANGELOG and plugin metadata
---
CHANGELOG.md | 17 ++++++++++++++---
rider/src/main/resources/META-INF/plugin.xml | 13 ++++++++++++-
2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c8e132539..ef7a0ca23 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,16 +7,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0).
This plugin has functionality that is common to both ReSharper and Rider. It also contains a plugin for the Unity editor that is used to communicate with Rider. Changes marked with a "Rider:" prefix are specific to Rider, while changes for the Unity editor plugin are marked with a "Unity editor:" prefix. No prefix means that the change is common to both Rider and ReSharper.
## 2019.3.1
-* [Commits](https://github.com/JetBrains/resharper-unity/compare/net193-eap7-rtm-2019.3.0...net193)
+* [Commits](https://github.com/JetBrains/resharper-unity/compare/net193-eap7-rtm-2019.3.0...net193-eap7-rtm-2019.3.0-rtm-2019.3.1)
* [Milestone](https://github.com/JetBrains/resharper-unity/milestone/33?closed=1)
### Added
-- Added proper file icons for `*.uxml` and `*.uss` ([RIDER-34788](https://youtrack.jetbrains.com/issue/RIDER-34788), [#1443](https://github.com/JetBrains/resharper-unity/pull/1443))
+- Rider: Added proper file icons for `*.uxml` and `*.uss` ([RIDER-34788](https://youtrack.jetbrains.com/issue/RIDER-34788), [#1443](https://github.com/JetBrains/resharper-unity/pull/1443))
### Changed
-- Entire plugin is no longer disabled if the CSS plugin is disabled ([RIDER-36523](https://youtrack.jetbrains.com/issue/RIDER-36523), [#1443](https://github.com/JetBrains/resharper-unity/pull/1443))
+- Rider: Entire plugin is no longer disabled if the CSS plugin is disabled ([RIDER-36523](https://youtrack.jetbrains.com/issue/RIDER-36523), [#1443](https://github.com/JetBrains/resharper-unity/pull/1443))
+- Rider: Make Attach to Unity Process dialog resizable ([#1446](https://github.com/JetBrains/resharper-unity/issues/1446), [#1450](https://github.com/JetBrains/resharper-unity/pull/1450))
+- Rider: Identify child processes by role in Attach to Unity Process dialog ([#1328](https://github.com/JetBrains/resharper-unity/issues/1328), [#1450](https://github.com/JetBrains/resharper-unity/pull/1450))
+
+### Fixed
+
+- Rider: Show correct project name when Unity started with certain command line on Windows ([#1450](https://github.com/JetBrains/resharper-unity/pull/1450))
+- Rider: Show correct project name when multiple Unity processes listed in Attach to Process popup list ([#1456](https://github.com/JetBrains/resharper-unity/issues/1456), [#1450](https://github.com/JetBrains/resharper-unity/pull/1450))
+- Rider: Fix exception in Attach to Unity Process dialog causing list to be empty ([#1454](https://github.com/JetBrains/resharper-unity/issues/1454), [#1450](https://github.com/JetBrains/resharper-unity/pull/1450))
+- Rider: Show run configuration dialog for Unity class library projects ([#1445](https://github.com/JetBrains/resharper-unity/issues/1445), [#1450](https://github.com/JetBrains/resharper-unity/pull/1450))
+- Rider: Fix finding existing Unity instance to debug ([RIDER-36256](https://youtrack.jetbrains.com/issue/RIDER-36256), [#1450](https://github.com/JetBrains/resharper-unity/pull/1450))
+- Rider: Fix `EditorInstance.json` being locked by Rider ([#1450](https://github.com/JetBrains/resharper-unity/pull/1450))
diff --git a/rider/src/main/resources/META-INF/plugin.xml b/rider/src/main/resources/META-INF/plugin.xml
index 22086b88a..391e8c92c 100644
--- a/rider/src/main/resources/META-INF/plugin.xml
+++ b/rider/src/main/resources/META-INF/plugin.xml
@@ -270,9 +270,20 @@
Changed:
- Entire plugin is no longer disabled if the CSS plugin is disabled (RIDER-36523, #1443)
+ - Rider: Make Attach to Unity Process dialog resizable (#1446, #1450)
+ - Rider: Identify child processes by role in Attach to Unity Process dialog (#1328, #1450)
+
+Fixed:
+
+ - Rider: Show correct project name when Unity started with certain command line on Windows (#1450)
+ - Rider: Show correct project name when multiple Unity processes listed in Attach to Process popup list (#1456, #1450)
+ - Rider: Fix exception in Attach to Unity Process dialog causing list to be empty (#1454, #1450)
+ - Rider: Show run configuration dialog for Unity class library projects (#1445, #1450)
+ - Rider: Fix finding existing Unity instance to debug (RIDER-36256, #1450)
+ - Rider: Fix EditorInstance.json being locked by Rider (#1450)
-See the CHANGELOG for more details and history.
+See the CHANGELOG for more details and history.
]]>