use nu_ansi_term::Style;
use std::ops::Range;
use std::sync::Arc;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct Span {
pub start: usize,
pub end: usize,
}
pub type Suggestions = Arc<[Suggestion]>;
impl Span {
pub fn new(start: usize, end: usize) -> Span {
assert!(
end >= start,
"Can't create a Span whose end < start, start={start}, end={end}"
);
Span { start, end }
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CompletionOrigin {
pub(crate) buffer: String,
pub(crate) insertion_point: usize,
}
impl CompletionOrigin {
pub fn new(buffer: impl Into<String>, insertion_point: usize) -> Self {
Self {
buffer: buffer.into(),
insertion_point,
}
}
pub fn matches(&self, buffer: &str, insertion_point: usize) -> bool {
self.insertion_point == insertion_point && self.buffer == buffer
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Partial {
pub span: Span,
pub insert: String,
}
#[derive(Debug, Clone)]
pub enum CompletionResult {
Fresh {
suggestions: Suggestions,
partial: Option<Partial>,
},
Stale {
suggestions: Suggestions,
origin: CompletionOrigin,
partial: Option<Partial>,
},
Pending,
}
impl CompletionResult {
pub fn fresh(suggestions: impl Into<Suggestions>) -> Self {
CompletionResult::Fresh {
suggestions: suggestions.into(),
partial: None,
}
}
pub fn stale_or_pending(fallback: Suggestions, origin: CompletionOrigin) -> Self {
if fallback.is_empty() {
CompletionResult::Pending
} else {
CompletionResult::Stale {
suggestions: fallback,
origin,
partial: None,
}
}
}
pub fn with_partial(mut self, partial: Option<Partial>) -> Self {
match &mut self {
Self::Fresh { partial: slot, .. } | Self::Stale { partial: slot, .. } => {
*slot = partial;
}
Self::Pending => {}
}
self
}
pub fn suggestions(&self) -> &[Suggestion] {
match self {
CompletionResult::Fresh { suggestions, .. }
| CompletionResult::Stale { suggestions, .. } => suggestions,
CompletionResult::Pending => &[],
}
}
pub fn partial(&self) -> Option<&Partial> {
match self {
CompletionResult::Fresh { partial, .. } | CompletionResult::Stale { partial, .. } => {
partial.as_ref()
}
CompletionResult::Pending => None,
}
}
pub fn into_shared(self) -> Option<Suggestions> {
match self {
CompletionResult::Fresh { suggestions, .. }
| CompletionResult::Stale { suggestions, .. } => Some(suggestions),
CompletionResult::Pending => None,
}
}
pub fn is_pending(&self) -> bool {
matches!(self, CompletionResult::Pending)
}
pub fn is_provisional(&self) -> bool {
!matches!(self, CompletionResult::Fresh { .. })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompletionStatus {
Idle,
Pending,
Ready,
}
pub trait Completer {
fn complete(&mut self, line: &str, pos: usize) -> CompletionResult;
fn complete_with_base_ranges(
&mut self,
line: &str,
pos: usize,
) -> (CompletionResult, Vec<Range<usize>>) {
let result = self.complete(line, pos);
let mut ranges = vec![];
for suggestion in result.suggestions() {
ranges.push(suggestion.span.start..suggestion.span.end);
}
ranges.dedup();
(result, ranges)
}
fn partial_complete(
&mut self,
line: &str,
pos: usize,
start: usize,
offset: usize,
) -> Suggestions {
self.complete(line, pos)
.suggestions()
.iter()
.skip(start)
.take(offset)
.cloned()
.collect()
}
fn total_completions(&mut self, line: &str, pos: usize) -> usize {
self.complete(line, pos).suggestions().len()
}
fn poll_completion(&mut self) -> CompletionStatus {
CompletionStatus::Idle
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Suggestion {
pub value: String,
pub display_override: Option<String>,
pub description: Option<String>,
pub style: Option<Style>,
pub extra: Option<Vec<String>>,
pub span: Span,
pub append_whitespace: bool,
pub match_indices: Option<Vec<usize>>,
}
impl Suggestion {
pub fn display_value(&self) -> &str {
self.display_override.as_ref().unwrap_or(&self.value)
}
}