use nautilus_schema::{
analysis::{
analyze, completion_with_analysis, goto_definition_with_analysis, hover_with_analysis,
AnalysisResult, CompletionItem, HoverInfo,
},
format_schema, Span,
};
#[derive(Clone)]
pub struct DocumentState {
pub source: String,
pub analysis: AnalysisResult,
}
impl DocumentState {
pub fn new(source: String) -> Self {
let analysis = analyze(&source);
Self { source, analysis }
}
pub fn completion(&self, offset: usize) -> Vec<CompletionItem> {
completion_with_analysis(&self.source, &self.analysis, offset)
}
pub fn hover(&self, offset: usize) -> Option<HoverInfo> {
hover_with_analysis(&self.source, &self.analysis, offset)
}
pub fn goto_definition(&self, offset: usize) -> Option<Span> {
goto_definition_with_analysis(&self.analysis, offset)
}
pub fn formatted(&self) -> Option<String> {
self.analysis
.ast
.as_ref()
.map(|ast| format_schema(ast, &self.source))
}
}
#[cfg(test)]
mod tests {
use super::DocumentState;
#[test]
fn cached_analysis_serves_completion_hover_and_definition() {
let source = r#"
type Address {
kind Role
}
enum Role {
Home
Work
}
model User {
id Int @id
address
}
"#;
let state = DocumentState::new(source.to_string());
let completion_offset = source.find("address ").unwrap() + "address ".len();
let completion_labels: Vec<_> = state
.completion(completion_offset)
.into_iter()
.map(|item| item.label)
.collect();
assert!(completion_labels.iter().any(|label| label == "Address"));
let hover_offset = source.find("kind").unwrap() + 1;
let hover = state.hover(hover_offset).expect("hover");
assert!(hover.content.contains("Role"));
let definition_offset = source.find("kind Role").unwrap() + "kind ".len() + 1;
let definition = state
.goto_definition(definition_offset)
.expect("definition");
assert!(source[definition.start..definition.end].contains("Role"));
}
#[test]
fn formatted_uses_cached_ast() {
let source = "model User {\nname String\nid Int @id\n}\n";
let state = DocumentState::new(source.to_string());
let formatted = state.formatted().expect("formatted source");
assert!(formatted.contains("name String"));
assert_ne!(formatted, source);
}
}