load file history

It was necessary make pos in file args an option to prevent it from
overwriting the file positions loaded from persistence.

Alignment is not quite right... I think we need to persist selections
instead of view positions, or disable center aligning
pull/9143/head
Ingrid 10 months ago
parent ed7fb431aa
commit 1cefa2874c

@ -1,5 +1,5 @@
use crate::{command_histfile, file_histfile, search_histfile};
use bincode::serialize_into;
use bincode::{deserialize_from, serialize_into};
use serde::{Deserialize, Serialize};
use std::{
fs::{File, OpenOptions},
@ -12,10 +12,10 @@ use std::{
// since this crate is a dependency of helix-view
#[derive(Debug, Serialize, Deserialize)]
pub struct FileHistoryEntry {
path: PathBuf,
anchor: usize,
vertical_offset: usize,
horizontal_offset: usize,
pub path: PathBuf,
pub anchor: usize,
pub vertical_offset: usize,
pub horizontal_offset: usize,
}
impl FileHistoryEntry {
@ -46,6 +46,25 @@ pub fn push_file_history(entry: FileHistoryEntry) {
serialize_into(file, &entry).unwrap();
}
pub fn read_file_history() -> Vec<FileHistoryEntry> {
match File::open(file_histfile()) {
Ok(file) => {
let mut read = BufReader::new(file);
let mut entries = Vec::new();
// TODO: more sophisticated error handling
while let Ok(entry) = deserialize_from(&mut read) {
entries.push(entry);
}
entries
}
Err(e) => match e.kind() {
io::ErrorKind::NotFound => Vec::new(),
// TODO: do something about this panic
_ => panic!(),
},
}
}
pub fn push_history(register: char, line: &str) {
let filepath = match register {
':' => command_histfile(),

@ -16,6 +16,7 @@ use helix_view::{
graphics::Rect,
theme,
tree::Layout,
view::ViewPosition,
Align, Editor,
};
use serde_json::json;
@ -34,7 +35,12 @@ use crate::{
use log::{debug, error, info, warn};
#[cfg(not(feature = "integration"))]
use std::io::stdout;
use std::{collections::btree_map::Entry, io::stdin, path::Path, sync::Arc};
use std::{
collections::{btree_map::Entry, HashMap},
io::stdin,
path::Path,
sync::Arc,
};
#[cfg(not(windows))]
use anyhow::Context;
@ -147,6 +153,16 @@ impl Application {
&config.editor
})),
handlers,
HashMap::from_iter(session::read_file_history().iter().map(|entry| {
(
entry.path.clone(),
ViewPosition {
anchor: entry.anchor,
horizontal_offset: entry.horizontal_offset,
vertical_offset: entry.vertical_offset,
},
)
})),
);
// TODO: do most of this in the background?
@ -217,10 +233,13 @@ impl Application {
// NOTE: this isn't necessarily true anymore. If
// `--vsplit` or `--hsplit` are used, the file which is
// opened last is focused on.
let view_id = editor.tree.focus;
let doc = doc_mut!(editor, &doc_id);
let pos = Selection::point(pos_at_coords(doc.text().slice(..), pos, true));
doc.set_selection(view_id, pos);
if let Some(pos) = pos {
let view_id = editor.tree.focus;
let doc = doc_mut!(editor, &doc_id);
let pos =
Selection::point(pos_at_coords(doc.text().slice(..), pos, true));
doc.set_selection(view_id, pos);
}
}
}

@ -16,7 +16,7 @@ pub struct Args {
pub verbosity: u64,
pub log_file: Option<PathBuf>,
pub config_file: Option<PathBuf>,
pub files: Vec<(PathBuf, Position)>,
pub files: Vec<(PathBuf, Option<Position>)>,
pub working_directory: Option<PathBuf>,
}
@ -106,7 +106,10 @@ impl Args {
if let Some(file) = args.files.first_mut() {
if line_number != 0 {
file.1.row = line_number;
file.1 = match file.1 {
Some(pos) => Some(Position::new(line_number, pos.col)),
None => Some(Position::new(line_number, 0)),
}
}
}
@ -115,8 +118,8 @@ impl Args {
}
/// Parse arg into [`PathBuf`] and position.
pub(crate) fn parse_file(s: &str) -> (PathBuf, Position) {
let def = || (PathBuf::from(s), Position::default());
pub(crate) fn parse_file(s: &str) -> (PathBuf, Option<Position>) {
let def = || (PathBuf::from(s), None);
if Path::new(s).exists() {
return def();
}
@ -128,22 +131,22 @@ pub(crate) fn parse_file(s: &str) -> (PathBuf, Position) {
/// Split file.rs:10:2 into [`PathBuf`], row and col.
///
/// Does not validate if file.rs is a file or directory.
fn split_path_row_col(s: &str) -> Option<(PathBuf, Position)> {
fn split_path_row_col(s: &str) -> Option<(PathBuf, Option<Position>)> {
let mut s = s.rsplitn(3, ':');
let col: usize = s.next()?.parse().ok()?;
let row: usize = s.next()?.parse().ok()?;
let path = s.next()?.into();
let pos = Position::new(row.saturating_sub(1), col.saturating_sub(1));
Some((path, pos))
Some((path, Some(pos)))
}
/// Split file.rs:10 into [`PathBuf`] and row.
///
/// Does not validate if file.rs is a file or directory.
fn split_path_row(s: &str) -> Option<(PathBuf, Position)> {
fn split_path_row(s: &str) -> Option<(PathBuf, Option<Position>)> {
let (path, row) = s.rsplit_once(':')?;
let row: usize = row.parse().ok()?;
let path = path.into();
let pos = Position::new(row.saturating_sub(1), 0);
Some((path, pos))
Some((path, Some(pos)))
}

@ -129,8 +129,10 @@ fn open(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
// Otherwise, just open the file
let _ = cx.editor.open(&path, Action::Replace)?;
let (view, doc) = current!(cx.editor);
let pos = Selection::point(pos_at_coords(doc.text().slice(..), pos, true));
doc.set_selection(view.id, pos);
if let Some(pos) = pos {
let pos = Selection::point(pos_at_coords(doc.text().slice(..), pos, true));
doc.set_selection(view.id, pos);
}
// does not affect opening a buffer without pos
align_view(doc, view, Align::Center);
}

@ -1034,6 +1034,8 @@ pub struct Editor {
pub debugger_events: SelectAll<UnboundedReceiverStream<dap::Payload>>,
pub breakpoints: HashMap<PathBuf, Vec<Breakpoint>>,
pub old_file_locs: HashMap<PathBuf, ViewPosition>,
pub syn_loader: Arc<ArcSwap<syntax::Loader>>,
pub theme_loader: Arc<theme::Loader>,
/// last_theme is used for theme previews. We store the current theme here,
@ -1150,6 +1152,7 @@ impl Editor {
syn_loader: Arc<ArcSwap<syntax::Loader>>,
config: Arc<dyn DynAccess<Config>>,
handlers: Handlers,
old_file_locs: HashMap<PathBuf, ViewPosition>,
) -> Self {
let language_servers = helix_lsp::Registry::new(syn_loader.clone());
let conf = config.load();
@ -1177,6 +1180,7 @@ impl Editor {
debugger: None,
debugger_events: SelectAll::new(),
breakpoints: HashMap::new(),
old_file_locs,
syn_loader,
theme_loader,
last_theme: None,
@ -1636,6 +1640,18 @@ impl Editor {
);
// initialize selection for view
let doc = doc_mut!(self, &id);
let view = self.tree.get_mut(view_id);
view.offset = self
.old_file_locs
.get(doc.path().unwrap())
.map(|x| x.to_owned())
.unwrap_or_default();
doc.set_selection(
view_id,
Selection::single(view.offset.anchor, view.offset.anchor),
);
doc.ensure_view_init(view_id);
doc.mark_as_focused();
}
@ -1731,11 +1747,12 @@ impl Editor {
let doc = self.document(view.doc).unwrap();
if let Some(path) = doc.path() {
push_file_history(FileHistoryEntry::new(
path.to_owned(),
path.clone(),
view.offset.anchor,
view.offset.vertical_offset,
view.offset.horizontal_offset,
));
self.old_file_locs.insert(path.to_owned(), view.offset);
};
// Remove selections for the closed view on all documents.

Loading…
Cancel
Save