use super::acceptance::AcceptanceRefusal;
use super::context::CompletionContext;
use super::words::WordSuggestions;
use std::sync::Arc;
use strop_core::worker::{Outcome, WorkerId};
use strop_lsp::completion::{CompletionList, CompletionReply};
use strop_lsp::ReplyContext;
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub(super) struct CompletionQuery {
pub request: WorkerId,
pub context: Arc<CompletionContext>,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub(super) enum CandidateKey {
Word(Arc<str>),
Language {
request: strop_lsp::RequestStamp,
ordinal: u32,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub(super) enum DeliveryScope {
Query,
Selection(WorkerId),
Preparation { selection: WorkerId, work: WorkerId },
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub(super) struct LanguageSuggestions {
pub origin: ReplyContext,
pub owner: Arc<super::context::LanguageContext>,
pub items: CompletionList,
pub resolve_required: bool,
pub prefix: Arc<str>,
pub suffix: Arc<str>,
}
impl LanguageSuggestions {
pub fn reusable(&self) -> bool {
!self.resolve_required
&& !self.items.server_incomplete
&& !self.items.client_truncated
&& self.items.omitted_oversized == 0
&& self.items.items.iter().all(|entry| {
let item = &entry.item.protocol();
item.text_edit.is_none()
&& item
.additional_text_edits
.as_ref()
.is_none_or(Vec::is_empty)
&& item.command.is_none()
&& item.insert_text_format.is_none_or(|format| {
format == strop_lsp::completion::InsertTextFormat::PLAIN_TEXT
})
&& item
.insert_text_mode
.is_none_or(|mode| mode == strop_lsp::completion::InsertTextMode::AS_IS)
})
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub(super) struct PreparedDocumentation {
pub text: Arc<str>,
pub spans: Vec<strop_syntax::Span>,
pub truncated: bool,
}
#[derive(Debug, Clone, Copy, Default, serde::Serialize, serde::Deserialize)]
pub(super) struct CompletionWorkStats {
pub builds: u64,
pub incremental_updates: u64,
pub scanned_bytes: u64,
pub queries: u64,
pub indexed_words: usize,
pub indexed_word_bytes: usize,
pub retired_snapshots: u64,
pub retirement_high_water_bytes: usize,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub(super) enum CompletionPayload {
Words {
outcome: Outcome<WordSuggestions>,
work: CompletionWorkStats,
},
Language(Outcome<LanguageSuggestions>),
Resolved {
candidate: CandidateKey,
reply: CompletionReply,
},
Documentation {
candidate: CandidateKey,
outcome: Outcome<PreparedDocumentation>,
},
Acceptance {
candidate: CandidateKey,
prepared: Result<(), AcceptanceRefusal>,
},
}
impl CompletionPayload {
pub fn slot(&self) -> usize {
match self {
Self::Words { .. } => 0,
Self::Language(_) => 1,
Self::Resolved { .. } => 2,
Self::Documentation { .. } => 3,
Self::Acceptance { .. } => 4,
}
}
pub fn charged_bytes(&self) -> usize {
let payload = match self {
Self::Words { .. } => 40 * 1024,
Self::Language(_) => 5 * 1024 * 1024,
Self::Resolved { .. } => 576 * 1024,
Self::Documentation { .. } => 128 * 1024,
Self::Acceptance { .. } => 144 * 1024,
};
payload + 1024 * 1024
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub(super) struct CompletionDelivery {
pub query: Arc<CompletionQuery>,
pub scope: DeliveryScope,
pub payload: CompletionPayload,
}