@@ -8,6 +8,7 @@ use anyhow::{anyhow, Context, Result};
use collections::HashSet;
use futures::future::try_join_all;
use futures::FutureExt;
+
use gpui::{
elements::*, geometry::vector::vec2f, AppContext, Entity, ModelHandle, MutableAppContext,
RenderContext, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
@@ -765,6 +766,7 @@ impl Item for Editor {
fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
let workspace_id = workspace.database_id();
let item_id = cx.view_id();
+ self.workspace_id = Some(workspace_id);
fn serialize(
buffer: ModelHandle<Buffer>,
@@ -836,7 +838,11 @@ impl Item for Editor {
.context("Project item at stored path was not a buffer")?;
Ok(cx.update(|cx| {
- cx.add_view(pane, |cx| Editor::for_buffer(buffer, Some(project), cx))
+ cx.add_view(pane, |cx| {
+ let mut editor = Editor::for_buffer(buffer, Some(project), cx);
+ editor.read_scroll_position_from_db(item_id, workspace_id, cx);
+ editor
+ })
}))
})
})
@@ -2,9 +2,19 @@ use std::path::PathBuf;
use db::sqlez_macros::sql;
use db::{define_connection, query};
+
use workspace::{ItemId, WorkspaceDb, WorkspaceId};
define_connection!(
+ // Current table shape using pseudo-rust syntax:
+ // editors(
+ // item_id: usize,
+ // workspace_id: usize,
+ // path: PathBuf,
+ // scroll_top_row: usize,
+ // scroll_vertical_offset: f32,
+ // scroll_horizontal_offset: f32,
+ // )
pub static ref DB: EditorDb<WorkspaceDb> =
&[sql! (
CREATE TABLE editors(
@@ -18,9 +28,9 @@ define_connection!(
) STRICT;
),
sql! (
- ALTER TABLE editors ADD COLUMN scroll_top_row INTEGER;
- ALTER TABLE editors ADD COLUMN scroll_vertical_offset REAL;
- ALTER TABLE editors ADD COLUMN scroll_horizontal_offset REAL;
+ ALTER TABLE editors ADD COLUMN scroll_top_row INTEGER NOT NULL DEFAULT 0;
+ ALTER TABLE editors ADD COLUMN scroll_horizontal_offset REAL NOT NULL DEFAULT 0;
+ ALTER TABLE editors ADD COLUMN scroll_vertical_offset REAL NOT NULL DEFAULT 0;
)];
);
@@ -34,8 +44,40 @@ impl EditorDb {
query! {
pub async fn save_path(item_id: ItemId, workspace_id: WorkspaceId, path: PathBuf) -> Result<()> {
- INSERT OR REPLACE INTO editors(item_id, workspace_id, path)
- VALUES (?, ?, ?)
+ INSERT INTO editors
+ (item_id, workspace_id, path)
+ VALUES
+ (?1, ?2, ?3)
+ ON CONFLICT DO UPDATE SET
+ item_id = ?1,
+ workspace_id = ?2,
+ path = ?3
+ }
+ }
+
+ // Returns the scroll top row, and offset
+ query! {
+ pub fn get_scroll_position(item_id: ItemId, workspace_id: WorkspaceId) -> Result<Option<(u32, f32, f32)>> {
+ SELECT scroll_top_row, scroll_horizontal_offset, scroll_vertical_offset
+ FROM editors
+ WHERE item_id = ? AND workspace_id = ?
+ }
+ }
+
+ query! {
+ pub async fn save_scroll_position(
+ item_id: ItemId,
+ workspace_id: WorkspaceId,
+ top_row: u32,
+ vertical_offset: f32,
+ horizontal_offset: f32
+ ) -> Result<()> {
+ UPDATE OR IGNORE editors
+ SET
+ scroll_top_row = ?3,
+ scroll_horizontal_offset = ?4,
+ scroll_vertical_offset = ?5
+ WHERE item_id = ?1 AND workspace_id = ?2
}
}
}
@@ -11,11 +11,14 @@ use gpui::{
geometry::vector::{vec2f, Vector2F},
Axis, MutableAppContext, Task, ViewContext,
};
-use language::Bias;
+use language::{Bias, Point};
+use util::ResultExt;
+use workspace::WorkspaceId;
use crate::{
display_map::{DisplaySnapshot, ToDisplayPoint},
hover_popover::hide_hover,
+ persistence::DB,
Anchor, DisplayPoint, Editor, EditorMode, Event, MultiBufferSnapshot, ToPoint,
};
@@ -170,37 +173,68 @@ impl ScrollManager {
scroll_position: Vector2F,
map: &DisplaySnapshot,
local: bool,
+ workspace_id: Option<i64>,
cx: &mut ViewContext<Editor>,
) {
- let new_anchor = if scroll_position.y() <= 0. {
- ScrollAnchor {
- top_anchor: Anchor::min(),
- offset: scroll_position.max(vec2f(0., 0.)),
- }
+ let (new_anchor, top_row) = if scroll_position.y() <= 0. {
+ (
+ ScrollAnchor {
+ top_anchor: Anchor::min(),
+ offset: scroll_position.max(vec2f(0., 0.)),
+ },
+ 0,
+ )
} else {
- let scroll_top_buffer_offset =
- DisplayPoint::new(scroll_position.y() as u32, 0).to_offset(&map, Bias::Right);
+ let scroll_top_buffer_point =
+ DisplayPoint::new(scroll_position.y() as u32, 0).to_point(&map);
let top_anchor = map
.buffer_snapshot
- .anchor_at(scroll_top_buffer_offset, Bias::Right);
-
- ScrollAnchor {
- top_anchor,
- offset: vec2f(
- scroll_position.x(),
- scroll_position.y() - top_anchor.to_display_point(&map).row() as f32,
- ),
- }
+ .anchor_at(scroll_top_buffer_point, Bias::Right);
+
+ (
+ ScrollAnchor {
+ top_anchor,
+ offset: vec2f(
+ scroll_position.x(),
+ scroll_position.y() - top_anchor.to_display_point(&map).row() as f32,
+ ),
+ },
+ scroll_top_buffer_point.row,
+ )
};
- self.set_anchor(new_anchor, local, cx);
+ self.set_anchor(new_anchor, top_row, local, workspace_id, cx);
}
- fn set_anchor(&mut self, anchor: ScrollAnchor, local: bool, cx: &mut ViewContext<Editor>) {
+ fn set_anchor(
+ &mut self,
+ anchor: ScrollAnchor,
+ top_row: u32,
+ local: bool,
+ workspace_id: Option<i64>,
+ cx: &mut ViewContext<Editor>,
+ ) {
self.anchor = anchor;
cx.emit(Event::ScrollPositionChanged { local });
self.show_scrollbar(cx);
self.autoscroll_request.take();
+ if let Some(workspace_id) = workspace_id {
+ let item_id = cx.view_id();
+
+ cx.background()
+ .spawn(async move {
+ DB.save_scroll_position(
+ item_id,
+ workspace_id,
+ top_row,
+ anchor.offset.x(),
+ anchor.offset.y(),
+ )
+ .await
+ .log_err()
+ })
+ .detach()
+ }
cx.notify();
}
@@ -274,8 +308,13 @@ impl Editor {
let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
hide_hover(self, cx);
- self.scroll_manager
- .set_scroll_position(scroll_position, &map, local, cx);
+ self.scroll_manager.set_scroll_position(
+ scroll_position,
+ &map,
+ local,
+ self.workspace_id,
+ cx,
+ );
}
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
@@ -285,7 +324,12 @@ impl Editor {
pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
hide_hover(self, cx);
- self.scroll_manager.set_anchor(scroll_anchor, true, cx);
+ let top_row = scroll_anchor
+ .top_anchor
+ .to_point(&self.buffer().read(cx).snapshot(cx))
+ .row;
+ self.scroll_manager
+ .set_anchor(scroll_anchor, top_row, true, self.workspace_id, cx);
}
pub(crate) fn set_scroll_anchor_remote(
@@ -294,7 +338,12 @@ impl Editor {
cx: &mut ViewContext<Self>,
) {
hide_hover(self, cx);
- self.scroll_manager.set_anchor(scroll_anchor, false, cx);
+ let top_row = scroll_anchor
+ .top_anchor
+ .to_point(&self.buffer().read(cx).snapshot(cx))
+ .row;
+ self.scroll_manager
+ .set_anchor(scroll_anchor, top_row, false, self.workspace_id, cx);
}
pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
@@ -345,4 +394,25 @@ impl Editor {
Ordering::Greater
}
+
+ pub fn read_scroll_position_from_db(
+ &mut self,
+ item_id: usize,
+ workspace_id: WorkspaceId,
+ cx: &mut ViewContext<Editor>,
+ ) {
+ let scroll_position = DB.get_scroll_position(item_id, workspace_id);
+ if let Ok(Some((top_row, x, y))) = scroll_position {
+ let top_anchor = self
+ .buffer()
+ .read(cx)
+ .snapshot(cx)
+ .anchor_at(Point::new(top_row as u32, 0), Bias::Left);
+ let scroll_anchor = ScrollAnchor {
+ offset: Vector2F::new(x, y),
+ top_anchor,
+ };
+ self.set_scroll_anchor(scroll_anchor, cx);
+ }
+ }
}
@@ -89,6 +89,26 @@ impl Column for f64 {
}
}
+impl Bind for f32 {
+ fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
+ statement
+ .bind_double(start_index, *self as f64)
+ .with_context(|| format!("Failed to bind f64 at index {start_index}"))?;
+ Ok(start_index + 1)
+ }
+}
+
+impl Column for f32 {
+ fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
+ let result = statement
+ .column_double(start_index)
+ .with_context(|| format!("Failed to parse f32 at index {start_index}"))?
+ as f32;
+
+ Ok((result, start_index + 1))
+ }
+}
+
impl Bind for i32 {
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
statement
@@ -122,6 +142,21 @@ impl Column for i64 {
}
}
+impl Bind for u32 {
+ fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
+ (*self as i64)
+ .bind(statement, start_index)
+ .with_context(|| format!("Failed to bind usize at index {start_index}"))
+ }
+}
+
+impl Column for u32 {
+ fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
+ let result = statement.column_int64(start_index)?;
+ Ok((result as u32, start_index + 1))
+ }
+}
+
impl Bind for usize {
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
(*self as i64)