use crate::dsl::tokenizer::{Token, TokenKind};
macro_rules! tok {
($kind:ident == $text:literal) => {
$crate::dsl::tokenizer::Token {
kind: $crate::dsl::tokenizer::TokenKind::$kind,
text: $text,
..
}
};
($kind:ident => $binding:ident) => {
$crate::dsl::tokenizer::Token {
kind: $crate::dsl::tokenizer::TokenKind::$kind,
text: $binding,
..
}
};
($kind:ident) => {
$crate::dsl::tokenizer::Token { kind: $crate::dsl::tokenizer::TokenKind::$kind, .. }
};
}
pub(super) use tok;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompletionItem {
pub label: String,
pub kind: CompletionKind,
pub detail: String,
pub insert_text: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CompletionKind {
Method,
Function,
Constant,
Parameter,
Variable,
Type,
Field,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum CompletionContext {
TopLevel,
DotAccess {
receiver_type: String,
},
FnCall {
fn_name: String,
namespace: Option<String>,
arg_index: usize,
},
DoubleColon {
namespace: String,
},
StructInit {
struct_name: String,
filled_fields: Vec<String>,
},
}
impl CompletionItem {
pub(super) fn new_type(label: &str, detail: &str) -> Self {
Self {
label: label.to_string(),
kind: CompletionKind::Type,
detail: detail.into(),
insert_text: None,
description: None,
}
}
pub(super) fn new_param(param_type: &str, arg_index: usize, arg_count: usize) -> Self {
CompletionItem {
label: format!("{param_type}::"),
kind: CompletionKind::Parameter,
detail: format!("Parameter {} of {arg_count}", arg_index + 1),
insert_text: None,
description: None,
}
}
}
impl From<&CallableItem> for CompletionItem {
fn from(callable: &CallableItem) -> Self {
let name = callable.name();
CompletionItem {
label: name.to_string(),
kind: if callable.is_static() {
CompletionKind::Function
} else {
CompletionKind::Method
},
detail: format!("{}({})", name, callable.params().join(", ")),
insert_text: Some(format!("{name}($0)")),
description: callable
.description()
.map(alloc::string::ToString::to_string),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct LetBinding {
pub(super) name: String,
pub(super) binding_type: String,
}
impl LetBinding {
pub(super) fn new(name: &str, binding_type: &str) -> Self {
Self {
name: name.to_string(),
binding_type: binding_type.to_string(),
}
}
}
pub(super) enum TokenCursor {
InToken { token_index: usize, offset: usize },
BetweenTokens,
}
impl TokenCursor {
pub(super) fn from_tokens(tokens: &[Token<'_>], cursor_char_idx: u32) -> Self {
let mut token_index = tokens
.iter()
.position(|t| t.contains_index(cursor_char_idx));
if token_index.is_none() {
token_index = tokens.iter().position(|t| {
t.span.1 == cursor_char_idx && matches!(t.kind, TokenKind::Identifier)
});
}
if let Some(idx) = token_index {
let offset = cursor_char_idx.saturating_sub(tokens[idx].span.0) as usize;
Self::InToken { token_index: idx, offset }
} else {
Self::BetweenTokens
}
}
pub(super) fn extract_partial_token(&self, tokens: &[Token]) -> String {
match self {
TokenCursor::InToken { token_index, offset } => {
let token = tokens[*token_index];
if matches!(token.kind, TokenKind::Identifier) {
token.text.chars().take(*offset).collect()
} else {
String::new()
}
},
TokenCursor::BetweenTokens => String::new(),
}
}
pub(super) fn token_index(&self) -> Option<usize> {
match self {
Self::InToken { token_index, .. } => Some(*token_index),
Self::BetweenTokens => None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub(super) enum CallableItem {
Constructor {
name: &'static str,
params: &'static [&'static str],
#[allow(dead_code)]
declaring_type: &'static str,
description: Option<&'static str>,
},
#[allow(dead_code)]
StaticMethod {
name: &'static str,
params: &'static [&'static str],
declaring_type: &'static str,
description: Option<&'static str>,
},
InstanceMethod {
name: &'static str,
params: &'static [&'static str],
#[allow(dead_code)]
declaring_type: &'static str,
description: Option<&'static str>,
},
}
impl CallableItem {
pub(super) const fn constructor(
declaring_type: &'static str,
name: &'static str,
params: &'static [&'static str],
description: Option<&'static str>,
) -> Self {
Self::Constructor { name, params, declaring_type, description }
}
#[allow(dead_code)]
pub(super) const fn static_method(
declaring_type: &'static str,
name: &'static str,
params: &'static [&'static str],
description: Option<&'static str>,
) -> Self {
Self::StaticMethod { name, params, declaring_type, description }
}
pub(super) const fn instance_method(
declaring_type: &'static str,
name: &'static str,
params: &'static [&'static str],
description: Option<&'static str>,
) -> Self {
Self::InstanceMethod { name, params, declaring_type, description }
}
pub(super) const fn name(&self) -> &str {
match self {
Self::Constructor { name, .. }
| Self::StaticMethod { name, .. }
| Self::InstanceMethod { name, .. } => name,
}
}
pub(super) const fn params(&self) -> &[&'static str] {
match self {
Self::Constructor { params, .. }
| Self::StaticMethod { params, .. }
| Self::InstanceMethod { params, .. } => params,
}
}
#[allow(dead_code)]
pub(super) const fn declaring_type(&self) -> &str {
match self {
Self::Constructor { declaring_type, .. }
| Self::StaticMethod { declaring_type, .. }
| Self::InstanceMethod { declaring_type, .. } => declaring_type,
}
}
pub(super) const fn description(&self) -> Option<&'static str> {
match self {
Self::Constructor { description, .. }
| Self::StaticMethod { description, .. }
| Self::InstanceMethod { description, .. } => *description,
}
}
pub(super) const fn is_static(&self) -> bool {
matches!(self, Self::Constructor { .. } | Self::StaticMethod { .. })
}
}