1use super::*;
4
5impl DebugAdapter {
6 const SUPPORTED_COMMANDS: [&str; 37] = [
7 "initialize",
8 "launch",
9 "attach",
10 "disconnect",
11 "terminate",
12 "setBreakpoints",
13 "setFunctionBreakpoints",
14 "setExceptionBreakpoints",
15 "configurationDone",
16 "threads",
17 "stackTrace",
18 "scopes",
19 "variables",
20 "setVariable",
21 "continue",
22 "next",
23 "stepIn",
24 "stepOut",
25 "pause",
26 "evaluate",
27 "inlineValues",
28 "breakpointLocations",
29 "source",
30 "loadedSources",
31 "modules",
32 "completions",
33 "exceptionInfo",
34 "restart",
35 "setExpression",
36 "dataBreakpointInfo",
37 "setDataBreakpoints",
38 "cancel",
39 "stepInTargets",
40 "gotoTargets",
41 "goto",
42 "restartFrame",
43 "terminateThreads",
44 ];
45
46 pub fn handle_request(
51 &mut self,
52 request_seq: i64,
53 command: &str,
54 arguments: Option<Value>,
55 ) -> DapMessage {
56 tracing::debug!(command, arguments = ?arguments, "DAP request");
57
58 let response = self.dispatch_request(request_seq, command, arguments);
59
60 if command == "initialize" && Self::response_succeeded_for_command(&response, "initialize")
62 {
63 self.send_event("initialized", None);
64 }
65
66 response
67 }
68
69 pub fn handle_request_mock(
71 &mut self,
72 request_seq: i64,
73 command: &str,
74 arguments: Option<Value>,
75 ) -> DapMessage {
76 tracing::debug!(command, arguments = ?arguments, "DAP request (mock)");
77
78 let response = self.dispatch_request(request_seq, command, arguments);
79 if command == "initialize" && Self::response_succeeded_for_command(&response, "initialize")
80 {
81 self.send_event("initialized", None);
82 }
83 response
84 }
85
86 pub(super) fn dispatch_request(
87 &mut self,
88 request_seq: i64,
89 command: &str,
90 arguments: Option<Value>,
91 ) -> DapMessage {
92 let seq = self.next_seq();
93
94 match command {
95 "initialize" => self.handle_initialize(seq, request_seq, arguments),
96 "launch" => self.handle_launch(seq, request_seq, arguments),
97 "attach" => self.handle_attach(seq, request_seq, arguments),
98 "disconnect" => self.handle_disconnect(seq, request_seq, arguments),
99 "terminate" => self.handle_terminate(seq, request_seq, arguments),
100 "setBreakpoints" => self.handle_set_breakpoints(seq, request_seq, arguments),
101 "setFunctionBreakpoints" => {
102 self.handle_set_function_breakpoints(seq, request_seq, arguments)
103 }
104 "setExceptionBreakpoints" => {
105 self.handle_set_exception_breakpoints(seq, request_seq, arguments)
106 }
107 "configurationDone" => self.handle_configuration_done(seq, request_seq),
108 "threads" => self.handle_threads(seq, request_seq),
109 "stackTrace" => self.handle_stack_trace(seq, request_seq, arguments),
110 "scopes" => self.handle_scopes(seq, request_seq, arguments),
111 "variables" => self.handle_variables(seq, request_seq, arguments),
112 "setVariable" => self.handle_set_variable(seq, request_seq, arguments),
113 "continue" => self.handle_continue(seq, request_seq, arguments),
114 "next" => self.handle_next(seq, request_seq, arguments),
115 "stepIn" => self.handle_step_in(seq, request_seq, arguments),
116 "stepOut" => self.handle_step_out(seq, request_seq, arguments),
117 "pause" => self.handle_pause(seq, request_seq, arguments),
118 "evaluate" => self.handle_evaluate(seq, request_seq, arguments),
119 "inlineValues" => self.handle_inline_values(seq, request_seq, arguments),
120 "breakpointLocations" => self.handle_breakpoint_locations(seq, request_seq, arguments),
121 "source" => self.handle_source(seq, request_seq, arguments),
122 "loadedSources" => self.handle_loaded_sources(seq, request_seq, arguments),
123 "modules" => self.handle_modules(seq, request_seq, arguments),
124 "completions" => self.handle_completions(seq, request_seq, arguments),
125 "exceptionInfo" => self.handle_exception_info(seq, request_seq, arguments),
126 "restart" => self.handle_restart(seq, request_seq, arguments),
127 "setExpression" => self.handle_set_expression(seq, request_seq, arguments),
128 "dataBreakpointInfo" => self.handle_data_breakpoint_info(seq, request_seq, arguments),
129 "setDataBreakpoints" => self.handle_set_data_breakpoints(seq, request_seq, arguments),
130 "cancel" => self.handle_cancel(seq, request_seq, arguments),
131 "stepInTargets" => self.handle_step_in_targets(seq, request_seq, arguments),
132 "gotoTargets" => self.handle_goto_targets(seq, request_seq, arguments),
133 "goto" => self.handle_goto(seq, request_seq, arguments),
134 "restartFrame" => self.handle_restart_frame(seq, request_seq, arguments),
135 "terminateThreads" => self.handle_terminate_threads(seq, request_seq, arguments),
136 _ => DapMessage::Response {
137 seq,
138 request_seq,
139 success: false,
140 command: command.to_string(),
141 body: None,
142 message: Some(Self::unknown_command_message(command)),
143 },
144 }
145 }
146
147 fn unknown_command_message(command: &str) -> String {
148 if let Some(suggestion) = Self::suggested_command(command) {
149 format!("Unknown command: {command}. Did you mean '{suggestion}'?")
150 } else {
151 format!("Unknown command: {command}")
152 }
153 }
154
155 fn suggested_command(command: &str) -> Option<&'static str> {
156 if let Some(case_suggestion) = Self::SUPPORTED_COMMANDS
157 .iter()
158 .copied()
159 .find(|known| known.eq_ignore_ascii_case(command))
160 {
161 return Some(case_suggestion);
162 }
163
164 Self::SUPPORTED_COMMANDS
165 .iter()
166 .copied()
167 .filter_map(|known| {
168 let distance = Self::command_edit_distance(command, known);
169 (distance <= Self::suggestion_threshold(command, known))
170 .then_some((known, distance))
171 })
172 .min_by_key(|(known, distance)| (*distance, known.len()))
173 .map(|(known, _)| known)
174 }
175
176 fn suggestion_threshold(command: &str, known: &str) -> usize {
177 command.len().max(known.len()).saturating_div(4).clamp(1, 4)
178 }
179
180 fn command_edit_distance(left: &str, right: &str) -> usize {
181 let left = left.to_ascii_lowercase();
182 let right = right.to_ascii_lowercase();
183 let left_bytes = left.as_bytes();
184 let right_bytes = right.as_bytes();
185
186 let mut previous: Vec<usize> = (0..=right_bytes.len()).collect();
187 let mut current = vec![0; right_bytes.len() + 1];
188
189 for (left_index, left_byte) in left_bytes.iter().enumerate() {
190 current[0] = left_index + 1;
191
192 for (right_index, right_byte) in right_bytes.iter().enumerate() {
193 let substitution_cost = usize::from(left_byte != right_byte);
194 let deletion = previous[right_index + 1] + 1;
195 let insertion = current[right_index] + 1;
196 let substitution = previous[right_index] + substitution_cost;
197 current[right_index + 1] = deletion.min(insertion).min(substitution);
198 }
199
200 std::mem::swap(&mut previous, &mut current);
201 }
202
203 previous[right_bytes.len()]
204 }
205
206 pub(super) fn response_succeeded_for_command(
207 response: &DapMessage,
208 expected_command: &str,
209 ) -> bool {
210 matches!(
211 response,
212 DapMessage::Response {
213 success: true,
214 command,
215 ..
216 } if command == expected_command
217 )
218 }
219}