copilot: Clear completions upon discard (#40185)

Tim Vermeulen , Piotr Osiewicz , and Ben Brandt created

Closes #37836

This behavior was already fixed for Supermaven in #37047, but is still
present in Copilot. What's actually happening:
- Receive a multi-line edit prediction
- Dismiss it with escape
- Clicking anywhere in the editor below the cursor calls
`Editor::select` which starts out by calling `Editor::hide_context_menu`
-> `Editor::update_visible_edit_prediction`, bringing back the
prediction that was just dismissed and updating the editor's display map
- The subsequent selection logic in `Editor::select` now operates using
a display map that is inconsistent with what the user saw when clicking
- If the click was anywhere where the prediction inlay used to be,
`Editor::select` thinks the user clicked on the inlay and does nothing,
and the inlay reappears
- If the click was below where the prediction inlay used to be, the
inlay is immediately removed again but the cursor is moved to the wrong
position because the inlay temporarily added a vertical offset to all
lines after it in the buffer

Ultimately, `Editor::select` should be handling the user input using the
same display map that the user saw when making the input. This can
obviously be solved in multiple ways, I chose to clear the current
completions in `CopilotCompletionProvider::discard` such that any
subsequent calls to `Editor::update_visible_edit_prediction` doesn't
immediately nullify the dismissal of the edit prediction by the user.

Note that this also changes the behavior that occurs after dismissing an
edit prediction, moving to a different position in the buffer, and then
returning: currently, this resurfaces the dismissed edit prediction
(which the `test_copilot` test exercises), and after this change it
doesn’t. This current behavior didn't seem desirable to me because it
doesn't happen when using Zeta or Supermaven, but if we want to keep it,
then we could fix the incorrect selection behavior some other way.

Release Notes:

- Fixed bug that resurfaced dismissed Copilot edit predictions when
moving the cursor around

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>

Change summary

crates/copilot/src/copilot_edit_prediction_delegate.rs | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)

Detailed changes

crates/copilot/src/copilot_edit_prediction_delegate.rs 🔗

@@ -129,7 +129,9 @@ impl EditPredictionDelegate for CopilotEditPredictionDelegate {
         }
     }
 
-    fn discard(&mut self, _reason: EditPredictionDiscardReason, _: &mut Context<Self>) {}
+    fn discard(&mut self, _reason: EditPredictionDiscardReason, _: &mut Context<Self>) {
+        self.completion.take();
+    }
 
     fn suggest(
         &mut self,
@@ -410,8 +412,14 @@ mod tests {
             assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
 
-            // When undoing the previously active suggestion is shown again.
+            // When undoing the previously active suggestion isn't shown again.
             editor.undo(&Default::default(), window, cx);
+            assert!(!editor.has_active_edit_prediction());
+            assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
+            assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
+        });
+        executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+        cx.editor(|editor, _, cx| {
             assert!(editor.has_active_edit_prediction());
             assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
             assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");