Skip to content

Commit

Permalink
Collapse skipped frames
Browse files Browse the repository at this point in the history
  • Loading branch information
adpi2 committed Mar 25, 2024
1 parent 8e97536 commit 43a795d
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 3 deletions.
Expand Up @@ -10,6 +10,8 @@ import org.eclipse.lsp4j.Position
import org.eclipse.lsp4j.debug.InitializeRequestArguments
import org.eclipse.lsp4j.debug.InitializeRequestArgumentsPathFormat
import org.eclipse.lsp4j.debug.SourceBreakpoint
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive

/**
* The [[ClientConfigurationAdapter]] uses the client configuration coming from the initialize request
Expand All @@ -19,6 +21,7 @@ import org.eclipse.lsp4j.debug.SourceBreakpoint
* @param linesStartAt1 true if client line numbers start at 1
*/
private[debug] final case class ClientConfigurationAdapter(
clientId: Option[String],
pathFormat: String,
val linesStartAt1: Boolean,
sourceMapper: SourceMapper,
Expand All @@ -35,6 +38,33 @@ private[debug] final case class ClientConfigurationAdapter(
sourceMapper.mappedLineForClient(path, adaptedLine)
}

/**
* In the DAP specification, the presentationHint of a StackFrame can be
* 'normal', 'label' or 'subtle'. Most DAP implementations use 'subtle' to
* indicate that a frame is skipped by the debugger. The problem is that
* VSCode does not collapse 'subtle' frames, as other DAP clients do.
* Instead it collapses 'deemphasize' frames, even if it is not part of the
* spec.
*
* See https://github.com/microsoft/vscode/issues/206801
*/
def adaptStackTraceResponse(result: JsonObject): JsonObject = {
if (clientId.contains("vscode")) {
// For VSCode only, we hack the json result of the stack trace response
// to replace all occurrences of 'subtle' by 'deemphasize'.
val frames = result.get("stackFrames").getAsJsonArray()
for (i <- 0.until(frames.size)) {
val frame = frames.get(i).getAsJsonObject()
val presentationHint = Option(frame.get("presentationHint"))
.map(_.getAsJsonPrimitive.getAsString)
if (presentationHint.contains("subtle")) {
frame.add("presentationHint", new JsonPrimitive("deemphasize"))
}
}
}
result
}

def toLspPosition(breakpoint: SourceBreakpoint): Position = {
val line = breakpoint.getLine
// LSP Position is 0-based
Expand Down Expand Up @@ -79,6 +109,7 @@ private[debug] object ClientConfigurationAdapter {

def default(sourceMapper: SourceMapper): ClientConfigurationAdapter = {
ClientConfigurationAdapter(
None,
defautlPathFormat,
defaultLinesStartAt1,
sourceMapper,
Expand All @@ -95,6 +126,7 @@ private[debug] object ClientConfigurationAdapter {
.map(_.booleanValue)
.getOrElse(defaultLinesStartAt1)
ClientConfigurationAdapter(
Option(initRequest.getClientID),
pathFormat,
linesStartAt1,
sourceMapper,
Expand Down
Expand Up @@ -29,7 +29,7 @@ import org.eclipse.lsp4j.{debug => dap}
import org.eclipse.{lsp4j => l}

object DebugProtocol {
import scala.meta.internal.metals.debug.DapJsonParser._
import DapJsonParser._
val FirstMessageId = 1

val serverName = "dap-server"
Expand Down
Expand Up @@ -239,7 +239,7 @@ private[debug] final class DebugProxy(
// output window gets refreshed resulting in stale messages being printed on top, before
// any actual logs from the restarted process
case response @ DebugProtocol.StackTraceResponse(args) =>
import scala.meta.internal.metals.JsonParser._
import DapJsonParser._
for {
stackFrame <- args.getStackFrames
frameSource <- Option(stackFrame.getSource)
Expand All @@ -256,7 +256,8 @@ private[debug] final class DebugProxy(
mappedSourcePath
)
} frameSource.setPath(clientAdapter.adaptPathForClient(metalsSource))
response.setResult(args.toJson)
val result = clientAdapter.adaptStackTraceResponse(args.toJsonObject)
response.setResult(result)
for (frame <- args.getStackFrames()) {
frameIdToFrame.put(frame.getId, frame)
}
Expand Down

0 comments on commit 43a795d

Please sign in to comment.