on master help sign in
lsp: Check server provider capabilities (#3554)
9059c65Michael Davis
helix-lsp/src/client.rs+168−61
@@ -4,7 +4,6 @@ use crate::{
4 Call, Error, OffsetEncoding, Result,
4 Call, Error, OffsetEncoding, Result,
5};
5};
6
6
7−use anyhow::anyhow;
8use helix_core::{find_root, ChangeSet, Rope};
7use helix_core::{find_root, ChangeSet, Rope};
9use lsp_types as lsp;
8use lsp_types as lsp;
10use serde::Deserialize;
9use serde::Deserialize;
@@ -546,16 +545,17 @@ impl Client {
546 new_text: &Rope,
545 new_text: &Rope,
547 changes: &ChangeSet,
546 changes: &ChangeSet,
548 ) -> Option<impl Future<Output = Result<()>>> {
547 ) -> Option<impl Future<Output = Result<()>>> {
549− // figure out what kind of sync the server supports
550−
551 let capabilities = self.capabilities.get().unwrap();
548 let capabilities = self.capabilities.get().unwrap();
552
549
550+ // Return early if the server does not support document sync.
553 let sync_capabilities = match capabilities.text_document_sync {
551 let sync_capabilities = match capabilities.text_document_sync {
552+ Some(
554− Some(lsp::TextDocumentSyncCapability::Kind(kind))
553+ lsp::TextDocumentSyncCapability::Kind(kind)
555− | Some(lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions {
554+ | lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions {
556− change: Some(kind),
555+ change: Some(kind),
557− ..
556+ ..
557+ }),
558− })) => kind,
558+ ) => kind,
559 // None | SyncOptions { changes: None }
559 // None | SyncOptions { changes: None }
560 _ => return None,
560 _ => return None,
561 };
561 };
@@ -631,8 +631,12 @@ impl Client {
631 text_document: lsp::TextDocumentIdentifier,
631 text_document: lsp::TextDocumentIdentifier,
632 position: lsp::Position,
632 position: lsp::Position,
633 work_done_token: Option<lsp::ProgressToken>,
633 work_done_token: Option<lsp::ProgressToken>,
634− ) -> impl Future<Output = Result<Value>> {
634+ ) -> Option<impl Future<Output = Result<Value>>> {
635− // ) -> Result<Vec<lsp::CompletionItem>> {
635+ let capabilities = self.capabilities.get().unwrap();
636+
637+ // Return early if the server does not support completion.
638+ capabilities.completion_provider.as_ref()?;
639+
636 let params = lsp::CompletionParams {
640 let params = lsp::CompletionParams {
637 text_document_position: lsp::TextDocumentPositionParams {
641 text_document_position: lsp::TextDocumentPositionParams {
638 text_document,
642 text_document,
@@ -647,14 +651,25 @@ impl Client {
647 // lsp::CompletionContext { trigger_kind: , trigger_character: Some(), }
651 // lsp::CompletionContext { trigger_kind: , trigger_character: Some(), }
648 };
652 };
649
653
650− self.call::<lsp::request::Completion>(params)
654+ Some(self.call::<lsp::request::Completion>(params))
651 }
655 }
652
656
653 pub fn resolve_completion_item(
657 pub fn resolve_completion_item(
654 &self,
658 &self,
655 completion_item: lsp::CompletionItem,
659 completion_item: lsp::CompletionItem,
656− ) -> impl Future<Output = Result<Value>> {
660+ ) -> Option<impl Future<Output = Result<Value>>> {
661+ let capabilities = self.capabilities.get().unwrap();
662+
663+ // Return early if the server does not support resolving completion items.
664+ match capabilities.completion_provider {
665+ Some(lsp::CompletionOptions {
666+ resolve_provider: Some(true),
667+ ..
668+ }) => (),
669+ _ => return None,
670+ }
671+
657− self.call::<lsp::request::ResolveCompletionItem>(completion_item)
672+ Some(self.call::<lsp::request::ResolveCompletionItem>(completion_item))
658 }
673 }
659
674
660 pub fn text_document_signature_help(
675 pub fn text_document_signature_help(
@@ -665,7 +680,7 @@ impl Client {
665 ) -> Option<impl Future<Output = Result<Value>>> {
680 ) -> Option<impl Future<Output = Result<Value>>> {
666 let capabilities = self.capabilities.get().unwrap();
681 let capabilities = self.capabilities.get().unwrap();
667
682
668− // Return early if signature help is not supported
683+ // Return early if the server does not support signature help.
669 capabilities.signature_help_provider.as_ref()?;
684 capabilities.signature_help_provider.as_ref()?;
670
685
671 let params = lsp::SignatureHelpParams {
686 let params = lsp::SignatureHelpParams {
@@ -686,7 +701,18 @@ impl Client {
686 text_document: lsp::TextDocumentIdentifier,
701 text_document: lsp::TextDocumentIdentifier,
687 position: lsp::Position,
702 position: lsp::Position,
688 work_done_token: Option<lsp::ProgressToken>,
703 work_done_token: Option<lsp::ProgressToken>,
689− ) -> impl Future<Output = Result<Value>> {
704+ ) -> Option<impl Future<Output = Result<Value>>> {
705+ let capabilities = self.capabilities.get().unwrap();
706+
707+ // Return early if the server does not support hover.
708+ match capabilities.hover_provider {
709+ Some(
710+ lsp::HoverProviderCapability::Simple(true)
711+ | lsp::HoverProviderCapability::Options(_),
712+ ) => (),
713+ _ => return None,
714+ }
715+
690 let params = lsp::HoverParams {
716 let params = lsp::HoverParams {
691 text_document_position_params: lsp::TextDocumentPositionParams {
717 text_document_position_params: lsp::TextDocumentPositionParams {
692 text_document,
718 text_document,
@@ -696,7 +722,7 @@ impl Client {
696 // lsp::SignatureHelpContext
722 // lsp::SignatureHelpContext
697 };
723 };
698
724
699− self.call::<lsp::request::HoverRequest>(params)
725+ Some(self.call::<lsp::request::HoverRequest>(params))
700 }
726 }
701
727
702 // formatting
728 // formatting
@@ -709,13 +735,11 @@ impl Client {
709 ) -> Option<impl Future<Output = Result<Vec<lsp::TextEdit>>>> {
735 ) -> Option<impl Future<Output = Result<Vec<lsp::TextEdit>>>> {
710 let capabilities = self.capabilities.get().unwrap();
736 let capabilities = self.capabilities.get().unwrap();
711
737
712− // check if we're able to format
738+ // Return early if the server does not support formatting.
713 match capabilities.document_formatting_provider {
739 match capabilities.document_formatting_provider {
714− Some(lsp::OneOf::Left(true)) | Some(lsp::OneOf::Right(_)) => (),
740+ Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (),
715− // None | Some(false)
716 _ => return None,
741 _ => return None,
717 };
742 };
718− // TODO: return err::unavailable so we can fall back to tree sitter formatting
719
743
720 // merge FormattingOptions with 'config.format'
744 // merge FormattingOptions with 'config.format'
721 let config_format = self
745 let config_format = self
@@ -750,22 +774,20 @@ impl Client {
750 })
774 })
751 }
775 }
752
776
753− pub async fn text_document_range_formatting(
777+ pub fn text_document_range_formatting(
754 &self,
778 &self,
755 text_document: lsp::TextDocumentIdentifier,
779 text_document: lsp::TextDocumentIdentifier,
756 range: lsp::Range,
780 range: lsp::Range,
757 options: lsp::FormattingOptions,
781 options: lsp::FormattingOptions,
758 work_done_token: Option<lsp::ProgressToken>,
782 work_done_token: Option<lsp::ProgressToken>,
759− ) -> anyhow::Result<Vec<lsp::TextEdit>> {
783+ ) -> Option<impl Future<Output = Result<Vec<lsp::TextEdit>>>> {
760 let capabilities = self.capabilities.get().unwrap();
784 let capabilities = self.capabilities.get().unwrap();
761
785
762− // check if we're able to format
786+ // Return early if the server does not support range formatting.
763 match capabilities.document_range_formatting_provider {
787 match capabilities.document_range_formatting_provider {
764− Some(lsp::OneOf::Left(true)) | Some(lsp::OneOf::Right(_)) => (),
788+ Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (),
765− // None | Some(false)
789+ _ => return None,
766− _ => return Ok(Vec::new()),
767 };
790 };
768− // TODO: return err::unavailable so we can fall back to tree sitter formatting
769
791
770 let params = lsp::DocumentRangeFormattingParams {
792 let params = lsp::DocumentRangeFormattingParams {
771 text_document,
793 text_document,
@@ -774,11 +796,13 @@ impl Client {
774 work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token },
796 work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token },
775 };
797 };
776
798
777− let response = self
778− .request::<lsp::request::RangeFormatting>(params)
799+ let request = self.call::<lsp::request::RangeFormatting>(params);
779− .await?;
780
800
801+ Some(async move {
802+ let json = request.await?;
803+ let response: Option<Vec<lsp::TextEdit>> = serde_json::from_value(json)?;
781− Ok(response.unwrap_or_default())
804+ Ok(response.unwrap_or_default())
805+ })
782 }
806 }
783
807
784 pub fn text_document_document_highlight(
808 pub fn text_document_document_highlight(
@@ -786,7 +810,15 @@ impl Client {
786 text_document: lsp::TextDocumentIdentifier,
810 text_document: lsp::TextDocumentIdentifier,
787 position: lsp::Position,
811 position: lsp::Position,
788 work_done_token: Option<lsp::ProgressToken>,
812 work_done_token: Option<lsp::ProgressToken>,
789− ) -> impl Future<Output = Result<Value>> {
813+ ) -> Option<impl Future<Output = Result<Value>>> {
814+ let capabilities = self.capabilities.get().unwrap();
815+
816+ // Return early if the server does not support document highlight.
817+ match capabilities.document_highlight_provider {
818+ Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (),
819+ _ => return None,
820+ }
821+
790 let params = lsp::DocumentHighlightParams {
822 let params = lsp::DocumentHighlightParams {
791 text_document_position_params: lsp::TextDocumentPositionParams {
823 text_document_position_params: lsp::TextDocumentPositionParams {
792 text_document,
824 text_document,
@@ -798,7 +830,7 @@ impl Client {
798 },
830 },
799 };
831 };
800
832
801− self.call::<lsp::request::DocumentHighlightRequest>(params)
833+ Some(self.call::<lsp::request::DocumentHighlightRequest>(params))
802 }
834 }
803
835
804 fn goto_request<
836 fn goto_request<
@@ -831,8 +863,20 @@ impl Client {
831 text_document: lsp::TextDocumentIdentifier,
863 text_document: lsp::TextDocumentIdentifier,
832 position: lsp::Position,
864 position: lsp::Position,
833 work_done_token: Option<lsp::ProgressToken>,
865 work_done_token: Option<lsp::ProgressToken>,
834− ) -> impl Future<Output = Result<Value>> {
866+ ) -> Option<impl Future<Output = Result<Value>>> {
867+ let capabilities = self.capabilities.get().unwrap();
868+
869+ // Return early if the server does not support goto-definition.
870+ match capabilities.definition_provider {
871+ Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (),
872+ _ => return None,
873+ }
874+
835− self.goto_request::<lsp::request::GotoDefinition>(text_document, position, work_done_token)
875+ Some(self.goto_request::<lsp::request::GotoDefinition>(
876+ text_document,
877+ position,
878+ work_done_token,
879+ ))
836 }
880 }
837
881
838 pub fn goto_type_definition(
882 pub fn goto_type_definition(
@@ -840,12 +884,23 @@ impl Client {
840 text_document: lsp::TextDocumentIdentifier,
884 text_document: lsp::TextDocumentIdentifier,
841 position: lsp::Position,
885 position: lsp::Position,
842 work_done_token: Option<lsp::ProgressToken>,
886 work_done_token: Option<lsp::ProgressToken>,
843− ) -> impl Future<Output = Result<Value>> {
887+ ) -> Option<impl Future<Output = Result<Value>>> {
888+ let capabilities = self.capabilities.get().unwrap();
889+
890+ // Return early if the server does not support goto-type-definition.
891+ match capabilities.type_definition_provider {
892+ Some(
893+ lsp::TypeDefinitionProviderCapability::Simple(true)
894+ | lsp::TypeDefinitionProviderCapability::Options(_),
895+ ) => (),
896+ _ => return None,
897+ }
898+
844− self.goto_request::<lsp::request::GotoTypeDefinition>(
899+ Some(self.goto_request::<lsp::request::GotoTypeDefinition>(
845 text_document,
900 text_document,
846 position,
901 position,
847 work_done_token,
902 work_done_token,
848− )
903+ ))
849 }
904 }
850
905
851 pub fn goto_implementation(
906 pub fn goto_implementation(
@@ -853,12 +908,23 @@ impl Client {
853 text_document: lsp::TextDocumentIdentifier,
908 text_document: lsp::TextDocumentIdentifier,
854 position: lsp::Position,
909 position: lsp::Position,
855 work_done_token: Option<lsp::ProgressToken>,
910 work_done_token: Option<lsp::ProgressToken>,
856− ) -> impl Future<Output = Result<Value>> {
911+ ) -> Option<impl Future<Output = Result<Value>>> {
912+ let capabilities = self.capabilities.get().unwrap();
913+
914+ // Return early if the server does not support goto-definition.
915+ match capabilities.implementation_provider {
916+ Some(
917+ lsp::ImplementationProviderCapability::Simple(true)
918+ | lsp::ImplementationProviderCapability::Options(_),
919+ ) => (),
920+ _ => return None,
921+ }
922+
857− self.goto_request::<lsp::request::GotoImplementation>(
923+ Some(self.goto_request::<lsp::request::GotoImplementation>(
858 text_document,
924 text_document,
859 position,
925 position,
860 work_done_token,
926 work_done_token,
861− )
927+ ))
862 }
928 }
863
929
864 pub fn goto_reference(
930 pub fn goto_reference(
@@ -866,7 +932,15 @@ impl Client {
866 text_document: lsp::TextDocumentIdentifier,
932 text_document: lsp::TextDocumentIdentifier,
867 position: lsp::Position,
933 position: lsp::Position,
868 work_done_token: Option<lsp::ProgressToken>,
934 work_done_token: Option<lsp::ProgressToken>,
869− ) -> impl Future<Output = Result<Value>> {
935+ ) -> Option<impl Future<Output = Result<Value>>> {
936+ let capabilities = self.capabilities.get().unwrap();
937+
938+ // Return early if the server does not support goto-reference.
939+ match capabilities.references_provider {
940+ Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (),
941+ _ => return None,
942+ }
943+
870 let params = lsp::ReferenceParams {
944 let params = lsp::ReferenceParams {
871 text_document_position: lsp::TextDocumentPositionParams {
945 text_document_position: lsp::TextDocumentPositionParams {
872 text_document,
946 text_document,
@@ -881,31 +955,47 @@ impl Client {
881 },
955 },
882 };
956 };
883
957
884− self.call::<lsp::request::References>(params)
958+ Some(self.call::<lsp::request::References>(params))
885 }
959 }
886
960
887 pub fn document_symbols(
961 pub fn document_symbols(
888 &self,
962 &self,
889 text_document: lsp::TextDocumentIdentifier,
963 text_document: lsp::TextDocumentIdentifier,
890− ) -> impl Future<Output = Result<Value>> {
964+ ) -> Option<impl Future<Output = Result<Value>>> {
965+ let capabilities = self.capabilities.get().unwrap();
966+
967+ // Return early if the server does not support document symbols.
968+ match capabilities.document_symbol_provider {
969+ Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (),
970+ _ => return None,
971+ }
972+
891 let params = lsp::DocumentSymbolParams {
973 let params = lsp::DocumentSymbolParams {
892 text_document,
974 text_document,
893 work_done_progress_params: lsp::WorkDoneProgressParams::default(),
975 work_done_progress_params: lsp::WorkDoneProgressParams::default(),
894 partial_result_params: lsp::PartialResultParams::default(),
976 partial_result_params: lsp::PartialResultParams::default(),
895 };
977 };
896
978
897− self.call::<lsp::request::DocumentSymbolRequest>(params)
979+ Some(self.call::<lsp::request::DocumentSymbolRequest>(params))
898 }
980 }
899
981
900 // empty string to get all symbols
982 // empty string to get all symbols
901− pub fn workspace_symbols(&self, query: String) -> impl Future<Output = Result<Value>> {
983+ pub fn workspace_symbols(&self, query: String) -> Option<impl Future<Output = Result<Value>>> {
984+ let capabilities = self.capabilities.get().unwrap();
985+
986+ // Return early if the server does not support workspace symbols.
987+ match capabilities.workspace_symbol_provider {
988+ Some(lsp::OneOf::Left(true) | lsp::OneOf::Right(_)) => (),
989+ _ => return None,
990+ }
991+
902 let params = lsp::WorkspaceSymbolParams {
992 let params = lsp::WorkspaceSymbolParams {
903 query,
993 query,
904 work_done_progress_params: lsp::WorkDoneProgressParams::default(),
994 work_done_progress_params: lsp::WorkDoneProgressParams::default(),
905 partial_result_params: lsp::PartialResultParams::default(),
995 partial_result_params: lsp::PartialResultParams::default(),
906 };
996 };
907
997
908− self.call::<lsp::request::WorkspaceSymbol>(params)
998+ Some(self.call::<lsp::request::WorkspaceSymbol>(params))
909 }
999 }
910
1000
911 pub fn code_actions(
1001 pub fn code_actions(
@@ -913,7 +1003,18 @@ impl Client {
913 text_document: lsp::TextDocumentIdentifier,
1003 text_document: lsp::TextDocumentIdentifier,
914 range: lsp::Range,
1004 range: lsp::Range,
915 context: lsp::CodeActionContext,
1005 context: lsp::CodeActionContext,
916− ) -> impl Future<Output = Result<Value>> {
1006+ ) -> Option<impl Future<Output = Result<Value>>> {
1007+ let capabilities = self.capabilities.get().unwrap();
1008+
1009+ // Return early if the server does not support code actions.
1010+ match capabilities.code_action_provider {
1011+ Some(
1012+ lsp::CodeActionProviderCapability::Simple(true)
1013+ | lsp::CodeActionProviderCapability::Options(_),
1014+ ) => (),
1015+ _ => return None,
1016+ }
1017+
917 let params = lsp::CodeActionParams {
1018 let params = lsp::CodeActionParams {
918 text_document,
1019 text_document,
919 range,
1020 range,
@@ -922,26 +1023,22 @@ impl Client {
922 partial_result_params: lsp::PartialResultParams::default(),
1023 partial_result_params: lsp::PartialResultParams::default(),
923 };
1024 };
924
1025
925− self.call::<lsp::request::CodeActionRequest>(params)
1026+ Some(self.call::<lsp::request::CodeActionRequest>(params))
926 }
1027 }
927
1028
928− pub async fn rename_symbol(
1029+ pub fn rename_symbol(
929 &self,
1030 &self,
930 text_document: lsp::TextDocumentIdentifier,
1031 text_document: lsp::TextDocumentIdentifier,
931 position: lsp::Position,
1032 position: lsp::Position,
932 new_name: String,
1033 new_name: String,
933− ) -> anyhow::Result<lsp::WorkspaceEdit> {
1034+ ) -> Option<impl Future<Output = Result<lsp::WorkspaceEdit>>> {
934 let capabilities = self.capabilities.get().unwrap();
1035 let capabilities = self.capabilities.get().unwrap();
935
1036
936− // check if we're able to rename
1037+ // Return early if the language server does not support renaming.
937 match capabilities.rename_provider {
1038 match capabilities.rename_provider {
938 Some(lsp::OneOf::Left(true)) | Some(lsp::OneOf::Right(_)) => (),
1039 Some(lsp::OneOf::Left(true)) | Some(lsp::OneOf::Right(_)) => (),
939 // None | Some(false)
1040 // None | Some(false)
940− _ => {
941− log::warn!("rename_symbol failed: The server does not support rename");
942− let err = "The server does not support rename";
943− return Err(anyhow!(err));
1041+ _ => return None,
944− }
945 };
1042 };
946
1043
947 let params = lsp::RenameParams {
1044 let params = lsp::RenameParams {
@@ -955,11 +1052,21 @@ impl Client {
955 },
1052 },
956 };
1053 };
957
1054
958− let response = self.request::<lsp::request::Rename>(params).await?;
1055+ let request = self.call::<lsp::request::Rename>(params);
1056+
1057+ Some(async move {
1058+ let json = request.await?;
1059+ let response: Option<lsp::WorkspaceEdit> = serde_json::from_value(json)?;
959− Ok(response.unwrap_or_default())
1060+ Ok(response.unwrap_or_default())
1061+ })
960 }
1062 }
961
1063
962− pub fn command(&self, command: lsp::Command) -> impl Future<Output = Result<Value>> {
1064+ pub fn command(&self, command: lsp::Command) -> Option<impl Future<Output = Result<Value>>> {
1065+ let capabilities = self.capabilities.get().unwrap();
1066+
1067+ // Return early if the language server does not support executing commands.
1068+ capabilities.execute_command_provider.as_ref()?;
1069+
963 let params = lsp::ExecuteCommandParams {
1070 let params = lsp::ExecuteCommandParams {
964 command: command.command,
1071 command: command.command,
965 arguments: command.arguments.unwrap_or_default(),
1072 arguments: command.arguments.unwrap_or_default(),
@@ -968,6 +1075,6 @@ impl Client {
968 },
1075 },
969 };
1076 };
970
1077
971− self.call::<lsp::request::ExecuteCommand>(params)
1078+ Some(self.call::<lsp::request::ExecuteCommand>(params))
972 }
1079 }
973}
1080}
helix-term/src/commands.rs+19−10
@@ -3801,15 +3801,21 @@ fn format_selections(cx: &mut Context) {
3801
3801
3802 let range = ranges[0];
3802 let range = ranges[0];
3803
3803
3804+ let request = match language_server.text_document_range_formatting(
3805+ doc.identifier(),
3806+ range,
3807+ lsp::FormattingOptions::default(),
3808+ None,
3809+ ) {
3810+ Some(future) => future,
3811+ None => {
3812+ cx.editor
3813+ .set_error("Language server does not support range formatting");
3814+ return;
3815+ }
3816+ };
3817+
3804− let edits = tokio::task::block_in_place(|| {
3818+ let edits = tokio::task::block_in_place(|| helix_lsp::block_on(request)).unwrap_or_default();
3805− helix_lsp::block_on(language_server.text_document_range_formatting(
3806− doc.identifier(),
3807− range,
3808− lsp::FormattingOptions::default(),
3809− None,
3810− ))
3811− })
3812− .unwrap_or_default();
3813
3819
3814 let transaction = helix_lsp::util::generate_transaction_from_edits(
3820 let transaction = helix_lsp::util::generate_transaction_from_edits(
3815 doc.text(),
3821 doc.text(),
@@ -3953,7 +3959,10 @@ pub fn completion(cx: &mut Context) {
3953
3959
3954 let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
3960 let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
3955
3961
3956− let future = language_server.completion(doc.identifier(), pos, None);
3962+ let future = match language_server.completion(doc.identifier(), pos, None) {
3963+ Some(future) => future,
3964+ None => return,
3965+ };
3957
3966
3958 let trigger_offset = cursor;
3967 let trigger_offset = cursor;
3959
3968
helix-term/src/commands/lsp.rs+100−15
@@ -333,7 +333,14 @@ pub fn symbol_picker(cx: &mut Context) {
333 let current_url = doc.url();
333 let current_url = doc.url();
334 let offset_encoding = language_server.offset_encoding();
334 let offset_encoding = language_server.offset_encoding();
335
335
336− let future = language_server.document_symbols(doc.identifier());
336+ let future = match language_server.document_symbols(doc.identifier()) {
337+ Some(future) => future,
338+ None => {
339+ cx.editor
340+ .set_error("Language server does not support document symbols");
341+ return;
342+ }
343+ };
337
344
338 cx.callback(
345 cx.callback(
339 future,
346 future,
@@ -365,7 +372,14 @@ pub fn workspace_symbol_picker(cx: &mut Context) {
365 let current_url = doc.url();
372 let current_url = doc.url();
366 let language_server = language_server!(cx.editor, doc);
373 let language_server = language_server!(cx.editor, doc);
367 let offset_encoding = language_server.offset_encoding();
374 let offset_encoding = language_server.offset_encoding();
368− let future = language_server.workspace_symbols("".to_string());
375+ let future = match language_server.workspace_symbols("".to_string()) {
376+ Some(future) => future,
377+ None => {
378+ cx.editor
379+ .set_error("Language server does not support workspace symbols");
380+ return;
381+ }
382+ };
369
383
370 cx.callback(
384 cx.callback(
371 future,
385 future,
@@ -493,7 +507,7 @@ pub fn code_action(cx: &mut Context) {
493
507
494 let range = range_to_lsp_range(doc.text(), selection_range, offset_encoding);
508 let range = range_to_lsp_range(doc.text(), selection_range, offset_encoding);
495
509
496− let future = language_server.code_actions(
510+ let future = match language_server.code_actions(
497 doc.identifier(),
511 doc.identifier(),
498 range,
512 range,
499 // Filter and convert overlapping diagnostics
513 // Filter and convert overlapping diagnostics
@@ -509,7 +523,14 @@ pub fn code_action(cx: &mut Context) {
509 .collect(),
523 .collect(),
510 only: None,
524 only: None,
511 },
525 },
512− );
526+ ) {
527+ Some(future) => future,
528+ None => {
529+ cx.editor
530+ .set_error("Language server does not support code actions");
531+ return;
532+ }
533+ };
513
534
514 cx.callback(
535 cx.callback(
515 future,
536 future,
@@ -617,9 +638,16 @@ pub fn execute_lsp_command(editor: &mut Editor, cmd: lsp::Command) {
617
638
618 // the command is executed on the server and communicated back
639 // the command is executed on the server and communicated back
619 // to the client asynchronously using workspace edits
640 // to the client asynchronously using workspace edits
620− let command_future = language_server.command(cmd);
641+ let future = match language_server.command(cmd) {
642+ Some(future) => future,
643+ None => {
644+ editor.set_error("Language server does not support executing commands");
645+ return;
646+ }
647+ };
648+
621 tokio::spawn(async move {
649 tokio::spawn(async move {
622− let res = command_future.await;
650+ let res = future.await;
623
651
624 if let Err(e) = res {
652 if let Err(e) = res {
625 log::error!("execute LSP command: {}", e);
653 log::error!("execute LSP command: {}", e);
@@ -853,7 +881,14 @@ pub fn goto_definition(cx: &mut Context) {
853
881
854 let pos = doc.position(view.id, offset_encoding);
882 let pos = doc.position(view.id, offset_encoding);
855
883
856− let future = language_server.goto_definition(doc.identifier(), pos, None);
884+ let future = match language_server.goto_definition(doc.identifier(), pos, None) {
885+ Some(future) => future,
886+ None => {
887+ cx.editor
888+ .set_error("Language server does not support goto-definition");
889+ return;
890+ }
891+ };
857
892
858 cx.callback(
893 cx.callback(
859 future,
894 future,
@@ -871,7 +906,14 @@ pub fn goto_type_definition(cx: &mut Context) {
871
906
872 let pos = doc.position(view.id, offset_encoding);
907 let pos = doc.position(view.id, offset_encoding);
873
908
874− let future = language_server.goto_type_definition(doc.identifier(), pos, None);
909+ let future = match language_server.goto_type_definition(doc.identifier(), pos, None) {
910+ Some(future) => future,
911+ None => {
912+ cx.editor
913+ .set_error("Language server does not support goto-type-definition");
914+ return;
915+ }
916+ };
875
917
876 cx.callback(
918 cx.callback(
877 future,
919 future,
@@ -889,7 +931,14 @@ pub fn goto_implementation(cx: &mut Context) {
889
931
890 let pos = doc.position(view.id, offset_encoding);
932 let pos = doc.position(view.id, offset_encoding);
891
933
892− let future = language_server.goto_implementation(doc.identifier(), pos, None);
934+ let future = match language_server.goto_implementation(doc.identifier(), pos, None) {
935+ Some(future) => future,
936+ None => {
937+ cx.editor
938+ .set_error("Language server does not support goto-implementation");
939+ return;
940+ }
941+ };
893
942
894 cx.callback(
943 cx.callback(
895 future,
944 future,
@@ -907,7 +956,14 @@ pub fn goto_reference(cx: &mut Context) {
907
956
908 let pos = doc.position(view.id, offset_encoding);
957 let pos = doc.position(view.id, offset_encoding);
909
958
910− let future = language_server.goto_reference(doc.identifier(), pos, None);
959+ let future = match language_server.goto_reference(doc.identifier(), pos, None) {
960+ Some(future) => future,
961+ None => {
962+ cx.editor
963+ .set_error("Language server does not support goto-reference");
964+ return;
965+ }
966+ };
911
967
912 cx.callback(
968 cx.callback(
913 future,
969 future,
@@ -950,7 +1006,13 @@ pub fn signature_help_impl(cx: &mut Context, invoked: SignatureHelpInvoked) {
950
1006
951 let future = match language_server.text_document_signature_help(doc.identifier(), pos, None) {
1007 let future = match language_server.text_document_signature_help(doc.identifier(), pos, None) {
952 Some(f) => f,
1008 Some(f) => f,
1009+ None => {
1010+ if was_manually_invoked {
1011+ cx.editor
1012+ .set_error("Language server does not support signature-help");
1013+ }
953− None => return,
1014+ return;
1015+ }
954 };
1016 };
955
1017
956 cx.callback(
1018 cx.callback(
@@ -1051,7 +1113,14 @@ pub fn hover(cx: &mut Context) {
1051
1113
1052 let pos = doc.position(view.id, offset_encoding);
1114 let pos = doc.position(view.id, offset_encoding);
1053
1115
1054− let future = language_server.text_document_hover(doc.identifier(), pos, None);
1116+ let future = match language_server.text_document_hover(doc.identifier(), pos, None) {
1117+ Some(future) => future,
1118+ None => {
1119+ cx.editor
1120+ .set_error("Language server does not support hover");
1121+ return;
1122+ }
1123+ };
1055
1124
1056 cx.callback(
1125 cx.callback(
1057 future,
1126 future,
@@ -1121,8 +1190,16 @@ pub fn rename_symbol(cx: &mut Context) {
1121
1190
1122 let pos = doc.position(view.id, offset_encoding);
1191 let pos = doc.position(view.id, offset_encoding);
1123
1192
1193+ let future =
1124− let task = language_server.rename_symbol(doc.identifier(), pos, input.to_string());
1194+ match language_server.rename_symbol(doc.identifier(), pos, input.to_string()) {
1195+ Some(future) => future,
1196+ None => {
1197+ cx.editor
1198+ .set_error("Language server does not support symbol renaming");
1199+ return;
1200+ }
1201+ };
1125− match block_on(task) {
1202+ match block_on(future) {
1126 Ok(edits) => apply_workspace_edit(cx.editor, offset_encoding, &edits),
1203 Ok(edits) => apply_workspace_edit(cx.editor, offset_encoding, &edits),
1127 Err(err) => cx.editor.set_error(err.to_string()),
1204 Err(err) => cx.editor.set_error(err.to_string()),
1128 }
1205 }
@@ -1137,7 +1214,15 @@ pub fn select_references_to_symbol_under_cursor(cx: &mut Context) {
1137
1214
1138 let pos = doc.position(view.id, offset_encoding);
1215 let pos = doc.position(view.id, offset_encoding);
1139
1216
1140− let future = language_server.text_document_document_highlight(doc.identifier(), pos, None);
1217+ let future = match language_server.text_document_document_highlight(doc.identifier(), pos, None)
1218+ {
1219+ Some(future) => future,
1220+ None => {
1221+ cx.editor
1222+ .set_error("Language server does not support document highlight");
1223+ return;
1224+ }
1225+ };
1141
1226
1142 cx.callback(
1227 cx.callback(
1143 future,
1228 future,
helix-term/src/ui/completion.rs+6−11
@@ -245,21 +245,13 @@ impl Completion {
245 completion_item: lsp::CompletionItem,
245 completion_item: lsp::CompletionItem,
246 ) -> Option<CompletionItem> {
246 ) -> Option<CompletionItem> {
247 let language_server = doc.language_server()?;
247 let language_server = doc.language_server()?;
248− let completion_resolve_provider = language_server
249− .capabilities()
250− .completion_provider
251− .as_ref()?
252− .resolve_provider;
253− if completion_resolve_provider != Some(true) {
254− return None;
255− }
256
248
257− let future = language_server.resolve_completion_item(completion_item);
249+ let future = language_server.resolve_completion_item(completion_item)?;
258 let response = helix_lsp::block_on(future);
250 let response = helix_lsp::block_on(future);
259 match response {
251 match response {
260 Ok(value) => serde_json::from_value(value).ok(),
252 Ok(value) => serde_json::from_value(value).ok(),
261 Err(err) => {
253 Err(err) => {
262− log::error!("execute LSP command: {}", err);
254+ log::error!("Failed to resolve completion item: {}", err);
263 None
255 None
264 }
256 }
265 }
257 }
@@ -330,7 +322,10 @@ impl Completion {
330 };
322 };
331
323
332 // This method should not block the compositor so we handle the response asynchronously.
324 // This method should not block the compositor so we handle the response asynchronously.
333− let future = language_server.resolve_completion_item(current_item.clone());
325+ let future = match language_server.resolve_completion_item(current_item.clone()) {
326+ Some(future) => future,
327+ None => return false,
328+ };
334
329
335 cx.callback(
330 cx.callback(
336 future,
331 future,