mod hover_format;
use super::symbol_table::{SymbolKind, SymbolTable};
use crate::location::Span;
use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum CompletionKind {
Keyword,
ModelTrait,
Model,
Enum,
Field,
EnumVariant,
LetBinding,
PrimitiveType,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct CompletionCandidate {
pub label: String,
pub kind: CompletionKind,
pub detail: Option<String>,
pub insert_text: Option<String>,
pub documentation: Option<String>,
}
impl CompletionCandidate {
pub fn new(label: impl Into<String>, kind: CompletionKind) -> Self {
Self {
label: label.into(),
kind,
detail: None,
insert_text: None,
documentation: None,
}
}
#[must_use]
pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
self.detail = Some(detail.into());
self
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct HoverInfo {
pub symbol_name: String,
pub kind: SymbolKind,
pub signature: String,
pub documentation: Option<String>,
pub source_span: Span,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct DefinitionInfo {
pub symbol_name: String,
pub kind: SymbolKind,
pub span: Span,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct QueryProvider<'a> {
pub symbols: &'a SymbolTable,
pub module_cache:
Option<&'a std::collections::HashMap<PathBuf, (crate::ast::File, SymbolTable)>>,
}
impl<'a> QueryProvider<'a> {
#[must_use]
pub const fn new(symbols: &'a SymbolTable) -> Self {
Self {
symbols,
module_cache: None,
}
}
#[must_use]
pub const fn with_modules(
symbols: &'a SymbolTable,
module_cache: &'a std::collections::HashMap<PathBuf, (crate::ast::File, SymbolTable)>,
) -> Self {
Self {
symbols,
module_cache: Some(module_cache),
}
}
#[must_use]
pub fn get_all_completions(&self) -> Vec<CompletionCandidate> {
let mut completions = vec![
CompletionCandidate::new("pub", CompletionKind::Keyword),
CompletionCandidate::new("use", CompletionKind::Keyword),
CompletionCandidate::new("let", CompletionKind::Keyword),
CompletionCandidate::new("model", CompletionKind::Keyword),
CompletionCandidate::new("view", CompletionKind::Keyword),
CompletionCandidate::new("trait", CompletionKind::Keyword),
CompletionCandidate::new("enum", CompletionKind::Keyword),
];
for name in self.symbols.traits.keys() {
completions.push(CompletionCandidate::new(
name.clone(),
CompletionKind::ModelTrait,
));
}
for name in self.symbols.structs.keys() {
completions.push(CompletionCandidate::new(
name.clone(),
CompletionKind::Model,
));
}
for name in self.symbols.enums.keys() {
completions.push(CompletionCandidate::new(name.clone(), CompletionKind::Enum));
}
for name in self.symbols.lets.keys() {
completions.push(CompletionCandidate::new(
name.clone(),
CompletionKind::LetBinding,
));
}
completions
}
#[must_use]
pub fn get_type_completions(&self) -> Vec<CompletionCandidate> {
let mut completions = vec![
CompletionCandidate::new("String", CompletionKind::PrimitiveType),
CompletionCandidate::new("I32", CompletionKind::PrimitiveType),
CompletionCandidate::new("I64", CompletionKind::PrimitiveType),
CompletionCandidate::new("F32", CompletionKind::PrimitiveType),
CompletionCandidate::new("F64", CompletionKind::PrimitiveType),
CompletionCandidate::new("Boolean", CompletionKind::PrimitiveType),
CompletionCandidate::new("Path", CompletionKind::PrimitiveType),
CompletionCandidate::new("Regex", CompletionKind::PrimitiveType),
];
for name in self.symbols.structs.keys() {
completions.push(CompletionCandidate::new(
name.clone(),
CompletionKind::Model,
));
}
for name in self.symbols.enums.keys() {
completions.push(CompletionCandidate::new(name.clone(), CompletionKind::Enum));
}
for name in self.symbols.traits.keys() {
completions.push(CompletionCandidate::new(
name.clone(),
CompletionKind::ModelTrait,
));
}
completions
}
#[must_use]
pub fn get_hover_for_symbol(&self, name: &str) -> Option<HoverInfo> {
if let Some(info) = self.symbols.traits.get(name) {
return Some(hover_format::trait_info_to_hover(
name,
info,
SymbolKind::Trait,
));
}
if let Some(info) = self.symbols.structs.get(name) {
return Some(hover_format::struct_info_to_hover(name, info));
}
if let Some(info) = self.symbols.enums.get(name) {
return Some(hover_format::enum_info_to_hover(name, info));
}
if let Some(info) = self.symbols.lets.get(name) {
return Some(hover_format::let_info_to_hover(name, info));
}
if let Some(info) = self.symbols.get_function(name) {
return Some(hover_format::function_info_to_hover(name, info));
}
if let Some(cache) = self.module_cache {
for (_, symbols) in cache.values() {
if let Some(info) = symbols.traits.get(name) {
return Some(hover_format::trait_info_to_hover(
name,
info,
SymbolKind::Trait,
));
}
if let Some(info) = symbols.structs.get(name) {
return Some(hover_format::struct_info_to_hover(name, info));
}
if let Some(info) = symbols.enums.get(name) {
return Some(hover_format::enum_info_to_hover(name, info));
}
if let Some(info) = symbols.lets.get(name) {
return Some(hover_format::let_info_to_hover(name, info));
}
if let Some(info) = symbols.get_function(name) {
return Some(hover_format::function_info_to_hover(name, info));
}
}
}
None
}
#[must_use]
pub fn find_definition_by_name(&self, name: &str) -> Option<DefinitionInfo> {
let local = self
.symbols
.traits
.get(name)
.map(|i| (SymbolKind::Trait, i.span))
.or_else(|| {
self.symbols
.structs
.get(name)
.map(|i| (SymbolKind::Struct, i.span))
})
.or_else(|| {
self.symbols
.enums
.get(name)
.map(|i| (SymbolKind::Enum, i.span))
})
.or_else(|| {
self.symbols
.lets
.get(name)
.map(|i| (SymbolKind::Let, i.span))
})
.or_else(|| {
self.symbols
.get_function(name)
.map(|i| (SymbolKind::Function, i.span))
});
if let Some((kind, span)) = local {
return Some(DefinitionInfo {
symbol_name: name.to_string(),
kind,
span,
});
}
if let Some(cache) = self.module_cache {
for (_, symbols) in cache.values() {
let hit = symbols
.traits
.get(name)
.map(|i| (SymbolKind::Trait, i.span))
.or_else(|| {
symbols
.structs
.get(name)
.map(|i| (SymbolKind::Struct, i.span))
})
.or_else(|| symbols.enums.get(name).map(|i| (SymbolKind::Enum, i.span)))
.or_else(|| symbols.lets.get(name).map(|i| (SymbolKind::Let, i.span)))
.or_else(|| {
symbols
.get_function(name)
.map(|i| (SymbolKind::Function, i.span))
});
if let Some((kind, span)) = hit {
return Some(DefinitionInfo {
symbol_name: name.to_string(),
kind,
span,
});
}
}
}
None
}
}