complition
This commit is contained in:
+179
-3
@@ -1,5 +1,9 @@
|
||||
//! Константы оформления окна и сценариев.
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
/// Ширина окна при запуске.
|
||||
pub const WINDOW_WIDTH: f32 = 900.0;
|
||||
|
||||
@@ -33,11 +37,183 @@ pub const MIN_MAIN_AREA_WIDTH: f32 = 240.0;
|
||||
/// Высота нижней панели при открытии.
|
||||
pub const BOTTOM_PANEL_HEIGHT: f32 = 160.0;
|
||||
|
||||
/// Каталог, в котором приложение ищет Lua-сценарии.
|
||||
/// Каталог, в котором приложение ищет Lua-сценарии по умолчанию.
|
||||
///
|
||||
/// По умолчанию это текущий рабочий каталог процесса — именно там приложение
|
||||
/// ожидает `init.lua`.
|
||||
/// Это текущий рабочий каталог процесса — именно там приложение ожидает
|
||||
/// `init.lua`, если не передан аргумент `--config`. Конкретный каталог всегда
|
||||
/// возвращает [`script_dir`].
|
||||
pub const SCRIPT_DIR: &str = ".";
|
||||
|
||||
/// Каталог сценариев, заданный через `--config`.
|
||||
static SCRIPT_DIR_OVERRIDE: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
/// Файл, который нужно открыть при старте (первый позиционный аргумент).
|
||||
static STARTUP_FILE: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
/// Разобранные аргументы командной строки.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct CommandLine {
|
||||
/// Каталог, из которого брать `init.lua`.
|
||||
pub config_dir: Option<PathBuf>,
|
||||
/// Файл, открываемый при старте как `vim file.go`.
|
||||
pub file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Каталог, из которого загружаются `init.lua`, `include` и `require`.
|
||||
///
|
||||
/// Возвращает значение `--config`, если оно было задано, иначе текущий
|
||||
/// рабочий каталог.
|
||||
pub fn script_dir() -> &'static Path {
|
||||
SCRIPT_DIR_OVERRIDE
|
||||
.get()
|
||||
.map_or_else(|| Path::new(SCRIPT_DIR), PathBuf::as_path)
|
||||
}
|
||||
|
||||
/// Файл, который надо открыть при старте редактора.
|
||||
pub fn startup_file() -> Option<&'static Path> {
|
||||
STARTUP_FILE.get().map(PathBuf::as_path)
|
||||
}
|
||||
|
||||
/// Запоминает каталог сценариев из аргумента `--config`.
|
||||
///
|
||||
/// Путь канонизируется, поэтому относительный `--config ./scripts` работает
|
||||
/// относительно каталога запуска, а `include`/`require` видят абсолютные пути.
|
||||
pub fn set_script_dir(path: PathBuf) -> Result<(), String> {
|
||||
let canonical = std::fs::canonicalize(&path).map_err(|err| {
|
||||
format!(
|
||||
"не удалось открыть каталог конфигурации {}: {err}",
|
||||
path.display()
|
||||
)
|
||||
})?;
|
||||
if !canonical.is_dir() {
|
||||
return Err(format!(
|
||||
"--config: {} не является каталогом",
|
||||
canonical.display()
|
||||
));
|
||||
}
|
||||
|
||||
SCRIPT_DIR_OVERRIDE
|
||||
.set(canonical)
|
||||
.map_err(|_| "каталог конфигурации уже задан".to_owned())
|
||||
}
|
||||
|
||||
/// Запоминает файл, открываемый при старте.
|
||||
pub fn set_startup_file(path: PathBuf) -> Result<(), String> {
|
||||
STARTUP_FILE
|
||||
.set(path)
|
||||
.map_err(|_| "файл для открытия уже задан".to_owned())
|
||||
}
|
||||
|
||||
/// Разбирает аргументы командной строки.
|
||||
///
|
||||
/// Поддерживаются:
|
||||
/// * `файл` — первый позиционный аргумент, как в `vim file.go`;
|
||||
/// * `--config <каталог>` и `--config=<каталог>`;
|
||||
/// * `--` — всё после него считается позиционным (для имён, начинающихся с `-`).
|
||||
///
|
||||
/// Неизвестные флаги игнорируются, чтобы запуск не ломался от лишних
|
||||
/// аргументов окружения/обёрток.
|
||||
pub fn parse_command_line<I>(args: I) -> Result<CommandLine, String>
|
||||
where
|
||||
I: IntoIterator<Item = OsString>,
|
||||
{
|
||||
let mut command_line = CommandLine::default();
|
||||
let mut args = args.into_iter();
|
||||
let mut positional_only = false;
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
let text = arg.to_string_lossy();
|
||||
|
||||
if positional_only {
|
||||
if command_line.file.is_none() {
|
||||
command_line.file = Some(PathBuf::from(arg));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if text == "--" {
|
||||
positional_only = true;
|
||||
} else if text == "--config" {
|
||||
let value = args
|
||||
.next()
|
||||
.ok_or_else(|| "--config требует путь к каталогу".to_owned())?;
|
||||
command_line.config_dir = Some(PathBuf::from(value));
|
||||
} else if let Some(value) = text.strip_prefix("--config=") {
|
||||
if value.is_empty() {
|
||||
return Err("--config требует путь к каталогу".to_owned());
|
||||
}
|
||||
command_line.config_dir = Some(PathBuf::from(value));
|
||||
} else if !text.starts_with('-') && command_line.file.is_none() {
|
||||
command_line.file = Some(PathBuf::from(arg));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(command_line)
|
||||
}
|
||||
|
||||
/// Имя стартового сценария, который выполняется при запуске приложения.
|
||||
pub const STARTUP_SCRIPT: &str = "init.lua";
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn args(values: &[&str]) -> Vec<OsString> {
|
||||
values.iter().map(OsString::from).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_dir_is_read_from_both_argument_forms() {
|
||||
assert_eq!(
|
||||
parse_command_line(args(&["--config", "/tmp/project"]))
|
||||
.unwrap()
|
||||
.config_dir,
|
||||
Some(PathBuf::from("/tmp/project"))
|
||||
);
|
||||
assert_eq!(
|
||||
parse_command_line(args(&["--config=/tmp/project"]))
|
||||
.unwrap()
|
||||
.config_dir,
|
||||
Some(PathBuf::from("/tmp/project"))
|
||||
);
|
||||
assert!(parse_command_line(args(&[])).unwrap().config_dir.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_dir_requires_a_value() {
|
||||
assert!(parse_command_line(args(&["--config"])).is_err());
|
||||
assert!(parse_command_line(args(&["--config="])).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_arguments_do_not_hide_the_config_dir() {
|
||||
let parsed = parse_command_line(args(&["--verbose", "--config", "cfg"])).unwrap();
|
||||
assert_eq!(parsed.config_dir, Some(PathBuf::from("cfg")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_positional_argument_is_the_file_to_open() {
|
||||
let parsed = parse_command_line(args(&["main.go"])).unwrap();
|
||||
assert_eq!(parsed.file, Some(PathBuf::from("main.go")));
|
||||
|
||||
let parsed = parse_command_line(args(&["--config", "cfg", "main.go"])).unwrap();
|
||||
assert_eq!(parsed.config_dir, Some(PathBuf::from("cfg")));
|
||||
assert_eq!(parsed.file, Some(PathBuf::from("main.go")));
|
||||
|
||||
let parsed = parse_command_line(args(&["main.go", "--config=cfg"])).unwrap();
|
||||
assert_eq!(parsed.config_dir, Some(PathBuf::from("cfg")));
|
||||
assert_eq!(parsed.file, Some(PathBuf::from("main.go")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_dash_allows_file_names_starting_with_dash() {
|
||||
let parsed = parse_command_line(args(&["--", "-weird.go"])).unwrap();
|
||||
assert_eq!(parsed.file, Some(PathBuf::from("-weird.go")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_the_first_positional_argument_is_opened() {
|
||||
let parsed = parse_command_line(args(&["first.go", "second.go"])).unwrap();
|
||||
assert_eq!(parsed.file, Some(PathBuf::from("first.go")));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user