summaryrefslogtreecommitdiff
path: root/helix-term/src/ui
diff options
context:
space:
mode:
authorBlaž Hrastnik2023-05-19 00:39:35 +0000
committerGitHub2023-05-19 00:39:35 +0000
commit53f47bc47771c94dab51626ca025be28e62eba0c (patch)
treec8f5c59d40d1ecde227c209f898cc7afd6da5477 /helix-term/src/ui
parent7f5940be80eaa3aec7903903072b7108f41dd97b (diff)
parent2a512f7c487f0a707a7eb158e24bd478433bcd91 (diff)
Merge pull request #2507 from Philipp-M/multiple-language-servers
Add support for multiple language servers per language
Diffstat (limited to 'helix-term/src/ui')
-rw-r--r--helix-term/src/ui/completion.rs101
-rw-r--r--helix-term/src/ui/editor.rs17
-rw-r--r--helix-term/src/ui/mod.rs22
-rw-r--r--helix-term/src/ui/statusline.rs11
4 files changed, 70 insertions, 81 deletions
diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs
index c5c40580..d997e8ae 100644
--- a/helix-term/src/ui/completion.rs
+++ b/helix-term/src/ui/completion.rs
@@ -15,7 +15,7 @@ use helix_view::{graphics::Rect, Document, Editor};
use crate::commands;
use crate::ui::{menu, Markdown, Menu, Popup, PromptEvent};
-use helix_lsp::{lsp, util};
+use helix_lsp::{lsp, util, OffsetEncoding};
impl menu::Item for CompletionItem {
type Data = ();
@@ -38,6 +38,7 @@ impl menu::Item for CompletionItem {
|| self.item.tags.as_ref().map_or(false, |tags| {
tags.contains(&lsp::CompletionItemTag::DEPRECATED)
});
+
menu::Row::new(vec![
menu::Cell::from(Span::styled(
self.item.label.as_str(),
@@ -79,19 +80,15 @@ impl menu::Item for CompletionItem {
}
None => "",
}),
- // self.detail.as_deref().unwrap_or("")
- // self.label_details
- // .as_ref()
- // .or(self.detail())
- // .as_str(),
])
}
}
#[derive(Debug, PartialEq, Default, Clone)]
-struct CompletionItem {
- item: lsp::CompletionItem,
- resolved: bool,
+pub struct CompletionItem {
+ pub item: lsp::CompletionItem,
+ pub language_server_id: usize,
+ pub resolved: bool,
}
/// Wraps a Menu.
@@ -109,29 +106,21 @@ impl Completion {
pub fn new(
editor: &Editor,
savepoint: Arc<SavePoint>,
- mut items: Vec<lsp::CompletionItem>,
- offset_encoding: helix_lsp::OffsetEncoding,
+ mut items: Vec<CompletionItem>,
start_offset: usize,
trigger_offset: usize,
) -> Self {
let replace_mode = editor.config().completion_replace;
// Sort completion items according to their preselect status (given by the LSP server)
- items.sort_by_key(|item| !item.preselect.unwrap_or(false));
- let items = items
- .into_iter()
- .map(|item| CompletionItem {
- item,
- resolved: false,
- })
- .collect();
+ items.sort_by_key(|item| !item.item.preselect.unwrap_or(false));
// Then create the menu
let menu = Menu::new(items, (), move |editor: &mut Editor, item, event| {
fn item_to_transaction(
doc: &Document,
view_id: ViewId,
- item: &CompletionItem,
- offset_encoding: helix_lsp::OffsetEncoding,
+ item: &lsp::CompletionItem,
+ offset_encoding: OffsetEncoding,
trigger_offset: usize,
include_placeholder: bool,
replace_mode: bool,
@@ -141,7 +130,7 @@ impl Completion {
let text = doc.text().slice(..);
let primary_cursor = selection.primary().cursor(text);
- let (edit_offset, new_text) = if let Some(edit) = &item.item.text_edit {
+ let (edit_offset, new_text) = if let Some(edit) = &item.text_edit {
let edit = match edit {
lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
lsp::CompletionTextEdit::InsertAndReplace(item) => {
@@ -164,10 +153,9 @@ impl Completion {
(Some((start_offset, end_offset)), edit.new_text)
} else {
let new_text = item
- .item
.insert_text
.clone()
- .unwrap_or_else(|| item.item.label.clone());
+ .unwrap_or_else(|| item.label.clone());
// check that we are still at the correct savepoint
// we can still generate a transaction regardless but if the
// document changed (and not just the selection) then we will
@@ -176,9 +164,9 @@ impl Completion {
(None, new_text)
};
- if matches!(item.item.kind, Some(lsp::CompletionItemKind::SNIPPET))
+ if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET))
|| matches!(
- item.item.insert_text_format,
+ item.insert_text_format,
Some(lsp::InsertTextFormat::SNIPPET)
)
{
@@ -223,6 +211,23 @@ impl Completion {
let (view, doc) = current!(editor);
+ macro_rules! language_server {
+ ($item:expr) => {
+ match editor
+ .language_servers
+ .get_by_id($item.language_server_id)
+ {
+ Some(ls) => ls,
+ None => {
+ editor.set_error("completions are outdated");
+ // TODO close the completion menu somehow,
+ // currently there is no trivial way to access the EditorView to close the completion menu
+ return;
+ }
+ }
+ };
+ }
+
match event {
PromptEvent::Abort => {}
PromptEvent::Update => {
@@ -250,8 +255,8 @@ impl Completion {
let transaction = item_to_transaction(
doc,
view.id,
- item,
- offset_encoding,
+ &item.item,
+ language_server!(item).offset_encoding(),
trigger_offset,
true,
replace_mode,
@@ -267,10 +272,18 @@ impl Completion {
// always present here
let mut item = item.unwrap().clone();
+ let language_server = language_server!(item);
+ let offset_encoding = language_server.offset_encoding();
+
+ let language_server = editor
+ .language_servers
+ .get_by_id(item.language_server_id)
+ .unwrap();
+
// resolve item if not yet resolved
if !item.resolved {
if let Some(resolved) =
- Self::resolve_completion_item(doc, item.item.clone())
+ Self::resolve_completion_item(language_server, item.item.clone())
{
item.item = resolved;
}
@@ -280,7 +293,7 @@ impl Completion {
let transaction = item_to_transaction(
doc,
view.id,
- &item,
+ &item.item,
offset_encoding,
trigger_offset,
false,
@@ -323,11 +336,9 @@ impl Completion {
}
fn resolve_completion_item(
- doc: &Document,
+ language_server: &helix_lsp::Client,
completion_item: lsp::CompletionItem,
) -> Option<lsp::CompletionItem> {
- let language_server = doc.language_server()?;
-
let future = language_server.resolve_completion_item(completion_item)?;
let response = helix_lsp::block_on(future);
match response {
@@ -398,16 +409,10 @@ impl Completion {
_ => return false,
};
- let language_server = match doc!(cx.editor).language_server() {
- Some(language_server) => language_server,
- None => return false,
- };
+ let Some(language_server) = cx.editor.language_server_by_id(current_item.language_server_id) else { return false; };
// This method should not block the compositor so we handle the response asynchronously.
- let future = match language_server.resolve_completion_item(current_item.item.clone()) {
- Some(future) => future,
- None => return false,
- };
+ let Some(future) = language_server.resolve_completion_item(current_item.item.clone()) else { return false; };
cx.callback(
future,
@@ -422,13 +427,13 @@ impl Completion {
.unwrap()
.completion
{
- completion.replace_item(
- current_item,
- CompletionItem {
- item: resolved_item,
- resolved: true,
- },
- );
+ let resolved_item = CompletionItem {
+ item: resolved_item,
+ language_server_id: current_item.language_server_id,
+ resolved: true,
+ };
+
+ completion.replace_item(current_item, resolved_item);
}
},
);
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index f0989fa8..43b5d1af 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -33,7 +33,7 @@ use std::{mem::take, num::NonZeroUsize, path::PathBuf, rc::Rc, sync::Arc};
use tui::{buffer::Buffer as Surface, text::Span};
-use super::statusline;
+use super::{completion::CompletionItem, statusline};
use super::{document::LineDecoration, lsp::SignatureHelp};
pub struct EditorView {
@@ -650,7 +650,7 @@ impl EditorView {
.primary()
.cursor(doc.text().slice(..));
- let diagnostics = doc.diagnostics().iter().filter(|diagnostic| {
+ let diagnostics = doc.shown_diagnostics().filter(|diagnostic| {
diagnostic.range.start <= cursor && diagnostic.range.end >= cursor
});
@@ -953,20 +953,13 @@ impl EditorView {
&mut self,
editor: &mut Editor,
savepoint: Arc<SavePoint>,
- items: Vec<helix_lsp::lsp::CompletionItem>,
- offset_encoding: helix_lsp::OffsetEncoding,
+ items: Vec<CompletionItem>,
start_offset: usize,
trigger_offset: usize,
size: Rect,
) -> Option<Rect> {
- let mut completion = Completion::new(
- editor,
- savepoint,
- items,
- offset_encoding,
- start_offset,
- trigger_offset,
- );
+ let mut completion =
+ Completion::new(editor, savepoint, items, start_offset, trigger_offset);
if completion.is_empty() {
// skip if we got no completion results
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index 3e9a14b0..ec328ec5 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -17,7 +17,7 @@ mod text;
use crate::compositor::{Component, Compositor};
use crate::filter_picker_entry;
use crate::job::{self, Callback};
-pub use completion::Completion;
+pub use completion::{Completion, CompletionItem};
pub use editor::EditorView;
pub use markdown::Markdown;
pub use menu::Menu;
@@ -238,6 +238,7 @@ pub mod completers {
use crate::ui::prompt::Completion;
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
use fuzzy_matcher::FuzzyMatcher;
+ use helix_core::syntax::LanguageServerFeature;
use helix_view::document::SCRATCH_BUFFER_NAME;
use helix_view::theme;
use helix_view::{editor::Config, Editor};
@@ -393,20 +394,11 @@ pub mod completers {
pub fn lsp_workspace_command(editor: &Editor, input: &str) -> Vec<Completion> {
let matcher = Matcher::default();
- let (_, doc) = current_ref!(editor);
-
- let language_server = match doc.language_server() {
- Some(language_server) => language_server,
- None => {
- return vec![];
- }
- };
-
- let options = match &language_server.capabilities().execute_command_provider {
- Some(options) => options,
- None => {
- return vec![];
- }
+ let Some(options) = doc!(editor)
+ .language_servers_with_feature(LanguageServerFeature::WorkspaceCommand)
+ .find_map(|ls| ls.capabilities().execute_command_provider.as_ref())
+ else {
+ return vec![];
};
let mut matches: Vec<_> = options
diff --git a/helix-term/src/ui/statusline.rs b/helix-term/src/ui/statusline.rs
index 88786351..4aa64634 100644
--- a/helix-term/src/ui/statusline.rs
+++ b/helix-term/src/ui/statusline.rs
@@ -197,15 +197,15 @@ where
);
}
+// TODO think about handling multiple language servers
fn render_lsp_spinner<F>(context: &mut RenderContext, write: F)
where
F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
{
+ let language_server = context.doc.language_servers().next();
write(
context,
- context
- .doc
- .language_server()
+ language_server
.and_then(|srv| {
context
.spinners
@@ -225,8 +225,7 @@ where
{
let (warnings, errors) = context
.doc
- .diagnostics()
- .iter()
+ .shown_diagnostics()
.fold((0, 0), |mut counts, diag| {
use helix_core::diagnostic::Severity;
match diag.severity {
@@ -266,7 +265,7 @@ where
.diagnostics
.values()
.flatten()
- .fold((0, 0), |mut counts, diag| {
+ .fold((0, 0), |mut counts, (diag, _)| {
match diag.severity {
Some(DiagnosticSeverity::WARNING) => counts.0 += 1,
Some(DiagnosticSeverity::ERROR) | None => counts.1 += 1,