lapce--lapce
641 行
20 KiB
Rust
641 行
20 KiB
Rust
use std::{
|
|
collections::{BTreeMap, HashMap},
|
|
fmt::Display,
|
|
path::PathBuf,
|
|
rc::Rc,
|
|
time::Instant,
|
|
};
|
|
|
|
use floem::{
|
|
ext_event::create_ext_action,
|
|
reactive::{Memo, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith},
|
|
views::VirtualVector,
|
|
};
|
|
use lapce_rpc::{
|
|
dap_types::{
|
|
self, DapId, RunDebugConfig, SourceBreakpoint, StackFrame, Stopped,
|
|
ThreadId, Variable,
|
|
},
|
|
proxy::ProxyResponse,
|
|
terminal::TermId,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
command::InternalCommand,
|
|
editor::location::{EditorLocation, EditorPosition},
|
|
window_tab::CommonData,
|
|
};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum RunDebugMode {
|
|
Run,
|
|
Debug,
|
|
}
|
|
|
|
impl Display for RunDebugMode {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let s = match self {
|
|
RunDebugMode::Run => "Run",
|
|
RunDebugMode::Debug => "Debug",
|
|
};
|
|
f.write_str(s)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct RunDebugProcess {
|
|
pub mode: RunDebugMode,
|
|
pub config: RunDebugConfig,
|
|
pub stopped: bool,
|
|
pub created: Instant,
|
|
pub is_prelaunch: bool,
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
pub struct RunDebugConfigs {
|
|
pub configs: Vec<RunDebugConfig>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct RunDebugData {
|
|
pub active_term: RwSignal<Option<TermId>>,
|
|
pub daps: RwSignal<im::HashMap<DapId, DapData>>,
|
|
pub breakpoints: RwSignal<BTreeMap<PathBuf, BTreeMap<usize, LapceBreakpoint>>>,
|
|
}
|
|
|
|
impl RunDebugData {
|
|
pub fn new(
|
|
cx: Scope,
|
|
breakpoints: RwSignal<BTreeMap<PathBuf, BTreeMap<usize, LapceBreakpoint>>>,
|
|
) -> Self {
|
|
let active_term: RwSignal<Option<TermId>> = cx.create_rw_signal(None);
|
|
let daps: RwSignal<im::HashMap<DapId, DapData>> =
|
|
cx.create_rw_signal(im::HashMap::new());
|
|
|
|
Self {
|
|
active_term,
|
|
daps,
|
|
breakpoints,
|
|
}
|
|
}
|
|
|
|
pub fn source_breakpoints(&self) -> HashMap<PathBuf, Vec<SourceBreakpoint>> {
|
|
self.breakpoints
|
|
.get_untracked()
|
|
.iter()
|
|
.map(|(path, breakpoints)| {
|
|
(
|
|
path.to_path_buf(),
|
|
breakpoints
|
|
.iter()
|
|
.filter_map(|(_, b)| {
|
|
if b.active {
|
|
Some(SourceBreakpoint {
|
|
line: b.line + 1,
|
|
column: None,
|
|
condition: None,
|
|
hit_condition: None,
|
|
log_message: None,
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect(),
|
|
)
|
|
})
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq)]
|
|
pub struct StackTraceData {
|
|
pub expanded: RwSignal<bool>,
|
|
pub frames: RwSignal<im::Vector<StackFrame>>,
|
|
pub frames_shown: usize,
|
|
}
|
|
|
|
#[derive(Clone, Serialize, Deserialize)]
|
|
pub struct LapceBreakpoint {
|
|
pub id: Option<usize>,
|
|
pub verified: bool,
|
|
pub message: Option<String>,
|
|
pub line: usize,
|
|
pub offset: usize,
|
|
pub dap_line: Option<usize>,
|
|
pub active: bool,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
#[allow(clippy::large_enum_variant)]
|
|
pub enum ScopeOrVar {
|
|
Scope(dap_types::Scope),
|
|
Var(dap_types::Variable),
|
|
}
|
|
|
|
impl Default for ScopeOrVar {
|
|
fn default() -> Self {
|
|
ScopeOrVar::Scope(dap_types::Scope::default())
|
|
}
|
|
}
|
|
|
|
impl ScopeOrVar {
|
|
pub fn name(&self) -> &str {
|
|
match self {
|
|
ScopeOrVar::Scope(scope) => &scope.name,
|
|
ScopeOrVar::Var(var) => &var.name,
|
|
}
|
|
}
|
|
|
|
pub fn value(&self) -> Option<&str> {
|
|
match self {
|
|
ScopeOrVar::Scope(_) => None,
|
|
ScopeOrVar::Var(var) => Some(&var.value),
|
|
}
|
|
}
|
|
|
|
pub fn ty(&self) -> Option<&str> {
|
|
match self {
|
|
ScopeOrVar::Scope(_) => None,
|
|
ScopeOrVar::Var(var) => var.ty.as_deref(),
|
|
}
|
|
}
|
|
|
|
pub fn reference(&self) -> usize {
|
|
match self {
|
|
ScopeOrVar::Scope(scope) => scope.variables_reference,
|
|
ScopeOrVar::Var(var) => var.variables_reference,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Default)]
|
|
pub struct DapVariable {
|
|
pub item: ScopeOrVar,
|
|
pub parent: Vec<usize>,
|
|
pub expanded: bool,
|
|
pub read: bool,
|
|
pub children: Vec<DapVariable>,
|
|
pub children_expanded_count: usize,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct DapData {
|
|
pub term_id: TermId,
|
|
pub dap_id: DapId,
|
|
pub stopped: RwSignal<bool>,
|
|
pub thread_id: RwSignal<Option<ThreadId>>,
|
|
pub stack_traces: RwSignal<BTreeMap<ThreadId, StackTraceData>>,
|
|
pub variables_id: RwSignal<usize>,
|
|
pub variables: RwSignal<DapVariable>,
|
|
pub breakline: Memo<Option<(usize, PathBuf)>>,
|
|
pub common: Rc<CommonData>,
|
|
}
|
|
|
|
impl DapData {
|
|
pub fn new(
|
|
cx: Scope,
|
|
dap_id: DapId,
|
|
term_id: TermId,
|
|
common: Rc<CommonData>,
|
|
) -> Self {
|
|
let stopped = cx.create_rw_signal(false);
|
|
let thread_id = cx.create_rw_signal(None);
|
|
let stack_traces: RwSignal<BTreeMap<ThreadId, StackTraceData>> =
|
|
cx.create_rw_signal(BTreeMap::new());
|
|
let breakline = cx.create_memo(move |_| {
|
|
let thread_id = thread_id.get();
|
|
if let Some(thread_id) = thread_id {
|
|
let trace = stack_traces
|
|
.with(|stack_traces| stack_traces.get(&thread_id).cloned());
|
|
|
|
if let Some(trace) = trace {
|
|
let breakline = trace.frames.with(|f| {
|
|
f.get(0)
|
|
.and_then(|f| {
|
|
f.source
|
|
.as_ref()
|
|
.map(|s| (f.line.saturating_sub(1), s))
|
|
})
|
|
.and_then(|(line, s)| s.path.clone().map(|p| (line, p)))
|
|
});
|
|
return breakline;
|
|
}
|
|
None
|
|
} else {
|
|
None
|
|
}
|
|
});
|
|
Self {
|
|
term_id,
|
|
dap_id,
|
|
stopped,
|
|
thread_id,
|
|
stack_traces,
|
|
variables_id: cx.create_rw_signal(0),
|
|
variables: cx.create_rw_signal(DapVariable {
|
|
item: ScopeOrVar::Scope(dap_types::Scope::default()),
|
|
parent: Vec::new(),
|
|
expanded: true,
|
|
read: true,
|
|
children: Vec::new(),
|
|
children_expanded_count: 0,
|
|
}),
|
|
breakline,
|
|
common,
|
|
}
|
|
}
|
|
|
|
pub fn stopped(
|
|
&self,
|
|
cx: Scope,
|
|
stopped: &Stopped,
|
|
stack_traces: &HashMap<ThreadId, Vec<StackFrame>>,
|
|
variables: &[(dap_types::Scope, Vec<Variable>)],
|
|
) {
|
|
self.stopped.set(true);
|
|
self.thread_id.update(|thread_id| {
|
|
*thread_id = Some(stopped.thread_id.unwrap_or_default());
|
|
});
|
|
|
|
let main_thread_id = self.thread_id.get_untracked();
|
|
let mut current_stack_traces = self.stack_traces.get_untracked();
|
|
current_stack_traces.retain(|t, _| stack_traces.contains_key(t));
|
|
for (thread_id, frames) in stack_traces {
|
|
let is_main_thread = main_thread_id.as_ref() == Some(thread_id);
|
|
if is_main_thread {
|
|
if let Some(frame) = frames.first() {
|
|
if let Some(path) =
|
|
frame.source.as_ref().and_then(|source| source.path.clone())
|
|
{
|
|
self.common.internal_command.send(
|
|
InternalCommand::JumpToLocation {
|
|
location: EditorLocation {
|
|
path,
|
|
position: Some(EditorPosition::Line(
|
|
frame.line.saturating_sub(1),
|
|
)),
|
|
scroll_offset: None,
|
|
ignore_unconfirmed: false,
|
|
same_editor_tab: false,
|
|
},
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if let Some(current) = current_stack_traces.get_mut(thread_id) {
|
|
current.frames.set(frames.into());
|
|
if is_main_thread {
|
|
current.expanded.set(true);
|
|
}
|
|
} else {
|
|
current_stack_traces.insert(
|
|
*thread_id,
|
|
StackTraceData {
|
|
expanded: cx.create_rw_signal(is_main_thread),
|
|
frames: cx.create_rw_signal(frames.into()),
|
|
frames_shown: 20,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
self.stack_traces.set(current_stack_traces);
|
|
self.variables.update(|dap_var| {
|
|
dap_var.children = variables
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, (scope, vars))| DapVariable {
|
|
item: ScopeOrVar::Scope(scope.to_owned()),
|
|
parent: Vec::new(),
|
|
expanded: i == 0,
|
|
read: true,
|
|
children: vars
|
|
.iter()
|
|
.map(|var| DapVariable {
|
|
item: ScopeOrVar::Var(var.to_owned()),
|
|
parent: vec![scope.variables_reference],
|
|
expanded: false,
|
|
read: false,
|
|
children: Vec::new(),
|
|
children_expanded_count: 0,
|
|
})
|
|
.collect(),
|
|
children_expanded_count: if i == 0 { vars.len() } else { 0 },
|
|
})
|
|
.collect();
|
|
dap_var.children_expanded_count = dap_var
|
|
.children
|
|
.iter()
|
|
.map(|v| v.children_expanded_count + 1)
|
|
.sum::<usize>();
|
|
});
|
|
}
|
|
|
|
pub fn toggle_expand(&self, parent: Vec<usize>, reference: usize) {
|
|
self.variables_id.update(|id| {
|
|
*id += 1;
|
|
});
|
|
self.variables.update(|variables| {
|
|
if let Some(var) = variables.get_var_mut(&parent, reference) {
|
|
if var.expanded {
|
|
var.expanded = false;
|
|
variables.update_count_recursive(&parent, reference);
|
|
} else {
|
|
var.expanded = true;
|
|
if !var.read {
|
|
var.read = true;
|
|
self.read_var_children(&parent, reference);
|
|
} else {
|
|
variables.update_count_recursive(&parent, reference);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
fn read_var_children(&self, parent: &[usize], reference: usize) {
|
|
let root = self.variables;
|
|
let parent = parent.to_vec();
|
|
let variables_id = self.variables_id;
|
|
|
|
let send = create_ext_action(self.common.scope, move |result| {
|
|
if let Ok(ProxyResponse::DapVariableResponse { variables }) = result {
|
|
variables_id.update(|id| {
|
|
*id += 1;
|
|
});
|
|
root.update(|root| {
|
|
if let Some(var) = root.get_var_mut(&parent, reference) {
|
|
let mut new_parent = parent.clone();
|
|
new_parent.push(reference);
|
|
var.read = true;
|
|
var.children = variables
|
|
.into_iter()
|
|
.map(|v| DapVariable {
|
|
item: ScopeOrVar::Var(v),
|
|
parent: new_parent.clone(),
|
|
expanded: false,
|
|
read: false,
|
|
children: Vec::new(),
|
|
children_expanded_count: 0,
|
|
})
|
|
.collect();
|
|
root.update_count_recursive(&parent, reference);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
self.common
|
|
.proxy
|
|
.dap_variable(self.dap_id, reference, move |result| {
|
|
send(result);
|
|
});
|
|
}
|
|
}
|
|
|
|
pub struct DapVariableViewdata {
|
|
pub item: ScopeOrVar,
|
|
pub parent: Vec<usize>,
|
|
pub expanded: bool,
|
|
pub level: usize,
|
|
}
|
|
|
|
impl VirtualVector<DapVariableViewdata> for DapVariable {
|
|
fn total_len(&self) -> usize {
|
|
self.children_expanded_count
|
|
}
|
|
|
|
fn slice(
|
|
&mut self,
|
|
range: std::ops::Range<usize>,
|
|
) -> impl Iterator<Item = DapVariableViewdata> {
|
|
let min = range.start;
|
|
let max = range.end;
|
|
let mut i = 0;
|
|
let mut view_items = Vec::new();
|
|
for item in self.children.iter() {
|
|
i = item.append_view_slice(&mut view_items, min, max, i + 1, 0);
|
|
if i > max {
|
|
return view_items.into_iter();
|
|
}
|
|
}
|
|
|
|
view_items.into_iter()
|
|
}
|
|
}
|
|
|
|
impl DapVariable {
|
|
pub fn append_view_slice(
|
|
&self,
|
|
view_items: &mut Vec<DapVariableViewdata>,
|
|
min: usize,
|
|
max: usize,
|
|
current: usize,
|
|
level: usize,
|
|
) -> usize {
|
|
if current > max {
|
|
return current;
|
|
}
|
|
if current + self.children_expanded_count < min {
|
|
return current + self.children_expanded_count;
|
|
}
|
|
|
|
let mut i = current;
|
|
if current >= min {
|
|
view_items.push(DapVariableViewdata {
|
|
item: self.item.clone(),
|
|
parent: self.parent.clone(),
|
|
expanded: self.expanded,
|
|
level,
|
|
});
|
|
}
|
|
|
|
if self.expanded {
|
|
for item in self.children.iter() {
|
|
i = item.append_view_slice(view_items, min, max, i + 1, level + 1);
|
|
if i > max {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
i
|
|
}
|
|
|
|
pub fn get_var_mut(
|
|
&mut self,
|
|
parent: &[usize],
|
|
reference: usize,
|
|
) -> Option<&mut DapVariable> {
|
|
let parent = if parent.is_empty() {
|
|
self
|
|
} else {
|
|
parent.iter().try_fold(self, |item, parent| {
|
|
item.children
|
|
.iter_mut()
|
|
.find(|c| c.item.reference() == *parent)
|
|
})?
|
|
};
|
|
parent
|
|
.children
|
|
.iter_mut()
|
|
.find(|c| c.item.reference() == reference)
|
|
}
|
|
|
|
pub fn update_count_recursive(&mut self, parent: &[usize], reference: usize) {
|
|
let mut parent = parent.to_vec();
|
|
self.update_count(&parent, reference);
|
|
while let Some(reference) = parent.pop() {
|
|
self.update_count(&parent, reference);
|
|
}
|
|
self.children_expanded_count = self
|
|
.children
|
|
.iter()
|
|
.map(|item| item.children_expanded_count + 1)
|
|
.sum::<usize>();
|
|
}
|
|
|
|
pub fn update_count(
|
|
&mut self,
|
|
parent: &[usize],
|
|
reference: usize,
|
|
) -> Option<()> {
|
|
let var = self.get_var_mut(parent, reference)?;
|
|
var.children_expanded_count = if var.expanded {
|
|
var.children
|
|
.iter()
|
|
.map(|item| item.children_expanded_count + 1)
|
|
.sum::<usize>()
|
|
} else {
|
|
0
|
|
};
|
|
Some(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use lapce_rpc::dap_types::{Scope, Variable};
|
|
|
|
use super::{DapVariable, ScopeOrVar};
|
|
|
|
#[test]
|
|
fn test_update_count() {
|
|
let variables = [
|
|
(
|
|
Scope {
|
|
variables_reference: 0,
|
|
..Default::default()
|
|
},
|
|
vec![
|
|
Variable {
|
|
variables_reference: 3,
|
|
..Default::default()
|
|
},
|
|
Variable {
|
|
variables_reference: 4,
|
|
..Default::default()
|
|
},
|
|
],
|
|
),
|
|
(
|
|
Scope {
|
|
variables_reference: 1,
|
|
..Default::default()
|
|
},
|
|
vec![
|
|
Variable {
|
|
variables_reference: 5,
|
|
..Default::default()
|
|
},
|
|
Variable {
|
|
variables_reference: 6,
|
|
..Default::default()
|
|
},
|
|
],
|
|
),
|
|
(
|
|
Scope {
|
|
variables_reference: 2,
|
|
..Default::default()
|
|
},
|
|
vec![
|
|
Variable {
|
|
variables_reference: 7,
|
|
..Default::default()
|
|
},
|
|
Variable {
|
|
variables_reference: 8,
|
|
..Default::default()
|
|
},
|
|
],
|
|
),
|
|
];
|
|
|
|
let mut root = DapVariable {
|
|
item: ScopeOrVar::Scope(Scope::default()),
|
|
parent: Vec::new(),
|
|
expanded: true,
|
|
read: true,
|
|
children: variables
|
|
.iter()
|
|
.map(|(scope, vars)| DapVariable {
|
|
item: ScopeOrVar::Scope(scope.to_owned()),
|
|
parent: Vec::new(),
|
|
expanded: true,
|
|
read: true,
|
|
children: vars
|
|
.iter()
|
|
.map(|var| DapVariable {
|
|
item: ScopeOrVar::Var(var.to_owned()),
|
|
parent: vec![scope.variables_reference],
|
|
expanded: false,
|
|
read: false,
|
|
children: Vec::new(),
|
|
children_expanded_count: 0,
|
|
})
|
|
.collect(),
|
|
children_expanded_count: vars.len(),
|
|
})
|
|
.collect(),
|
|
children_expanded_count: 0,
|
|
};
|
|
root.children_expanded_count = root
|
|
.children
|
|
.iter()
|
|
.map(|v| v.children_expanded_count + 1)
|
|
.sum::<usize>();
|
|
assert_eq!(root.children_expanded_count, 9);
|
|
|
|
let var = root.get_var_mut(&[0], 3).unwrap();
|
|
var.expanded = true;
|
|
var.read = true;
|
|
var.children = [
|
|
Variable {
|
|
variables_reference: 9,
|
|
..Default::default()
|
|
},
|
|
Variable {
|
|
variables_reference: 10,
|
|
..Default::default()
|
|
},
|
|
]
|
|
.iter()
|
|
.map(|var| DapVariable {
|
|
item: ScopeOrVar::Var(var.to_owned()),
|
|
parent: vec![0, 3],
|
|
expanded: false,
|
|
read: false,
|
|
children: Vec::new(),
|
|
children_expanded_count: 0,
|
|
})
|
|
.collect();
|
|
root.update_count_recursive(&[0], 3);
|
|
let var = root.get_var_mut(&[0], 3).unwrap();
|
|
assert_eq!(var.children_expanded_count, 2);
|
|
let var = root.get_var_mut(&[], 0).unwrap();
|
|
assert_eq!(var.children_expanded_count, 4);
|
|
assert_eq!(root.children_expanded_count, 11);
|
|
}
|
|
}
|