1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
//! Add-Ed is a library implementing the parsing and runtime for Ed in rust. //! //! It exports two traits, Buffer and UI, which define the exchangeable parts of the editor. //! //! An implementation of the UI trait is needed to support the 'g' command and similar, DummyUI. //! It is used for macro execution, by taking prepared input from a input list rather than prompting the user. //! //! Since the buffer is rather complex a standard Buffer implementation can be build in with the feature "vecbuffer". //! It is recommended to compare the behaviour of any Buffer implementation to the VecBuffer until Buffer tests are set up. //! //! An example of how to use this library is in src/bin/classic.rs pub mod error_consts; mod cmd; pub mod ui; pub mod buffer; use ui::UI; use buffer::Buffer; /// A small reference struct that gives insight into the editor's state pub struct EdState<'a> { pub selection: &'a Option<(usize, usize)>, pub buffer: &'a dyn Buffer, pub path: &'a str, } /// The state variable used by the editor to track its internal state pub struct Ed <'a, B: Buffer> { // Track the currently selected lines in the buffer // This is usually separate from viewed lines in the UI selection: Option<(usize, usize)>, // A mutable reference to a Buffer implementor // The buffer implementor will handle most of the operations and store the data buffer: &'a mut B, // The path to the currently selected file path: String, // The previous search_replace's arguments, to support repeating the last s_args: Option<(String, String, bool)>, // Prefix for command input. Traditionally : but none by default cmd_prefix: Option<char>, // Wether or not to print errors when they occur (if not, print ? instead of error) print_errors: bool, // The previous error that occured, since we may not have printed it error: Option<&'static str>, } impl <'a, B: Buffer> Ed <'a, B> { /// Construct a new instance of Ed /// /// * An empty file string is recommended if no filepath is opened /// * Note that you can initialise the buffer with contents before this pub fn new( buffer: &'a mut B, path: String, ) -> Result<Self, &'static str> { let len = path.len(); if len != 0 { buffer.read_from(&path, None, false)?; } let tmp = Self { // Sane defaults for initial settings print_errors: true, error: None, s_args: None, cmd_prefix: None, // Trying to set a reasonable default tends to cause trouble selection: None, // And the given values buffer: buffer, path: path, }; Ok(tmp) } /// Run the given command /// /// Returns true if the command was to quit pub fn run_command( &mut self, ui: &mut dyn UI, command: &str, ) -> Result<bool, &'static str> { // Just hand execution into the cmd module match cmd::run(self, ui, command) { // If error, note it in state Err(e) => { self.error = Some(e); Err(e) }, x => x, } } /// Run given instance of Ed until it receives a command to quit or errors /// /// The returned error type could be improved, suggestions welcome. pub fn run_macro( &mut self, ui: &mut dyn UI, ) -> Result<(), &'static str> { // Loop until quit or error loop { let cmd = match ui.get_command( self.see_state(), self.cmd_prefix ) { Err(e) => { self.error = Some(e); return Err(e) }, Ok(x) => x, }; if self.run_command(ui, &cmd)? { break; } } Ok(()) } pub fn run( &mut self, ui: &mut dyn UI, ) -> Result<(), &'static str> { loop { match self.run_macro(ui) { Ok(()) => break, Err(_) => { if self.print_errors { ui.print(self.see_state(), self.error.unwrap())?; } else { ui.print(self.see_state(), "?\n")?; } }, } } Ok(()) } /// Get an immutable reference to part of the editors state pub fn see_state(&self) -> EdState { EdState{ selection: &self.selection, path: &self.path, buffer: self.buffer, } } }