Resolve a deferred "Extract Function/Method" code action. This is **Phase 2** of the two-phase code-action model. Phase 1 (`collect_extract_function_actions`) already validated the selection and emitted a lightweight `CodeAction` with a title but no edit. Here we re-run the full extraction logic from the selection range stored in `data` and produce the workspace edit.
(
&self,
data: &CodeActionData,
content: &str,
)
| 2139 | /// but no edit. Here we re-run the full extraction logic from the |
| 2140 | /// selection range stored in `data` and produce the workspace edit. |
| 2141 | pub(crate) fn resolve_extract_function( |
| 2142 | &self, |
| 2143 | data: &CodeActionData, |
| 2144 | content: &str, |
| 2145 | ) -> Option<WorkspaceEdit> { |
| 2146 | let uri = &data.uri; |
| 2147 | let range = &data.range; |
| 2148 | |
| 2149 | // ── Re-validate the selection (content may have changed) ──── |
| 2150 | let start_offset = position_to_byte_offset(content, range.start); |
| 2151 | let end_offset = position_to_byte_offset(content, range.end); |
| 2152 | |
| 2153 | let (start, end) = trim_selection(content, start_offset, end_offset)?; |
| 2154 | |
| 2155 | if !selection_covers_complete_statements(content, start, end) { |
| 2156 | return None; |
| 2157 | } |
| 2158 | |
| 2159 | // ── Scope map & classification ────────────────────────────── |
| 2160 | let scope_map = build_scope_map(content, start as u32); |
| 2161 | let classification = scope_map.classify_range(start as u32, end as u32); |
| 2162 | |
| 2163 | let return_value_count = classification.return_values.len(); |
| 2164 | let return_strategy = analyse_returns(content, start, end, return_value_count); |
| 2165 | |
| 2166 | if return_strategy == ReturnStrategy::Unsafe { |
| 2167 | return None; |
| 2168 | } |
| 2169 | |
| 2170 | let uses_this = if scope_map.has_this_or_self { |
| 2171 | classification.uses_this |
| 2172 | } else { |
| 2173 | false |
| 2174 | }; |
| 2175 | |
| 2176 | if scope_map.uses_reference_params() && !classification.reference_writes.is_empty() { |
| 2177 | return None; |
| 2178 | } |
| 2179 | |
| 2180 | if classification.return_values.len() > 4 { |
| 2181 | return None; |
| 2182 | } |
| 2183 | |
| 2184 | // ── Enclosing context ─────────────────────────────────────── |
| 2185 | let enclosing = find_enclosing_context(content, start as u32, uses_this)?; |
| 2186 | |
| 2187 | // ── Naming ────────────────────────────────────────────────── |
| 2188 | let body_line_start_for_naming = find_line_start(content, start); |
| 2189 | let body_text_for_naming = &content[body_line_start_for_naming..end]; |
| 2190 | let pre_trailing_return_type = if matches!(return_strategy, ReturnStrategy::TrailingReturn) |
| 2191 | { |
| 2192 | resolve_enclosing_return_type(content, start as u32) |
| 2193 | } else { |
| 2194 | PhpType::untyped() |
| 2195 | }; |
| 2196 | let naming_ctx = NamingContext { |
| 2197 | enclosing_name: &enclosing.enclosing_name, |
| 2198 | return_strategy: &return_strategy, |
no test coverage detected