use std::{cell::RefCell, rc::Rc};
use lsp_types::{CompletionContext, CompletionItem, CompletionResponse};
use ropey::Rope;
use crate::App;
#[derive(Debug, Clone, Copy)]
pub struct CompletionMenuOptions {
pub max_width: crate::Pixels,
pub max_visible_items: usize,
pub show_documentation: bool,
pub show_detail: bool,
}
impl Default for CompletionMenuOptions {
fn default() -> Self {
Self {
max_width: crate::px(360.),
max_visible_items: 15,
show_documentation: true,
show_detail: true,
}
}
}
pub trait CompletionProvider {
fn completions(
&self,
text: &Rope,
offset: usize,
trigger: CompletionContext,
window: &mut crate::Window,
cx: &mut App,
) -> crate::Task<anyhow::Result<CompletionResponse>>;
fn resolve_completions(
&self,
indices: Vec<usize>,
completions: Rc<RefCell<Box<[Completion]>>>,
_cx: &mut App,
) -> crate::Task<anyhow::Result<bool>> {
let _ = (indices, completions);
crate::Task::ready(Ok(false))
}
fn trigger_characters(&self) -> &[String] {
&[]
}
fn menu_options(&self) -> CompletionMenuOptions {
CompletionMenuOptions::default()
}
}
#[derive(Debug, Clone)]
pub struct Completion {
pub label: String,
pub insert_text: String,
pub detail: Option<String>,
pub documentation: Option<String>,
pub kind: Option<lsp_types::CompletionItemKind>,
pub score: f32,
pub lsp_item: CompletionItem,
}
#[derive(Default)]
pub struct CompletionState {
pub completions: Vec<Completion>,
pub selected_index: usize,
pub prefix: String,
pub visible: bool,
pub options: CompletionMenuOptions,
}
impl CompletionState {
pub fn clear(&mut self) {
self.completions.clear();
self.selected_index = 0;
self.prefix.clear();
self.visible = false;
}
pub fn select_next(&mut self) {
if self.completions.is_empty() {
return;
}
self.selected_index = (self.selected_index + 1) % self.completions.len();
}
pub fn select_previous(&mut self) {
if self.completions.is_empty() {
return;
}
if self.selected_index == 0 {
self.selected_index = self.completions.len() - 1;
} else {
self.selected_index -= 1;
}
}
pub fn selected(&self) -> Option<&Completion> {
self.completions.get(self.selected_index)
}
pub fn from_response(response: CompletionResponse, max_items: usize) -> Vec<Completion> {
let items = match response {
CompletionResponse::Array(items) => items,
CompletionResponse::List(list) => list.items,
};
items
.into_iter()
.take(max_items)
.map(|item| {
let insert_text = item
.text_edit
.as_ref()
.map(|edit| match edit {
lsp_types::CompletionTextEdit::Edit(edit) => edit.new_text.clone(),
lsp_types::CompletionTextEdit::InsertAndReplace(edit) => {
edit.new_text.clone()
}
})
.or(item.insert_text.clone())
.unwrap_or_else(|| item.label.clone());
Completion {
label: item.label.clone(),
insert_text,
detail: item.detail.clone(),
documentation: item.documentation.as_ref().map(|doc| match doc {
lsp_types::Documentation::String(s) => s.clone(),
lsp_types::Documentation::MarkupContent(content) => content.value.clone(),
}),
kind: item.kind,
score: 0.0,
lsp_item: item,
}
})
.collect()
}
}
pub struct InlineCompletion {
pub text: String,
pub range: std::ops::Range<usize>,
pub is_required: bool,
}