Try to offer exception type completions inside a `catch(…)` clause. Analyses the corresponding try block and suggests only the exception types that are thrown or documented there. When no specific thrown types are found, falls back to Throwable-filtered class completion. Returns `None` when the cursor is not inside a catch clause or when no completions could be produced.
(
&self,
content: &str,
position: Position,
ctx: &FileContext,
uri: &str,
)
| 1074 | /// Returns `None` when the cursor is not inside a catch clause or when |
| 1075 | /// no completions could be produced. |
| 1076 | fn try_catch_completion( |
| 1077 | &self, |
| 1078 | content: &str, |
| 1079 | position: Position, |
| 1080 | ctx: &FileContext, |
| 1081 | uri: &str, |
| 1082 | ) -> Option<CompletionResponse> { |
| 1083 | let catch_ctx = |
| 1084 | crate::completion::catch_completion::detect_catch_context(content, position)?; |
| 1085 | |
| 1086 | let items = crate::completion::catch_completion::build_catch_completions( |
| 1087 | &catch_ctx, |
| 1088 | &ctx.use_map, |
| 1089 | &ctx.namespace, |
| 1090 | ); |
| 1091 | if catch_ctx.has_specific_types && !items.is_empty() { |
| 1092 | // These items don't carry snippets, but guard for consistency. |
| 1093 | return Some(CompletionResponse::Array(items)); |
| 1094 | } |
| 1095 | |
| 1096 | // No specific throws discovered — fall back to |
| 1097 | // Throwable-filtered class completion. Already-parsed |
| 1098 | // classes are only offered when their parent chain |
| 1099 | // reaches \Throwable / \Exception / \Error. Class index |
| 1100 | // and stub classes are included unfiltered because |
| 1101 | // checking their ancestry would require on-demand parsing. |
| 1102 | // |
| 1103 | // Use the partial from the catch context rather than |
| 1104 | // `extract_partial_class_name` — the latter returns |
| 1105 | // `None` when the cursor sits right after `(` with |
| 1106 | // nothing typed, but the catch context already |
| 1107 | // captured the (possibly empty) partial correctly. |
| 1108 | let partial = if catch_ctx.partial.is_empty() { |
| 1109 | Self::extract_partial_class_name(content, position).unwrap_or_default() |
| 1110 | } else { |
| 1111 | catch_ctx.partial.clone() |
| 1112 | }; |
| 1113 | let (class_items, class_incomplete) = |
| 1114 | self.build_catch_class_name_completions(ctx, &partial, content, false, position, uri); |
| 1115 | let mut all_items = items; // Throwable item (if matched) |
| 1116 | for ci in class_items { |
| 1117 | if !all_items.iter().any(|existing| existing.label == ci.label) { |
| 1118 | all_items.push(ci); |
| 1119 | } |
| 1120 | } |
| 1121 | if all_items.is_empty() { |
| 1122 | None |
| 1123 | } else { |
| 1124 | let items = if paren_follows_cursor(content, position) { |
| 1125 | strip_snippet_parens(all_items) |
| 1126 | } else { |
| 1127 | all_items |
| 1128 | }; |
| 1129 | Some(CompletionResponse::List(CompletionList { |
| 1130 | is_incomplete: class_incomplete, |
| 1131 | items, |
| 1132 | })) |
| 1133 | } |
no test coverage detected