1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#![allow(non_snake_case)]

use crate::{
    client::ExecuteProvider,
    jupyter_message::JupyterMessage,
    value_type::{InspectModule, InspectVariable, InspectVariableRequest},
    ExecutionResult, JupyterKernelProtocol, JupyterResult,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_lsp::dap::{
    DebugCapability, DisconnectArguments, Module, ModulesResponseBody, Request, Response, Variable, VariablesArguments,
    VariablesResponseBody,
};
use std::{collections::HashMap, ops::Deref};
use uuid::Uuid;

#[derive(Clone, Debug, Serialize)]
pub struct DebugInfoResponse {
    /// whether the debugger is started
    isStarted: bool,
    /// the hash method for code cell. Default is 'Murmur2'
    hashMethod: String,
    /// the seed for the hashing of code cells
    hashSeed: u32,
    /// prefix for temporary file names
    tmpFilePrefix: String,
    /// suffix for temporary file names
    tmpFileSuffix: String,
    /// breakpoints currently registered in the debugger
    breakpoints: Vec<SourceBreakpoints>,
    /// threads in which the debugger is currently in a stopped state
    stoppedThreads: Vec<i32>,
    /// whether the debugger supports rich rendering of variables
    richRendering: bool,
    /// exception names used to match leaves or nodes in a tree of exception
    exceptionPaths: Vec<String>,
}

impl DebugInfoResponse {
    pub fn new(start: bool) -> DebugInfoResponse {
        Self { isStarted: start, ..Default::default() }
    }
}

#[derive(Clone, Debug, Serialize)]
pub struct SourceBreakpoints {
    source: String,
    breakpoints: Vec<Breakpoint>,
}

#[derive(Clone, Debug, Serialize)]
pub struct Breakpoint {}

impl Default for DebugInfoResponse {
    fn default() -> Self {
        Self {
            isStarted: false,
            hashMethod: "Murmur2".to_string(),
            hashSeed: Uuid::new_v4().as_u128() as u32,
            tmpFilePrefix: "".to_string(),
            tmpFileSuffix: "".to_string(),
            breakpoints: vec![],
            stoppedThreads: vec![],
            richRendering: true,
            exceptionPaths: vec![],
        }
    }
}

#[derive(Clone, Debug, Serialize)]
pub struct DumpCell {
    sourcePath: String,
}

#[derive(Clone, Debug, Serialize)]
pub struct RichInspectVariables {
    data: HashMap<String, String>,
    metadata: HashMap<String, String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DebugSource {
    content: String,
}

impl From<InspectModule> for Module {
    fn from(value: InspectModule) -> Self {
        Module {
            id: value.id,
            name: value.name,
            path: value.path,
            is_optimized: false,
            is_user_code: false,
            version: "".to_string(),
        }
    }
}

impl From<InspectVariable> for Variable {
    fn from(value: InspectVariable) -> Self {
        Variable {
            name: value.name,
            value: value.value,
            typing: value.typing,
            evaluate_name: "".to_string(),
            variables_reference: value.id.map(|v| v.get()).unwrap_or(0),
            named_variables: value.named_variables,
            indexed_variables: value.indexed_variables,
            memory_reference: format!("{:x}", value.memory_reference),
        }
    }
}

impl JupyterMessage {
    pub(crate) async fn debug_response<K: JupyterKernelProtocol>(&self, kernel: ExecuteProvider<K>) -> JupyterResult<Value> {
        let request = self.recast::<Request>()?;
        let response = match request.command.as_str() {
            "debugInfo" => {
                if kernel.sockets.get_debug_mode() {
                    Response::success(request, DebugInfoResponse::new(true))?
                }
                else {
                    kernel.sockets.set_debug_mode(true);
                    Response::success(request, DebugInfoResponse::new(false))?
                }
            }
            "initialize" => Response::success(request, DebugCapability::default())?,
            // Root variable query event when first opened
            "inspectVariables" => {
                let runner = kernel.context.lock().await;
                Response::success(request, VariablesResponseBody::from_iter(runner.inspect_variables(None)))?
            }
            // Subquery event after manual click on variable
            "variables" => {
                let args = request.recast::<VariablesArguments>()?;
                let runner = kernel.context.lock().await;
                let variables = runner.inspect_variables(Some(InspectVariableRequest {
                    id: args.variables_reference,
                    filter: args.filter,
                    start: args.start,
                    limit: args.count,
                }));
                Response::success(request, VariablesResponseBody::from_iter(variables))?
            }
            "richInspectVariables" => {
                let runner = kernel.context.lock().await;
                let result = runner.inspect_details(&InspectVariable::default());
                Response::success(request, ExecutionResult::new(result.deref()))?
            }
            "source" => {
                let runner = kernel.context.lock().await;
                let content = runner.inspect_sources();
                Response::success(request, DebugSource { content })?
            }
            "dumpCell" => Response::success(request, DumpCell { sourcePath: "sourcePath".to_string() })?,
            "modules" => {
                let runner = kernel.context.lock().await;
                let modules = runner.inspect_modules(0);
                Response::success(request, ModulesResponseBody::from_iter(modules))?
            }
            "attach" => Response::success(request, "")?,
            "disconnect" => {
                // let runner = kernel.context.lock().await;
                let dis = request.recast::<DisconnectArguments>()?;
                tracing::info!("Disconnecting Debugger, {:?}", dis);
                Response::success(request, "")?
            }
            _ => {
                tracing::error!("Unknown DAP command: {}\n{:#?}", request.command, request.arguments);
                Value::Null
            }
        };
        Ok(response)
    }
}