debugger: Reverse Python repr escaping (#50554)

Conrad Irwin and Nikolas Gauder created

Closes #37168

Authored-By: @ngauder

Release Notes:

- debugger: Unescape Python strings

Co-authored-by: Nikolas Gauder <nikolas.gauder@tum.de>

Change summary

crates/project/src/debugger/session.rs | 32 +++++++++++++++++++++++++++
1 file changed, 31 insertions(+), 1 deletion(-)

Detailed changes

crates/project/src/debugger/session.rs 🔗

@@ -2645,10 +2645,40 @@ impl Session {
         self.fetch(
             command,
             move |this, variables, cx| {
-                let Some(variables) = variables.log_err() else {
+                let Some(mut variables) = variables.log_err() else {
                     return;
                 };
 
+                if this.adapter.0.as_ref() == "Debugpy" {
+                    for variable in variables.iter_mut() {
+                        if variable.type_ == Some("str".into()) {
+                            // reverse Python repr() escaping
+                            let mut unescaped = String::with_capacity(variable.value.len());
+                            let mut chars = variable.value.chars();
+                            while let Some(c) = chars.next() {
+                                if c != '\\' {
+                                    unescaped.push(c);
+                                } else {
+                                    match chars.next() {
+                                        Some('\\') => unescaped.push('\\'),
+                                        Some('n') => unescaped.push('\n'),
+                                        Some('t') => unescaped.push('\t'),
+                                        Some('r') => unescaped.push('\r'),
+                                        Some('\'') => unescaped.push('\''),
+                                        Some('"') => unescaped.push('"'),
+                                        Some(c) => {
+                                            unescaped.push('\\');
+                                            unescaped.push(c);
+                                        }
+                                        None => {}
+                                    }
+                                }
+                            }
+                            variable.value = unescaped;
+                        }
+                    }
+                }
+
                 this.active_snapshot
                     .variables
                     .insert(variables_reference, variables);