alma 0.1.0

A Bevy-native modal text editor with Vim-style navigation.
Documentation
//! Editor ECS schedule sets.

use bevy::prelude::{App, IntoScheduleConfigs, Startup, SystemSet, Update};

/// Ordered startup stages for editor entity construction.
#[derive(SystemSet, Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EditorStartupSet {
    /// Creates authoritative editor buffer entities.
    Buffer,
    /// Creates editor scene/view entities that consume buffers.
    Scene,
    /// Creates chrome and other UI entities.
    Chrome,
}

/// Ordered dataflow stages for editor systems.
#[derive(SystemSet, Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EditorSet {
    /// Reads raw platform or Bevy input into editor-facing input state.
    Input,
    /// Interprets input as high-level editor or Vim intent.
    Intent,
    /// Applies edits and command side effects to authoritative domain state.
    Edit,
    /// Derives visible layout state from buffer, cursor, and viewport data.
    Layout,
    /// Converts layout into shaped text or glyph-facing presentation data.
    Shape,
    /// Synchronizes derived presentation data into Bevy render/UI entities.
    Render,
}

/// Configures the ordered editor dataflow pipeline for the Bevy `Update` schedule.
pub fn configure_editor_sets(app: &mut App) {
    let _app = app
        .configure_sets(
            Startup,
            (
                EditorStartupSet::Buffer,
                EditorStartupSet::Scene.after(EditorStartupSet::Buffer),
                EditorStartupSet::Chrome.after(EditorStartupSet::Scene),
            ),
        )
        .configure_sets(
            Update,
            (
                EditorSet::Input,
                EditorSet::Intent.after(EditorSet::Input),
                EditorSet::Edit.after(EditorSet::Intent),
                EditorSet::Layout.after(EditorSet::Edit),
                EditorSet::Shape.after(EditorSet::Layout),
                EditorSet::Render.after(EditorSet::Shape),
            ),
        );
}

#[cfg(test)]
mod tests {
    use super::{EditorSet, configure_editor_sets};
    use bevy::prelude::{App, IntoScheduleConfigs, ResMut, Resource, Update};

    /// Execution order observed by the schedule-order regression test.
    #[derive(Default, Resource)]
    struct ObservedOrder {
        /// Names pushed by systems as they run.
        stages: Vec<&'static str>,
    }

    /// Records that the input stage ran.
    fn input_stage(mut order: ResMut<ObservedOrder>) {
        order.stages.push("input");
    }

    /// Records that the intent stage ran.
    fn intent_stage(mut order: ResMut<ObservedOrder>) {
        order.stages.push("intent");
    }

    /// Records that the edit stage ran.
    fn edit_stage(mut order: ResMut<ObservedOrder>) {
        order.stages.push("edit");
    }

    /// Records that the layout stage ran.
    fn layout_stage(mut order: ResMut<ObservedOrder>) {
        order.stages.push("layout");
    }

    /// Records that the shape stage ran.
    fn shape_stage(mut order: ResMut<ObservedOrder>) {
        order.stages.push("shape");
    }

    /// Records that the render stage ran.
    fn render_stage(mut order: ResMut<ObservedOrder>) {
        order.stages.push("render");
    }

    #[test]
    fn editor_sets_run_in_dataflow_order() {
        let mut app = App::new();
        configure_editor_sets(&mut app);
        let _app = app
            .init_resource::<ObservedOrder>()
            .add_systems(Update, input_stage.in_set(EditorSet::Input))
            .add_systems(Update, intent_stage.in_set(EditorSet::Intent))
            .add_systems(Update, edit_stage.in_set(EditorSet::Edit))
            .add_systems(Update, layout_stage.in_set(EditorSet::Layout))
            .add_systems(Update, shape_stage.in_set(EditorSet::Shape))
            .add_systems(Update, render_stage.in_set(EditorSet::Render));

        app.update();

        let order = app.world().resource::<ObservedOrder>();
        assert_eq!(
            order.stages,
            ["input", "intent", "edit", "layout", "shape", "render"]
        );
    }
}