Add edit subcommand. Fix #171

This commit is contained in:
Federico Terzi 2020-02-27 21:56:20 +01:00
parent a89438f3ba
commit 3e98748c54
3 changed files with 99 additions and 33 deletions

View File

@ -38,7 +38,7 @@ pub(crate) mod runtime;
const DEFAULT_CONFIG_FILE_CONTENT : &str = include_str!("../res/config.yml");
pub const DEFAULT_CONFIG_FILE_NAME : &str = "default.yml";
const USER_CONFIGS_FOLDER_NAME: &str = "user";
pub const USER_CONFIGS_FOLDER_NAME: &str = "user";
// Default values for primitives
fn default_name() -> String{ "default".to_owned() }
@ -68,9 +68,16 @@ fn default_exclude_default_entries() -> bool {false}
fn default_matches() -> Vec<Match> { Vec::new() }
fn default_global_vars() -> Vec<MatchVariable> { Vec::new() }
#[cfg(target_os = "linux")]
fn default_editor() -> String{ "/bin/nano".to_owned() }
#[cfg(target_os = "macos")]
fn default_editor() -> String{ "/usr/bin/nano".to_owned() } // TODO: change
#[cfg(target_os = "windows")]
fn default_editor() -> String{ "C:\\Windows\\System32\\notepad.exe".to_owned() }
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Configs {
#[serde(default = "default_name")]
#[serde(default = "default_name")]
pub name: String,
#[serde(default = "default_parent")]
@ -148,6 +155,9 @@ pub struct Configs {
#[serde(default = "default_exclude_default_entries")]
pub exclude_default_entries: bool,
#[serde(default = "default_editor")]
pub editor: String,
#[serde(default = "default_matches")]
pub matches: Vec<Match>,

View File

@ -18,44 +18,40 @@
*/
use crate::config::ConfigSet;
use std::path::Path;
#[cfg(target_os = "linux")]
pub fn open_editor(config: &ConfigSet) -> bool {
// TODO
}
#[cfg(target_os = "macos")]
pub fn open_editor(config: &ConfigSet) -> bool {
// TODO
}
#[cfg(target_os = "windows")]
pub fn open_editor(config: &ConfigSet) -> bool {
pub fn open_editor(config: &ConfigSet, file_path: &Path) -> bool {
use std::process::Command;
// Get the configuration file path
let file_path = crate::context::get_config_dir().join(crate::config::DEFAULT_CONFIG_FILE_NAME);
// Check if another editor is defined in the environment variables
let editor_var = std::env::var_os("EDITOR");
let visual_var = std::env::var_os("VISUAL");
// Prioritize the editors specified by the environment variable, otherwise use the config
let editor : String = if let Some(editor_var) = editor_var {
editor_var.to_string_lossy().to_string()
}else if let Some(visual_var) = visual_var {
visual_var.to_string_lossy().to_string()
}else{
config.default.editor.clone()
};
// Start the editor and wait for its termination
let status = Command::new("cmd")
.arg("/C")
.arg("start")
.arg("/wait")
.arg("C:\\Windows\\System32\\notepad.exe")
let status = Command::new(editor)
.arg(file_path)
.spawn();
if let Ok(mut child) = status {
// Wait for the user to edit the configuration
child.wait();
let result = child.wait();
// TODO: instead of waiting, a file watcher should be started to detect file changes and
// after each of them a reload should be issued
println!("Ok");
true
if let Ok(exit_status) = result {
exit_status.success()
}else{
false
}
}else{
println!("Error: could not start editor.");
println!("Error: could not start editor at: {}", config.default.editor);
false
}
}

View File

@ -98,7 +98,9 @@ fn main() {
.about("Toggle the status of the espanso replacement engine."))
)
.subcommand(SubCommand::with_name("edit")
.about("Open the default text editor to edit config files and reload them automatically when exiting"))
.about("Open the default text editor to edit config files and reload them automatically when exiting")
.arg(Arg::with_name("config")
.help("Defaults to \"default\". The configuration file name to edit (without the .yml extension).")))
.subcommand(SubCommand::with_name("dump")
.about("Prints all current configuration options."))
.subcommand(SubCommand::with_name("detect")
@ -166,8 +168,8 @@ fn main() {
return;
}
if matches.subcommand_matches("edit").is_some() {
edit_main(config_set);
if let Some(matches) = matches.subcommand_matches("edit") {
edit_main(config_set, matches);
return;
}
@ -881,8 +883,66 @@ fn path_main(_config_set: ConfigSet, matches: &ArgMatches) {
}
}
fn edit_main(config_set: ConfigSet) {
crate::edit::open_editor(&config_set);
fn edit_main(config_set: ConfigSet, matches: &ArgMatches) {
// Determine which is the file to edit
let config = matches.value_of("config").unwrap_or("default");
let config_dir = crate::context::get_config_dir();
let config_path = match config {
"default" => {
config_dir.join(crate::config::DEFAULT_CONFIG_FILE_NAME)
},
name => { // Otherwise, search in the user/ config folder
config_dir.join(crate::config::USER_CONFIGS_FOLDER_NAME)
.join(name.to_owned() + ".yml")
}
};
println!("Editing file: {:?}", &config_path);
// Based on the fact that the file already exists or not, we should detect in different
// ways if a reload is needed
let should_reload =if config_path.exists() {
// Get the last modified date, so that we can detect if the user actually edits the file
// before reloading
let metadata = std::fs::metadata(&config_path).expect("cannot gather file metadata");
let last_modified = metadata.modified().expect("cannot read file last modified date");
let result = crate::edit::open_editor(&config_set, &config_path);
if result {
let new_metadata = std::fs::metadata(&config_path).expect("cannot gather file metadata");
let new_last_modified = new_metadata.modified().expect("cannot read file last modified date");
if last_modified != new_last_modified {
println!("File has been modified, reloading configuration");
true
}else{
println!("File has not been modified, avoiding reload");
false
}
}else{
false
}
}else{
let result = crate::edit::open_editor(&config_set, &config_path);
if result {
// If the file has been created, we should reload the espanso config
if config_path.exists() {
println!("A new file has been created, reloading configuration");
true
}else{
println!("No file has been created, avoiding reload");
false
}
}else{
false
}
};
if should_reload {
restart_main(config_set)
}
}
fn acquire_lock() -> Option<File> {