-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Write path fixes #2267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Write path fixes #2267
Changes from all commits
d706194
a5a9318
83b6042
e1f7bdb
69c9e44
b8a07f7
cb23399
c941858
aaa1450
8c667ef
faa00d4
e5fd5e2
d544376
7b11e9a
57de4e6
6cffc7f
f82a551
18c3211
af03df3
b3fc31a
b530a86
3f07885
9e64974
31d1bbf
bf378e7
beb3427
30c9399
b0212b3
b155e86
55b50d9
1b6f731
2a43ee0
52ba550
e645804
759d55c
9a406b5
756253b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,19 @@ | ||
use arc_swap::{access::Map, ArcSwap}; | ||
use futures_util::Stream; | ||
use helix_core::{ | ||
config::{default_syntax_loader, user_syntax_loader}, | ||
diagnostic::{DiagnosticTag, NumberOrString}, | ||
path::get_relative_path, | ||
pos_at_coords, syntax, Selection, | ||
}; | ||
use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap}; | ||
use helix_view::{align_view, editor::ConfigEvent, theme, tree::Layout, Align, Editor}; | ||
use helix_view::{ | ||
align_view, | ||
document::DocumentSavedEventResult, | ||
editor::{ConfigEvent, EditorEvent}, | ||
theme, | ||
tree::Layout, | ||
Align, Editor, | ||
}; | ||
use serde_json::json; | ||
|
||
use crate::{ | ||
|
@@ -19,7 +26,7 @@ use crate::{ | |
ui::{self, overlay::overlayed}, | ||
}; | ||
|
||
use log::{error, warn}; | ||
use log::{debug, error, warn}; | ||
use std::{ | ||
io::{stdin, stdout, Write}, | ||
sync::Arc, | ||
|
@@ -102,7 +109,11 @@ fn restore_term() -> Result<(), Error> { | |
} | ||
|
||
impl Application { | ||
pub fn new(args: Args, config: Config) -> Result<Self, Error> { | ||
pub fn new( | ||
args: Args, | ||
config: Config, | ||
syn_loader_conf: syntax::Configuration, | ||
) -> Result<Self, Error> { | ||
#[cfg(feature = "integration")] | ||
setup_integration_logging(); | ||
|
||
|
@@ -129,14 +140,6 @@ impl Application { | |
}) | ||
.unwrap_or_else(|| theme_loader.default_theme(true_color)); | ||
|
||
let syn_loader_conf = user_syntax_loader().unwrap_or_else(|err| { | ||
eprintln!("Bad language config: {}", err); | ||
eprintln!("Press <ENTER> to continue with default language config"); | ||
use std::io::Read; | ||
// This waits for an enter press. | ||
let _ = std::io::stdin().read(&mut []); | ||
default_syntax_loader() | ||
}); | ||
let syn_loader = std::sync::Arc::new(syntax::Loader::new(syn_loader_conf)); | ||
|
||
let mut compositor = Compositor::new().context("build compositor")?; | ||
|
@@ -245,6 +248,10 @@ impl Application { | |
Ok(app) | ||
} | ||
|
||
#[cfg(feature = "integration")] | ||
fn render(&mut self) {} | ||
|
||
#[cfg(not(feature = "integration"))] | ||
fn render(&mut self) { | ||
let compositor = &mut self.compositor; | ||
|
||
|
@@ -275,9 +282,6 @@ impl Application { | |
where | ||
S: Stream<Item = crossterm::Result<crossterm::event::Event>> + Unpin, | ||
{ | ||
#[cfg(feature = "integration")] | ||
let mut idle_handled = false; | ||
|
||
loop { | ||
if self.editor.should_close() { | ||
return false; | ||
|
@@ -294,26 +298,6 @@ impl Application { | |
Some(signal) = self.signals.next() => { | ||
self.handle_signals(signal).await; | ||
} | ||
Some((id, call)) = self.editor.language_servers.incoming.next() => { | ||
self.handle_language_server_message(call, id).await; | ||
// limit render calls for fast language server messages | ||
let last = self.editor.language_servers.incoming.is_empty(); | ||
|
||
if last || self.last_render.elapsed() > LSP_DEADLINE { | ||
self.render(); | ||
self.last_render = Instant::now(); | ||
} | ||
} | ||
Some(payload) = self.editor.debugger_events.next() => { | ||
let needs_render = self.editor.handle_debugger_message(payload).await; | ||
if needs_render { | ||
self.render(); | ||
} | ||
} | ||
Some(config_event) = self.editor.config_events.1.recv() => { | ||
self.handle_config_events(config_event); | ||
self.render(); | ||
} | ||
Some(callback) = self.jobs.futures.next() => { | ||
self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback); | ||
self.render(); | ||
|
@@ -322,26 +306,22 @@ impl Application { | |
self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback); | ||
self.render(); | ||
} | ||
_ = &mut self.editor.idle_timer => { | ||
// idle timeout | ||
self.editor.clear_idle_timer(); | ||
self.handle_idle_timeout(); | ||
event = self.editor.wait_event() => { | ||
let _idle_handled = self.handle_editor_event(event).await; | ||
|
||
#[cfg(feature = "integration")] | ||
{ | ||
idle_handled = true; | ||
if _idle_handled { | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// for integration tests only, reset the idle timer after every | ||
// event to make a signal when test events are done processing | ||
// event to signal when test events are done processing | ||
#[cfg(feature = "integration")] | ||
{ | ||
if idle_handled { | ||
return true; | ||
} | ||
|
||
self.editor.reset_idle_timer(); | ||
} | ||
} | ||
|
@@ -446,6 +426,111 @@ impl Application { | |
} | ||
} | ||
|
||
pub fn handle_document_write(&mut self, doc_save_event: DocumentSavedEventResult) { | ||
let doc_save_event = match doc_save_event { | ||
Ok(event) => event, | ||
Err(err) => { | ||
self.editor.set_error(err.to_string()); | ||
return; | ||
} | ||
}; | ||
|
||
let doc = match self.editor.document_mut(doc_save_event.doc_id) { | ||
None => { | ||
warn!( | ||
"received document saved event for non-existent doc id: {}", | ||
doc_save_event.doc_id | ||
); | ||
|
||
return; | ||
} | ||
Some(doc) => doc, | ||
}; | ||
|
||
debug!( | ||
"document {:?} saved with revision {}", | ||
doc.path(), | ||
doc_save_event.revision | ||
); | ||
Comment on lines
+450
to
+454
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I wonder why editors don't show user the revision, if it did then users are able to use time travel easily. Like |
||
|
||
doc.set_last_saved_revision(doc_save_event.revision); | ||
|
||
let lines = doc_save_event.text.len_lines(); | ||
let bytes = doc_save_event.text.len_bytes(); | ||
|
||
if doc.path() != Some(&doc_save_event.path) { | ||
if let Err(err) = doc.set_path(Some(&doc_save_event.path)) { | ||
log::error!( | ||
"error setting path for doc '{:?}': {}", | ||
doc.path(), | ||
err.to_string(), | ||
); | ||
|
||
self.editor.set_error(err.to_string()); | ||
dead10ck marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return; | ||
} | ||
|
||
let loader = self.editor.syn_loader.clone(); | ||
|
||
// borrowing the same doc again to get around the borrow checker | ||
let doc = doc_mut!(self.editor, &doc_save_event.doc_id); | ||
let id = doc.id(); | ||
doc.detect_language(loader); | ||
let _ = self.editor.refresh_language_server(id); | ||
} | ||
|
||
// TODO: fix being overwritten by lsp | ||
self.editor.set_status(format!( | ||
"'{}' written, {}L {}B", | ||
get_relative_path(&doc_save_event.path).to_string_lossy(), | ||
lines, | ||
bytes | ||
)); | ||
} | ||
|
||
#[inline(always)] | ||
pub async fn handle_editor_event(&mut self, event: EditorEvent) -> bool { | ||
log::debug!("received editor event: {:?}", event); | ||
|
||
match event { | ||
EditorEvent::DocumentSaved(event) => { | ||
self.handle_document_write(event); | ||
self.render(); | ||
} | ||
EditorEvent::ConfigEvent(event) => { | ||
self.handle_config_events(event); | ||
self.render(); | ||
} | ||
EditorEvent::LanguageServerMessage((id, call)) => { | ||
self.handle_language_server_message(call, id).await; | ||
// limit render calls for fast language server messages | ||
let last = self.editor.language_servers.incoming.is_empty(); | ||
|
||
if last || self.last_render.elapsed() > LSP_DEADLINE { | ||
self.render(); | ||
self.last_render = Instant::now(); | ||
} | ||
} | ||
EditorEvent::DebuggerEvent(payload) => { | ||
let needs_render = self.editor.handle_debugger_message(payload).await; | ||
if needs_render { | ||
self.render(); | ||
} | ||
} | ||
EditorEvent::IdleTimer => { | ||
self.editor.clear_idle_timer(); | ||
self.handle_idle_timeout(); | ||
|
||
#[cfg(feature = "integration")] | ||
{ | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
false | ||
} | ||
|
||
pub fn handle_terminal_events(&mut self, event: Result<CrosstermEvent, crossterm::ErrorKind>) { | ||
let mut cx = crate::compositor::Context { | ||
editor: &mut self.editor, | ||
|
@@ -866,25 +951,44 @@ impl Application { | |
|
||
self.event_loop(input_stream).await; | ||
|
||
let err = self.close().await.err(); | ||
|
||
let close_errs = self.close().await; | ||
restore_term()?; | ||
|
||
if let Some(err) = err { | ||
for err in close_errs { | ||
self.editor.exit_code = 1; | ||
eprintln!("Error: {}", err); | ||
} | ||
|
||
Ok(self.editor.exit_code) | ||
} | ||
|
||
pub async fn close(&mut self) -> anyhow::Result<()> { | ||
self.jobs.finish().await?; | ||
pub async fn close(&mut self) -> Vec<anyhow::Error> { | ||
// [NOTE] we intentionally do not return early for errors because we | ||
// want to try to run as much cleanup as we can, regardless of | ||
// errors along the way | ||
let mut errs = Vec::new(); | ||
|
||
if let Err(err) = self | ||
.jobs | ||
.finish(&mut self.editor, Some(&mut self.compositor)) | ||
.await | ||
{ | ||
log::error!("Error executing job: {}", err); | ||
errs.push(err); | ||
}; | ||
|
||
if let Err(err) = self.editor.flush_writes().await { | ||
log::error!("Error writing: {}", err); | ||
errs.push(err); | ||
} | ||
|
||
if self.editor.close_language_servers(None).await.is_err() { | ||
log::error!("Timed out waiting for language servers to shutdown"); | ||
}; | ||
errs.push(anyhow::format_err!( | ||
"Timed out waiting for language servers to shutdown" | ||
)); | ||
} | ||
|
||
Ok(()) | ||
errs | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.