Add edit subcommand. Fix #171
This commit is contained in:
		
							parent
							
								
									a89438f3ba
								
							
						
					
					
						commit
						3e98748c54
					
				| 
						 | 
					@ -38,7 +38,7 @@ pub(crate) mod runtime;
 | 
				
			||||||
const DEFAULT_CONFIG_FILE_CONTENT : &str = include_str!("../res/config.yml");
 | 
					const DEFAULT_CONFIG_FILE_CONTENT : &str = include_str!("../res/config.yml");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const DEFAULT_CONFIG_FILE_NAME : &str = "default.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
 | 
					// Default values for primitives
 | 
				
			||||||
fn default_name() -> String{ "default".to_owned() }
 | 
					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_matches() -> Vec<Match> { Vec::new() }
 | 
				
			||||||
fn default_global_vars() -> Vec<MatchVariable> { 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)]
 | 
					#[derive(Clone, Debug, Serialize, Deserialize)]
 | 
				
			||||||
pub struct Configs {
 | 
					pub struct Configs {
 | 
				
			||||||
#[serde(default = "default_name")]
 | 
					    #[serde(default = "default_name")]
 | 
				
			||||||
    pub name: String,
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[serde(default = "default_parent")]
 | 
					    #[serde(default = "default_parent")]
 | 
				
			||||||
| 
						 | 
					@ -148,6 +155,9 @@ pub struct Configs {
 | 
				
			||||||
    #[serde(default = "default_exclude_default_entries")]
 | 
					    #[serde(default = "default_exclude_default_entries")]
 | 
				
			||||||
    pub exclude_default_entries: bool,
 | 
					    pub exclude_default_entries: bool,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[serde(default = "default_editor")]
 | 
				
			||||||
 | 
					    pub editor: String,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[serde(default = "default_matches")]
 | 
					    #[serde(default = "default_matches")]
 | 
				
			||||||
    pub matches: Vec<Match>,
 | 
					    pub matches: Vec<Match>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										48
									
								
								src/edit.rs
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								src/edit.rs
									
									
									
									
									
								
							| 
						 | 
					@ -18,44 +18,40 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::config::ConfigSet;
 | 
					use crate::config::ConfigSet;
 | 
				
			||||||
 | 
					use std::path::Path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(target_os = "linux")]
 | 
					pub fn open_editor(config: &ConfigSet, file_path: &Path) -> bool {
 | 
				
			||||||
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 {
 | 
					 | 
				
			||||||
    use std::process::Command;
 | 
					    use std::process::Command;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Get the configuration file path
 | 
					    // Check if another editor is defined in the environment variables
 | 
				
			||||||
    let file_path = crate::context::get_config_dir().join(crate::config::DEFAULT_CONFIG_FILE_NAME);
 | 
					    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
 | 
					    // Start the editor and wait for its termination
 | 
				
			||||||
    let status = Command::new("cmd")
 | 
					    let status = Command::new(editor)
 | 
				
			||||||
        .arg("/C")
 | 
					 | 
				
			||||||
        .arg("start")
 | 
					 | 
				
			||||||
        .arg("/wait")
 | 
					 | 
				
			||||||
        .arg("C:\\Windows\\System32\\notepad.exe")
 | 
					 | 
				
			||||||
        .arg(file_path)
 | 
					        .arg(file_path)
 | 
				
			||||||
        .spawn();
 | 
					        .spawn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Ok(mut child) = status {
 | 
					    if let Ok(mut child) = status {
 | 
				
			||||||
        // Wait for the user to edit the configuration
 | 
					        // 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
 | 
					        if let Ok(exit_status) = result {
 | 
				
			||||||
        // after each of them a reload should be issued
 | 
					            exit_status.success()
 | 
				
			||||||
 | 
					 | 
				
			||||||
        println!("Ok");
 | 
					 | 
				
			||||||
        true
 | 
					 | 
				
			||||||
        }else{
 | 
					        }else{
 | 
				
			||||||
        println!("Error: could not start editor.");
 | 
					            false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }else{
 | 
				
			||||||
 | 
					        println!("Error: could not start editor at: {}", config.default.editor);
 | 
				
			||||||
        false
 | 
					        false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										70
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								src/main.rs
									
									
									
									
									
								
							| 
						 | 
					@ -98,7 +98,9 @@ fn main() {
 | 
				
			||||||
                .about("Toggle the status of the espanso replacement engine."))
 | 
					                .about("Toggle the status of the espanso replacement engine."))
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .subcommand(SubCommand::with_name("edit")
 | 
					        .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")
 | 
					        .subcommand(SubCommand::with_name("dump")
 | 
				
			||||||
            .about("Prints all current configuration options."))
 | 
					            .about("Prints all current configuration options."))
 | 
				
			||||||
        .subcommand(SubCommand::with_name("detect")
 | 
					        .subcommand(SubCommand::with_name("detect")
 | 
				
			||||||
| 
						 | 
					@ -166,8 +168,8 @@ fn main() {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if matches.subcommand_matches("edit").is_some() {
 | 
					    if let Some(matches) = matches.subcommand_matches("edit") {
 | 
				
			||||||
        edit_main(config_set);
 | 
					        edit_main(config_set, matches);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -881,8 +883,66 @@ fn path_main(_config_set: ConfigSet, matches: &ArgMatches) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn edit_main(config_set: ConfigSet) {
 | 
					fn edit_main(config_set: ConfigSet, matches: &ArgMatches) {
 | 
				
			||||||
    crate::edit::open_editor(&config_set);
 | 
					    // 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> {
 | 
					fn acquire_lock() -> Option<File> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user