Skip to content

Commit ff5e89f

Browse files
committed
feat(plz): fully configurable plz menu
BREAKING CHANGE: this PR introduces a bunch of brick changes to both the configuration system and the command-line interface. - Change the `plz` menu system to read the menu entries from the config file - Each entry contains its display string that will displayed in the menu and its operation/args - The `operation` is just a hash map that contains the operation key and its value and the rest of the arguments - Each operation has a specific set of pre-defined arguments that can be used to configure the `operation handler` and change its default behavior. - The operation handler will ignore any argument that doesn't know about - However, all the argument values will be passed to the template engine to be parsed before they are passed to the operation handler, which means that if there's any `template error` in any of the arguments the program will exit even if the argument that have the error doesn't needed by the operation handler - The currently available operations is: - `fetch` | `url`: fetch any page from the web(mainly) by HTTP - `run` | `command`: run command/s - `file`: just display the contents of any local file in the pager - Remove the following CLI options: - `--cheat-sh-url` - `--eg-url` - `--man-cmd` - `--cheat-url` - Introduce the following CLI options: - `-s` | `--selected-position`: used to set the default selected position, the posable values are: - `start`: The first item in the menu - `center`: The middle item in the menu (in case of an even number of items, the first item in the second half) - `end`: The last item in the menu COMMITS LIST: da8bb01 fix: un-use the wildcard pattern and use `as` instead (2 weeks ago) 0fdc42e chore(git): merge main and resolve confilcts (2 weeks ago) 9fd106d docs(readme): update the outdated examples (3 weeks ago) 446efff chore(config): just update the config file (3 weeks ago) a418fbd fix(test): fix the cli::update_config test and fi doc (3 weeks ago) 0187928 feat(file): create the file handler (3 weeks ago) a08101f feat(cli): add the --selected-position option to plz subcommand (3 weeks ago) 6d07d80 refactor: refactor and and lint and re-format the whole proj (3 weeks ago) 4dd0702 feat(command): create the command operation handler (3 weeks ago) 3eb71a6 refactor(plz): improve the operation handleing system (3 weeks ago) 3bb10a6 refactor: remove all the deprecate code and re-format the code (3 weeks ago) 9bfc239 revert(idea): remove the `.idea` directory (3 weeks ago) 1babee0 feat(plz): use the new system (3 weeks ago) f4d845e feat(cmd_parse): take a refrence to the hashmap (3 weeks ago) 0ed22dd feat(fetch): create the fetch handler and stabilize the `Handler` interface (3 weeks ago) 1646c14 feat(parser): cover more cases for the command parser (3 weeks ago) 5b054fb feat(parser): create the command parser (3 weeks ago) 399dfee feat(parser): create the template parser (3 weeks ago) ad409ab feat(plz): make the defualt selected item configrable (4 weeks ago) 218316a feat(plz): improve the cheat.sh url template (4 weeks ago) 08e7867 feat(plz): create the handler trait (4 weeks ago) bba0696 feat(plz): read the plz menu from the config file (4 weeks ago)
1 parent e35374e commit ff5e89f

File tree

19 files changed

+993
-335
lines changed

19 files changed

+993
-335
lines changed

README.md

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,22 @@ halp [OPTIONS] plz <CMD>
235235

236236
```
237237
Options:
238-
-m, --man-cmd <MAN_CMD> Sets the manual page command to run
239-
--cheat-sh-url <URL> Use a custom URL for cheat.sh [env: CHEAT_SH_URL=]
240-
-p, --pager <PAGER> Sets the pager to use
241-
--no-pager Disables the pager
242-
-h, --help Print help
238+
-p, --pager <PAGER>
239+
Sets the pager to use
240+
241+
--no-pager
242+
Disables the pager
243+
244+
-s, --selected-position <SELECTED_POSITION>
245+
Sets the default selected position
246+
247+
Possible values:
248+
- start: The first item in the menu
249+
- center: The middle item in the menu (in case of even number of items, the first item in the second half)
250+
- end: The last item in the menu
251+
252+
-h, --help
253+
Print help (see a summary with '-h')
243254
```
244255

245256
## Examples
@@ -313,12 +324,6 @@ To disable the pager:
313324
halp plz --no-pager bat vim
314325
```
315326

316-
##### Custom cheat.sh host URL
317-
318-
```sh
319-
halp plz --cheat-sh-url https://cht.sh vim
320-
```
321-
322327
## Configuration
323328

324329
`halp` can be configured with a configuration file that uses the [TOML](https://en.wikipedia.org/wiki/INI_file) format. It can be specified via `--config` or `HALP_CONFIG` environment variable. It can also be placed in one of the following global locations:
@@ -330,7 +335,7 @@ halp plz --cheat-sh-url https://cht.sh vim
330335
`<config_dir>` depends on the platform as shown in the following table:
331336

332337
| Platform | Value | Example |
333-
| -------- | ------------------------------------- | ---------------------------------------- |
338+
|----------|---------------------------------------|------------------------------------------|
334339
| Linux | `$XDG_CONFIG_HOME` or `$HOME`/.config | /home/orhun/.config |
335340
| macOS | `$HOME`/Library/Application Support | /Users/Orhun/Library/Application Support |
336341
| Windows | `{FOLDERID_RoamingAppData}` | C:\Users\Orhun\AppData\Roaming |

config/halp.toml

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,33 @@
1-
# configuration for https://github.com/orhun/halp
2-
3-
# check the version flag
41
check_version = true
5-
# check the help flag
62
check_help = true
7-
# arguments to check
8-
check = [ [ "-v", "-V", "--version" ], [ "-h", "--help", "help", "-H" ] ]
9-
# command to run for manual pages
3+
check = [["-v", "-V", "--version", "version"], ["-h", "--help", "help", "-H"]]
104
man_command = "man"
11-
# pager to use for command outputs
125
pager_command = "less -R"
13-
# Cheat.sh URL
14-
cheat_sh_url = "https://cheat.sh"
6+
7+
[plz_menu]
8+
selected_pos = "Center"
9+
10+
[[plz_menu.entries]]
11+
display_msg = "Show man page"
12+
13+
[plz_menu.entries.operation]
14+
run = "man {cmd}"
15+
16+
[[plz_menu.entries]]
17+
display_msg = "Show cheat.sh page"
18+
19+
[plz_menu.entries.operation]
20+
user-agent = "fetch"
21+
fetch = "https://cheat.sh/{cmd}{?/{subcommand}}{? {args}}"
22+
23+
[[plz_menu.entries]]
24+
display_msg = "Show eg page"
25+
26+
[plz_menu.entries.operation]
27+
fetch = "https://raw.githubusercontent.com/srsudar/eg/master/eg/examples/{cmd}.md"
28+
29+
[[plz_menu.entries]]
30+
display_msg = "Show cheatsheets page"
31+
32+
[plz_menu.entries.operation]
33+
fetch = "https://raw.githubusercontent.com/cheat/cheatsheets/master/{cmd}"

src/cli.rs

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::config::plz_menu::PlzMenuSelection;
12
use crate::config::Config;
23
use clap::{Parser, Subcommand};
34
use std::path::PathBuf;
@@ -45,24 +46,15 @@ pub enum CliCommands {
4546
Plz {
4647
/// Command or binary name.
4748
cmd: String,
48-
/// Sets the manual page command to run.
49-
#[arg(short, long)]
50-
man_cmd: Option<String>,
51-
/// Use a custom URL for cheat.sh.
52-
#[arg(long, env = "CHEAT_SH_URL", value_name = "URL")]
53-
cheat_sh_url: Option<String>,
54-
/// Use a custom provider URL for `eg` pages.
55-
#[arg(long, env = "EG_PAGES_URL", value_name = "URL")]
56-
eg_url: Option<String>,
57-
/// Use a custom URL for cheat sheets.
58-
#[arg(long, env = "CHEATSHEETS_URL", value_name = "URL")]
59-
cheat_url: Option<String>,
6049
/// Sets the pager to use.
6150
#[arg(short, long)]
6251
pager: Option<String>,
6352
/// Disables the pager.
6453
#[arg(long)]
6554
no_pager: bool,
55+
/// Sets the default selected position.
56+
#[arg(long, short)]
57+
selected_position: Option<PlzMenuSelection>,
6658
},
6759
}
6860

@@ -80,28 +72,20 @@ impl CliArgs {
8072
config.check_args = Some(args.iter().map(|s| vec![s.to_string()]).collect());
8173
}
8274
if let Some(CliCommands::Plz {
83-
ref man_cmd,
84-
ref cheat_sh_url,
85-
ref eg_url,
8675
no_pager,
8776
ref pager,
77+
ref selected_position,
8878
..
8979
}) = self.subcommand
9080
{
91-
if let Some(man_cmd) = man_cmd {
92-
config.man_command = man_cmd.clone();
93-
}
94-
if let Some(cheat_sh_url) = cheat_sh_url {
95-
config.cheat_sh_url = Some(cheat_sh_url.clone());
96-
}
97-
if let Some(eg_url) = eg_url {
98-
config.eg_url = Some(eg_url.to_owned());
99-
}
10081
if no_pager {
10182
config.pager_command = None;
10283
} else if let Some(pager) = pager {
10384
config.pager_command = Some(pager.clone());
10485
}
86+
if let Some(selected_position) = selected_position {
87+
config.plz_menu.selected_pos = *selected_position;
88+
}
10589
}
10690
}
10791
}
@@ -125,10 +109,7 @@ mod tests {
125109
subcommand: Some(CliCommands::Plz {
126110
cmd: "ps".to_string(),
127111
pager: Some("bat".to_string()),
128-
cheat_sh_url: None,
129-
cheat_url: None,
130-
eg_url: None,
131-
man_cmd: None,
112+
selected_position: Some(PlzMenuSelection::Center),
132113
no_pager: false,
133114
}),
134115
..Default::default()

src/config.rs renamed to src/config/mod.rs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
/// The `plz` configuration stuff.
2+
pub mod plz_menu;
3+
4+
use crate::config::plz_menu::PlzMenu;
15
use crate::error::Result;
26
use crate::helper::args::common::{HelpArg, VersionArg};
37
use crate::helper::args::FOUND_EMOTICON;
4-
use crate::helper::docs::cheat_sh::DEFAULT_CHEAT_SHEET_PROVIDER;
5-
use crate::helper::docs::cheatsheets::DEFAULT_CHEATSHEETS_PROVIDER;
6-
use crate::helper::docs::eg::DEFAULT_EG_PAGES_PROVIDER;
78
use colored::*;
89
use serde::{Deserialize, Serialize};
910
use std::env;
@@ -25,12 +26,8 @@ pub struct Config {
2526
pub man_command: String,
2627
/// Pager to use for command outputs, None to disable.
2728
pub pager_command: Option<String>,
28-
/// Use a custom URL for cheat.sh.
29-
pub cheat_sh_url: Option<String>,
30-
/// Use a custom URL for `eg` pages provider.
31-
pub eg_url: Option<String>,
32-
/// Use a custom URL for cheatsheets provider.
33-
pub cheatsheets_url: Option<String>,
29+
/// Plz menu options.
30+
pub plz_menu: PlzMenu,
3431
}
3532

3633
impl Default for Config {
@@ -50,9 +47,7 @@ impl Default for Config {
5047
]),
5148
man_command: "man".to_string(),
5249
pager_command: Some("less -R".to_string()),
53-
cheat_sh_url: Some(DEFAULT_CHEAT_SHEET_PROVIDER.to_string()),
54-
eg_url: Some(DEFAULT_EG_PAGES_PROVIDER.to_string()),
55-
cheatsheets_url: Some(DEFAULT_CHEATSHEETS_PROVIDER.to_string()),
50+
plz_menu: PlzMenu::default(),
5651
}
5752
}
5853
}
@@ -122,7 +117,6 @@ impl Config {
122117
#[cfg(test)]
123118
mod tests {
124119
use super::*;
125-
use pretty_assertions::assert_eq;
126120
use std::path::PathBuf;
127121

128122
#[test]
@@ -136,10 +130,6 @@ mod tests {
136130
let config = Config::parse(&path)?;
137131
assert!(config.check_help);
138132
assert!(config.check_version);
139-
assert_eq!(
140-
config.cheat_sh_url,
141-
Some(DEFAULT_CHEAT_SHEET_PROVIDER.to_string())
142-
);
143133
Ok(())
144134
}
145135
}

src/config/plz_menu.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use clap::ValueEnum;
2+
use serde::{Deserialize, Serialize};
3+
use std::collections::HashMap;
4+
5+
/// Default cheat sheet provider URL.
6+
const CHEAT_SH_URL_TEMPLATE: &str = "https://cheat.sh/{cmd}{?/{subcommand}}{? {args}}";
7+
8+
/// User agent for the cheat sheet provider.
9+
///
10+
/// See <https://github.com/chubin/cheat.sh/blob/83bffa587b6c1048cbcc40ea8fa6af675203fd5f/bin/app.py#L76>
11+
const CHEAT_SH_USER_AGENT: &str = "fetch";
12+
13+
/// EG page provider URL.
14+
const EG_PAGES_URL_TEMPLATE: &str =
15+
"https://raw.githubusercontent.com/srsudar/eg/master/eg/examples/{cmd}.md";
16+
17+
/// The default cheatsheets provider URL.
18+
const CHEATSHEETS_URL_TEMPLATE: &str =
19+
"https://raw.githubusercontent.com/cheat/cheatsheets/master/{cmd}";
20+
21+
/// Plz menu config.
22+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23+
pub struct PlzMenu {
24+
/// The default selected poison in the menu.
25+
pub selected_pos: PlzMenuSelection,
26+
/// The menu entries.
27+
pub entries: Vec<PlzMenuEntry>,
28+
}
29+
30+
/// Plz menu selection position.
31+
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, ValueEnum)]
32+
pub enum PlzMenuSelection {
33+
/// The first item in the menu.
34+
Start,
35+
/// The middle item in the menu (in case of even number of items, the first item in the second half).
36+
Center,
37+
/// The last item in the menu.
38+
End,
39+
}
40+
41+
/// Plz menu item.
42+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43+
pub struct PlzMenuEntry {
44+
/// The string to display in the menu.
45+
pub display_msg: String,
46+
/// The operation to perform. and its arguments.
47+
pub operation: HashMap<String, String>,
48+
}
49+
50+
impl Default for PlzMenu {
51+
fn default() -> Self {
52+
PlzMenu {
53+
selected_pos: PlzMenuSelection::Center,
54+
entries: vec![
55+
PlzMenuEntry {
56+
display_msg: "Show man page".to_string(),
57+
operation: HashMap::from([("run".to_string(), "man {cmd}".to_string())]),
58+
},
59+
PlzMenuEntry {
60+
display_msg: "Show cheat.sh page".to_string(),
61+
operation: HashMap::from([
62+
("fetch".to_string(), CHEAT_SH_URL_TEMPLATE.to_string()),
63+
("user-agent".to_string(), CHEAT_SH_USER_AGENT.to_string()),
64+
]),
65+
},
66+
PlzMenuEntry {
67+
display_msg: "Show eg page".to_string(),
68+
operation: HashMap::from([(
69+
"fetch".to_string(),
70+
EG_PAGES_URL_TEMPLATE.to_string(),
71+
)]),
72+
},
73+
PlzMenuEntry {
74+
display_msg: "Show cheatsheets page".to_string(),
75+
operation: HashMap::from([(
76+
"fetch".to_string(),
77+
CHEATSHEETS_URL_TEMPLATE.to_string(),
78+
)]),
79+
},
80+
],
81+
}
82+
}
83+
}

src/error.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::helper::docs::template::HalpTemplateError;
12
use thiserror::Error as ThisError;
23

34
/// Custom error type.
@@ -21,6 +22,24 @@ pub enum Error {
2122
/// Error that might occur when tray to get help from an external provider.
2223
#[error("External help provider error: `{0}`")]
2324
ProviderError(String),
25+
/// Error that might occur when trying to parse a template.
26+
#[error("Template error: `{0}`")]
27+
TemplateError(#[from] HalpTemplateError),
28+
/// Error that might occur if the user provides invalid arguments for the operation handler.
29+
#[error("Invalid argument: `{0}`")]
30+
InvalidArgument(String),
31+
/// Error that might occur if the user dosen't provide any operation handler.
32+
#[error("No operation provided.")]
33+
PlzMenuNoOperation,
34+
/// Error that might occur if the user provides an invalid operation.
35+
#[error("Invalid operation there is no operation named `{0}`.")]
36+
PlzMenuInvalidOperation(String),
37+
/// Error that might occur if the timeout is reached while executing a command.
38+
#[error("Command timeout.")]
39+
CommandTimeoutError,
40+
/// Error that might occur when trying to execute a command or collect its output.
41+
#[error("Command error: `{0}`")]
42+
CommandError(String),
2443
}
2544

2645
/// Type alias for the standard [`Result`] type.

src/helper/args/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ fn check_args<'a, ArgsIter: Iterator<Item = &'a str>, Output: Write>(
8787
/// Shows command-line help about the given command.
8888
pub fn get_args_help<Output: Write>(
8989
cmd: &str,
90-
config: &Config,
90+
config: Config,
9191
verbose: bool,
9292
output: &mut Output,
9393
) -> Result<()> {
@@ -215,7 +215,7 @@ Options:
215215
fn test_get_default_help() -> Result<()> {
216216
let config = Config::default();
217217
let mut output = Vec::new();
218-
get_args_help(&get_test_bin(), &config, false, &mut output)?;
218+
get_args_help(&get_test_bin(), config, false, &mut output)?;
219219
println!("{}", String::from_utf8_lossy(&output));
220220
assert_eq!(
221221
r"(°ロ°) checking 'test -v'
@@ -250,7 +250,7 @@ Options:
250250
..Default::default()
251251
};
252252
let mut output = Vec::new();
253-
get_args_help(&get_test_bin(), &config, false, &mut output)?;
253+
get_args_help(&get_test_bin(), config, false, &mut output)?;
254254
println!("{}", String::from_utf8_lossy(&output));
255255
assert_eq!(
256256
r"(°ロ°) checking 'test -x'
@@ -277,7 +277,7 @@ halp 0.1.0
277277
..Default::default()
278278
};
279279
let mut output = Vec::new();
280-
get_args_help("", &config, false, &mut output)?;
280+
get_args_help("", config, false, &mut output)?;
281281
assert!(String::from_utf8_lossy(&output).is_empty());
282282
Ok(())
283283
}

0 commit comments

Comments
 (0)