helix/helix-term/src/args.rs
Joe bee05dd32a
Add refresh-config and open-config command (#1803)
* Add refresh-config and open-config command

* clippy

* Use dynamic dispatch for editor config

* Refactor Result::Ok to Ok

* Remove unused import

* cargo fmt

* Modify config error handling

* cargo xtask docgen

* impl display for ConfigLoadError

* cargo fmt

* Put keymaps behind dyn access, refactor config.load()

* Update command names

* Update helix-term/src/application.rs

Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>

* Switch to unbounded_channel

* Remove --edit-config command

* Update configuration docs

* Revert "Put keymaps behind dyn access", too hard

This reverts commit 06bad8cf492b9331d0a2d1e9242f3ad4e2c1cf79.

* Add refresh for keys

* Refactor default_keymaps, fix config default, add test

* swap -> store, remove unneeded clone

* cargo fmt

* Rename default_keymaps to default

Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
2022-03-25 18:05:20 +09:00

101 lines
3.4 KiB
Rust

use anyhow::Result;
use helix_core::Position;
use std::path::{Path, PathBuf};
#[derive(Default)]
pub struct Args {
pub display_help: bool,
pub display_version: bool,
pub health: bool,
pub health_arg: Option<String>,
pub load_tutor: bool,
pub fetch_grammars: bool,
pub build_grammars: bool,
pub verbosity: u64,
pub files: Vec<(PathBuf, Position)>,
}
impl Args {
pub fn parse_args() -> Result<Args> {
let mut args = Args::default();
let mut argv = std::env::args().peekable();
argv.next(); // skip the program, we don't care about that
while let Some(arg) = argv.next() {
match arg.as_str() {
"--" => break, // stop parsing at this point treat the remaining as files
"--version" => args.display_version = true,
"--help" => args.display_help = true,
"--tutor" => args.load_tutor = true,
"--health" => {
args.health = true;
args.health_arg = argv.next_if(|opt| !opt.starts_with('-'));
}
"-g" | "--grammar" => match argv.next().as_deref() {
Some("fetch") => args.fetch_grammars = true,
Some("build") => args.build_grammars = true,
_ => {
anyhow::bail!("--grammar must be followed by either 'fetch' or 'build'")
}
},
arg if arg.starts_with("--") => {
anyhow::bail!("unexpected double dash argument: {}", arg)
}
arg if arg.starts_with('-') => {
let arg = arg.get(1..).unwrap().chars();
for chr in arg {
match chr {
'v' => args.verbosity += 1,
'V' => args.display_version = true,
'h' => args.display_help = true,
_ => anyhow::bail!("unexpected short arg {}", chr),
}
}
}
arg => args.files.push(parse_file(arg)),
}
}
// push the remaining args, if any to the files
for arg in argv {
args.files.push(parse_file(&arg));
}
Ok(args)
}
}
/// Parse arg into [`PathBuf`] and position.
pub(crate) fn parse_file(s: &str) -> (PathBuf, Position) {
let def = || (PathBuf::from(s), Position::default());
if Path::new(s).exists() {
return def();
}
split_path_row_col(s)
.or_else(|| split_path_row(s))
.unwrap_or_else(def)
}
/// Split file.rs:10:2 into [`PathBuf`], row and col.
///
/// Does not validate if file.rs is a file or directory.
fn split_path_row_col(s: &str) -> Option<(PathBuf, Position)> {
let mut s = s.rsplitn(3, ':');
let col: usize = s.next()?.parse().ok()?;
let row: usize = s.next()?.parse().ok()?;
let path = s.next()?.into();
let pos = Position::new(row.saturating_sub(1), col.saturating_sub(1));
Some((path, pos))
}
/// Split file.rs:10 into [`PathBuf`] and row.
///
/// Does not validate if file.rs is a file or directory.
fn split_path_row(s: &str) -> Option<(PathBuf, Position)> {
let (path, row) = s.rsplit_once(':')?;
let row: usize = row.parse().ok()?;
let path = path.into();
let pos = Position::new(row.saturating_sub(1), 0);
Some((path, pos))
}