forked from mars/breed
Merge pull request 'basic file handling' (#7) from emma/breed:main into main
Reviewed-on: mars/breed#7
This commit is contained in:
commit
aa013fc422
|
@ -33,7 +33,7 @@ use crate::{Cursor, Direction};
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Buffer {
|
pub struct Buffer {
|
||||||
styles: Arc<Mutex<StyleStore>>,
|
styles: Arc<Mutex<StyleStore>>,
|
||||||
text: Rope,
|
pub text: Rope,
|
||||||
syntax_set: Arc<SyntaxSet>,
|
syntax_set: Arc<SyntaxSet>,
|
||||||
parser: ParseState,
|
parser: ParseState,
|
||||||
styled: Vec<Vec<(Style, Range<usize>)>>,
|
styled: Vec<Vec<(Style, Range<usize>)>>,
|
||||||
|
|
105
src/main.rs
105
src/main.rs
|
@ -19,9 +19,16 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
env::args,
|
env::args,
|
||||||
fs::File,
|
ffi::OsString,
|
||||||
io::{stdout, Read, Stdout, Write},
|
fs::{ File, OpenOptions },
|
||||||
|
io::{
|
||||||
|
Read,
|
||||||
|
stdout,
|
||||||
|
Stdout,
|
||||||
|
Write,
|
||||||
|
},
|
||||||
os::fd::FromRawFd,
|
os::fd::FromRawFd,
|
||||||
|
path::{ Path },
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,14 +106,15 @@ struct State {
|
||||||
pub styles: Arc<Mutex<StyleStore>>,
|
pub styles: Arc<Mutex<StyleStore>>,
|
||||||
pub buffer: Buffer,
|
pub buffer: Buffer,
|
||||||
pub cursor: Cursor,
|
pub cursor: Cursor,
|
||||||
|
pub file: Option<OsString>,
|
||||||
|
pub mode: Mode,
|
||||||
pub scroll: Cursor,
|
pub scroll: Cursor,
|
||||||
pub size: (usize, usize),
|
pub size: (usize, usize),
|
||||||
pub mode: Mode,
|
|
||||||
pub quit: bool,
|
pub quit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
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 styles = Arc::new(Mutex::new(StyleStore::default()));
|
||||||
let buffer = Buffer::from_str(styles.clone(), text);
|
let buffer = Buffer::from_str(styles.clone(), text);
|
||||||
let (cols, rows) = terminal::size()?;
|
let (cols, rows) = terminal::size()?;
|
||||||
|
@ -115,9 +123,10 @@ impl State {
|
||||||
styles,
|
styles,
|
||||||
buffer,
|
buffer,
|
||||||
cursor: Cursor::default(),
|
cursor: Cursor::default(),
|
||||||
|
file: file_name,
|
||||||
|
mode: Mode::default(),
|
||||||
scroll: Cursor::default(),
|
scroll: Cursor::default(),
|
||||||
size: (cols as usize, rows as usize),
|
size: (cols as usize, rows as usize),
|
||||||
mode: Mode::default(),
|
|
||||||
quit: false,
|
quit: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -331,12 +340,53 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_command(&mut self, command: &str) -> std::result::Result<(), String> {
|
fn write_buffer(&mut self, file: OsString) -> Result<()> {
|
||||||
match command {
|
let out = self.buffer.text.bytes().collect::<Vec<u8>>();
|
||||||
"q" => self.quit = true,
|
let mut handle = OpenOptions::new().write(true).open(file)?;
|
||||||
command => return Err(format!("unrecognized command {:?}", command)),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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() {
|
||||||
|
handle = part;
|
||||||
|
} else {
|
||||||
|
handle = match command_parts.get(1) {
|
||||||
|
Some(part) => OsString::from(part),
|
||||||
|
None => {
|
||||||
|
return Err(
|
||||||
|
format!("{}: No file name.", command)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_buffer(handle).map_err(|err| {
|
||||||
|
format!("{}", err)
|
||||||
|
})?;
|
||||||
|
},
|
||||||
|
command => {
|
||||||
|
return Err(
|
||||||
|
format!("{}: Unrecognized command.", command)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,23 +420,34 @@ fn screen_main(stdout: &mut Stdout, mut state: State) -> Result<()> {
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let argv = args().collect::<Vec<String>>();
|
let argv = args().collect::<Vec<String>>();
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
|
let file_name: Option<OsString>;
|
||||||
|
|
||||||
match argv.get(1).map(|s| s.as_str()) {
|
match argv.get(1).map(|s| s.as_str()) {
|
||||||
Some("-") | None => {
|
Some("-") | None => {
|
||||||
|
file_name = None;
|
||||||
let stdin = 0; // get stdin as a file descriptor
|
let stdin = 0; // get stdin as a file descriptor
|
||||||
if unsafe { libc::isatty(stdin) } == 0 {
|
if unsafe { libc::isatty(stdin) } == 0 {
|
||||||
unsafe { File::from_raw_fd(stdin) }
|
unsafe { File::from_raw_fd(stdin) }
|
||||||
} else {
|
} else { File::open("/dev/null").unwrap() }
|
||||||
File::open("/dev/null").unwrap()
|
},
|
||||||
}
|
Some(path) => {
|
||||||
}
|
let input_file = Path::new(path);
|
||||||
Some(path) => std::fs::File::open(path).unwrap_or_else(|_| {
|
file_name = Some(OsString::from(
|
||||||
eprintln!("{}: {}: No such file or directory.", argv[0], argv[1]);
|
input_file.clone().display().to_string()
|
||||||
exit(EX_UNAVAILABLE);
|
));
|
||||||
}),
|
|
||||||
}
|
File::open(input_file).unwrap_or_else(|_| {
|
||||||
.read_to_end(&mut buf)
|
let mut err = String::new();
|
||||||
.unwrap();
|
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(|_| {
|
let text = String::from_utf8(buf).unwrap_or_else(|_| {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
@ -396,7 +457,7 @@ fn main() -> Result<()> {
|
||||||
exit(EX_DATAERR);
|
exit(EX_DATAERR);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = State::from_str(&text)?;
|
let state = State::from_str(file_name, &text)?;
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
terminal::enable_raw_mode()?;
|
terminal::enable_raw_mode()?;
|
||||||
stdout.execute(terminal::EnterAlternateScreen)?;
|
stdout.execute(terminal::EnterAlternateScreen)?;
|
||||||
|
|
Loading…
Reference in New Issue