basic file handling #7
|
@ -33,7 +33,7 @@ use crate::{Cursor, Direction};
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Buffer {
|
||||
styles: Arc<Mutex<StyleStore>>,
|
||||
text: Rope,
|
||||
pub text: Rope,
|
||||
syntax_set: Arc<SyntaxSet>,
|
||||
parser: ParseState,
|
||||
styled: Vec<Vec<(Style, Range<usize>)>>,
|
||||
|
|
105
src/main.rs
105
src/main.rs
|
@ -19,9 +19,16 @@
|
|||
|
||||
use std::{
|
||||
env::args,
|
||||
fs::File,
|
||||
io::{stdout, Read, Stdout, Write},
|
||||
ffi::OsString,
|
||||
fs::{ File, OpenOptions },
|
||||
io::{
|
||||
Read,
|
||||
stdout,
|
||||
Stdout,
|
||||
Write,
|
||||
},
|
||||
os::fd::FromRawFd,
|
||||
path::{ Path },
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
|
@ -99,14 +106,15 @@ struct State {
|
|||
pub styles: Arc<Mutex<StyleStore>>,
|
||||
pub buffer: Buffer,
|
||||
pub cursor: Cursor,
|
||||
pub file: Option<OsString>,
|
||||
pub mode: Mode,
|
||||
pub scroll: Cursor,
|
||||
pub size: (usize, usize),
|
||||
pub mode: Mode,
|
||||
pub quit: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn from_str(text: &str) -> Result<Self> {
|
||||
pub fn from_str(file_name: Option<OsString>, text: &str) -> Result<Self> {
|
||||
let styles = Arc::new(Mutex::new(StyleStore::default()));
|
||||
let buffer = Buffer::from_str(styles.clone(), text);
|
||||
let (cols, rows) = terminal::size()?;
|
||||
|
@ -115,9 +123,10 @@ impl State {
|
|||
styles,
|
||||
buffer,
|
||||
cursor: Cursor::default(),
|
||||
file: file_name,
|
||||
mode: Mode::default(),
|
||||
scroll: Cursor::default(),
|
||||
size: (cols as usize, rows as usize),
|
||||
mode: Mode::default(),
|
||||
quit: false,
|
||||
})
|
||||
}
|
||||
|
@ -331,12 +340,53 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
fn execute_command(&mut self, command: &str) -> std::result::Result<(), String> {
|
||||
match command {
|
||||
"q" => self.quit = true,
|
||||
command => return Err(format!("unrecognized command {:?}", command)),
|
||||
}
|
||||
fn write_buffer(&mut self, file: OsString) -> Result<()> {
|
||||
let out = self.buffer.text.bytes().collect::<Vec<u8>>();
|
||||
let mut handle = OpenOptions::new().write(true).open(file)?;
|
||||
|
||||
handle.write_all(out.as_slice())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_command(&mut self, command: &str) -> std::result::Result<(), String> {
|
||||
let command_parts = command.split(' ').collect::<Vec<&str>>();
|
||||
|
||||
let commands = match command_parts.get(0) {
|
||||
Some(parts) => parts.chars().collect::<Vec<char>>(),
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
for command in commands.clone().iter() {
|
||||
match command {
|
||||
'q' => self.quit = true,
|
||||
'w' => {
|
||||
let handle: OsString;
|
||||
|
||||
|
||||
if let Some(part) = self.file.clone() {
|
||||
mars
commented
If "q" is given multiple command parts, it still quits without issuing a warning. I'm not sure if this should exit silently even when given more arguments than expected. What do you think? Edit: how we decide to do this now will help us handle argc differences in the future. If "q" is given multiple command parts, it still quits without issuing a warning. I'm not sure if this should exit silently even when given more arguments than expected. What do you think?
Edit: how we decide to do this now will help us handle argc differences in the future.
emma
commented
It definitely should not exit silently, it should error, stating that It definitely should not exit silently, it should error, stating that `:q` doesn’t take arguments (on its own).
|
||||
handle = part;
|
||||
} else {
|
||||
handle = match command_parts.get(1) {
|
||||
Some(part) => OsString::from(part),
|
||||
None => {
|
||||
return Err(
|
||||
format!("{}: No file name.", command)
|
||||
mars
commented
Same kind of deal here; what if there is more than one argument? Same kind of deal here; what if there is more than one argument?
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
self.write_buffer(handle).map_err(|err| {
|
||||
format!("{}", err)
|
||||
})?;
|
||||
},
|
||||
command => {
|
||||
return Err(
|
||||
format!("{}: Unrecognized command.", command)
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -370,23 +420,34 @@ fn screen_main(stdout: &mut Stdout, mut state: State) -> Result<()> {
|
|||
fn main() -> Result<()> {
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
let mut buf = Vec::new();
|
||||
let file_name: Option<OsString>;
|
||||
|
||||
match argv.get(1).map(|s| s.as_str()) {
|
||||
Some("-") | None => {
|
||||
file_name = None;
|
||||
let stdin = 0; // get stdin as a file descriptor
|
||||
if unsafe { libc::isatty(stdin) } == 0 {
|
||||
unsafe { File::from_raw_fd(stdin) }
|
||||
} else {
|
||||
File::open("/dev/null").unwrap()
|
||||
}
|
||||
}
|
||||
Some(path) => std::fs::File::open(path).unwrap_or_else(|_| {
|
||||
eprintln!("{}: {}: No such file or directory.", argv[0], argv[1]);
|
||||
exit(EX_UNAVAILABLE);
|
||||
}),
|
||||
}
|
||||
.read_to_end(&mut buf)
|
||||
.unwrap();
|
||||
} else { File::open("/dev/null").unwrap() }
|
||||
},
|
||||
Some(path) => {
|
||||
let input_file = Path::new(path);
|
||||
file_name = Some(OsString::from(
|
||||
input_file.clone().display().to_string()
|
||||
));
|
||||
|
||||
File::open(input_file).unwrap_or_else(|_| {
|
||||
let mut err = String::new();
|
||||
if !input_file.exists() {
|
||||
err = "No such file or directory.".to_string();
|
||||
} else if input_file.is_dir() {
|
||||
err = "Is a directory.".to_string();
|
||||
}
|
||||
eprintln!("{}: {}: {}", argv[0], path, err);
|
||||
exit(EX_UNAVAILABLE);
|
||||
})
|
||||
},
|
||||
}.read_to_end(&mut buf).unwrap();
|
||||
|
||||
let text = String::from_utf8(buf).unwrap_or_else(|_| {
|
||||
eprintln!(
|
||||
|
@ -396,7 +457,7 @@ fn main() -> Result<()> {
|
|||
exit(EX_DATAERR);
|
||||
});
|
||||
|
||||
let state = State::from_str(&text)?;
|
||||
let state = State::from_str(file_name, &text)?;
|
||||
let mut stdout = stdout();
|
||||
terminal::enable_raw_mode()?;
|
||||
stdout.execute(terminal::EnterAlternateScreen)?;
|
||||
|
|
Loading…
Reference in New Issue
I don't think that splitting up commands into chars before processing like this is the right approach. For example,
q!
would be treated as two separate commands despite it performing a different operation thanq
.I think if we were going to add commands like
q!
orwq
than those should be matched on the corresponding branches. Makingwrite_buffer()
as a separate function definitely makes doing that easier, so that was the right call.I was planning on making the Iterator peekable, so we could do:
or more likely
match
ing the!
, but idk if that would be good or notI think it's much more straight-forward just to match the strings.
Probably. I’ll try that out