[lldb-vscode] Custom request to execute LLDB commands

Hello,

I wanted to get input on whether the maintainers of lldb-vscode would be open to accepting a patch for a custom DAP request. VS Code has an API for extensions to send a custom DAP request, i.e. a request not defined in the DAP spec that only their specific debug adapter understands. lldb-vscode is only a debug adapter and has no extension capable of sending custom requests so this would only be useful for those who implement an extension around lldb-vscode. I’ve been using a private extension that implements multiple custom requests but most of the functionality centers around executing lldb commands so I think a single custom request to execute LLDB commands would be sufficient and more generally useful.

There is similar functionality available with some launch configuration properties (e.g. initCommands, attachCommands, etc) but those need to be provided before the start of the debug session. An extension using the custom request would be able to provide commands during the debug session to enable expensive features only when needed.

Another existing approach with similar functionality is using the evaluate request to execute LLDB commands. This can get us most of the way there but this request is designed to be user facing so processing output can be fragile.

e.g.

// Evaluate response body
{
  result: "(lldb) process status
error: invalid target, create a target using the 'target create' command
"
  variablesReference: 0
}

vs

// Potential custom request response body
{
  success: false,
  message: "invalid target, create a target using the 'target create' command"
}

The spec for the custom request I’m proposing is as follows:

// "RunCommandsRequest": {
//   "allOf": [ { "$ref": "#/definitions/Request" }, {
//     "type": "object",
//     "description": "RunCommands request; value of command field is
//     'runCommands'. Runs each of the commands given with the LLDB command
//     interpreter.",
//     "properties": {
//       "command": {
//         "type": "string",
//         "enum": [ "runCommands" ]
//       },
//       "arguments": {
//         "$ref": "#/definitions/RunCommandsArguments"
//       }
//     },
//     "required": [ "command", "arguments" ]
//   }]
// },
// "RunCommandsArguments": {
//   "type": "object",
//   "description": "Arguments for 'runCommands' request.",
//   "properties": {
//     "commands": {
//       "type": "array",
//       "items": {
//         "type": "string"
//       },
//       "description": "The LLDB commands to run."
//     },
//     "stopOnError": {
//       "type": "boolean",
//       "description": "If true, command execution will stop if one of the
//       commands fails."
//     },
//     "stopProcess": {
//       "type": "boolean",
//       "description": "If true, the process will be stopped prior to running
//       the LLDB commands if it isn't already. The process will be resumed
//       after execution if it wasn't paused prior to execution."
//     },
//     "logToConsole": {
//       "type": "boolean",
//       "description": "If true, the command and its output will be logged to
//       the debug console."
//     },
//     "prefix": {
//       "type": "string",
//       "description": "An optional string to prepend to the debug console
//       output. Ignored if 'logToConsole' is not true."
//     },
//   },
//   "required": [ "commands" ]
// },
// "RunCommandsResponse": {
//   "allOf": [ { "$ref": "#/definitions/Response" }, {
//     "type": "object",
//     "description": "Response to 'runCommands' request.",
//     "properties": {
//       "body": {
//         "type": "object",
//         "properties": {
//           "results": {
//             "type": "array",
//             "items": {
//               "$ref": "#/definitions/CommandResult"
//             },
//             "description": "The results of each command that was run."
//           }
//         },
//         "required": [ "results" ]
//       },
//     },
//     "required": [ "body" ]
//   }]
// },
// "CommandResult": {
//   "type": "object",
//   "description": "The result from running an LLDB command.",
//   "properties": {
//     "success": {
//       "type": "boolean",
//       "description": "Whether the command interpreter reported success
//       running the command."
//     },
//     "message": {
//       "type": "string",
//       "description": "The command output or the error message depending on
//       the value of 'success'."
//     },
//   }
// }

An example of how this can be used from an extension:

vscode.session.customRequest('runCommands', {
  commands: [
    "process load /path/to/my_awesome_library.dylib",
    "expression myAwesomeLibrary_myAwesomeFunction()"
  ],
  stopOnError: true, // Doesn't make sense to execute myAwesomeFunction if the dylib failed to load
  stopProcess: true, // Loading a dylib requires the process be paused
  logToConsole: false, // User probably doesn't care about specifics of this feature
  prefix: "", // logToConsole is false so this can be omitted entirely.
});

If there aren’t any major concerns or reluctance to adding a non-standard DAP request I’d be happy to work on a patch for this.