feat: reload clean buffers and guard external-change writes
This commit is contained in:
+90
-1
@@ -8,6 +8,7 @@ use std::{
|
||||
mpsc::{self, Receiver, Sender},
|
||||
},
|
||||
thread::{self, JoinHandle},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -173,6 +174,7 @@ pub struct EditorSession {
|
||||
diff_workers: BTreeMap<usize, DiffWorker>,
|
||||
battery: Option<crate::battery::Battery>,
|
||||
battery_enabled: bool,
|
||||
last_file_refresh: Instant,
|
||||
}
|
||||
|
||||
impl EditorSession {
|
||||
@@ -272,6 +274,7 @@ impl EditorSession {
|
||||
diff_workers: BTreeMap::new(),
|
||||
battery: None,
|
||||
battery_enabled: false,
|
||||
last_file_refresh: Instant::now(),
|
||||
};
|
||||
session.synchronize_documents();
|
||||
session.poll_battery();
|
||||
@@ -1044,9 +1047,22 @@ impl EditorSession {
|
||||
changed
|
||||
}
|
||||
|
||||
fn refresh_files_at(&mut self, now: Instant) -> bool {
|
||||
if now.saturating_duration_since(self.last_file_refresh) < Duration::from_millis(250) {
|
||||
return false;
|
||||
}
|
||||
self.last_file_refresh = now;
|
||||
self.editor.refresh_files()
|
||||
}
|
||||
|
||||
/// Drains ready plugin work without waiting for command or server responses.
|
||||
pub fn poll(&mut self) -> bool {
|
||||
let mut changed = self.synchronize_documents();
|
||||
self.poll_at(Instant::now())
|
||||
}
|
||||
|
||||
fn poll_at(&mut self, now: Instant) -> bool {
|
||||
let mut changed = self.refresh_files_at(now);
|
||||
changed |= self.synchronize_documents();
|
||||
changed |= self.poll_battery();
|
||||
changed |= self.poll_diff();
|
||||
for update in self.lsp.poll() {
|
||||
@@ -3396,6 +3412,79 @@ end
|
||||
assert_eq!(session.editor().label("battery"), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idle_poll_refreshes_clean_and_parked_files_on_the_interval_but_keeps_dirty_text() {
|
||||
let fixture = Fixture::new();
|
||||
let active = fixture.file("buffer.txt", "old");
|
||||
let parked = fixture.file("parked.txt", "parked");
|
||||
let mut session = fixture.open("");
|
||||
session.execute(&format!("edit {}", parked.display()));
|
||||
session.execute("buffer 1");
|
||||
let now = session.last_file_refresh;
|
||||
let revision = session.editor().text_revision();
|
||||
fs::write(&active, "external replacement").unwrap();
|
||||
fs::write(&parked, "external parked replacement").unwrap();
|
||||
|
||||
session.poll_at(now + Duration::from_millis(249));
|
||||
assert_eq!(session.editor().lines(), &["old"]);
|
||||
session.dispatch(Invocation {
|
||||
plugin: "core".into(),
|
||||
command: "lsp.definition".into(),
|
||||
args: String::new(),
|
||||
});
|
||||
assert!(session.requests["core"].latest.is_some());
|
||||
assert!(session.poll_at(now + Duration::from_millis(250)));
|
||||
assert!(session.requests["core"].latest.is_none());
|
||||
assert_eq!(session.editor().lines(), &["external replacement"]);
|
||||
assert!(session.editor().text_revision() > revision);
|
||||
let documents: Vec<_> = session.editor().documents().collect();
|
||||
assert_eq!(documents[1].lines, ["external parked replacement"]);
|
||||
|
||||
session.handle(Input::Char('x'));
|
||||
let dirty = session.editor().lines().to_vec();
|
||||
let revision = session.editor().text_revision();
|
||||
fs::write(&active, "second external replacement").unwrap();
|
||||
session.poll_at(now + Duration::from_millis(500));
|
||||
assert_eq!(session.editor().lines(), dirty.as_slice());
|
||||
assert_eq!(session.editor().text_revision(), revision);
|
||||
assert_eq!(
|
||||
fs::read_to_string(&active).unwrap(),
|
||||
"second external replacement"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clean_external_reload_notifies_async_plugin_with_change_without_save() {
|
||||
let fixture = Fixture::new();
|
||||
let file = fixture.file("buffer.txt", "old");
|
||||
let mut session = fixture.asynchronous(Some(file.clone()));
|
||||
barrier(&mut session, "opened");
|
||||
let now = session.last_file_refresh;
|
||||
let revision = session.editor().text_revision();
|
||||
fs::write(&file, "updated external text").unwrap();
|
||||
assert!(session.poll_at(now + Duration::from_millis(250)));
|
||||
assert_eq!(session.editor().lines(), &["updated external text"]);
|
||||
let updated_revision = session.editor().text_revision();
|
||||
assert!(updated_revision > revision);
|
||||
assert_eq!(
|
||||
session.documents[&session.editor().buffer_id()]
|
||||
.snapshot
|
||||
.revision,
|
||||
updated_revision
|
||||
);
|
||||
barrier(&mut session, "reload delivered");
|
||||
|
||||
let events = fixture.events();
|
||||
let changes: Vec<_> = events
|
||||
.iter()
|
||||
.filter(|event| event["event"] == "change")
|
||||
.collect();
|
||||
assert_eq!(changes.len(), 1);
|
||||
assert_eq!(changes[0]["document"]["text"], "updated external text");
|
||||
assert_eq!(changes[0]["document"]["revision"], updated_revision);
|
||||
assert!(!events.iter().any(|event| event["event"] == "save"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn async_documents_include_unnamed_hidden_versions_real_saves_renames_and_closes() {
|
||||
let fixture = Fixture::new();
|
||||
|
||||
Reference in New Issue
Block a user