use std::io::Cursor;
use std::ops::{Range, RangeInclusive};
use ariadne::{Label, Report, ReportKind, Source};
use serde::Serialize;
use tsify::Tsify;
#[derive(Debug, Clone, Serialize, PartialEq, Eq, Tsify)]
#[tsify(into_wasm_abi)]
pub struct Completion {
pub at: Range<usize>,
pub filter: Option<bool>,
pub items: Vec<CompletionItem>,
}
impl Completion {
pub fn new(at: RangeInclusive<usize>) -> Self {
Self::new_exclusive(*at.start()..*at.end() + 1)
}
pub fn new_exclusive(at: Range<usize>) -> Self {
Self {
at,
filter: None,
items: Vec::default(),
}
}
pub fn add_item(&mut self, item: CompletionItem) {
self.items.push(item);
}
pub fn add_items(&mut self, items: Vec<CompletionItem>) {
self.items.extend(items);
}
pub fn filter(&mut self, filter: bool) {
self.filter = Some(filter);
}
pub fn make_pretty(&self, hamelin: &String) -> Result<String, std::fmt::Error> {
use std::fmt::Write;
let input = hamelin.as_str();
let source = Source::from(input);
let mut string_buf = String::new();
let mut colors = ariadne::ColorGenerator::default();
let kind = ReportKind::Advice;
let mut report =
Report::build(kind, self.at.clone()).with_message("Completion suggestions to offer");
for item in self.items.iter() {
report = report.with_label(
Label::new(self.at.clone())
.with_message(format!(
"{} {}({:?})",
item.label,
item.detail
.as_ref()
.map(|d| format!("[{}] ", d))
.unwrap_or_default(),
item.kind
))
.with_color(colors.next()),
);
}
if let Some(filter) = self.filter {
report = report.with_note(format!("editor should filter: {}", filter))
}
let final_report = report.finish();
let buf = Vec::new();
let mut cursor = Cursor::new(buf);
final_report
.write(source, &mut cursor)
.map_err(|_| std::fmt::Error)?;
write!(string_buf, "{}", String::from_utf8_lossy(cursor.get_ref()))?;
Ok(string_buf)
}
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq, Tsify)]
#[tsify(into_wasm_abi)]
pub struct CompletionItem {
pub label: String,
pub apply_string: String,
pub detail: Option<String>,
pub description: Option<String>,
pub kind: CompletionItemKind,
pub snippet: Option<String>,
pub section: Option<String>,
}
impl CompletionItem {
pub fn new<S>(label: S) -> Self
where
S: Into<String> + Clone,
{
Self {
label: label.clone().into(),
apply_string: label.into(),
detail: None,
description: None,
kind: CompletionItemKind::Text,
snippet: None,
section: None,
}
}
pub fn with_apply_string<S>(mut self, apply_string: S) -> Self
where
S: Into<String>,
{
self.apply_string = apply_string.into();
self
}
pub fn with_detail<S>(mut self, detail: S) -> Self
where
S: Into<String>,
{
self.detail = Some(detail.into());
self
}
pub fn with_description<S>(mut self, description: S) -> Self
where
S: Into<String>,
{
self.description = Some(description.into());
self
}
pub fn with_kind(mut self, kind: CompletionItemKind) -> Self {
self.kind = kind;
self
}
pub fn with_snippet(mut self, snippet: String) -> Self {
self.snippet = Some(snippet);
self
}
pub fn with_section(mut self, section: String) -> Self {
self.section = Some(section);
self
}
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq, Tsify)]
#[tsify(into_wasm_abi)]
pub enum CompletionItemKind {
Text,
Variable,
Function,
Command,
Keyword,
}