Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package hoard for openSUSE:Factory checked in at 2022-12-22 16:22:45 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/hoard (Old) and /work/SRC/openSUSE:Factory/.hoard.new.1835 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "hoard" Thu Dec 22 16:22:45 2022 rev:2 rq:1044110 version:1.3.0~0 Changes: -------- --- /work/SRC/openSUSE:Factory/hoard/hoard.changes 2022-12-07 17:37:35.281400871 +0100 +++ /work/SRC/openSUSE:Factory/.hoard.new.1835/hoard.changes 2022-12-22 16:22:47.837933880 +0100 @@ -1,0 +2,12 @@ +Wed Dec 21 18:31:33 UTC 2022 - Michael Vetter <mvetter@suse.com> + +- Update to 1.3.0: + * Inline command editing in the GUI. Press to get started. + Only editing the command, its description and the tags are + supported for now + * Inline command deletion in the GUI. Press to delete a command + in the GUI view + * Inline command creation in the GUI. Press to create a new + command in the GUI view + +------------------------------------------------------------------- Old: ---- hoard-1.2.0~0.tar.xz New: ---- hoard-1.3.0~0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ hoard.spec ++++++ --- /var/tmp/diff_new_pack.h2fAdA/_old 2022-12-22 16:22:52.997963559 +0100 +++ /var/tmp/diff_new_pack.h2fAdA/_new 2022-12-22 16:22:53.005963605 +0100 @@ -17,7 +17,7 @@ Name: hoard -Version: 1.2.0~0 +Version: 1.3.0~0 Release: 0 Summary: CLI command organizer License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.h2fAdA/_old 2022-12-22 16:22:53.049963857 +0100 +++ /var/tmp/diff_new_pack.h2fAdA/_new 2022-12-22 16:22:53.053963881 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/Hyde46/hoard.git</param> <param name="versionformat">@PARENT_TAG@~@TAG_OFFSET@</param> <param name="scm">git</param> - <param name="revision">v1.2.0</param> + <param name="revision">v1.3.0</param> <param name="match-tag">*</param> <param name="versionrewrite-pattern">v(\d+\.\d+\.\d+)</param> <param name="versionrewrite-replacement">\1</param> @@ -20,6 +20,9 @@ <param name="compression">xz</param> <param name="update">true</param> </service> + <service name="cargo_audit" mode="disabled"> + <param name="srcdir">hoard</param> + </service> </services> ++++++ hoard-1.2.0~0.tar.xz -> hoard-1.3.0~0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/CHANGES.md new/hoard-1.3.0~0/CHANGES.md --- old/hoard-1.2.0~0/CHANGES.md 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/CHANGES.md 2022-12-21 14:06:28.000000000 +0100 @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## 1.3 +- ��� Inline command editing in the GUI. Press `<TAB>` to get started. Only editing the command, its description and the tags are supported for now +- ��� Inline command deletion in the GUI. Press `<Ctrl-X>` to delete a command in the GUI view +- ��� Inline command creation in the GUI. Press `<Ctrl-W>` to create a new command in the GUI view ## 1.2 - ��� You can now synchronize your commands across multiple terminals! Run `hoard sync --help` to start ## 1.1.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/Cargo.lock new/hoard-1.3.0~0/Cargo.lock --- old/hoard-1.2.0~0/Cargo.lock 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/Cargo.lock 2022-12-21 14:06:28.000000000 +0100 @@ -4,9 +4,9 @@ [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "array_tool" @@ -20,7 +20,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -101,14 +101,14 @@ [[package]] name = "clap" -version = "4.0.17" +version = "4.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06badb543e734a2d6568e19a40af66ed5364360b9226184926f89d229b4b4267" +checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" dependencies = [ - "atty", "bitflags", "clap_derive", "clap_lex", + "is-terminal", "once_cell", "strsim", "termcolor", @@ -116,9 +116,9 @@ [[package]] name = "clap_derive" -version = "4.0.13" +version = "4.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" dependencies = [ "heck", "proc-macro-error", @@ -149,13 +149,13 @@ [[package]] name = "console" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847" +checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" dependencies = [ "encode_unicode 0.3.6", + "lazy_static", "libc", - "once_cell", "terminal_size", "unicode-width", "winapi", @@ -338,6 +338,27 @@ ] [[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] name = "eyre" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -486,8 +507,17 @@ ] [[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] name = "hoard-rs" -version = "1.2.0" +version = "1.3.0" dependencies = [ "anyhow", "array_tool", @@ -623,12 +653,34 @@ ] [[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys 0.42.0", +] + +[[package]] name = "ipnet" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] +name = "is-terminal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys 0.42.0", +] + +[[package]] name = "itoa" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -657,9 +709,9 @@ [[package]] name = "libc" -version = "0.2.126" +version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "linked-hash-map" @@ -668,6 +720,12 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] +name = "linux-raw-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" + +[[package]] name = "lock_api" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -707,7 +765,7 @@ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -753,7 +811,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -849,7 +907,7 @@ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -1050,6 +1108,20 @@ ] [[package]] +name = "rustix" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.42.0", +] + +[[package]] name = "rustversion" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1068,7 +1140,7 @@ checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -1295,18 +1367,18 @@ [[package]] name = "thiserror" -version = "1.0.35" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.35" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -1660,44 +1732,101 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", ] [[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] name = "winreg" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/Cargo.toml new/hoard-1.3.0~0/Cargo.toml --- old/hoard-1.2.0~0/Cargo.toml 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/Cargo.toml 2022-12-21 14:06:28.000000000 +0100 @@ -1,6 +1,6 @@ [package] name = "hoard-rs" -version = "1.2.0" +version = "1.3.0" edition = "2021" readme = "README.md" license = "MIT" @@ -28,7 +28,7 @@ [dependencies] # Command line argument parser -clap = { version = "4.0.7", features = ["derive"] } +clap = { version = "4.0.29", features = ["derive"] } # pretty dialogues in terminal dialoguer = "0.10.2" termion = "1.5.6" @@ -36,7 +36,7 @@ serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.8" serde_json = "1.0" -anyhow = "1.0.65" +anyhow = "1.0.68" dirs = "4.0.0" log = "0.4" eyre = "0.6" @@ -51,7 +51,7 @@ array_tool = "1.0.3" reqwest = { version = "0.11.12", features = ["blocking"] } url = {version="2.3.1"} -console = "0.15.1" +console = "0.15.2" enum-iterator = "1.2.0" base64 = "0.13.1" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/README.md new/hoard-1.3.0~0/README.md --- old/hoard-1.2.0~0/README.md 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/README.md 2022-12-21 14:06:28.000000000 +0100 @@ -92,7 +92,7 @@ If you are running `fish` shell ``` -LATEST_RELEASE=1.2.0 ./install.fish +LATEST_RELEASE=1.3.0 ./install.fish ``` ### Brew on MacOS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/example_troves/git.yml new/hoard-1.3.0~0/example_troves/git.yml --- old/hoard-1.2.0~0/example_troves/git.yml 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/example_troves/git.yml 2022-12-21 14:06:28.000000000 +0100 @@ -1,5 +1,5 @@ --- -version: 1.2.0 +version: 1.3.0 commands: - name: git_status namespace: git diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/install.sh new/hoard-1.3.0~0/install.sh --- old/hoard-1.2.0~0/install.sh 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/install.sh 2022-12-21 14:06:28.000000000 +0100 @@ -42,7 +42,7 @@ __hoard_install_ubuntu(){ echo "Assuming Ubuntu distro. Trying to install .deb package" - ARTIFACT_URL="https://github.com/hyde46/hoard/releases/download/v1.2.0/hoard_1.2.0.deb" + ARTIFACT_URL="https://github.com/hyde46/hoard/releases/download/v1.3.0/hoard_1.3.0.deb" TEMP_DEB="$(mktemp)" && wget -O "$TEMP_DEB" "$ARTIFACT_URL" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/command/hoard_command.rs new/hoard-1.3.0~0/src/command/hoard_command.rs --- old/hoard-1.2.0~0/src/command/hoard_command.rs 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/src/command/hoard_command.rs 2022-12-21 14:06:28.000000000 +0100 @@ -24,6 +24,30 @@ } } + pub fn is_command_valid(c: &str) -> (bool, String) { + if c.is_empty() { + return (false, String::from("Command can't be empty")); + } + (true, String::new()) + } + + pub fn is_name_valid(c: &str) -> (bool, String) { + if c.is_empty() { + return (false, String::from("Name can't be empty")); + } + if c.contains(' ') { + return (false, String::from("Name can't contain whitespaces")); + } + (true, String::new()) + } + + pub fn are_tags_valid(c: &str) -> (bool, String) { + if c.is_empty() { + return (false, String::from("Tags can't be empty")); + } + (true, String::new()) + } + #[allow(dead_code)] pub fn is_complete(&self) -> bool { if self.name.is_empty() @@ -77,14 +101,7 @@ Self { name: self.name, namespace: self.namespace, - tags: Some( - tags.chars() - .filter(|c| !c.is_whitespace()) - .collect::<String>() - .split(',') - .map(std::string::ToString::to_string) - .collect(), - ), + tags: Some(string_to_tags(tags)), command: self.command, description: self.description, } @@ -207,6 +224,15 @@ } } +pub fn string_to_tags(tags: &str) -> Vec<String> { + tags.chars() + .filter(|c| !c.is_whitespace()) + .collect::<String>() + .split(',') + .map(std::string::ToString::to_string) + .collect() +} + pub trait Parameterized { // Check if parameter pointers are present fn is_parameterized(&self, token: &str) -> bool; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/command/trove.rs new/hoard-1.3.0~0/src/command/trove.rs --- old/hoard-1.2.0~0/src/command/trove.rs 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/src/command/trove.rs 2022-12-21 14:06:28.000000000 +0100 @@ -12,7 +12,7 @@ const CARGO_VERSION: &str = env!("CARGO_PKG_VERSION"); #[allow(clippy::module_name_repetitions)] -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Clone, Deserialize)] pub struct CommandTrove { pub version: String, pub commands: Vec<HoardCommand>, @@ -81,7 +81,7 @@ fs::write(path, s).expect("Unable to write config file"); } - fn check_name_collision(&self, command: &HoardCommand) -> Option<HoardCommand> { + pub fn check_name_collision(&self, command: &HoardCommand) -> Option<HoardCommand> { let colliding_commands = self .commands .iter() @@ -176,6 +176,15 @@ ) } + pub fn update_command_by_name(&mut self, command: &HoardCommand) -> &mut Self { + for c in &mut self.commands.iter_mut() { + if c.name == command.name { + *c = command.clone(); + } + } + self + } + pub fn is_empty(&self) -> bool { self.commands.is_empty() } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/gui/commands_gui.rs new/hoard-1.3.0~0/src/gui/commands_gui.rs --- old/hoard-1.2.0~0/src/gui/commands_gui.rs 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/src/gui/commands_gui.rs 2022-12-21 14:06:28.000000000 +0100 @@ -3,11 +3,15 @@ use crate::config::HoardConfig; use crate::gui::event::{Config, Event, Events}; use crate::gui::help::{draw as draw_help, key_handler as key_handler_help}; +use crate::gui::inline_edit::controls::key_handler as key_handler_inline_edit; use crate::gui::list_search::controls::key_handler as key_handler_list_search; use crate::gui::list_search::render::draw as draw_list_search; +use crate::gui::new_command::controls::key_handler as key_handler_create_command; +use crate::gui::new_command::render::draw as draw_new_command_input; use crate::gui::parameter_input::controls::key_handler as key_handler_parameter_input; use crate::gui::parameter_input::render::draw as draw_parameter_input; use eyre::Result; +use std::fmt; use std::io::stdout; use std::time::Duration; use termion::{raw::IntoRawMode, screen::AlternateScreen}; @@ -19,11 +23,35 @@ pub command_list_state: ListState, pub namespace_tab_state: ListState, pub should_exit: bool, + pub should_delete: bool, pub draw_state: DrawState, + pub control_state: ControlState, + pub edit_selection: EditSelection, + pub new_command: Option<HoardCommand>, + pub string_to_edit: String, pub parameter_token: String, pub parameter_ending_token: String, pub selected_command: Option<HoardCommand>, pub provided_parameter_count: u16, + pub error_message: String, +} + +impl State { + pub fn update_string_to_edit(&mut self) -> &mut Self { + let selected_idx = self.command_list_state.selected().unwrap(); + let cloned_selected_command = self.commands.get(selected_idx).unwrap().clone(); + match self.edit_selection { + EditSelection::Name => self.string_to_edit = cloned_selected_command.name, + EditSelection::Tags => self.string_to_edit = cloned_selected_command.tags_as_string(), + EditSelection::Description => { + self.string_to_edit = cloned_selected_command.description.unwrap_or_default(); + } + + EditSelection::Command => self.string_to_edit = cloned_selected_command.command, + EditSelection::Namespace => (), + }; + self + } } #[derive(Debug, Eq, PartialEq)] @@ -31,6 +59,65 @@ Search, ParameterInput, Help, + Create, +} + +#[derive(Debug, Eq, PartialEq)] +pub enum ControlState { + Search, + Edit, +} + +impl fmt::Display for ControlState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Search => write!(f, "Search (<Tab>/<Ctrl-E> to edit)"), + Self::Edit => write!( + f, + "Edit (<Enter> to confirm. <Tab> to switch. <Esc> to abort)" + ), + } + } +} + +#[derive(Debug, Eq, PartialEq)] +pub enum EditSelection { + Name, + Tags, + Description, + Command, + Namespace, +} + +impl fmt::Display for EditSelection { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Name => write!(f, "Name"), + Self::Tags => write!(f, "Tags"), + Self::Description => write!(f, "Description"), + Self::Command => write!(f, "Command"), + Self::Namespace => write!(f, "Namespace"), + } + } +} + +impl EditSelection { + pub const fn next(&self) -> Self { + match self { + Self::Name | Self::Namespace | Self::Command => Self::Tags, + Self::Tags => Self::Description, + Self::Description => Self::Command, + } + } + pub const fn edit_next(&self) -> Self { + match self { + Self::Command => Self::Namespace, + Self::Namespace => Self::Name, + Self::Name => Self::Description, + Self::Description => Self::Tags, + Self::Tags => Self::Command, + } + } } #[allow(clippy::too_many_lines)] @@ -38,19 +125,25 @@ let events = Events::with_config(Config { tick_rate: Duration::from_millis(200), }); - + let trove_clone = trove.clone(); let mut app_state = State { input: String::new(), - commands: trove.commands.clone(), + commands: trove_clone.commands.clone(), command_list_state: ListState::default(), namespace_tab_state: ListState::default(), should_exit: false, + should_delete: false, draw_state: DrawState::Search, + control_state: ControlState::Search, + edit_selection: EditSelection::Command, + new_command: None, + string_to_edit: String::new(), parameter_token: config.parameter_token.as_ref().unwrap().clone(), parameter_ending_token: config.parameter_ending_token.as_ref().unwrap().clone(), selected_command: None, provided_parameter_count: 0, + error_message: String::new(), }; app_state.command_list_state.select(Some(0)); @@ -63,7 +156,7 @@ terminal.clear()?; //let menu_titles = vec!["List", "Search", "Add", "Delete", "Quit"]; - let mut namespace_tabs: Vec<&str> = trove.namespaces(); + let mut namespace_tabs: Vec<&str> = trove_clone.namespaces(); namespace_tabs.insert(0, "All"); loop { // Draw GUI @@ -77,20 +170,53 @@ DrawState::Help => { draw_help(config, &mut terminal)?; } + DrawState::Create => { + draw_new_command_input( + &mut app_state, + config, + &mut terminal, + &config.default_namespace, + )?; + } } if let Event::Input(input) = events.next()? { let command = match app_state.draw_state { - DrawState::Search => { - key_handler_list_search(input, &mut app_state, &trove.commands, &namespace_tabs) - } + DrawState::Search => match app_state.control_state { + ControlState::Search => key_handler_list_search( + input, + &mut app_state, + &trove.commands, + &namespace_tabs, + ), + ControlState::Edit => key_handler_inline_edit(input, &mut app_state), + }, DrawState::ParameterInput => key_handler_parameter_input(input, &mut app_state), DrawState::Help => key_handler_help(input, &mut app_state), + DrawState::Create => { + key_handler_create_command(input, &mut app_state, &config.default_namespace) + } }; if let Some(output) = command { - terminal.show_cursor()?; - return Ok(Some(output)); + if app_state.draw_state == DrawState::Create { + trove.add_command(output); + app_state.commands = trove.commands.clone(); + app_state.draw_state = DrawState::Search; + } else if app_state.control_state == ControlState::Edit { + // Command has been edited + trove.update_command_by_name(&output); + app_state.commands = trove.commands.clone(); + app_state.control_state = ControlState::Search; + } else if app_state.should_delete { + trove.remove_command(&output.name).ok(); + app_state.commands = trove.commands.clone(); + app_state.should_delete = false; + } else { + // Command has been selected + terminal.show_cursor()?; + return Ok(Some(output)); + } } if app_state.should_exit { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/gui/help.rs new/hoard-1.3.0~0/src/gui/help.rs --- old/hoard-1.2.0~0/src/gui/help.rs 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/src/gui/help.rs 2022-12-21 14:06:28.000000000 +0100 @@ -19,6 +19,11 @@ ("Next namespace tab", "<Ctrl-L> / <Right-Arrow>"), ("Previous namespace tab", "<Ctrl-H> / <Left-Arrow>"), ("Select command", "<Enter>"), + ("Create new command", "<Ctrl-W>"), + ("Delete command", "<Ctrl-X>"), + ("Toggle search/edit mode", "<Tab> / <Ctrl-E>"), + ("Toggle Command to edit in edit mode", "<Tab>"), + ("Exit edit mode", "<Esc>"), ("Quit", "<Ctrl-D> / <Ctrl-C> / <Ctrl-G>"), ("Show help", HELP_KEY), ("Close help", "<Any key>"), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/gui/inline_edit/controls.rs new/hoard-1.3.0~0/src/gui/inline_edit/controls.rs --- old/hoard-1.2.0~0/src/gui/inline_edit/controls.rs 1970-01-01 01:00:00.000000000 +0100 +++ new/hoard-1.3.0~0/src/gui/inline_edit/controls.rs 2022-12-21 14:06:28.000000000 +0100 @@ -0,0 +1,45 @@ +use crate::command::hoard_command::{string_to_tags, HoardCommand}; +use crate::gui::commands_gui::{ControlState, EditSelection, State}; +use termion::event::Key; + +pub fn key_handler(input: Key, state: &mut State) -> Option<HoardCommand> { + match input { + // Quit command + Key::Esc => { + // Only exit the edit mode + state.control_state = ControlState::Search; + None + } + Key::Char('\n') => { + let mut edited_command = state.selected_command.clone().unwrap(); + let new_string = state.string_to_edit.clone(); + match state.edit_selection { + EditSelection::Description => edited_command.description = Some(new_string), + EditSelection::Command => edited_command.command = new_string, + EditSelection::Tags => edited_command.tags = Some(string_to_tags(&new_string)), + EditSelection::Name | EditSelection::Namespace => (), + }; + Some(edited_command) + } + Key::Char('\t') => { + state.edit_selection = state.edit_selection.next(); + state.update_string_to_edit(); + None + } + Key::Ctrl('c' | 'd' | 'g') => { + // Definitely exit program + state.should_exit = true; + None + } + // Handle query input + Key::Backspace => { + state.string_to_edit.pop(); + None + } + Key::Char(c) => { + state.string_to_edit.push(c); + None + } + _ => None, + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/gui/inline_edit/mod.rs new/hoard-1.3.0~0/src/gui/inline_edit/mod.rs --- old/hoard-1.2.0~0/src/gui/inline_edit/mod.rs 1970-01-01 01:00:00.000000000 +0100 +++ new/hoard-1.3.0~0/src/gui/inline_edit/mod.rs 2022-12-21 14:06:28.000000000 +0100 @@ -0,0 +1 @@ +pub mod controls; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/gui/list_search/controls.rs new/hoard-1.3.0~0/src/gui/list_search/controls.rs --- old/hoard-1.2.0~0/src/gui/list_search/controls.rs 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/src/gui/list_search/controls.rs 2022-12-21 14:06:28.000000000 +0100 @@ -1,7 +1,8 @@ use crate::command::hoard_command::{HoardCommand, Parameterized}; -use crate::gui::commands_gui::{DrawState, State}; +use crate::gui::commands_gui::{ControlState, DrawState, EditSelection, State}; use termion::event::Key; +#[allow(clippy::too_many_lines)] pub fn key_handler( input: Key, state: &mut State, @@ -9,8 +10,9 @@ namespace_tabs: &[&str], ) -> Option<HoardCommand> { match input { - // Quit command Key::Esc | Key::Ctrl('c' | 'd' | 'g') => { + // Definitely exit program + state.control_state = ControlState::Search; state.should_exit = true; None } @@ -19,6 +21,31 @@ state.draw_state = DrawState::Help; None } + // Show help + Key::Ctrl('w') => { + state.draw_state = DrawState::Create; + state.edit_selection = EditSelection::Command; + state.new_command = Some(HoardCommand::default()); + None + } + // Switch to edit command mode + Key::Ctrl('e') | Key::Char('\t') => { + let selected_command = state + .commands + .clone() + .get( + state + .command_list_state + .selected() + .expect("there is always a selected command"), + ) + .expect("exists") + .clone(); + state.control_state = ControlState::Edit; + state.selected_command = Some(selected_command); + state.update_string_to_edit(); + None + } // Switch namespace Key::Left | Key::Ctrl('h') => { if let Some(selected) = state.namespace_tab_state.selected() { @@ -53,6 +80,24 @@ } None } + Key::Ctrl('x') => { + if state.commands.is_empty() { + return None; + } + let selected_command = state + .commands + .clone() + .get( + state + .command_list_state + .selected() + .expect("there is always a selected command"), + ) + .expect("exists") + .clone(); + state.should_delete = true; + Some(selected_command) + } // Select command Key::Char('\n') => { if state.commands.is_empty() { @@ -193,11 +238,17 @@ command_list_state: ListState::default(), namespace_tab_state: ListState::default(), should_exit: false, + should_delete: false, draw_state: DrawState::Search, + control_state: ControlState::Search, + new_command: None, + edit_selection: crate::gui::commands_gui::EditSelection::Command, + string_to_edit: String::new(), parameter_token: "#".to_string(), parameter_ending_token: "!".to_string(), selected_command: None, provided_parameter_count: 0, + error_message: String::new(), }; state.command_list_state.select(Some(0)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/gui/list_search/render.rs new/hoard-1.3.0~0/src/gui/list_search/render.rs --- old/hoard-1.2.0~0/src/gui/list_search/render.rs 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/src/gui/list_search/render.rs 2022-12-21 14:06:28.000000000 +0100 @@ -1,6 +1,7 @@ use crate::command::hoard_command::HoardCommand; use crate::config::HoardConfig; use crate::gui::commands_gui::State; +use crate::gui::commands_gui::{ControlState, EditSelection}; use crate::gui::help::HELP_KEY; use termion::screen::AlternateScreen; use tui::backend::TermionBackend; @@ -12,6 +13,7 @@ const VERSION: &str = env!("CARGO_PKG_VERSION"); +#[allow(clippy::too_many_lines)] pub fn draw( app_state: &mut State, config: &HoardConfig, @@ -102,19 +104,80 @@ rect.render_widget(command, command_detail_chunks[2]); rect.render_widget(input, chunks[2]); - let help_hint = Paragraph::new(format!("Show help - {HELP_KEY} ")) + let (footer_left, footer_right) = get_footer_constraints(&app_state.control_state); + let footer_chunk = Layout::default() + .direction(Direction::Horizontal) + .margin(0) + .constraints([ + Constraint::Percentage(footer_left), + Constraint::Percentage(footer_right), + ]) + .split(chunks[3]); + + let control_state_str = &app_state.control_state; + let help_hint_l = Paragraph::new(format!("{control_state_str}")) .style(Style::default().fg(Color::Rgb( config.primary_color.unwrap().0, config.primary_color.unwrap().1, config.primary_color.unwrap().2, ))) - .alignment(Alignment::Right); + .alignment(Alignment::Left); + let help_hint = Paragraph::new(format!( + "Create <Ctrl-W> | Delete <Ctrl-X> | Help {HELP_KEY}" + )) + .style(Style::default().fg(Color::Rgb( + config.primary_color.unwrap().0, + config.primary_color.unwrap().1, + config.primary_color.unwrap().2, + ))) + .alignment(Alignment::Right); - rect.render_widget(help_hint, chunks[3]); + rect.render_widget(help_hint_l, footer_chunk[0]); + if app_state.control_state == ControlState::Search { + rect.render_widget(help_hint, footer_chunk[1]); + } })?; Ok(()) } +fn get_color( + app: &mut State, + config: &HoardConfig, + command_render: &EditSelection, +) -> tui::style::Color { + let highlighted = Color::Rgb( + config.secondary_color.unwrap().0, + config.secondary_color.unwrap().1, + config.secondary_color.unwrap().2, + ); + let normal = Color::Rgb( + config.primary_color.unwrap().0, + config.primary_color.unwrap().1, + config.primary_color.unwrap().2, + ); + match app.control_state { + ControlState::Search => normal, + ControlState::Edit => { + if command_render == &app.edit_selection { + return highlighted; + } + normal + } + } +} + +fn coerce_string_by_mode(s: String, app: &State, command_render: &EditSelection) -> String { + match app.control_state { + ControlState::Search => s, + ControlState::Edit => { + if command_render == &app.edit_selection { + return app.string_to_edit.clone(); + } + s + } + } +} + #[allow(clippy::too_many_lines)] fn render_commands<'a>( commands_list: &[HoardCommand], @@ -129,11 +192,7 @@ ) { let commands = Block::default() .borders(Borders::ALL) - .style(Style::default().fg(Color::Rgb( - config.primary_color.unwrap().0, - config.primary_color.unwrap().1, - config.primary_color.unwrap().2, - ))) + .style(Style::default().fg(get_color(app, config, &EditSelection::Name))) .title(" Commands ") .border_type(BorderType::Plain); @@ -181,64 +240,64 @@ .add_modifier(Modifier::BOLD), ); - let command = Paragraph::new(selected_command.command.clone()) - .style(Style::default().fg(Color::Rgb( - config.command_color.unwrap().0, - config.command_color.unwrap().1, - config.command_color.unwrap().2, - ))) - .alignment(Alignment::Left) - .wrap(Wrap { trim: true }) - .block( - Block::default() - .borders(Borders::ALL) - .style(Style::default().fg(Color::Rgb( - config.primary_color.unwrap().0, - config.primary_color.unwrap().1, - config.primary_color.unwrap().2, - ))) - .title(" Hoarded command ") - .border_type(BorderType::Plain), - ); + let command = Paragraph::new(coerce_string_by_mode( + selected_command.command.clone(), + app, + &EditSelection::Command, + )) + .style(Style::default().fg(Color::Rgb( + config.primary_color.unwrap().0, + config.primary_color.unwrap().1, + config.primary_color.unwrap().2, + ))) + .alignment(Alignment::Left) + .wrap(Wrap { trim: true }) + .block( + Block::default() + .borders(Borders::ALL) + .style(Style::default().fg(get_color(app, config, &EditSelection::Command))) + .title(" Hoarded command ") + .border_type(BorderType::Plain), + ); - let tags = Paragraph::new(selected_command.tags_as_string()) - .style(Style::default().fg(Color::Rgb( - config.primary_color.unwrap().0, - config.primary_color.unwrap().1, - config.primary_color.unwrap().2, - ))) - .alignment(Alignment::Left) - .block( - Block::default() - .borders(Borders::ALL) - .style(Style::default().fg(Color::Rgb( - config.primary_color.unwrap().0, - config.primary_color.unwrap().1, - config.primary_color.unwrap().2, - ))) - .title(" Tags ") - .border_type(BorderType::Plain), - ); + let tags = Paragraph::new(coerce_string_by_mode( + selected_command.tags_as_string(), + app, + &EditSelection::Tags, + )) + .style(Style::default().fg(Color::Rgb( + config.primary_color.unwrap().0, + config.primary_color.unwrap().1, + config.primary_color.unwrap().2, + ))) + .alignment(Alignment::Left) + .block( + Block::default() + .borders(Borders::ALL) + .style(Style::default().fg(get_color(app, config, &EditSelection::Tags))) + .title(" Tags ") + .border_type(BorderType::Plain), + ); - let description = Paragraph::new(selected_command.description.unwrap_or_default()) - .style(Style::default().fg(Color::Rgb( - config.primary_color.unwrap().0, - config.primary_color.unwrap().1, - config.primary_color.unwrap().2, - ))) - .alignment(Alignment::Left) - .wrap(Wrap { trim: true }) - .block( - Block::default() - .borders(Borders::ALL) - .style(Style::default().fg(Color::Rgb( - config.primary_color.unwrap().0, - config.primary_color.unwrap().1, - config.primary_color.unwrap().2, - ))) - .title(" Description ") - .border_type(BorderType::Plain), - ); + let description = Paragraph::new(coerce_string_by_mode( + selected_command.description.unwrap_or_default(), + app, + &EditSelection::Description, + )) + .style(Style::default().fg(Color::Rgb( + config.primary_color.unwrap().0, + config.primary_color.unwrap().1, + config.primary_color.unwrap().2, + ))) + .alignment(Alignment::Left) + .wrap(Wrap { trim: true }) + .block( + Block::default() + .borders(Borders::ALL) + .style(Style::default().fg(get_color(app, config, &EditSelection::Description))) + .title(" Description ") + .border_type(BorderType::Plain), + ); let mut query_string = config.query_prefix.clone(); query_string.push_str(&app.input.clone()[..]); @@ -256,3 +315,10 @@ (list, command, tags, description, input) } + +const fn get_footer_constraints(control_state: &ControlState) -> (u16, u16) { + match control_state { + ControlState::Search => (50, 50), + ControlState::Edit => (99, 1), + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/gui/mod.rs new/hoard-1.3.0~0/src/gui/mod.rs --- old/hoard-1.2.0~0/src/gui/mod.rs 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/src/gui/mod.rs 2022-12-21 14:06:28.000000000 +0100 @@ -3,8 +3,10 @@ pub mod event; #[allow(clippy::module_name_repetitions)] mod help; +mod inline_edit; mod list_search; pub mod merge; +mod new_command; mod parameter_input; pub mod prompts; pub mod theme; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/gui/new_command/controls.rs new/hoard-1.3.0~0/src/gui/new_command/controls.rs --- old/hoard-1.2.0~0/src/gui/new_command/controls.rs 1970-01-01 01:00:00.000000000 +0100 +++ new/hoard-1.3.0~0/src/gui/new_command/controls.rs 2022-12-21 14:06:28.000000000 +0100 @@ -0,0 +1,87 @@ +use crate::command::hoard_command::{string_to_tags, HoardCommand}; +use crate::command::trove::CommandTrove; +use crate::gui::commands_gui::{DrawState, EditSelection, State}; +use termion::event::Key; + +pub fn key_handler(input: Key, app: &mut State, default_namespace: &str) -> Option<HoardCommand> { + // Make sure there is an empty command set + if app.new_command.is_none() { + app.new_command = Some(HoardCommand::default()); + } + match input { + Key::Esc => { + app.draw_state = DrawState::Search; + app.new_command = None; + app.edit_selection = EditSelection::Command; + None + } + // Quit command + Key::Ctrl('c' | 'd' | 'g') => { + app.should_exit = true; + app.new_command = None; + app.edit_selection = EditSelection::Command; + None + } + Key::Char('\n') => { + let mut command = app.new_command.clone().unwrap(); + let parameter = app.input.clone(); + app.error_message = match app.edit_selection { + EditSelection::Command => { + let (_, msg) = HoardCommand::is_command_valid(¶meter); + command.command = parameter; + msg + } + EditSelection::Name => { + let (_, mut msg) = HoardCommand::is_name_valid(¶meter); + command.name = parameter; + let trove = CommandTrove::from_commands(&app.commands); + if trove.check_name_collision(&command).is_some() { + msg = String::from( + "Command with that name already exists in another namespace", + ); + } + msg + } + EditSelection::Namespace => { + if parameter.is_empty() { + command.namespace = default_namespace.into(); + } else { + command.namespace = parameter; + } + String::new() + } + EditSelection::Description => { + command.description = Some(parameter); + String::new() + } + EditSelection::Tags => { + let (is_valid, msg) = HoardCommand::are_tags_valid(¶meter); + if is_valid { + command.tags = Some(string_to_tags(¶meter)); + } + msg + } + }; + app.input = String::new(); + if !app.error_message.is_empty() { + return None; + } + app.edit_selection = app.edit_selection.edit_next(); + if app.edit_selection == EditSelection::Command { + return Some(command); + } + app.new_command = Some(command); + None + } + // Handle query input + Key::Backspace => { + app.input.pop(); + None + } + Key::Char(c) => { + app.input.push(c); + None + } + _ => None, + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/gui/new_command/mod.rs new/hoard-1.3.0~0/src/gui/new_command/mod.rs --- old/hoard-1.2.0~0/src/gui/new_command/mod.rs 1970-01-01 01:00:00.000000000 +0100 +++ new/hoard-1.3.0~0/src/gui/new_command/mod.rs 2022-12-21 14:06:28.000000000 +0100 @@ -0,0 +1,2 @@ +pub mod controls; +pub mod render; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/gui/new_command/render.rs new/hoard-1.3.0~0/src/gui/new_command/render.rs --- old/hoard-1.2.0~0/src/gui/new_command/render.rs 1970-01-01 01:00:00.000000000 +0100 +++ new/hoard-1.3.0~0/src/gui/new_command/render.rs 2022-12-21 14:06:28.000000000 +0100 @@ -0,0 +1,80 @@ +use crate::config::HoardConfig; +use crate::gui::commands_gui::State; +use termion::screen::AlternateScreen; +use tui::backend::TermionBackend; +use tui::layout::{Constraint, Direction, Layout}; +use tui::style::{Color, Style}; +use tui::widgets::{Block, Paragraph}; +use tui::Terminal; + +pub fn draw( + app_state: &mut State, + config: &HoardConfig, + terminal: &mut Terminal< + TermionBackend<AlternateScreen<termion::raw::RawTerminal<std::io::Stdout>>>, + >, + default_namespace: &str, +) -> Result<(), eyre::Error> { + terminal.draw(|rect| { + let size = rect.size(); + // Overlay + let overlay_chunks = Layout::default() + .direction(Direction::Vertical) + .margin(1) + .constraints( + [ + Constraint::Percentage(30), + Constraint::Percentage(30), + Constraint::Percentage(10), + Constraint::Percentage(20), + Constraint::Percentage(10), + ] + .as_ref(), + ) + .split(size); + + let mut query_string = config.query_prefix.clone(); + query_string.push_str(&app_state.input.clone()[..]); + let title_string = format!("Provide {} for the command", app_state.edit_selection); + + let command_style = Style::default().fg(Color::Rgb( + config.command_color.unwrap().0, + config.command_color.unwrap().1, + config.command_color.unwrap().2, + )); + + let primary_style = Style::default().fg(Color::Rgb( + config.primary_color.unwrap().0, + config.primary_color.unwrap().1, + config.primary_color.unwrap().2, + )); + + let input = Paragraph::new(query_string) + .style(primary_style) + .block(Block::default().style(command_style).title(title_string)); + let new_command = app_state.new_command.clone().unwrap(); + let command_render = format!( + "Command: {}\nNamespace: {}(\"{}\" if empty)\nName: {}\nDescription: {}\nTags: {}", + new_command.command, + new_command.namespace, + default_namespace, + new_command.name, + new_command.clone().description.unwrap_or_default(), + new_command.tags_as_string() + ); + let new_command = Paragraph::new(command_render) + .style(primary_style) + .block(Block::default().style(command_style).title("New command:")); + + let error_message = Paragraph::new(app_state.error_message.clone()) + .style(primary_style) + .block(Block::default().style(command_style).title("Error:")); + + rect.render_widget(new_command, overlay_chunks[1]); + rect.render_widget(input, overlay_chunks[2]); + if !app_state.error_message.is_empty() { + rect.render_widget(error_message, overlay_chunks[3]); + } + })?; + Ok(()) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/hoard.rs new/hoard-1.3.0~0/src/hoard.rs --- old/hoard-1.2.0~0/src/hoard.rs 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/src/hoard.rs 2022-12-21 14:06:28.000000000 +0100 @@ -176,6 +176,7 @@ } else { match commands_gui::run(&mut self.trove, self.config.as_ref().unwrap()) { Ok(selected_command) => { + self.save_trove(None); if let Some(c) = selected_command { // Is set if a command is selected in GUI if !c.command.is_empty() { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hoard-1.2.0~0/src/util.rs new/hoard-1.3.0~0/src/util.rs --- old/hoard-1.2.0~0/src/util.rs 2022-11-28 18:38:14.000000000 +0100 +++ new/hoard-1.3.0~0/src/util.rs 2022-12-21 14:06:28.000000000 +0100 @@ -32,37 +32,30 @@ } pub fn split_with_delim(s: &str, delim: &str) -> Vec<String> { - let mut result: Vec<String> = Vec::new(); - let s: String = s.to_string(); - - let matched_indices: Vec<(usize, usize)> = s - .match_indices(delim) - .map(|(i, _)| (i, i + delim.len() - 1)) - .collect(); - let mut split_indices: Vec<(usize, usize)> = Vec::new(); - - // string starts with delimiter - if matched_indices.first().unwrap().0 != 0 { - split_indices.push((0, matched_indices.first().unwrap().0 - 1)); + //Credits to chatGPT for simplifying this wtf + let mut result = Vec::new(); + let mut start = 0; + + for (i, _) in s.match_indices(delim) { + if i > start { + result.push(s[start..i].to_string()); + } + result.push(delim.to_string()); + start = i + delim.len(); } - for (i, indices) in matched_indices.iter().enumerate() { - split_indices.push((indices.0, indices.1)); - if let Some(peeked_index) = matched_indices.get(i + 1) { - split_indices.push((indices.1 + 1, peeked_index.0 - 1)); - } + if start < s.len() { + result.push(s[start..].to_string()); } - // Delimiter is at the end - if matched_indices.last().unwrap().1 != s.len() - 1 { - split_indices.push((matched_indices.last().unwrap().1 + 1, s.len() - 1)); + while result.first().map_or(false, std::string::String::is_empty) { + result.remove(0); } - // What the hell was I thinking - for (i, k) in &split_indices { - let slice = &s[(*i)..=(*k)]; - result.push(slice.to_string()); + while result.last().map_or(false, std::string::String::is_empty) { + result.pop(); } + result } ++++++ vendor.tar.xz ++++++ /work/SRC/openSUSE:Factory/hoard/vendor.tar.xz /work/SRC/openSUSE:Factory/.hoard.new.1835/vendor.tar.xz differ: char 26, line 1