mirror of
https://github.com/actions/runner.git
synced 2026-03-12 17:57:13 -04:00
Add completions support and friendly errors for unsupported commands
Completions (SupportsCompletionsRequest = true):
- Respond to DAP 'completions' requests with our DSL commands
(help, help("run"), run(...)) so they appear in the debug
console autocomplete across all DAP clients
- Add CompletionsArguments, CompletionItem, and
CompletionsResponseBody to DapMessages
Friendly error messages for unsupported stepping commands:
- stepIn: explain that Actions debug at the step level
- stepOut: suggest using 'continue'
- stepBack/reverseContinue: note 'not yet supported'
- pause: explain automatic pausing at step boundaries
The DAP spec does not provide a capability to hide stepIn/stepOut
buttons (they are considered fundamental operations). The best
server-side UX is clear error messages when clients send them.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -139,6 +139,12 @@ namespace GitHub.Runner.Worker.Dap
|
||||
"next" => HandleNext(request),
|
||||
"setBreakpoints" => HandleSetBreakpoints(request),
|
||||
"setExceptionBreakpoints" => HandleSetExceptionBreakpoints(request),
|
||||
"completions" => HandleCompletions(request),
|
||||
"stepIn" => CreateResponse(request, false, "Step In is not supported. Actions jobs debug at the step level — use 'next' to advance to the next step.", body: null),
|
||||
"stepOut" => CreateResponse(request, false, "Step Out is not supported. Actions jobs debug at the step level — use 'continue' to resume.", body: null),
|
||||
"stepBack" => CreateResponse(request, false, "Step Back is not yet supported.", body: null),
|
||||
"reverseContinue" => CreateResponse(request, false, "Reverse Continue is not yet supported.", body: null),
|
||||
"pause" => CreateResponse(request, false, "Pause is not supported. The debugger pauses automatically at step boundaries.", body: null),
|
||||
_ => CreateResponse(request, false, $"Unsupported command: {request.Command}", body: null)
|
||||
};
|
||||
}
|
||||
@@ -195,7 +201,7 @@ namespace GitHub.Runner.Worker.Dap
|
||||
SupportsRestartFrame = false,
|
||||
SupportsGotoTargetsRequest = false,
|
||||
SupportsStepInTargetsRequest = false,
|
||||
SupportsCompletionsRequest = false,
|
||||
SupportsCompletionsRequest = true,
|
||||
SupportsModulesRequest = false,
|
||||
SupportsTerminateRequest = false,
|
||||
SupportTerminateDebuggee = false,
|
||||
@@ -466,6 +472,52 @@ namespace GitHub.Runner.Worker.Dap
|
||||
}
|
||||
}
|
||||
|
||||
private Response HandleCompletions(Request request)
|
||||
{
|
||||
var args = request.Arguments?.ToObject<CompletionsArguments>();
|
||||
var text = args?.Text ?? string.Empty;
|
||||
|
||||
var items = new List<CompletionItem>();
|
||||
|
||||
// Offer DSL commands when the user is starting to type
|
||||
if (string.IsNullOrEmpty(text) || "help".StartsWith(text, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
items.Add(new CompletionItem
|
||||
{
|
||||
Label = "help",
|
||||
Text = "help",
|
||||
Detail = "Show available debug console commands",
|
||||
Type = "function"
|
||||
});
|
||||
}
|
||||
if (string.IsNullOrEmpty(text) || "help(\"run\")".StartsWith(text, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
items.Add(new CompletionItem
|
||||
{
|
||||
Label = "help(\"run\")",
|
||||
Text = "help(\"run\")",
|
||||
Detail = "Show help for the run command",
|
||||
Type = "function"
|
||||
});
|
||||
}
|
||||
if (string.IsNullOrEmpty(text) || "run(".StartsWith(text, System.StringComparison.OrdinalIgnoreCase)
|
||||
|| text.StartsWith("run(", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
items.Add(new CompletionItem
|
||||
{
|
||||
Label = "run(\"...\")",
|
||||
Text = "run(\"",
|
||||
Detail = "Execute a script (like a workflow run step)",
|
||||
Type = "function"
|
||||
});
|
||||
}
|
||||
|
||||
return CreateResponse(request, true, body: new CompletionsResponseBody
|
||||
{
|
||||
Targets = items
|
||||
});
|
||||
}
|
||||
|
||||
private Response HandleContinue(Request request)
|
||||
{
|
||||
Trace.Info("Continue command received");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
@@ -885,6 +885,103 @@ namespace GitHub.Runner.Worker.Dap
|
||||
|
||||
#endregion
|
||||
|
||||
#region Completions Request/Response
|
||||
|
||||
/// <summary>
|
||||
/// Arguments for 'completions' request.
|
||||
/// </summary>
|
||||
public class CompletionsArguments
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns completions in the scope of this stack frame.
|
||||
/// </summary>
|
||||
[JsonProperty("frameId", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? FrameId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// One or more source lines. Typically this is the text users have typed
|
||||
/// in the debug console (REPL).
|
||||
/// </summary>
|
||||
[JsonProperty("text")]
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The position within 'text' for which to determine the completion proposals.
|
||||
/// It is measured in UTF-16 code units.
|
||||
/// </summary>
|
||||
[JsonProperty("column")]
|
||||
public int Column { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A line for which to determine the completion proposals.
|
||||
/// If missing the first line of the text is assumed.
|
||||
/// </summary>
|
||||
[JsonProperty("line", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? Line { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A completion item in the debug console.
|
||||
/// </summary>
|
||||
public class CompletionItem
|
||||
{
|
||||
/// <summary>
|
||||
/// The label of this completion item.
|
||||
/// </summary>
|
||||
[JsonProperty("label")]
|
||||
public string Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If text is returned and not an empty string, then it is inserted instead
|
||||
/// of the label.
|
||||
/// </summary>
|
||||
[JsonProperty("text", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A human-readable string with additional information about this item.
|
||||
/// </summary>
|
||||
[JsonProperty("detail", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Detail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The item's type. Typically the client uses this information to render the item
|
||||
/// in the UI with an icon.
|
||||
/// Values: 'method', 'function', 'constructor', 'field', 'variable', 'class',
|
||||
/// 'interface', 'module', 'property', 'unit', 'value', 'enum', 'keyword',
|
||||
/// 'snippet', 'text', 'color', 'file', 'reference', 'customcolor'
|
||||
/// </summary>
|
||||
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Start position (0-based) within 'text' that should be replaced
|
||||
/// by the completion text.
|
||||
/// </summary>
|
||||
[JsonProperty("start", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? Start { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of the text that should be replaced by the completion text.
|
||||
/// </summary>
|
||||
[JsonProperty("length", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? Length { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response body for 'completions' request.
|
||||
/// </summary>
|
||||
public class CompletionsResponseBody
|
||||
{
|
||||
/// <summary>
|
||||
/// The possible completions.
|
||||
/// </summary>
|
||||
[JsonProperty("targets")]
|
||||
public List<CompletionItem> Targets { get; set; } = new List<CompletionItem>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user