sued 0.24.2

shut up editor - a minimalist line-based text editor written in Rust
Documentation
//! **sued** - shut up editor, a vector-oriented line editor by [Arsalan "Aeri" Kazmi (AeriaVelocity)](https://aeriavelocity.codeberg.page) ([on Codeberg](https://codeberg.org/AeriaVelocity))
//!
//! sued is a stateless vector-oriented command-based text editor written in Rust,
//! with focus on speed, simplicity, ease of use and staying the hell out of your
//! way. It's inspired by more contemporary editors, such as the ed family (ed, em,
//! ex, vi, Vim, Neovim, etc.).
//!
//! [Official website](https://aeriavelocity.codeberg.page/sued) | [Codeberg repo](https://codeberg.org/AeriaVelocity/sued)
//!
//! For info on sued as an editor, read [README.md](https://codeberg.org/AeriaVelocity/sued/src/branch/main/README.md).
//! For info on sued as a library, read [README.lib.md](https://codeberg.org/AeriaVelocity/sued/src/branch/main/README.lib.md).
//!
//! sued is free software licensed under the Apache License, Version 2.0.
//!
//! For full licensing terms, read [the "Legal" section of README.md](https://codeberg.org/AeriaVelocity/sued/src/branch/main/README.md#legal).

// ^ Looks cumbersome? It's for `rustdoc`, so it uses Markdown. You probably
//   want to look at the README.md instead.

// Please see the corresponding files for those definitions.
pub mod command;
pub mod commands;
pub mod exit_status;
pub mod file_buffer;
pub mod helper;
pub mod tilde_range;

#[cfg(feature = "lua")]
pub mod lua;

use command::*;
use exit_status::*;
use file_buffer::*;

#[cfg(feature = "lua")]
use mlua::Lua;

/// Encapsulates the file buffer and command registry into one state struct.
///
/// This struct is used to pass data between sued's modules and commands.
#[derive(Default)]
pub struct EditorState {
    // Encapsulates the file contents and file path.
    pub buffer: FileBuffer,
    // Includes all commands available to an instance of sued.
    pub registry: CommandRegistry,
    // The symbol you type before a command. Default is `~`.
    pub prefix: String,
    // The Y-position of the cursor in the editor. Used for positional editing.
    pub cursor: usize,
    // The existing content on the currently working line. Used by the `~overtake` command.
    pub existing_content: String,
    // I probably don't need to tell you what this is for.
    #[cfg(feature = "lua")]
    pub lua: Option<Lua>,
}

impl EditorState {
    pub fn new() -> EditorState {
        EditorState {
            prefix: "~".to_string(),
            ..Default::default()
        }
    }

    pub fn instantiate() -> EditorState {
        let mut state = EditorState::new();
        state.registry = CommandRegistry::instantiate();
        state
    }
}

/// Converts a command into a Vec<&str> that `run_command` can operate on.
pub fn vecify_command(command: &str) -> Vec<&str> {
    let mut command_args: Vec<&str> = Vec::new();
    for arg in command.split_whitespace() {
        command_args.push(arg);
    }
    command_args
}

/// Runs a `sued` command from a string, with `is_repl` set to `false`.
///
/// This is the recommended way to run commands if you're using sued as a library.
/// It sets `is_repl` to `false`, indicating that you're running sued as a library
/// and not in the REPL.
pub fn run_sued_command(command_args: Vec<&str>, state: &mut EditorState) -> ExitStatus {
    run_command(command_args, state, false)
}

/// Runs a `sued` command from a string, with `is_repl` set to `true`.
///
/// This is used internally by the sued REPL. It sets `is_repl` to `true`,
/// indicating that you're NOT running sued as a library.
///
/// This function is only available when the `repl` feature is enabled, which
/// it automatically is when running the sued text editor.
#[cfg(feature = "repl")]
pub fn run_repl_command(command_args: Vec<&str>, state: &mut EditorState) -> ExitStatus {
    run_command(command_args, state, true)
}

/// Process an editing command passed from `command_args`.
///
/// Requires mutable access to `buffer` and `registry`, since this function will
/// need to modify it.
///
/// Related functions are available in [suedfn](crate::suedfn).
fn run_command(command_args: Vec<&str>, state: &mut EditorState, is_repl: bool) -> ExitStatus {
    let command = command_args.get(0).map(|s| s.to_string());
    if command.is_none() {
        return ExitStatus::Failure("no command provided".to_string());
    }

    let command = command.unwrap();

    match state.registry.get_command(&command) {
        Some(command) => match command.scope {
            CommandScope::Global => {
                let mut action = command.action.clone();
                let result = action.call(command_args, state);
                ExitStatus::Success(result)
            }
            CommandScope::FileOnly => {
                if is_repl {
                    ExitStatus::Failure(format!(
                        "{} is not available in sued as a library",
                        command.name
                    ))
                } else {
                    let mut action = command.action.clone();
                    let result = action.call(command_args, state);
                    ExitStatus::Success(result)
                }
            }
            CommandScope::REPLOnly => {
                if !is_repl || !cfg!(feature = "repl") {
                    ExitStatus::Failure(format!(
                        "{} is only available in sued as a library",
                        command.name
                    ))
                } else {
                    let mut action = command.action.clone();
                    let result = action.call(command_args, state);
                    ExitStatus::Success(result)
                }
            }
            CommandScope::Private => {
                ExitStatus::Failure(format!("{} is marked as private", command.name))
            }
        },
        None => ExitStatus::Failure(format!("{} is not a command", command)),
    }
}

/// A placeholder command to accomodate people using the old `process_command`
/// function.
///
/// This redirects the user to `run_sued_command` instead, letting them know
/// that the API has changed, and triggers a panic to prevent the program from
/// proceeding with an invalid function call.
pub fn process_command() -> ! {
    panic!("`process_command` is removed as of sued v0.20.0; use `run_sued_command` instead");
}