1
1
use arc_swap:: { access:: Map , ArcSwap } ;
2
2
use futures_util:: Stream ;
3
3
use helix_core:: {
4
- config:: { default_syntax_loader, user_syntax_loader} ,
5
4
diagnostic:: { DiagnosticTag , NumberOrString } ,
5
+ path:: get_relative_path,
6
6
pos_at_coords, syntax, Selection ,
7
7
} ;
8
8
use helix_lsp:: { lsp, util:: lsp_pos_to_pos, LspProgressMap } ;
9
- use helix_view:: { align_view, editor:: ConfigEvent , theme, tree:: Layout , Align , Editor } ;
9
+ use helix_view:: {
10
+ align_view,
11
+ document:: DocumentSavedEventResult ,
12
+ editor:: { ConfigEvent , EditorEvent } ,
13
+ theme,
14
+ tree:: Layout ,
15
+ Align , Editor ,
16
+ } ;
10
17
use serde_json:: json;
11
18
12
19
use crate :: {
@@ -19,7 +26,7 @@ use crate::{
19
26
ui:: { self , overlay:: overlayed} ,
20
27
} ;
21
28
22
- use log:: { error, warn} ;
29
+ use log:: { debug , error, warn} ;
23
30
use std:: {
24
31
io:: { stdin, stdout, Write } ,
25
32
sync:: Arc ,
@@ -102,7 +109,11 @@ fn restore_term() -> Result<(), Error> {
102
109
}
103
110
104
111
impl Application {
105
- pub fn new ( args : Args , config : Config ) -> Result < Self , Error > {
112
+ pub fn new (
113
+ args : Args ,
114
+ config : Config ,
115
+ syn_loader_conf : syntax:: Configuration ,
116
+ ) -> Result < Self , Error > {
106
117
#[ cfg( feature = "integration" ) ]
107
118
setup_integration_logging ( ) ;
108
119
@@ -129,14 +140,6 @@ impl Application {
129
140
} )
130
141
. unwrap_or_else ( || theme_loader. default_theme ( true_color) ) ;
131
142
132
- let syn_loader_conf = user_syntax_loader ( ) . unwrap_or_else ( |err| {
133
- eprintln ! ( "Bad language config: {}" , err) ;
134
- eprintln ! ( "Press <ENTER> to continue with default language config" ) ;
135
- use std:: io:: Read ;
136
- // This waits for an enter press.
137
- let _ = std:: io:: stdin ( ) . read ( & mut [ ] ) ;
138
- default_syntax_loader ( )
139
- } ) ;
140
143
let syn_loader = std:: sync:: Arc :: new ( syntax:: Loader :: new ( syn_loader_conf) ) ;
141
144
142
145
let mut compositor = Compositor :: new ( ) . context ( "build compositor" ) ?;
@@ -245,6 +248,10 @@ impl Application {
245
248
Ok ( app)
246
249
}
247
250
251
+ #[ cfg( feature = "integration" ) ]
252
+ fn render ( & mut self ) { }
253
+
254
+ #[ cfg( not( feature = "integration" ) ) ]
248
255
fn render ( & mut self ) {
249
256
let compositor = & mut self . compositor ;
250
257
@@ -275,9 +282,6 @@ impl Application {
275
282
where
276
283
S : Stream < Item = crossterm:: Result < crossterm:: event:: Event > > + Unpin ,
277
284
{
278
- #[ cfg( feature = "integration" ) ]
279
- let mut idle_handled = false ;
280
-
281
285
loop {
282
286
if self . editor . should_close ( ) {
283
287
return false ;
@@ -294,26 +298,6 @@ impl Application {
294
298
Some ( signal) = self . signals. next( ) => {
295
299
self . handle_signals( signal) . await ;
296
300
}
297
- Some ( ( id, call) ) = self . editor. language_servers. incoming. next( ) => {
298
- self . handle_language_server_message( call, id) . await ;
299
- // limit render calls for fast language server messages
300
- let last = self . editor. language_servers. incoming. is_empty( ) ;
301
-
302
- if last || self . last_render. elapsed( ) > LSP_DEADLINE {
303
- self . render( ) ;
304
- self . last_render = Instant :: now( ) ;
305
- }
306
- }
307
- Some ( payload) = self . editor. debugger_events. next( ) => {
308
- let needs_render = self . editor. handle_debugger_message( payload) . await ;
309
- if needs_render {
310
- self . render( ) ;
311
- }
312
- }
313
- Some ( config_event) = self . editor. config_events. 1 . recv( ) => {
314
- self . handle_config_events( config_event) ;
315
- self . render( ) ;
316
- }
317
301
Some ( callback) = self . jobs. futures. next( ) => {
318
302
self . jobs. handle_callback( & mut self . editor, & mut self . compositor, callback) ;
319
303
self . render( ) ;
@@ -322,26 +306,22 @@ impl Application {
322
306
self . jobs. handle_callback( & mut self . editor, & mut self . compositor, callback) ;
323
307
self . render( ) ;
324
308
}
325
- _ = & mut self . editor. idle_timer => {
326
- // idle timeout
327
- self . editor. clear_idle_timer( ) ;
328
- self . handle_idle_timeout( ) ;
309
+ event = self . editor. wait_event( ) => {
310
+ let _idle_handled = self . handle_editor_event( event) . await ;
329
311
330
312
#[ cfg( feature = "integration" ) ]
331
313
{
332
- idle_handled = true ;
314
+ if _idle_handled {
315
+ return true ;
316
+ }
333
317
}
334
318
}
335
319
}
336
320
337
321
// for integration tests only, reset the idle timer after every
338
- // event to make a signal when test events are done processing
322
+ // event to signal when test events are done processing
339
323
#[ cfg( feature = "integration" ) ]
340
324
{
341
- if idle_handled {
342
- return true ;
343
- }
344
-
345
325
self . editor . reset_idle_timer ( ) ;
346
326
}
347
327
}
@@ -446,6 +426,111 @@ impl Application {
446
426
}
447
427
}
448
428
429
+ pub fn handle_document_write ( & mut self , doc_save_event : DocumentSavedEventResult ) {
430
+ let doc_save_event = match doc_save_event {
431
+ Ok ( event) => event,
432
+ Err ( err) => {
433
+ self . editor . set_error ( err. to_string ( ) ) ;
434
+ return ;
435
+ }
436
+ } ;
437
+
438
+ let doc = match self . editor . document_mut ( doc_save_event. doc_id ) {
439
+ None => {
440
+ warn ! (
441
+ "received document saved event for non-existent doc id: {}" ,
442
+ doc_save_event. doc_id
443
+ ) ;
444
+
445
+ return ;
446
+ }
447
+ Some ( doc) => doc,
448
+ } ;
449
+
450
+ debug ! (
451
+ "document {:?} saved with revision {}" ,
452
+ doc. path( ) ,
453
+ doc_save_event. revision
454
+ ) ;
455
+
456
+ doc. set_last_saved_revision ( doc_save_event. revision ) ;
457
+
458
+ let lines = doc_save_event. text . len_lines ( ) ;
459
+ let bytes = doc_save_event. text . len_bytes ( ) ;
460
+
461
+ if doc. path ( ) != Some ( & doc_save_event. path ) {
462
+ if let Err ( err) = doc. set_path ( Some ( & doc_save_event. path ) ) {
463
+ log:: error!(
464
+ "error setting path for doc '{:?}': {}" ,
465
+ doc. path( ) ,
466
+ err. to_string( ) ,
467
+ ) ;
468
+
469
+ self . editor . set_error ( err. to_string ( ) ) ;
470
+ return ;
471
+ }
472
+
473
+ let loader = self . editor . syn_loader . clone ( ) ;
474
+
475
+ // borrowing the same doc again to get around the borrow checker
476
+ let doc = doc_mut ! ( self . editor, & doc_save_event. doc_id) ;
477
+ let id = doc. id ( ) ;
478
+ doc. detect_language ( loader) ;
479
+ let _ = self . editor . refresh_language_server ( id) ;
480
+ }
481
+
482
+ // TODO: fix being overwritten by lsp
483
+ self . editor . set_status ( format ! (
484
+ "'{}' written, {}L {}B" ,
485
+ get_relative_path( & doc_save_event. path) . to_string_lossy( ) ,
486
+ lines,
487
+ bytes
488
+ ) ) ;
489
+ }
490
+
491
+ #[ inline( always) ]
492
+ pub async fn handle_editor_event ( & mut self , event : EditorEvent ) -> bool {
493
+ log:: debug!( "received editor event: {:?}" , event) ;
494
+
495
+ match event {
496
+ EditorEvent :: DocumentSaved ( event) => {
497
+ self . handle_document_write ( event) ;
498
+ self . render ( ) ;
499
+ }
500
+ EditorEvent :: ConfigEvent ( event) => {
501
+ self . handle_config_events ( event) ;
502
+ self . render ( ) ;
503
+ }
504
+ EditorEvent :: LanguageServerMessage ( ( id, call) ) => {
505
+ self . handle_language_server_message ( call, id) . await ;
506
+ // limit render calls for fast language server messages
507
+ let last = self . editor . language_servers . incoming . is_empty ( ) ;
508
+
509
+ if last || self . last_render . elapsed ( ) > LSP_DEADLINE {
510
+ self . render ( ) ;
511
+ self . last_render = Instant :: now ( ) ;
512
+ }
513
+ }
514
+ EditorEvent :: DebuggerEvent ( payload) => {
515
+ let needs_render = self . editor . handle_debugger_message ( payload) . await ;
516
+ if needs_render {
517
+ self . render ( ) ;
518
+ }
519
+ }
520
+ EditorEvent :: IdleTimer => {
521
+ self . editor . clear_idle_timer ( ) ;
522
+ self . handle_idle_timeout ( ) ;
523
+
524
+ #[ cfg( feature = "integration" ) ]
525
+ {
526
+ return true ;
527
+ }
528
+ }
529
+ }
530
+
531
+ false
532
+ }
533
+
449
534
pub fn handle_terminal_events ( & mut self , event : Result < CrosstermEvent , crossterm:: ErrorKind > ) {
450
535
let mut cx = crate :: compositor:: Context {
451
536
editor : & mut self . editor ,
@@ -866,25 +951,44 @@ impl Application {
866
951
867
952
self . event_loop ( input_stream) . await ;
868
953
869
- let err = self . close ( ) . await . err ( ) ;
870
-
954
+ let close_errs = self . close ( ) . await ;
871
955
restore_term ( ) ?;
872
956
873
- if let Some ( err) = err {
957
+ for err in close_errs {
874
958
self . editor . exit_code = 1 ;
875
959
eprintln ! ( "Error: {}" , err) ;
876
960
}
877
961
878
962
Ok ( self . editor . exit_code )
879
963
}
880
964
881
- pub async fn close ( & mut self ) -> anyhow:: Result < ( ) > {
882
- self . jobs . finish ( ) . await ?;
965
+ pub async fn close ( & mut self ) -> Vec < anyhow:: Error > {
966
+ // [NOTE] we intentionally do not return early for errors because we
967
+ // want to try to run as much cleanup as we can, regardless of
968
+ // errors along the way
969
+ let mut errs = Vec :: new ( ) ;
970
+
971
+ if let Err ( err) = self
972
+ . jobs
973
+ . finish ( & mut self . editor , Some ( & mut self . compositor ) )
974
+ . await
975
+ {
976
+ log:: error!( "Error executing job: {}" , err) ;
977
+ errs. push ( err) ;
978
+ } ;
979
+
980
+ if let Err ( err) = self . editor . flush_writes ( ) . await {
981
+ log:: error!( "Error writing: {}" , err) ;
982
+ errs. push ( err) ;
983
+ }
883
984
884
985
if self . editor . close_language_servers ( None ) . await . is_err ( ) {
885
986
log:: error!( "Timed out waiting for language servers to shutdown" ) ;
886
- } ;
987
+ errs. push ( anyhow:: format_err!(
988
+ "Timed out waiting for language servers to shutdown"
989
+ ) ) ;
990
+ }
887
991
888
- Ok ( ( ) )
992
+ errs
889
993
}
890
994
}
0 commit comments