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
124 lines
3.8 KiB
Rust
124 lines
3.8 KiB
Rust
//! Syntax highlighter for Storybook DSL
|
|
//!
|
|
//! Implements iced's Highlighter trait to provide line-by-line syntax
|
|
//! highlighting.
|
|
|
|
use std::ops::Range;
|
|
|
|
use iced::{
|
|
Color,
|
|
advanced::text::highlighter,
|
|
};
|
|
|
|
use crate::syntax_highlight::{
|
|
token_to_color_contextual,
|
|
tokenize,
|
|
};
|
|
|
|
/// Settings for the Storybook highlighter
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct Settings;
|
|
|
|
/// Storybook syntax highlighter
|
|
#[derive(Debug)]
|
|
pub struct StorybookHighlighter {
|
|
current_line: usize,
|
|
in_prose_block: bool, // Track if we're inside a prose block
|
|
}
|
|
|
|
impl iced::widget::text::Highlighter for StorybookHighlighter {
|
|
type Highlight = Color;
|
|
type Iterator<'a> = std::vec::IntoIter<(Range<usize>, Self::Highlight)>;
|
|
type Settings = Settings;
|
|
|
|
fn new(_settings: &Self::Settings) -> Self {
|
|
Self {
|
|
current_line: 0,
|
|
in_prose_block: false,
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, _new_settings: &Self::Settings) {
|
|
// No settings to update
|
|
}
|
|
|
|
fn change_line(&mut self, line: usize) {
|
|
self.current_line = line;
|
|
// Reset prose block state when jumping to a different line
|
|
self.in_prose_block = false;
|
|
}
|
|
|
|
fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_> {
|
|
let mut highlights = Vec::new();
|
|
|
|
// Check if this line starts or ends a prose block
|
|
let trimmed = line.trim_start();
|
|
|
|
if trimmed.starts_with("---") {
|
|
// This is a prose marker line
|
|
if self.in_prose_block {
|
|
// Ending a prose block
|
|
self.in_prose_block = false;
|
|
} else {
|
|
// Starting a prose block
|
|
self.in_prose_block = true;
|
|
}
|
|
// Color the entire line as a prose marker (gray)
|
|
if !line.is_empty() {
|
|
highlights.push((0..line.len(), Color::from_rgb8(0x6d, 0x6d, 0x6d)));
|
|
}
|
|
} else if self.in_prose_block {
|
|
// Inside a prose block - render as plain text in peach color
|
|
if !line.is_empty() {
|
|
highlights.push((0..line.len(), Color::from_rgb8(0xff, 0xb8, 0x6c)));
|
|
}
|
|
} else {
|
|
// Regular code - tokenize and highlight
|
|
let tokens = tokenize(line);
|
|
|
|
// Track if we're after a colon for context-aware coloring
|
|
let mut after_colon = false;
|
|
|
|
for (token, range) in tokens {
|
|
let color = token_to_color_contextual(&token, after_colon);
|
|
highlights.push((range, color));
|
|
|
|
// Update context: set after_colon when we see a colon,
|
|
// reset it when we see an identifier (field value) or certain other tokens
|
|
use storybook::syntax::lexer::Token;
|
|
match &token {
|
|
| Token::Colon => after_colon = true,
|
|
| Token::Ident(_) |
|
|
Token::IntLit(_) |
|
|
Token::FloatLit(_) |
|
|
Token::StringLit(_) |
|
|
Token::True |
|
|
Token::False |
|
|
Token::TimeLit(_) |
|
|
Token::DurationLit(_) => {
|
|
// Reset after consuming a value
|
|
after_colon = false;
|
|
},
|
|
// Don't reset for whitespace, commas, or other punctuation
|
|
| _ => {},
|
|
}
|
|
}
|
|
}
|
|
|
|
self.current_line += 1;
|
|
highlights.into_iter()
|
|
}
|
|
|
|
fn current_line(&self) -> usize {
|
|
self.current_line
|
|
}
|
|
}
|
|
|
|
/// Convert a highlight (Color) to a Format for rendering
|
|
pub fn to_format(color: &Color, _theme: &iced::Theme) -> highlighter::Format<iced::Font> {
|
|
highlighter::Format {
|
|
color: Some(*color),
|
|
font: None,
|
|
}
|
|
}
|