277 lines · 11.8 KB · Rust
RawDownload
use std::collections::{HashMap, HashSet}; #[derive(Debug, Clone, PartialEq, Eq)]pub enum VariableLookupKind {    Variable,    Expression,} #[derive(Debug, Clone, PartialEq, Eq)]pub enum VariableScope {    Local,    Global,} #[derive(Debug, Clone)]pub struct InlineValueLocation {    pub variable_name: String,    pub scope: VariableScope,    pub lookup: VariableLookupKind,    pub row: usize,    pub column: usize,} /// A trait for providing inline values for debugging purposes.////// Implementors of this trait are responsible for analyzing a given node in the/// source code and extracting variable information, including their names,/// scopes, and positions. This information is used to display inline values/// during debugging sessions. Implementors must also handle variable scoping/// themselves by traversing the syntax tree upwards to determine whether a/// variable is local or global.pub trait InlineValueProvider: 'static + Send + Sync {    /// Provides a list of inline value locations based on the given node and source code.    ///    /// # Parameters    /// - `node`: The root node of the active debug line. Implementors should traverse    ///   upwards from this node to gather variable information and determine their scope.    /// - `source`: The source code as a string slice, used to extract variable names.    /// - `max_row`: The maximum row to consider when collecting variables. Variables    ///   declared beyond this row should be ignored.    ///    /// # Returns    /// A vector of `InlineValueLocation` instances, each representing a variable's    /// name, scope, and the position of the inline value should be shown.    fn provide(        &self,        node: language::Node,        source: &str,        max_row: usize,    ) -> Vec<InlineValueLocation>;} pub struct RustInlineValueProvider; impl InlineValueProvider for RustInlineValueProvider {    fn provide(        &self,        mut node: language::Node,        source: &str,        max_row: usize,    ) -> Vec<InlineValueLocation> {        let mut variables = Vec::new();        let mut variable_names = HashSet::new();        let mut scope = VariableScope::Local;         loop {            let mut variable_names_in_scope = HashMap::new();            for child in node.named_children(&mut node.walk()) {                if child.start_position().row >= max_row {                    break;                }                 if scope == VariableScope::Local && child.kind() == "let_declaration" {                    if let Some(identifier) = child.child_by_field_name("pattern") {                        let variable_name = source[identifier.byte_range()].to_string();                         if variable_names.contains(&variable_name) {                            continue;                        }                         if let Some(index) = variable_names_in_scope.get(&variable_name) {                            variables.remove(*index);                        }                         variable_names_in_scope.insert(variable_name.clone(), variables.len());                        variables.push(InlineValueLocation {                            variable_name,                            scope: VariableScope::Local,                            lookup: VariableLookupKind::Variable,                            row: identifier.end_position().row,                            column: identifier.end_position().column,                        });                    }                } else if child.kind() == "static_item" {                    if let Some(name) = child.child_by_field_name("name") {                        let variable_name = source[name.byte_range()].to_string();                        variables.push(InlineValueLocation {                            variable_name,                            scope: scope.clone(),                            lookup: VariableLookupKind::Expression,                            row: name.end_position().row,                            column: name.end_position().column,                        });                    }                }            }             variable_names.extend(variable_names_in_scope.keys().cloned());             if matches!(node.kind(), "function_item" | "closure_expression") {                scope = VariableScope::Global;            }             if let Some(parent) = node.parent() {                node = parent;            } else {                break;            }        }         variables    }} pub struct PythonInlineValueProvider; impl InlineValueProvider for PythonInlineValueProvider {    fn provide(        &self,        mut node: language::Node,        source: &str,        max_row: usize,    ) -> Vec<InlineValueLocation> {        let mut variables = Vec::new();        let mut variable_names = HashSet::new();        let mut scope = VariableScope::Local;         loop {            let mut variable_names_in_scope = HashMap::new();            for child in node.named_children(&mut node.walk()) {                if child.start_position().row >= max_row {                    break;                }                 if scope == VariableScope::Local {                    match child.kind() {                        "expression_statement" => {                            if let Some(expr) = child.child(0) {                                if expr.kind() == "assignment" {                                    if let Some(param) = expr.child(0) {                                        let param_identifier = if param.kind() == "identifier" {                                            Some(param)                                        } else if param.kind() == "typed_parameter" {                                            param.child(0)                                        } else {                                            None                                        };                                         if let Some(identifier) = param_identifier {                                            if identifier.kind() == "identifier" {                                                let variable_name =                                                    source[identifier.byte_range()].to_string();                                                 if variable_names.contains(&variable_name) {                                                    continue;                                                }                                                 if let Some(index) =                                                    variable_names_in_scope.get(&variable_name)                                                {                                                    variables.remove(*index);                                                }                                                 variable_names_in_scope                                                    .insert(variable_name.clone(), variables.len());                                                variables.push(InlineValueLocation {                                                    variable_name,                                                    scope: VariableScope::Local,                                                    lookup: VariableLookupKind::Variable,                                                    row: identifier.end_position().row,                                                    column: identifier.end_position().column,                                                });                                            }                                        }                                    }                                }                            }                        }                        "function_definition" => {                            if let Some(params) = child.child_by_field_name("parameters") {                                for param in params.named_children(&mut params.walk()) {                                    let param_identifier = if param.kind() == "identifier" {                                        Some(param)                                    } else if param.kind() == "typed_parameter" {                                        param.child(0)                                    } else {                                        None                                    };                                     if let Some(identifier) = param_identifier {                                        if identifier.kind() == "identifier" {                                            let variable_name =                                                source[identifier.byte_range()].to_string();                                             if variable_names.contains(&variable_name) {                                                continue;                                            }                                             if let Some(index) =                                                variable_names_in_scope.get(&variable_name)                                            {                                                variables.remove(*index);                                            }                                             variable_names_in_scope                                                .insert(variable_name.clone(), variables.len());                                            variables.push(InlineValueLocation {                                                variable_name,                                                scope: VariableScope::Local,                                                lookup: VariableLookupKind::Variable,                                                row: identifier.end_position().row,                                                column: identifier.end_position().column,                                            });                                        }                                    }                                }                            }                        }                        "for_statement" => {                            if let Some(target) = child.child_by_field_name("left") {                                if target.kind() == "identifier" {                                    let variable_name = source[target.byte_range()].to_string();                                     if variable_names.contains(&variable_name) {                                        continue;                                    }                                     if let Some(index) = variable_names_in_scope.get(&variable_name)                                    {                                        variables.remove(*index);                                    }                                     variable_names_in_scope                                        .insert(variable_name.clone(), variables.len());                                    variables.push(InlineValueLocation {                                        variable_name,                                        scope: VariableScope::Local,                                        lookup: VariableLookupKind::Variable,                                        row: target.end_position().row,                                        column: target.end_position().column,                                    });                                }                            }                        }                        _ => {}                    }                }            }             variable_names.extend(variable_names_in_scope.keys().cloned());             if matches!(node.kind(), "function_definition" | "module")                && node.range().end_point.row < max_row            {                scope = VariableScope::Global;            }             if let Some(parent) = node.parent() {                node = parent;            } else {                break;            }        }         variables    }}