tasks: Fix runnables retrieval to not bail when a single tag can't be matched (#12113)

Piotr Osiewicz created

This can happen with queries without `@run` indicator.

Release Notes:

- N/A

Change summary

crates/language/src/buffer.rs | 88 +++++++++++++++++++-----------------
1 file changed, 46 insertions(+), 42 deletions(-)

Detailed changes

crates/language/src/buffer.rs 🔗

@@ -3005,52 +3005,56 @@ impl BufferSnapshot {
             .map(|grammar| grammar.runnable_config.as_ref())
             .collect::<Vec<_>>();
 
-        iter::from_fn(move || {
-            let test_range = syntax_matches.peek().and_then(|mat| {
-                test_configs[mat.grammar_index].and_then(|test_configs| {
-                    let mut tags: SmallVec<[(Range<usize>, RunnableTag); 1]> =
-                        SmallVec::from_iter(mat.captures.iter().filter_map(|capture| {
-                            test_configs
-                                .runnable_tags
-                                .get(&capture.index)
-                                .cloned()
-                                .map(|tag_name| (capture.node.byte_range(), tag_name))
-                        }));
-                    let maximum_range = tags
-                        .iter()
-                        .max_by_key(|(byte_range, _)| byte_range.len())
-                        .map(|(range, _)| range)?
-                        .clone();
-                    tags.sort_by_key(|(range, _)| range == &maximum_range);
-                    let split_point = tags.partition_point(|(range, _)| range != &maximum_range);
-                    let (extra_captures, tags) = tags.split_at(split_point);
-                    let extra_captures = extra_captures
-                        .into_iter()
-                        .map(|(range, name)| {
-                            (
-                                name.0.to_string(),
-                                self.text_for_range(range.clone()).collect::<String>(),
-                            )
-                        })
-                        .collect();
-                    Some(RunnableRange {
-                        run_range: mat
-                            .captures
-                            .iter()
-                            .find(|capture| capture.index == test_configs.run_capture_ix)
-                            .map(|mat| mat.node.byte_range())?,
-                        runnable: Runnable {
-                            tags: tags.into_iter().cloned().map(|(_, tag)| tag).collect(),
-                            language: mat.language,
-                            buffer: self.remote_id(),
-                        },
-                        extra_captures,
-                        buffer_id: self.remote_id(),
+        iter::from_fn(move || loop {
+            let mat = syntax_matches.peek()?;
+            let test_range = test_configs[mat.grammar_index].and_then(|test_configs| {
+                let mut tags: SmallVec<[(Range<usize>, RunnableTag); 1]> =
+                    SmallVec::from_iter(mat.captures.iter().filter_map(|capture| {
+                        test_configs
+                            .runnable_tags
+                            .get(&capture.index)
+                            .cloned()
+                            .map(|tag_name| (capture.node.byte_range(), tag_name))
+                    }));
+                let maximum_range = tags
+                    .iter()
+                    .max_by_key(|(byte_range, _)| byte_range.len())
+                    .map(|(range, _)| range)?
+                    .clone();
+                tags.sort_by_key(|(range, _)| range == &maximum_range);
+                let split_point = tags.partition_point(|(range, _)| range != &maximum_range);
+                let (extra_captures, tags) = tags.split_at(split_point);
+                let extra_captures = extra_captures
+                    .into_iter()
+                    .map(|(range, name)| {
+                        (
+                            name.0.to_string(),
+                            self.text_for_range(range.clone()).collect::<String>(),
+                        )
                     })
+                    .collect();
+                Some(RunnableRange {
+                    run_range: mat
+                        .captures
+                        .iter()
+                        .find(|capture| capture.index == test_configs.run_capture_ix)
+                        .map(|mat| mat.node.byte_range())?,
+                    runnable: Runnable {
+                        tags: tags.into_iter().cloned().map(|(_, tag)| tag).collect(),
+                        language: mat.language,
+                        buffer: self.remote_id(),
+                    },
+                    extra_captures,
+                    buffer_id: self.remote_id(),
                 })
             });
+
             syntax_matches.advance();
-            test_range
+            if test_range.is_some() {
+                // It's fine for us to short-circuit on .peek()? returning None. We don't want to return None from this iter if we
+                // had a capture that did not contain a run marker, hence we'll just loop around for the next capture.
+                return test_range;
+            }
         })
     }