1
- use futures_util:: FutureExt ;
1
+ use futures_util:: { future :: BoxFuture , FutureExt } ;
2
2
use helix_lsp:: {
3
3
block_on,
4
4
lsp:: {
@@ -15,7 +15,7 @@ use tui::{
15
15
16
16
use super :: { align_view, push_jump, Align , Context , Editor , Open } ;
17
17
18
- use helix_core:: { path, text_annotations:: InlineAnnotation , Selection } ;
18
+ use helix_core:: { path, text_annotations:: InlineAnnotation , Range , Selection } ;
19
19
use helix_view:: {
20
20
document:: { DocumentInlayHints , DocumentInlayHintsId , Mode } ,
21
21
editor:: Action ,
@@ -544,31 +544,9 @@ fn action_fixes_diagnostics(action: &CodeActionOrCommand) -> bool {
544
544
pub fn code_action ( cx : & mut Context ) {
545
545
let ( view, doc) = current ! ( cx. editor) ;
546
546
547
- let language_server = language_server ! ( cx. editor, doc) ;
548
-
549
547
let selection_range = doc. selection ( view. id ) . primary ( ) ;
550
- let offset_encoding = language_server. offset_encoding ( ) ;
551
548
552
- let range = range_to_lsp_range ( doc. text ( ) , selection_range, offset_encoding) ;
553
-
554
- let future = match language_server. code_actions (
555
- doc. identifier ( ) ,
556
- range,
557
- // Filter and convert overlapping diagnostics
558
- lsp:: CodeActionContext {
559
- diagnostics : doc
560
- . diagnostics ( )
561
- . iter ( )
562
- . filter ( |& diag| {
563
- selection_range
564
- . overlaps ( & helix_core:: Range :: new ( diag. range . start , diag. range . end ) )
565
- } )
566
- . map ( |diag| diagnostic_to_lsp_diagnostic ( doc. text ( ) , diag, offset_encoding) )
567
- . collect ( ) ,
568
- only : None ,
569
- trigger_kind : Some ( CodeActionTriggerKind :: INVOKED ) ,
570
- } ,
571
- ) {
549
+ let future = match code_actions_for_range ( doc, selection_range) {
572
550
Some ( future) => future,
573
551
None => {
574
552
cx. editor
@@ -642,25 +620,7 @@ pub fn code_action(cx: &mut Context) {
642
620
// always present here
643
621
let code_action = code_action. unwrap ( ) ;
644
622
645
- match code_action {
646
- lsp:: CodeActionOrCommand :: Command ( command) => {
647
- log:: debug!( "code action command: {:?}" , command) ;
648
- execute_lsp_command ( editor, command. clone ( ) ) ;
649
- }
650
- lsp:: CodeActionOrCommand :: CodeAction ( code_action) => {
651
- log:: debug!( "code action: {:?}" , code_action) ;
652
- if let Some ( ref workspace_edit) = code_action. edit {
653
- log:: debug!( "edit: {:?}" , workspace_edit) ;
654
- let _ = apply_workspace_edit ( editor, offset_encoding, workspace_edit) ;
655
- }
656
-
657
- // if code action provides both edit and command first the edit
658
- // should be applied and then the command
659
- if let Some ( command) = & code_action. command {
660
- execute_lsp_command ( editor, command. clone ( ) ) ;
661
- }
662
- }
663
- }
623
+ apply_code_action ( editor, code_action) ;
664
624
} ) ;
665
625
picker. move_down ( ) ; // pre-select the first item
666
626
@@ -670,6 +630,103 @@ pub fn code_action(cx: &mut Context) {
670
630
)
671
631
}
672
632
633
+ pub fn code_actions_for_range (
634
+ doc : & mut Document ,
635
+ range : helix_core:: Range ,
636
+ ) -> Option < impl Future < Output = Result < serde_json:: Value , helix_lsp:: Error > > > {
637
+ let language_server = doc. language_server ( ) ?;
638
+ let offset_encoding = language_server. offset_encoding ( ) ;
639
+ let lsp_range = range_to_lsp_range ( doc. text ( ) , range, offset_encoding) ;
640
+
641
+ language_server. code_actions (
642
+ doc. identifier ( ) ,
643
+ lsp_range,
644
+ // Filter and convert overlapping diagnostics
645
+ lsp:: CodeActionContext {
646
+ diagnostics : doc
647
+ . diagnostics ( )
648
+ . iter ( )
649
+ . filter ( |& diag| {
650
+ range. overlaps ( & helix_core:: Range :: new ( diag. range . start , diag. range . end ) )
651
+ } )
652
+ . map ( |diag| diagnostic_to_lsp_diagnostic ( doc. text ( ) , diag, offset_encoding) )
653
+ . collect ( ) ,
654
+ only : None ,
655
+ trigger_kind : Some ( CodeActionTriggerKind :: INVOKED ) ,
656
+ } ,
657
+ )
658
+ }
659
+
660
+ pub fn code_actions_on_save (
661
+ doc : & mut Document ,
662
+ ) -> Option < BoxFuture < ' static , Result < Vec < helix_lsp:: lsp:: CodeActionOrCommand > , anyhow:: Error > > > {
663
+ let code_actions_on_save = doc
664
+ . language_config ( )
665
+ . map ( |c| c. code_actions_on_save . clone ( ) ) ?;
666
+
667
+ if code_actions_on_save. is_empty ( ) {
668
+ return None ;
669
+ }
670
+
671
+ let full_range = Range :: new ( 0 , doc. text ( ) . len_chars ( ) ) ;
672
+ let request = code_actions_for_range ( doc, full_range) ?;
673
+
674
+ let fut = async move {
675
+ log:: debug!( "Configured code actions on save {:?}" , code_actions_on_save) ;
676
+ let json = request. await ?;
677
+ let response: Option < helix_lsp:: lsp:: CodeActionResponse > = serde_json:: from_value ( json) ?;
678
+ let available_code_actions = match response {
679
+ Some ( value) => value,
680
+ None => helix_lsp:: lsp:: CodeActionResponse :: default ( ) ,
681
+ } ;
682
+ log:: debug!( "Available code actions {:?}" , available_code_actions) ;
683
+
684
+ let code_actions: Vec < CodeActionOrCommand > = available_code_actions
685
+ . into_iter ( )
686
+ . filter ( |action| match action {
687
+ helix_lsp:: lsp:: CodeActionOrCommand :: CodeAction ( x) if x. disabled . is_none ( ) => {
688
+ match & x. kind {
689
+ Some ( kind) => code_actions_on_save. get ( kind. as_str ( ) ) . is_some ( ) ,
690
+ None => false ,
691
+ }
692
+ }
693
+ _ => false ,
694
+ } )
695
+ . collect ( ) ;
696
+
697
+ Ok ( code_actions)
698
+ } ;
699
+ Some ( fut. boxed ( ) )
700
+ }
701
+
702
+ pub fn apply_code_action ( editor : & mut Editor , code_action : & CodeActionOrCommand ) {
703
+ let ( _view, doc) = current ! ( editor) ;
704
+
705
+ let language_server = language_server ! ( editor, doc) ;
706
+
707
+ let offset_encoding = language_server. offset_encoding ( ) ;
708
+
709
+ match code_action {
710
+ lsp:: CodeActionOrCommand :: Command ( command) => {
711
+ log:: debug!( "code action command: {:?}" , command) ;
712
+ execute_lsp_command ( editor, command. clone ( ) ) ;
713
+ }
714
+ lsp:: CodeActionOrCommand :: CodeAction ( code_action) => {
715
+ log:: debug!( "code action: {:?}" , code_action) ;
716
+ if let Some ( ref workspace_edit) = code_action. edit {
717
+ log:: debug!( "edit: {:?}" , workspace_edit) ;
718
+ let _ = apply_workspace_edit ( editor, offset_encoding, workspace_edit) ;
719
+ }
720
+
721
+ // if code action provides both edit and command first the edit
722
+ // should be applied and then the command
723
+ if let Some ( command) = & code_action. command {
724
+ execute_lsp_command ( editor, command. clone ( ) ) ;
725
+ }
726
+ }
727
+ }
728
+ }
729
+
673
730
impl ui:: menu:: Item for lsp:: Command {
674
731
type Data = ( ) ;
675
732
fn format ( & self , _data : & Self :: Data ) -> Row {
0 commit comments