release: Storybook v0.2.0 - Major syntax and features update
BREAKING CHANGES: - Relationship syntax now requires blocks for all participants - Removed self/other perspective blocks from relationships - Replaced 'guard' keyword with 'if' for behavior tree decorators Language Features: - Add tree-sitter grammar with improved if/condition disambiguation - Add comprehensive tutorial and reference documentation - Add SBIR v0.2.0 binary format specification - Add resource linking system for behaviors and schedules - Add year-long schedule patterns (day, season, recurrence) - Add behavior tree enhancements (named nodes, decorators) Documentation: - Complete tutorial series (9 chapters) with baker family examples - Complete reference documentation for all language features - SBIR v0.2.0 specification with binary format details - Added locations and institutions documentation Examples: - Convert all examples to baker family scenario - Add comprehensive working examples Tooling: - Zed extension with LSP integration - Tree-sitter grammar for syntax highlighting - Build scripts and development tools Version Updates: - Main package: 0.1.0 → 0.2.0 - Tree-sitter grammar: 0.1.0 → 0.2.0 - Zed extension: 0.1.0 → 0.2.0 - Storybook editor: 0.1.0 → 0.2.0
This commit is contained in:
205
src/lsp/inlay_hints.rs
Normal file
205
src/lsp/inlay_hints.rs
Normal file
@@ -0,0 +1,205 @@
|
||||
//! Inlay hints for implicit information
|
||||
//!
|
||||
//! Provides inline annotations showing:
|
||||
//! - Parameter names in action calls
|
||||
//! - Inferred types for field values
|
||||
//! - Template/species field sources
|
||||
|
||||
use tower_lsp::lsp_types::{
|
||||
InlayHint,
|
||||
InlayHintKind,
|
||||
InlayHintLabel,
|
||||
Position,
|
||||
};
|
||||
|
||||
use super::document::Document;
|
||||
use crate::syntax::ast::{
|
||||
Declaration,
|
||||
Field,
|
||||
Value,
|
||||
};
|
||||
|
||||
/// Get inlay hints for a document range
|
||||
pub fn get_inlay_hints(doc: &Document, start: Position, end: Position) -> Option<Vec<InlayHint>> {
|
||||
let ast = doc.ast.as_ref()?;
|
||||
let mut hints = Vec::new();
|
||||
let mut positions = doc.positions.clone();
|
||||
|
||||
// Convert positions to offsets
|
||||
let start_line = start.line as usize;
|
||||
let end_line = end.line as usize;
|
||||
|
||||
// Process all declarations
|
||||
for decl in &ast.declarations {
|
||||
match decl {
|
||||
| Declaration::Character(character) => {
|
||||
// Skip if character is outside requested range
|
||||
if character.span.start_line > end_line || character.span.end_line < start_line {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add type hints for character fields
|
||||
for field in &character.fields {
|
||||
if field.span.start_line >= start_line && field.span.start_line <= end_line {
|
||||
add_type_hint(&mut hints, &mut positions, field);
|
||||
}
|
||||
}
|
||||
},
|
||||
| Declaration::Template(template) => {
|
||||
// Skip if template is outside requested range
|
||||
if template.span.start_line > end_line || template.span.end_line < start_line {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add type hints for template fields
|
||||
for field in &template.fields {
|
||||
if field.span.start_line >= start_line && field.span.start_line <= end_line {
|
||||
add_type_hint(&mut hints, &mut positions, field);
|
||||
}
|
||||
}
|
||||
},
|
||||
| Declaration::Species(species) => {
|
||||
// Skip if species is outside requested range
|
||||
if species.span.start_line > end_line || species.span.end_line < start_line {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add type hints for species fields
|
||||
for field in &species.fields {
|
||||
if field.span.start_line >= start_line && field.span.start_line <= end_line {
|
||||
add_type_hint(&mut hints, &mut positions, field);
|
||||
}
|
||||
}
|
||||
},
|
||||
| Declaration::Institution(institution) => {
|
||||
// Skip if institution is outside requested range
|
||||
if institution.span.start_line > end_line || institution.span.end_line < start_line
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add type hints for institution fields
|
||||
for field in &institution.fields {
|
||||
if field.span.start_line >= start_line && field.span.start_line <= end_line {
|
||||
add_type_hint(&mut hints, &mut positions, field);
|
||||
}
|
||||
}
|
||||
},
|
||||
| Declaration::Location(location) => {
|
||||
// Skip if location is outside requested range
|
||||
if location.span.start_line > end_line || location.span.end_line < start_line {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add type hints for location fields
|
||||
for field in &location.fields {
|
||||
if field.span.start_line >= start_line && field.span.start_line <= end_line {
|
||||
add_type_hint(&mut hints, &mut positions, field);
|
||||
}
|
||||
}
|
||||
},
|
||||
| Declaration::Relationship(relationship) => {
|
||||
// Skip if relationship is outside requested range
|
||||
if relationship.span.start_line > end_line ||
|
||||
relationship.span.end_line < start_line
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add type hints for relationship fields
|
||||
for field in &relationship.fields {
|
||||
if field.span.start_line >= start_line && field.span.start_line <= end_line {
|
||||
add_type_hint(&mut hints, &mut positions, field);
|
||||
}
|
||||
}
|
||||
},
|
||||
| Declaration::Behavior(behavior) => {
|
||||
// Skip if behavior is outside requested range
|
||||
if behavior.span.start_line > end_line || behavior.span.end_line < start_line {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Add parameter name hints for action calls in behavior
|
||||
// trees Would need to traverse BehaviorNode
|
||||
// tree and match actions to schema
|
||||
},
|
||||
| _ => {},
|
||||
}
|
||||
}
|
||||
|
||||
if hints.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(hints)
|
||||
}
|
||||
}
|
||||
|
||||
/// Add type hint for a field value
|
||||
fn add_type_hint(
|
||||
hints: &mut Vec<InlayHint>,
|
||||
positions: &mut crate::position::PositionTracker,
|
||||
field: &Field,
|
||||
) {
|
||||
let type_str = infer_value_type(&field.value);
|
||||
|
||||
// Only add hints for non-obvious types
|
||||
// Skip if the type is clear from the literal (e.g., "string", 123, true)
|
||||
let should_hint = match &field.value {
|
||||
| Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Bool(_) => false,
|
||||
| Value::Identifier(_) => true, // Show type for identifier references
|
||||
| Value::List(_) => true, // Show list element type
|
||||
| Value::Object(_) => false, // Object structure is visible
|
||||
| Value::Range(_, _) => false, // Range syntax is clear
|
||||
| Value::Time(_) => false, // Time format is clear
|
||||
| Value::Duration(_) => false, // Duration format is clear
|
||||
| Value::ProseBlock(_) => false, // Prose is obvious
|
||||
| Value::Override(_) => true, // Show what's being overridden
|
||||
};
|
||||
|
||||
if !should_hint {
|
||||
return;
|
||||
}
|
||||
|
||||
// Position the hint at the end of the field value
|
||||
let (line, col) = positions.offset_to_position(field.span.end);
|
||||
|
||||
hints.push(InlayHint {
|
||||
position: Position {
|
||||
line: line as u32,
|
||||
character: col as u32,
|
||||
},
|
||||
label: InlayHintLabel::String(format!(": {}", type_str)),
|
||||
kind: Some(InlayHintKind::TYPE),
|
||||
text_edits: None,
|
||||
tooltip: None,
|
||||
padding_left: Some(true),
|
||||
padding_right: None,
|
||||
data: None,
|
||||
});
|
||||
}
|
||||
|
||||
/// Infer the type of a value for display
|
||||
fn infer_value_type(value: &Value) -> String {
|
||||
match value {
|
||||
| Value::Identifier(path) => path.join("."),
|
||||
| Value::String(_) => "String".to_string(),
|
||||
| Value::Int(_) => "Int".to_string(),
|
||||
| Value::Float(_) => "Float".to_string(),
|
||||
| Value::Bool(_) => "Bool".to_string(),
|
||||
| Value::List(items) => {
|
||||
if items.is_empty() {
|
||||
"[]".to_string()
|
||||
} else {
|
||||
format!("[{}]", infer_value_type(&items[0]))
|
||||
}
|
||||
},
|
||||
| Value::Object(_) => "Object".to_string(),
|
||||
| Value::Range(start, end) => {
|
||||
format!("{}..{}", infer_value_type(start), infer_value_type(end))
|
||||
},
|
||||
| Value::Time(_) => "Time".to_string(),
|
||||
| Value::Duration(_) => "Duration".to_string(),
|
||||
| Value::ProseBlock(_) => "Prose".to_string(),
|
||||
| Value::Override(_) => "Override".to_string(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user