ktrs_compose/core/util/
modifiers.rs1use ktrs_ast::psi::{
4 KtBlockExpression, KtCallExpression, KtCallableDeclaration, KtDotQualifiedExpression, KtFunction, KtParameter,
5 KtProperty, KtReferenceExpression, KtValueArgument,
6};
7use ktrs_ast::{Ast, NodeId};
8use ktrs_syntax::SyntaxKind::{CALL_EXPRESSION, VALUE_ARGUMENT_NAME};
9
10use crate::core::compose_kt_config::ComposeKtConfig;
11use crate::core::util::kt_dot_qualified_expressions::root_expression;
12use crate::core::util::psi_elements::find_all_children;
13
14fn insert(set: &mut Vec<String>, name: String) {
15 if !set.contains(&name) {
16 set.push(name);
17 }
18}
19
20pub fn obtain_all_modifier_names(ast: &Ast, block: KtBlockExpression, initial_name: &str) -> Vec<String> {
23 let root_block = block;
24 let mut last_size = 0;
25 let mut temp_modifier_names = vec![initial_name.to_owned()];
26 while last_size < temp_modifier_names.len() {
27 last_size = temp_modifier_names.len();
28 let found = find_modifier_manipulations(ast, block, |it| temp_modifier_names.iter().any(|n| n == it));
29 found.into_iter().for_each(|n| insert(&mut temp_modifier_names, n));
30 for child_block in find_all_children::<KtBlockExpression>(ast, block.node()) {
32 let shadowed = shadowed_modifier_names(ast, child_block, &temp_modifier_names, root_block);
33 let accessible: Vec<String> = temp_modifier_names.iter().filter(|n| !shadowed.contains(n)).cloned().collect();
34 if !accessible.is_empty() {
35 let found = find_modifier_manipulations(ast, child_block, |it| accessible.iter().any(|n| n == it));
36 found.into_iter().for_each(|n| insert(&mut temp_modifier_names, n));
37 }
38 }
39 }
40 temp_modifier_names
41}
42
43fn shadowed_modifier_names(ast: &Ast, block: KtBlockExpression, modifier_names: &[String], stop_at: KtBlockExpression) -> Vec<String> {
46 let mut set = Vec::new();
47 for function in ast.parents(block.node()).take_while(|&it| it != stop_at.node()).filter_map(|it| KtFunction::cast(ast, it)) {
48 for param in function.value_parameters(ast) {
49 for name in shadowing_names(ast, param, modifier_names) {
50 insert(&mut set, name);
51 }
52 }
53 }
54 set
55}
56
57pub(crate) fn shadowing_names(ast: &Ast, param: KtParameter, modifier_names: &[String]) -> Vec<String> {
59 let is_modifier_name = |name: &String| modifier_names.contains(name);
60 if let Some(name) = param.name(ast) {
61 return Some(name).filter(is_modifier_name).into_iter().collect();
62 }
63 param
64 .destructuring_declaration(ast)
65 .map(|d| d.entries(ast).into_iter().filter_map(|it| it.name(ast).filter(is_modifier_name)).collect())
66 .unwrap_or_default()
67}
68
69fn find_modifier_manipulations(ast: &Ast, block: KtBlockExpression, contains: impl Fn(&str) -> bool) -> Vec<String> {
72 block
73 .statements(ast)
74 .into_iter()
75 .filter_map(|it| KtProperty::cast(ast, it))
76 .flat_map(|property| {
77 find_all_children::<KtReferenceExpression>(ast, property.node())
78 .into_iter()
79 .filter(|reference| {
80 let parent = ast.tree_parent(reference.node()).map(|p| ast.element_type(p));
81 parent != Some(CALL_EXPRESSION) && parent != Some(VALUE_ARGUMENT_NAME) && contains(&reference.text(ast))
82 })
83 .map(move |_| property)
84 .collect::<Vec<_>>()
85 })
86 .filter_map(|it| it.name_identifier(ast).map(|n| ast.text(n)))
87 .collect()
88}
89
90pub fn is_using_modifiers(ast: &Ast, call: KtCallExpression, modifier_names: &[String], modifier_type_names: &[String]) -> bool {
91 !arguments_using_modifiers(ast, call, modifier_names, modifier_type_names).is_empty()
92}
93
94pub fn arguments_using_modifiers(
96 ast: &Ast,
97 call: KtCallExpression,
98 modifier_names: &[String],
99 modifier_type_names: &[String],
100) -> Vec<KtValueArgument> {
101 call.value_arguments(ast)
102 .into_iter()
103 .filter(|argument| {
104 let Some(expression) = argument.argument_expression(ast) else { return false };
105 if KtReferenceExpression::is(ast, expression) {
106 return modifier_names.contains(&ast.text(expression));
107 }
108 let Some(dot) = KtDotQualifiedExpression::cast(ast, expression) else { return false };
109 let root_text = ast.text(root_expression(ast, dot));
110 modifier_names.contains(&root_text)
111 || (modifier_type_names.contains(&root_text) && has_modifier_as_chain_argument(ast, dot, modifier_names))
112 })
113 .collect()
114}
115
116fn has_modifier_as_chain_argument(ast: &Ast, expression: KtDotQualifiedExpression, modifier_names: &[String]) -> bool {
119 let mut current = Some(expression);
120 while let Some(dot) = current {
121 let selector = dot.selector_expression(ast).and_then(|s| KtCallExpression::cast(ast, s));
122 if let Some(selector) = selector.filter(|s| s.callee_expression(ast).is_some_and(|c| ast.text(c) == "then")) {
123 for arg in selector.value_arguments(ast) {
124 let Some(expr) = arg.argument_expression(ast) else { continue };
125 if KtReferenceExpression::is(ast, expr) {
126 if modifier_names.contains(&ast.text(expr)) {
127 return true;
128 }
129 } else if let Some(nested) = KtDotQualifiedExpression::cast(ast, expr)
130 && modifier_names.contains(&ast.text(root_expression(ast, nested)))
131 {
132 return true;
133 }
134 }
135 }
136 current = dot.receiver_expression(ast).and_then(|r| KtDotQualifiedExpression::cast(ast, r));
137 }
138 false
139}
140
141pub const MODIFIER_NAMES: &[&str] = &["Modifier", "GlanceModifier"];
142
143pub fn modifier_type_names(config: &dyn ComposeKtConfig) -> Vec<String> {
145 let mut names: Vec<String> = MODIFIER_NAMES.iter().map(|s| s.to_string()).collect();
146 for name in config.get_set("customModifiers", &[]) {
147 insert(&mut names, name);
148 }
149 names
150}
151
152pub fn is_modifier(ast: &Ast, callable: NodeId, config: &dyn ComposeKtConfig) -> bool {
154 KtCallableDeclaration::of(ast, callable)
155 .type_reference(ast)
156 .is_some_and(|t| modifier_type_names(config).contains(&t.text(ast)))
157}
158
159pub fn is_modifier_receiver(ast: &Ast, callable: NodeId, config: &dyn ComposeKtConfig) -> bool {
161 KtCallableDeclaration::of(ast, callable)
162 .receiver_type_reference(ast)
163 .is_some_and(|t| modifier_type_names(config).contains(&t.text(ast)))
164}
165
166pub fn modifier_parameter(ast: &Ast, function: KtFunction, config: &dyn ComposeKtConfig) -> Option<KtParameter> {
168 let modifiers = modifier_parameters(ast, function, config);
169 modifiers.iter().copied().find(|it| it.name(ast).as_deref() == Some("modifier")).or_else(|| modifiers.first().copied())
170}
171
172pub fn modifier_parameters(ast: &Ast, function: KtFunction, config: &dyn ComposeKtConfig) -> Vec<KtParameter> {
173 function.value_parameters(ast).into_iter().filter(|it| is_modifier(ast, it.node(), config)).collect()
174}