~cdv/cedv

72ef9ca122b438ce4da88c8f9e1a1e5d11bc1f23 — Chris Vittal 5 months ago eb2defe master
Add Change and Shell commands
3 files changed, 107 insertions(+), 13 deletions(-)

M src/cmd.rs
M src/main.rs
M src/parser.rs
M src/cmd.rs => src/cmd.rs +13 -4
@@ 6,6 6,7 @@ pub struct Cmd {

#[derive(Debug, Clone)]
pub enum CmdKind {
    Change(Change),
    Delete(Delete),
    Edit {
        path: Option<String>,


@@ 30,6 31,7 @@ pub enum CmdKind {
        addr: AddrExpr,
        append: bool,
    },
    Shell(String),
    Write(Write),

    Unimplemented(char),


@@ 47,17 49,22 @@ impl CmdKind {
                    *addr = naddr;
                }
            }
            Self::Print(Print {
            Self::Change(Change {
                ref mut start,
                ref mut delim,
                ref mut end,
            })
            | Self::Number(Number {
            | Self::Delete(Delete {
                ref mut start,
                ref mut delim,
                ref mut end,
            })
            | Self::Delete(Delete {
            | Self::Print(Print {
                ref mut start,
                ref mut delim,
                ref mut end,
            })
            | Self::Number(Number {
                ref mut start,
                ref mut delim,
                ref mut end,


@@ 87,6 94,7 @@ impl CmdKind {
            Self::Edit { .. }
            | Self::File { .. }
            | Self::Quit { .. }
            | Self::Shell(_)
            | Self::Unimplemented(_)
            | Self::Unknown(_) => {}
        }


@@ 190,9 198,10 @@ impl Default for $name {
}

address_pair_cmd! {
    Change, AddrExpr::current(), AddrExpr::current();
    Delete, AddrExpr::current(), AddrExpr::current();
    Print, AddrExpr::current(), AddrExpr::current();
    Number, AddrExpr::current(), AddrExpr::current();
    Delete, AddrExpr::current(), AddrExpr::current();
}

#[derive(Debug, Clone)]

M src/main.rs => src/main.rs +87 -8
@@ 2,6 2,7 @@ use std::{
    fmt,
    fs::{File, OpenOptions},
    io::{self, prelude::*},
    process::Command,
};

use fehler::*;


@@ 37,6 38,7 @@ fn main() {
                        }
                    }
                    Err(e) => {
                        println!("?");
                        println!("cedv: {}", e);
                        if isatty {
                            ec = 1;


@@ 60,6 62,7 @@ struct Editor {
    prompt: String,
    use_prompt: bool,
    path: Option<String>,
    prev_cmd: Option<String>,
    text: Rope,
    line: usize,
    /// The result of the previous address evaluated


@@ 77,12 80,14 @@ impl Editor {
    fn run(&mut self) {
        let mut line = String::new();
        match self.read_line(&mut line) {
            Ok(0) => if !self.modified || self.quitting {
                throw!(Error::Quit)
            } else {
                self.quitting = true;
                throw!(Error::ModifiedWarning)
            },
            Ok(0) => {
                if !self.modified || self.quitting {
                    throw!(Error::Quit)
                } else {
                    self.quitting = true;
                    throw!(Error::ModifiedWarning)
                }
            }
            Ok(_) => {}
            Err(e) => throw!(Error::Io(e.into())),
        }


@@ 134,7 139,7 @@ impl Editor {
            _ => {
                self.new_edit = false;
                self.quitting = false;
            },
            }
        }

        match kind {


@@ 155,7 160,35 @@ impl Editor {
                    throw!(Error::NoFilename);
                }
            }
            Shell(s) => {
                if have_addrs {
                    throw!(Error::UnexpectedAddress)
                }

                let (m, s) = self.cmd_substitution(s)?;
                if m {
                    println!("{}", s);
                }

                let mut cmd = Command::new("sh");
                cmd.arg("-c").arg(&s);
                let mut child = cmd.spawn()?;
                let _ = child.wait()?;
                println!("!");

                self.prev_cmd = Some(s);
            }

            Change(cmd::Change { start, delim, end }) => {
                let start = self.resolve(start, delim)?;
                let end = self.resolve(end, Delim::End)?;

                let start_idx = self.text.line_to_char(start.saturating_sub(1));
                let end_idx = self.text.line_to_char(end);
                self.text.remove(start_idx..end_idx);
                self.line = start;
                self.insert()?;
            }
            SimpleInput { addr, append } => {
                self.resolve(addr, Delim::Semi)?;
                if append {


@@ 174,7 207,12 @@ impl Editor {
                let start_idx = self.text.line_to_char(start - 1);
                let end_idx = self.text.line_to_char(end);
                self.text.remove(start_idx..end_idx);
                self.line = self.text.char_to_line(start_idx) + 1;

                self.line = if self.text.len_bytes() != 0 {
                    self.text.char_to_line(start_idx) + 1
                } else {
                    0
                };
            }

            Null { addr } => {


@@ 410,6 448,44 @@ impl Editor {
        }
    }

    #[throws(Error)]
    fn cmd_substitution(&self, mut s: String) -> (bool, String) {
        let mut modified = false;
        let mut chars = s.char_indices().peekable();
        let mut percents = Vec::new();
        while let Some((pos, c)) = chars.next() {
            match c {
                '%' => percents.push(pos),
                '\\' => {
                    chars.next();
                }
                _ => {}
            }
        }

        if !percents.is_empty() && self.path.is_none() {
            throw!(Error::NoFilename);
        }

        if let Some(path) = &self.path {
            for p in percents.into_iter().rev() {
                modified = true;
                s.replace_range(p..p + 1, path);
            }
        }

        if s.starts_with("!") {
            if let Some(pc) = &self.prev_cmd {
                modified = true;
                s.replace_range(..1, pc);
            } else {
                throw!(Error::NoPrevCommand);
            }
        }

        (modified, s)
    }

    #[throws(Box<dyn std::error::Error>)]
    fn from_args() -> Self {
        let mut args: Vec<_> = std::env::args().collect();


@@ 470,6 546,7 @@ impl EditorBuilder {
            text: Rope::new(),
            line: 0,
            prev_line: 0,
            prev_cmd: None,

            suppress: self.suppress,
            modified: false,


@@ 518,6 595,7 @@ fn read_input_line(stdin: &mut io::StdinLock, input: &mut String) -> bool {
enum Error {
    InvalidAddress,
    NoFilename,
    NoPrevCommand,
    Quit,
    ModifiedWarning,
    UnexpectedAddress,


@@ 539,6 617,7 @@ impl fmt::Display for Error {
        match self {
            Self::InvalidAddress => f.pad("invalid address"),
            Self::NoFilename => f.pad("no current filename"),
            Self::NoPrevCommand => f.pad("no previous command"),
            Self::ModifiedWarning => f.pad("warning - buffer modified"),
            Self::Quit => f.pad("quit"), // should never be called
            Self::UnexpectedAddress => f.pad("unexpected address"),

M src/parser.rs => src/parser.rs +7 -1
@@ 41,6 41,7 @@ fn parse_kind(input: &str) -> IResult<&str, CmdKind> {
                addr: AddrExpr::current(),
                append: c == 'a',
            },
            'c' => CmdKind::Change(Default::default()),
            'd' => CmdKind::Delete(Default::default()),
            'e' | 'E' => {
                let (rest, path) = preceded(multispace0, rest)(input)?;


@@ 83,7 84,12 @@ fn parse_kind(input: &str) -> IResult<&str, CmdKind> {
            '=' => CmdKind::LineNo {
                addr: AddrExpr::last(),
            },
            c if "cgGhHjklmPrstuvVw!".contains(c) => CmdKind::Unimplemented(c),
            '!' => {
                let (rest, cmd) = preceded(multispace0, rest)(input)?;
                input = rest;
                CmdKind::Shell(cmd.into())
            }
            c if "gGhHjklmPrstuvV".contains(c) => CmdKind::Unimplemented(c),
            c => CmdKind::Unknown(c),
        };