@@ -3,12 +3,8 @@ package processing.app.gradle
33import androidx.compose.runtime.mutableStateListOf
44import androidx.compose.runtime.mutableStateOf
55import com.sun.jdi.*
6- import com.sun.jdi.connect.AttachingConnector
7- import com.sun.jdi.event.ExceptionEvent
8- import com.sun.jdi.request.EventRequest
96import kotlinx.coroutines.CoroutineScope
107import kotlinx.coroutines.Dispatchers
11- import kotlinx.coroutines.delay
128import kotlinx.coroutines.launch
139import org.gradle.tooling.BuildLauncher
1410import org.gradle.tooling.GradleConnector
@@ -24,11 +20,7 @@ import processing.app.Messages
2420import java.io.InputStreamReader
2521import java.io.PipedInputStream
2622import java.io.PipedOutputStream
27- import kotlin.time.Duration.Companion.seconds
28- import kotlin.time.TimeSource
2923
30-
31- // TODO: Refactor, reduce its scope
3224// TODO: Move the error reporting to its own file
3325// TODO: Move the output filtering to its own file
3426abstract class GradleJob {
@@ -65,17 +57,10 @@ abstract class GradleJob{
6557 withCancellationToken(cancel.token())
6658 addStateListener()
6759 addDebugging()
68- if (Base .DEBUG ) {
69- setStandardOutput(System .out )
70- setStandardError(System .err)
71- } else {
72- setStandardOutput(outputStream)
73- setStandardError(errorStream)
74- }
60+ setStandardOutput(outputStream)
61+ setStandardError(errorStream)
7562 run ()
7663 }
77-
78-
7964 }catch (e: Exception ){
8065 Messages .log(" Error while running: ${e.message} " )
8166 }finally {
@@ -130,7 +115,6 @@ abstract class GradleJob{
130115 fun cancel (){
131116 cancel.cancel()
132117 }
133-
134118 private fun BuildLauncher.addStateListener (){
135119 addProgressListener(ProgressListener { event ->
136120 if (event is TaskStartEvent ) {
@@ -154,137 +138,36 @@ abstract class GradleJob{
154138 }
155139 }
156140 if (event is DefaultSingleProblemEvent ) {
141+ // TODO: Move to UI instead of printing
157142 if (event.definition.severity == Severity .ADVICE ) return @ProgressListener
158143 problems.add(event)
159144
160145 val path = (event.locations.firstOrNull() as DefaultFileLocation ? )?.path
161- // Not Trimming indent as the content might contain newlines
162- service?.editor?.console?.err?.println (
163- """
164- ${event.definition.id.displayName} :
165- ${event.contextualLabel.contextualLabel}
166-
167- ${event.details.details?.replace(path ? : " " , " " )}
168- ${event.solutions.joinToString(" \n " ) { it.solution }}
169- """
170- )
171- }
172- })
173- }
174-
175- private fun listenForExceptions (virtualMachine : VirtualMachine ){
176- scope.launch {
177- try {
178- val manager = virtualMachine.eventRequestManager()
179-
180- val request = manager.createExceptionRequest(null , false , true )
181- request.setSuspendPolicy(EventRequest .SUSPEND_EVENT_THREAD )
182- request.enable()
183-
184- val queue = virtualMachine.eventQueue()
185- while (true ) {
186- val eventSet = queue.remove()
187- for (event in eventSet) {
188- if (event is ExceptionEvent ) {
189- printExceptionDetails(event)
190- event.thread().resume()
191- }
192- }
193- eventSet.resume()
194- }
195- } catch (e: Exception ) {
196- Messages .log(" Error while listening for exceptions: ${e.message} " )
197- }
198-
199- }
200- }
201- fun printExceptionDetails (event : ExceptionEvent ) {
202- val exception = event.exception()
203- val thread = event.thread()
204- val location = event.location()
205- val stackFrames = thread.frames()
206-
207- println (" \n 🚨 Exception Caught 🚨" )
208- println (" Type : ${exception.referenceType().name()} " )
209- // println("Message : ${getExceptionMessage(exception)}")
210- println (" Thread : ${thread.name()} " )
211- println (" Location : ${location.sourcePath()} :${location.lineNumber()} \n " )
212146
213- // Separate stack frames
214- val userFrames = mutableListOf<StackFrame >()
215- val processingFrames = mutableListOf<StackFrame >()
147+ val header = """
148+ ${event.definition.id.displayName} :
149+ ${event.contextualLabel.contextualLabel}
150+ """ .trimIndent()
216151
217- stackFrames.forEach { frame ->
218- val className = frame.location().declaringType().name()
219- if (className.startsWith(" processing." )) {
220- processingFrames.add(frame)
221- } else {
222- userFrames.add(frame)
152+ val details = event.details.details?.replace(path ? : " " , " " )
153+ val solutions = event.solutions.joinToString(" \n " ) { it.solution }
154+ val content = " $header \n $details \n $solutions "
155+ service?.editor?.console?.err?.println (content)
223156 }
224- }
225-
226- // Print user frames first
227- println (" 🔍 Stacktrace (Your Code First):" )
228- userFrames.forEachIndexed { index, frame -> printStackFrame(index, frame) }
229-
230- // Print Processing frames second
231- if (processingFrames.isNotEmpty()) {
232- println (" \n 🔧 Processing Stacktrace (Hidden Initially):" )
233- processingFrames.forEachIndexed { index, frame -> printStackFrame(index, frame) }
234- }
235-
236- println (" ──────────────────────────────────\n " )
237- }
238-
239- fun printStackFrame (index : Int , frame : StackFrame ) {
240- val location = frame.location()
241- val method = location.method()
242- println (" #$index ${location.sourcePath()} :${location.lineNumber()} -> ${method.declaringType().name()} .${method.name()} ()" )
243- }
244-
245- // Extracts the exception's message
246- fun getExceptionMessage (exception : ObjectReference ): String {
247- val messageMethod = exception.referenceType().methodsByName(" getMessage" ).firstOrNull() ? : return " Unknown"
248- val messageValue = exception.invokeMethod(null , messageMethod, emptyList(), ObjectReference .INVOKE_SINGLE_THREADED )
249- return (messageValue as ? StringReference )?.value() ? : " Unknown"
157+ })
250158 }
251159
252-
253- private fun BuildLauncher.addDebugging (){
160+ fun BuildLauncher.addDebugging () {
254161 addProgressListener(ProgressListener { event ->
255162 if (event !is TaskStartEvent ) return @ProgressListener
256163 if (event.descriptor.name != " :run" ) return @ProgressListener
257164
258- try {
259- val port = service?.debugPort.toString()
260- Messages .log(" Attaching to VM $port " )
261- val connector = Bootstrap .virtualMachineManager().allConnectors()
262- .firstOrNull { it.name() == " com.sun.jdi.SocketAttach" }
263- as AttachingConnector ?
264- ? : throw IllegalStateException (" No socket attach connector found" )
265- val args = connector.defaultArguments()
266- args[" port" ]?.setValue(port)
267-
268- // Try to attach the debugger, retrying if it fails
269- scope.launch {
270- val start = TimeSource .Monotonic .markNow()
271- while (start.elapsedNow() < 10 .seconds) {
272- try {
273- val sketch = connector.attach(args)
274- vm.value = sketch
275- sketch.resume()
276- Messages .log(" Attached to VM: ${sketch.name()} " )
277- listenForExceptions(sketch)
278- break
279- } catch (e: Exception ) {
280- Messages .log(" Error while attaching to VM: ${e.message} ... Retrying" )
281- }
282- delay(250 )
283- }
284- }
285- } catch (e: Exception ) {
286- Messages .log(" Error while attaching to VM: ${e.message} " )
165+ scope.launch {
166+ val debugger = Debugger .connect(service?.debugPort) ? : return @launch
167+ vm.value = debugger
168+ Exceptions .listen(debugger)
287169 }
170+
288171 })
289172 }
290173}
0 commit comments