Keep cursor stable as autocompletions are being streamed

This commit is contained in:
Antonio Scandurra 2023-06-14 10:41:18 +02:00
parent 75ad76bfb2
commit f8b9417406

View file

@ -7,11 +7,8 @@ use chrono::{DateTime, Local};
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
use editor::{ use editor::{
display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, ToDisplayPoint}, display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, ToDisplayPoint},
scroll::{ scroll::autoscroll::{Autoscroll, AutoscrollStrategy},
autoscroll::{Autoscroll, AutoscrollStrategy}, Anchor, Editor, ToOffset as _,
ScrollAnchor,
},
Anchor, DisplayPoint, Editor, ToOffset as _,
}; };
use fs::Fs; use fs::Fs;
use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt}; use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt};
@ -19,7 +16,7 @@ use gpui::{
actions, actions,
elements::*, elements::*,
executor::Background, executor::Background,
geometry::vector::vec2f, geometry::vector::{vec2f, Vector2F},
platform::{CursorStyle, MouseButton}, platform::{CursorStyle, MouseButton},
Action, AppContext, AsyncAppContext, ClipboardItem, Entity, ModelContext, ModelHandle, Action, AppContext, AsyncAppContext, ClipboardItem, Entity, ModelContext, ModelHandle,
Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
@ -801,11 +798,17 @@ enum AssistantEditorEvent {
TabContentChanged, TabContentChanged,
} }
#[derive(Copy, Clone, Debug, PartialEq)]
struct ScrollPosition {
offset_before_cursor: Vector2F,
cursor: Anchor,
}
struct AssistantEditor { struct AssistantEditor {
assistant: ModelHandle<Assistant>, assistant: ModelHandle<Assistant>,
editor: ViewHandle<Editor>, editor: ViewHandle<Editor>,
blocks: HashSet<BlockId>, blocks: HashSet<BlockId>,
scroll_bottom: ScrollAnchor, scroll_position: Option<ScrollPosition>,
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
} }
@ -833,10 +836,7 @@ impl AssistantEditor {
assistant, assistant,
editor, editor,
blocks: Default::default(), blocks: Default::default(),
scroll_bottom: ScrollAnchor { scroll_position: None,
offset: Default::default(),
anchor: Anchor::max(),
},
_subscriptions, _subscriptions,
}; };
this.update_message_headers(cx); this.update_message_headers(cx);
@ -874,7 +874,7 @@ impl AssistantEditor {
|selections| selections.select_ranges([cursor..cursor]), |selections| selections.select_ranges([cursor..cursor]),
); );
}); });
self.update_scroll_bottom(cx); self.scroll_position = self.cursor_scroll_position(cx);
} }
} }
@ -900,18 +900,16 @@ impl AssistantEditor {
} }
AssistantEvent::StreamedCompletion => { AssistantEvent::StreamedCompletion => {
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx); if let Some(scroll_position) = self.scroll_position {
let scroll_bottom_row = self let snapshot = editor.snapshot(cx);
.scroll_bottom let cursor_point = scroll_position.cursor.to_display_point(&snapshot);
.anchor let scroll_top =
.to_display_point(&snapshot.display_snapshot) cursor_point.row() as f32 - scroll_position.offset_before_cursor.y();
.row(); editor.set_scroll_position(
vec2f(scroll_position.offset_before_cursor.x(), scroll_top),
let scroll_bottom = scroll_bottom_row as f32 + self.scroll_bottom.offset.y(); cx,
let visible_line_count = editor.visible_line_count().unwrap_or(0.); );
let scroll_top = scroll_bottom - visible_line_count; }
editor
.set_scroll_position(vec2f(self.scroll_bottom.offset.x(), scroll_top), cx);
}); });
} }
} }
@ -924,11 +922,43 @@ impl AssistantEditor {
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
match event { match event {
editor::Event::ScrollPositionChanged { .. } => self.update_scroll_bottom(cx), editor::Event::ScrollPositionChanged { .. } => {
if self.cursor_scroll_position(cx) != self.scroll_position {
self.scroll_position = None;
}
}
editor::Event::SelectionsChanged { .. } => {
self.scroll_position = self.cursor_scroll_position(cx);
}
_ => {} _ => {}
} }
} }
fn cursor_scroll_position(&self, cx: &mut ViewContext<Self>) -> Option<ScrollPosition> {
self.editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);
let cursor = editor.selections.newest_anchor().head();
let cursor_row = cursor.to_display_point(&snapshot.display_snapshot).row() as f32;
let scroll_position = editor
.scroll_manager
.anchor()
.scroll_position(&snapshot.display_snapshot);
let scroll_bottom = scroll_position.y() + editor.visible_line_count().unwrap_or(0.);
if (scroll_position.y()..scroll_bottom).contains(&cursor_row) {
Some(ScrollPosition {
cursor,
offset_before_cursor: vec2f(
scroll_position.x(),
cursor_row - scroll_position.y(),
),
})
} else {
None
}
})
}
fn update_message_headers(&mut self, cx: &mut ViewContext<Self>) { fn update_message_headers(&mut self, cx: &mut ViewContext<Self>) {
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |editor, cx| {
let buffer = editor.buffer().read(cx).snapshot(cx); let buffer = editor.buffer().read(cx).snapshot(cx);
@ -1031,32 +1061,6 @@ impl AssistantEditor {
}); });
} }
fn update_scroll_bottom(&mut self, cx: &mut ViewContext<Self>) {
self.editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);
let scroll_position = editor
.scroll_manager
.anchor()
.scroll_position(&snapshot.display_snapshot);
let scroll_bottom = scroll_position.y() + editor.visible_line_count().unwrap_or(0.);
let scroll_bottom_point = cmp::min(
DisplayPoint::new(scroll_bottom.floor() as u32, 0),
snapshot.display_snapshot.max_point(),
);
let scroll_bottom_anchor = snapshot
.buffer_snapshot
.anchor_after(scroll_bottom_point.to_point(&snapshot.display_snapshot));
let scroll_bottom_offset = vec2f(
scroll_position.x(),
scroll_bottom - scroll_bottom_point.row() as f32,
);
self.scroll_bottom = ScrollAnchor {
anchor: scroll_bottom_anchor,
offset: scroll_bottom_offset,
};
});
}
fn quote_selection( fn quote_selection(
workspace: &mut Workspace, workspace: &mut Workspace,
_: &QuoteSelection, _: &QuoteSelection,