Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/commands/lsp.rs')
-rw-r--r--helix-term/src/commands/lsp.rs234
1 files changed, 10 insertions, 224 deletions
diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs
index 0494db3e..f4abf6e8 100644
--- a/helix-term/src/commands/lsp.rs
+++ b/helix-term/src/commands/lsp.rs
@@ -13,18 +13,9 @@ use tui::{text::Span, widgets::Row};
use super::{align_view, push_jump, Align, Context, Editor};
-use helix_core::{
- diagnostic::DiagnosticProvider, syntax::config::LanguageServerFeature,
- text_annotations::InlineAnnotation, Selection, Uri,
-};
+use helix_core::{diagnostic::DiagnosticProvider, syntax::LanguageServerFeature, Selection, Uri};
use helix_stdx::path;
-use helix_view::{
- document::{DocumentInlayHints, DocumentInlayHintsId},
- editor::Action,
- handlers::lsp::SignatureHelpInvoked,
- theme::Style,
- Document, View,
-};
+use helix_view::{editor::Action, handlers::lsp::SignatureHelpInvoked, theme::Style};
use crate::{
compositor::{self, Compositor},
@@ -46,7 +37,7 @@ macro_rules! language_server_with_feature {
match language_server {
Some(language_server) => language_server,
None => {
- $editor.set_error(format!(
+ $editor.set_status(format!(
"No configured language server supports {}",
$feature
));
@@ -100,7 +91,7 @@ struct PickerDiagnostic {
diag: lsp::Diagnostic,
}
-fn location_to_file_location(location: &Location) -> Option<FileLocation<'_>> {
+fn location_to_file_location(location: &Location) -> Option<FileLocation> {
let path = location.uri.as_path()?;
let line = Some((
location.range.start.line as usize,
@@ -231,13 +222,6 @@ fn diag_picker(
}
}
- flat_diag.sort_by(|a, b| {
- a.diag
- .severity
- .unwrap_or(lsp::DiagnosticSeverity::HINT)
- .cmp(&b.diag.severity.unwrap_or(lsp::DiagnosticSeverity::HINT))
- });
-
let styles = DiagnosticStyles {
hint: cx.editor.theme.get("hint"),
info: cx.editor.theme.get("info"),
@@ -259,9 +243,6 @@ fn diag_picker(
.into()
},
),
- ui::PickerColumn::new("source", |item: &PickerDiagnostic, _| {
- item.diag.source.as_deref().unwrap_or("").into()
- }),
ui::PickerColumn::new("code", |item: &PickerDiagnostic, _| {
match item.diag.code.as_ref() {
Some(NumberOrString::Number(n)) => n.to_string().into(),
@@ -273,12 +254,12 @@ fn diag_picker(
item.diag.message.as_str().into()
}),
];
- let mut primary_column = 3; // message
+ let mut primary_column = 2; // message
if format == DiagnosticsFormat::ShowSourcePath {
columns.insert(
// between message code and message
- 3,
+ 2,
ui::PickerColumn::new("path", |item: &PickerDiagnostic, _| {
if let Some(path) = item.location.uri.as_path() {
path::get_truncated_path(path)
@@ -589,7 +570,7 @@ struct CodeActionOrCommandItem {
impl ui::menu::Item for CodeActionOrCommandItem {
type Data = ();
- fn format(&self, _data: &Self::Data) -> Row<'_> {
+ fn format(&self, _data: &Self::Data) -> Row {
match &self.lsp_item {
lsp::CodeActionOrCommand::CodeAction(action) => action.title.as_str().into(),
lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(),
@@ -814,9 +795,7 @@ pub fn code_action(cx: &mut Context) {
});
picker.move_down(); // pre-select the first item
- let popup = Popup::new("code-action", picker)
- .with_scrollbar(false)
- .auto_close(true);
+ let popup = Popup::new("code-action", picker).with_scrollbar(false);
compositor.replace_or_push("code-action", popup);
};
@@ -935,13 +914,7 @@ where
}
let call = move |editor: &mut Editor, compositor: &mut Compositor| {
if locations.is_empty() {
- editor.set_error(match feature {
- LanguageServerFeature::GotoDeclaration => "No declaration found.",
- LanguageServerFeature::GotoDefinition => "No definition found.",
- LanguageServerFeature::GotoTypeDefinition => "No type definition found.",
- LanguageServerFeature::GotoImplementation => "No implementation found.",
- _ => "No location found.",
- });
+ editor.set_error("No definition found.");
} else {
goto_impl(editor, compositor, locations);
}
@@ -1146,7 +1119,7 @@ pub fn rename_symbol(cx: &mut Context) {
let Some(language_server) = doc
.language_servers_with_feature(LanguageServerFeature::RenameSymbol)
- .find(|ls| language_server_id.is_none_or(|id| id == ls.id()))
+ .find(|ls| language_server_id.map_or(true, |id| id == ls.id()))
else {
cx.editor
.set_error("No configured language server supports symbol renaming");
@@ -1269,190 +1242,3 @@ pub fn select_references_to_symbol_under_cursor(cx: &mut Context) {
},
);
}
-
-pub fn compute_inlay_hints_for_all_views(editor: &mut Editor, jobs: &mut crate::job::Jobs) {
- if !editor.config().lsp.display_inlay_hints {
- return;
- }
-
- for (view, _) in editor.tree.views() {
- let doc = match editor.documents.get(&view.doc) {
- Some(doc) => doc,
- None => continue,
- };
- if let Some(callback) = compute_inlay_hints_for_view(view, doc) {
- jobs.callback(callback);
- }
- }
-}
-
-fn compute_inlay_hints_for_view(
- view: &View,
- doc: &Document,
-) -> Option<std::pin::Pin<Box<impl Future<Output = Result<crate::job::Callback, anyhow::Error>>>>> {
- let view_id = view.id;
- let doc_id = view.doc;
-
- let language_server = doc
- .language_servers_with_feature(LanguageServerFeature::InlayHints)
- .next()?;
-
- let doc_text = doc.text();
- let len_lines = doc_text.len_lines();
-
- // Compute ~3 times the current view height of inlay hints, that way some scrolling
- // will not show half the view with hints and half without while still being faster
- // than computing all the hints for the full file (which could be dozens of time
- // longer than the view is).
- let view_height = view.inner_height();
- let first_visible_line =
- doc_text.char_to_line(doc.view_offset(view_id).anchor.min(doc_text.len_chars()));
- let first_line = first_visible_line.saturating_sub(view_height);
- let last_line = first_visible_line
- .saturating_add(view_height.saturating_mul(2))
- .min(len_lines);
-
- let new_doc_inlay_hints_id = DocumentInlayHintsId {
- first_line,
- last_line,
- };
- // Don't recompute the annotations in case nothing has changed about the view
- if !doc.inlay_hints_oudated
- && doc
- .inlay_hints(view_id)
- .is_some_and(|dih| dih.id == new_doc_inlay_hints_id)
- {
- return None;
- }
-
- let doc_slice = doc_text.slice(..);
- let first_char_in_range = doc_slice.line_to_char(first_line);
- let last_char_in_range = doc_slice.line_to_char(last_line);
-
- let range = helix_lsp::util::range_to_lsp_range(
- doc_text,
- helix_core::Range::new(first_char_in_range, last_char_in_range),
- language_server.offset_encoding(),
- );
-
- let offset_encoding = language_server.offset_encoding();
-
- let callback = super::make_job_callback(
- language_server.text_document_range_inlay_hints(doc.identifier(), range, None)?,
- move |editor, _compositor, response: Option<Vec<lsp::InlayHint>>| {
- // The config was modified or the window was closed while the request was in flight
- if !editor.config().lsp.display_inlay_hints || editor.tree.try_get(view_id).is_none() {
- return;
- }
-
- // Add annotations to relevant document, not the current one (it may have changed in between)
- let doc = match editor.documents.get_mut(&doc_id) {
- Some(doc) => doc,
- None => return,
- };
-
- // If we have neither hints nor an LSP, empty the inlay hints since they're now oudated
- let mut hints = match response {
- Some(hints) if !hints.is_empty() => hints,
- _ => {
- doc.set_inlay_hints(
- view_id,
- DocumentInlayHints::empty_with_id(new_doc_inlay_hints_id),
- );
- doc.inlay_hints_oudated = false;
- return;
- }
- };
-
- // Most language servers will already send them sorted but ensure this is the case to
- // avoid errors on our end.
- hints.sort_by_key(|inlay_hint| inlay_hint.position);
-
- let mut padding_before_inlay_hints = Vec::new();
- let mut type_inlay_hints = Vec::new();
- let mut parameter_inlay_hints = Vec::new();
- let mut other_inlay_hints = Vec::new();
- let mut padding_after_inlay_hints = Vec::new();
-
- let doc_text = doc.text();
- let inlay_hints_length_limit = doc.config.load().lsp.inlay_hints_length_limit;
-
- for hint in hints {
- let char_idx =
- match helix_lsp::util::lsp_pos_to_pos(doc_text, hint.position, offset_encoding)
- {
- Some(pos) => pos,
- // Skip inlay hints that have no "real" position
- None => continue,
- };
-
- let mut label = match hint.label {
- lsp::InlayHintLabel::String(s) => s,
- lsp::InlayHintLabel::LabelParts(parts) => parts
- .into_iter()
- .map(|p| p.value)
- .collect::<Vec<_>>()
- .join(""),
- };
- // Truncate the hint if too long
- if let Some(limit) = inlay_hints_length_limit {
- // Limit on displayed width
- use helix_core::unicode::{
- segmentation::UnicodeSegmentation, width::UnicodeWidthStr,
- };
-
- let width = label.width();
- let limit = limit.get().into();
- if width > limit {
- let mut floor_boundary = 0;
- let mut acc = 0;
- for (i, grapheme_cluster) in label.grapheme_indices(true) {
- acc += grapheme_cluster.width();
-
- if acc > limit {
- floor_boundary = i;
- break;
- }
- }
-
- label.truncate(floor_boundary);
- label.push('…');
- }
- }
-
- let inlay_hints_vec = match hint.kind {
- Some(lsp::InlayHintKind::TYPE) => &mut type_inlay_hints,
- Some(lsp::InlayHintKind::PARAMETER) => &mut parameter_inlay_hints,
- // We can't warn on unknown kind here since LSPs are free to set it or not, for
- // example Rust Analyzer does not: every kind will be `None`.
- _ => &mut other_inlay_hints,
- };
-
- if let Some(true) = hint.padding_left {
- padding_before_inlay_hints.push(InlineAnnotation::new(char_idx, " "));
- }
-
- inlay_hints_vec.push(InlineAnnotation::new(char_idx, label));
-
- if let Some(true) = hint.padding_right {
- padding_after_inlay_hints.push(InlineAnnotation::new(char_idx, " "));
- }
- }
-
- doc.set_inlay_hints(
- view_id,
- DocumentInlayHints {
- id: new_doc_inlay_hints_id,
- type_inlay_hints,
- parameter_inlay_hints,
- other_inlay_hints,
- padding_before_inlay_hints,
- padding_after_inlay_hints,
- },
- );
- doc.inlay_hints_oudated = false;
- },
- );
-
- Some(callback)
-}