Determine the extraction target and insertion point by walking the AST.
(content: &str, offset: u32, uses_this: bool)
| 153 | |
| 154 | /// Determine the extraction target and insertion point by walking the AST. |
| 155 | fn find_enclosing_context(content: &str, offset: u32, uses_this: bool) -> Option<EnclosingContext> { |
| 156 | let arena = Bump::new(); |
| 157 | let file_id = mago_database::file::FileId::new("extract_fn_ctx"); |
| 158 | let program = mago_syntax::parser::parse_file_content(&arena, file_id, content); |
| 159 | |
| 160 | let ctx = find_cursor_context(&program.statements, offset); |
| 161 | |
| 162 | match ctx { |
| 163 | CursorContext::InClassLike { |
| 164 | member, |
| 165 | all_members, |
| 166 | .. |
| 167 | } => { |
| 168 | if let MemberContext::Method(method, true) = member { |
| 169 | let is_static = method.modifiers.iter().any(|m| m.is_static()); |
| 170 | let enclosing_name = method.name.value.to_string(); |
| 171 | |
| 172 | // Collect sibling method names for scoped deduplication. |
| 173 | let sibling_method_names: Vec<String> = all_members |
| 174 | .iter() |
| 175 | .filter_map(|m| { |
| 176 | if let ClassLikeMember::Method(m) = m { |
| 177 | Some(m.name.value.to_string()) |
| 178 | } else { |
| 179 | None |
| 180 | } |
| 181 | }) |
| 182 | .collect(); |
| 183 | |
| 184 | // For method extraction, insert before the closing `}` of the class. |
| 185 | // Find the class closing brace by walking up from the method. |
| 186 | let class_end = find_class_end_offset(&program.statements, offset); |
| 187 | |
| 188 | if let MethodBody::Concrete(block) = &method.body { |
| 189 | let body_start = block.left_brace.start.offset as usize; |
| 190 | |
| 191 | if uses_this && is_static { |
| 192 | // $this in a static method — can't extract as method. |
| 193 | // Fall back to extracting as a function. |
| 194 | let func_end = block.right_brace.end.offset as usize; |
| 195 | return Some(EnclosingContext { |
| 196 | target: ExtractionTarget::Function, |
| 197 | insert_offset: find_after_class_end(&program.statements, offset) |
| 198 | .unwrap_or(func_end), |
| 199 | body_start, |
| 200 | is_static, |
| 201 | enclosing_name, |
| 202 | sibling_method_names: Vec::new(), |
| 203 | }); |
| 204 | } |
| 205 | |
| 206 | return Some(EnclosingContext { |
| 207 | target: ExtractionTarget::Method, |
| 208 | insert_offset: class_end.unwrap_or(block.right_brace.end.offset as usize), |
| 209 | body_start, |
| 210 | is_static, |
| 211 | enclosing_name, |
| 212 | sibling_method_names, |