Skip to content

Commit 4c791dc

Browse files
authored
Merge pull request #3325 from jsternberg/dap-alternate-stepping
dap: refactor how step in/step out works
2 parents 4dd5dd5 + 1e3c447 commit 4c791dc

File tree

4 files changed

+309
-329
lines changed

4 files changed

+309
-329
lines changed

dap/adapter.go

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -161,26 +161,21 @@ func (d *Adapter[C]) Next(c Context, req *dap.NextRequest, resp *dap.NextRespons
161161
}
162162

163163
func (d *Adapter[C]) StepIn(c Context, req *dap.StepInRequest, resp *dap.StepInResponse) error {
164-
var (
165-
subReq dap.NextRequest
166-
subResp dap.NextResponse
167-
)
164+
d.threadsMu.RLock()
165+
t := d.threads[req.Arguments.ThreadId]
166+
d.threadsMu.RUnlock()
168167

169-
subReq.Arguments.ThreadId = req.Arguments.ThreadId
170-
subReq.Arguments.SingleThread = req.Arguments.SingleThread
171-
subReq.Arguments.Granularity = req.Arguments.Granularity
172-
return d.Next(c, &subReq, &subResp)
168+
t.StepIn()
169+
return nil
173170
}
174171

175172
func (d *Adapter[C]) StepOut(c Context, req *dap.StepOutRequest, resp *dap.StepOutResponse) error {
176-
var (
177-
subReq dap.ContinueRequest
178-
subResp dap.ContinueResponse
179-
)
180-
181-
subReq.Arguments.ThreadId = req.Arguments.ThreadId
182-
subReq.Arguments.SingleThread = req.Arguments.SingleThread
183-
return d.Continue(c, &subReq, &subResp)
173+
d.threadsMu.RLock()
174+
t := d.threads[req.Arguments.ThreadId]
175+
d.threadsMu.RUnlock()
176+
177+
t.StepOut()
178+
return nil
184179
}
185180

186181
func (d *Adapter[C]) SetBreakpoints(c Context, req *dap.SetBreakpointsRequest, resp *dap.SetBreakpointsResponse) error {

dap/eval.go

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,43 +29,74 @@ func (d *Adapter[C]) Evaluate(ctx Context, req *dap.EvaluateRequest, resp *dap.E
2929
return nil
3030
}
3131

32-
var t *thread
33-
if req.Arguments.FrameId > 0 {
34-
if t = d.getThreadByFrameID(req.Arguments.FrameId); t == nil {
35-
return errors.Errorf("no thread with frame id %d", req.Arguments.FrameId)
36-
}
37-
} else {
38-
if t = d.getFirstThread(); t == nil {
39-
return errors.New("no paused thread")
40-
}
41-
}
42-
43-
cmd := d.replCommands(ctx, t, resp)
32+
var retErr error
33+
cmd := d.replCommands(ctx, req, resp, &retErr)
4434
cmd.SetArgs(args)
4535
cmd.SetErr(d.Out())
4636
if err := cmd.Execute(); err != nil {
47-
fmt.Fprintf(d.Out(), "ERROR: %+v\n", err)
37+
// This error should only happen if there was something command
38+
// related that malfunctioned as it will also print usage.
39+
// Normal errors should set retErr from replCommands.
40+
return err
4841
}
49-
return nil
42+
return retErr
5043
}
5144

52-
func (d *Adapter[C]) replCommands(ctx Context, t *thread, resp *dap.EvaluateResponse) *cobra.Command {
53-
rootCmd := &cobra.Command{}
45+
func (d *Adapter[C]) replCommands(ctx Context, req *dap.EvaluateRequest, resp *dap.EvaluateResponse, retErr *error) *cobra.Command {
46+
rootCmd := &cobra.Command{
47+
SilenceErrors: true,
48+
}
5449

55-
execCmd := &cobra.Command{
56-
Use: "exec",
57-
RunE: func(cmd *cobra.Command, args []string) error {
58-
if !d.supportsExec {
59-
return errors.New("cannot exec without runInTerminal client capability")
60-
}
61-
return t.Exec(ctx, args, resp)
62-
},
50+
execCmd, execOpts := replCmd(ctx, "exec", resp, retErr, d.execCmd)
51+
execCmd.PreRun = func(cmd *cobra.Command, args []string) {
52+
execOpts.FrameID = req.Arguments.FrameId
6353
}
6454
rootCmd.AddCommand(execCmd)
6555
return rootCmd
6656
}
6757

68-
func (t *thread) Exec(ctx Context, args []string, eresp *dap.EvaluateResponse) (retErr error) {
58+
type execOptions struct {
59+
FrameID int
60+
}
61+
62+
func (d *Adapter[C]) execCmd(ctx Context, args []string, flags execOptions) (string, error) {
63+
if !d.supportsExec {
64+
return "", errors.New("cannot exec without runInTerminal client capability")
65+
}
66+
67+
var t *thread
68+
if flags.FrameID > 0 {
69+
if t = d.getThreadByFrameID(flags.FrameID); t == nil {
70+
return "", errors.Errorf("no thread with frame id %d", flags.FrameID)
71+
}
72+
} else {
73+
if t = d.getFirstThread(); t == nil {
74+
return "", errors.New("no paused thread")
75+
}
76+
}
77+
return t.Exec(ctx, args)
78+
}
79+
80+
func replCmd[Flags any, RetVal any](ctx Context, name string, resp *dap.EvaluateResponse, retErr *error, fn func(ctx Context, args []string, flags Flags) (RetVal, error)) (*cobra.Command, *Flags) {
81+
flags := new(Flags)
82+
return &cobra.Command{
83+
Use: name,
84+
Run: func(cmd *cobra.Command, args []string) {
85+
v, err := fn(ctx, args, *flags)
86+
if err != nil {
87+
*retErr = err
88+
return
89+
}
90+
resp.Body.Result = fmt.Sprint(v)
91+
},
92+
}, flags
93+
}
94+
95+
func (t *thread) Exec(ctx Context, args []string) (message string, retErr error) {
96+
if t.rCtx == nil {
97+
return "", errors.New("no container context for exec")
98+
}
99+
69100
cfg := &build.InvokeConfig{Tty: true}
70101
if len(cfg.Entrypoint) == 0 && len(cfg.Cmd) == 0 {
71102
cfg.Entrypoint = []string{"/bin/sh"} // launch shell by default
@@ -75,7 +106,7 @@ func (t *thread) Exec(ctx Context, args []string, eresp *dap.EvaluateResponse) (
75106

76107
ctr, err := build.NewContainer(ctx, t.rCtx, cfg)
77108
if err != nil {
78-
return err
109+
return "", err
79110
}
80111
defer func() {
81112
if retErr != nil {
@@ -85,7 +116,7 @@ func (t *thread) Exec(ctx Context, args []string, eresp *dap.EvaluateResponse) (
85116

86117
dir, err := os.MkdirTemp("", "buildx-dap-exec")
87118
if err != nil {
88-
return err
119+
return "", err
89120
}
90121
defer func() {
91122
if retErr != nil {
@@ -96,7 +127,7 @@ func (t *thread) Exec(ctx Context, args []string, eresp *dap.EvaluateResponse) (
96127
socketPath := filepath.Join(dir, "s.sock")
97128
l, err := net.Listen("unix", socketPath)
98129
if err != nil {
99-
return err
130+
return "", err
100131
}
101132

102133
go func() {
@@ -121,11 +152,11 @@ func (t *thread) Exec(ctx Context, args []string, eresp *dap.EvaluateResponse) (
121152

122153
resp := ctx.Request(req)
123154
if !resp.GetResponse().Success {
124-
return errors.New(resp.GetResponse().Message)
155+
return "", errors.New(resp.GetResponse().Message)
125156
}
126157

127-
eresp.Body.Result = fmt.Sprintf("Started process attached to %s.", socketPath)
128-
return nil
158+
message = fmt.Sprintf("Started process attached to %s.", socketPath)
159+
return message, nil
129160
}
130161

131162
func (t *thread) runExec(l net.Listener, ctr *build.Container, cfg *build.InvokeConfig) {

0 commit comments

Comments
 (0)