editor: Fix `edit_predictions_disabled_in` not disabling predictions (#36469)

Smit Barmase created

Closes #25744

Only setting changes and editor init determined whether to show
predictions, so glob patterns and toggles correctly disabled them. On
cursor changes we call `update_visible_edit_prediction`, but we weren’t
discarding predictions when the scope changed. This PR fixes that.

Release Notes:

- Fixed an issue where the `edit_predictions_disabled_in` setting was
ignored in some cases.

Change summary

crates/editor/src/edit_prediction_tests.rs | 42 +++++++++++++++++++++++
crates/editor/src/editor.rs                |  8 ++++
2 files changed, 49 insertions(+), 1 deletion(-)

Detailed changes

crates/editor/src/edit_prediction_tests.rs 🔗

@@ -7,7 +7,9 @@ use std::ops::Range;
 use text::{Point, ToOffset};
 
 use crate::{
-    EditPrediction, editor_tests::init_test, test::editor_test_context::EditorTestContext,
+    EditPrediction,
+    editor_tests::{init_test, update_test_language_settings},
+    test::editor_test_context::EditorTestContext,
 };
 
 #[gpui::test]
@@ -271,6 +273,44 @@ async fn test_edit_prediction_jump_disabled_for_non_zed_providers(cx: &mut gpui:
     });
 }
 
+#[gpui::test]
+async fn test_edit_predictions_disabled_in_scope(cx: &mut gpui::TestAppContext) {
+    init_test(cx, |_| {});
+
+    update_test_language_settings(cx, |settings| {
+        settings.defaults.edit_predictions_disabled_in = Some(vec!["string".to_string()]);
+    });
+
+    let mut cx = EditorTestContext::new(cx).await;
+    let provider = cx.new(|_| FakeEditPredictionProvider::default());
+    assign_editor_completion_provider(provider.clone(), &mut cx);
+
+    let language = languages::language("javascript", tree_sitter_typescript::LANGUAGE_TSX.into());
+    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+
+    // Test disabled inside of string
+    cx.set_state("const x = \"hello ˇworld\";");
+    propose_edits(&provider, vec![(17..17, "beautiful ")], &mut cx);
+    cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
+    cx.editor(|editor, _, _| {
+        assert!(
+            editor.active_edit_prediction.is_none(),
+            "Edit predictions should be disabled in string scopes when configured in edit_predictions_disabled_in"
+        );
+    });
+
+    // Test enabled outside of string
+    cx.set_state("const x = \"hello world\"; ˇ");
+    propose_edits(&provider, vec![(24..24, "// comment")], &mut cx);
+    cx.update_editor(|editor, window, cx| editor.update_visible_edit_prediction(window, cx));
+    cx.editor(|editor, _, _| {
+        assert!(
+            editor.active_edit_prediction.is_some(),
+            "Edit predictions should work outside of disabled scopes"
+        );
+    });
+}
+
 fn assert_editor_active_edit_completion(
     cx: &mut EditorTestContext,
     assert: impl FnOnce(MultiBufferSnapshot, &Vec<(Range<Anchor>, String)>),

crates/editor/src/editor.rs 🔗

@@ -7764,6 +7764,14 @@ impl Editor {
         self.edit_prediction_settings =
             self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 
+        match self.edit_prediction_settings {
+            EditPredictionSettings::Disabled => {
+                self.discard_edit_prediction(false, cx);
+                return None;
+            }
+            _ => {}
+        };
+
         self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 
         if self.edit_prediction_indent_conflict {