View manpages in pager inside meli

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/384/head
Manos Pitsidianakis 2024-05-02 16:31:47 +03:00
parent 89c7972e12
commit 3a5306e9dd
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
7 changed files with 114 additions and 2 deletions

View File

@ -143,6 +143,11 @@ impl TokenStream {
tokens.append(&mut m);
}
}
AlternativeStrings(v) => {
for t in v.iter() {
sugg.insert(format!("{}{}", if s.is_empty() { " " } else { "" }, t));
}
}
Seq(_s) => {}
RestOfStringValue => {
sugg.insert(String::new());
@ -196,6 +201,20 @@ impl TokenStream {
*s = "";
}
}
AlternativeStrings(v) => {
for lit in v.iter() {
if lit.starts_with(*s) && lit.len() != s.len() {
sugg.insert(lit[s.len()..].to_string());
tokens.push((s, *t.inner()));
return tokens;
} else if s.starts_with(lit) {
tokens.push((&s[..lit.len()], *t.inner()));
*s = &s[lit.len()..];
} else {
return vec![];
}
}
}
Seq(_s) => {
return vec![];
}
@ -251,6 +270,7 @@ pub enum Token {
Literal(&'static str),
Filepath,
Alternatives(&'static [TokenStream]),
AlternativeStrings(&'static [&'static str]),
Seq(&'static [TokenAdicity]),
AccountName,
MailboxPath,
@ -488,6 +508,18 @@ Alternatives(&[to_stream!(One(Literal("add-attachment")), One(Filepath)), to_str
tokens: &[One(Literal("manage-mailboxes"))],
parser: parser::manage_mailboxes
},
{ tags: ["man"],
desc: "read documentation",
tokens: {
#[cfg(feature = "cli-docs")]
{
&[One(Literal("man")), One(AlternativeStrings(crate::manpages::POSSIBLE_VALUES))]
}
#[cfg(not(feature = "cli-docs"))]
{ &[] }
},
parser: parser::view_manpage
},
{ tags: ["manage-jobs"],
desc: "view and manage jobs",
tokens: &[One(Literal("manage-jobs"))],

View File

@ -70,6 +70,8 @@ pub enum TabAction {
New(Option<Box<dyn Component>>),
ManageMailboxes,
ManageJobs,
#[cfg(feature = "cli-docs")]
Man(crate::manpages::ManPages),
}
#[derive(Debug)]

View File

@ -139,7 +139,7 @@ pub fn view(input: &[u8]) -> IResult<&[u8], Result<Action, CommandError>> {
}
pub fn new_tab(input: &[u8]) -> IResult<&[u8], Result<Action, CommandError>> {
alt((manage_mailboxes, manage_jobs, compose_action))(input)
alt((manage_mailboxes, manage_jobs, compose_action, view_manpage))(input)
}
pub fn parse_command(input: &[u8]) -> Result<Action, CommandError> {
@ -999,6 +999,39 @@ pub fn manage_jobs(input: &[u8]) -> IResult<&[u8], Result<Action, CommandError>>
let (input, _) = eof(input)?;
Ok((input, Ok(Tab(ManageJobs))))
}
#[cfg(feature = "cli-docs")]
pub fn view_manpage(input: &[u8]) -> IResult<&[u8], Result<Action, CommandError>> {
let mut check = arg_init! { min_arg:1, max_arg: 1, view_manpage };
let (input, _) = tag("man")(input.trim())?;
arg_chk!(start check, input);
let (input, _) = is_a(" ")(input)?;
arg_chk!(inc check, input);
let (input, manpage) = map_res(not_line_ending, std::str::from_utf8)(input.trim())?;
let (input, _) = eof(input)?;
arg_chk!(finish check, input);
match crate::manpages::parse_manpage(manpage) {
Ok(m) => Ok((input, Ok(Tab(Man(m))))),
Err(err) => Ok((
input,
Err(CommandError::BadValue {
inner: err.to_string().into(),
suggestions: Some(crate::manpages::POSSIBLE_VALUES),
}),
)),
}
}
#[cfg(not(feature = "cli-docs"))]
pub fn view_manpage(input: &[u8]) -> IResult<&[u8], Result<Action, CommandError>> {
Ok((
input,
Err(CommandError::Other {
inner: "this meli binary has not been compiled with the cli-docs feature".into(),
}),
))
}
pub fn quit(input: &[u8]) -> IResult<&[u8], Result<Action, CommandError>> {
let mut check = arg_init! { min_arg:0, max_arg: 0, quit};
let (input, _) = tag("quit")(input.trim())?;

View File

@ -172,4 +172,20 @@ impl ManPages {
Ok(v)
}
/// Helper function to remove backspace markup from mandoc output.
pub fn remove_markup(input: &str) -> Result<String> {
use std::{
io::Write,
process::{Command, Stdio},
};
let mut child = Command::new("col")
.arg("-b")
.arg("-x")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
child.stdin.as_mut().unwrap().write_all(input.as_bytes())?;
Ok(String::from_utf8_lossy(&child.wait_with_output()?.stdout).to_string())
}
}

View File

@ -914,6 +914,27 @@ impl State {
)))
.unwrap();
}
#[cfg(feature = "cli-docs")]
Tab(Man(manpage)) => match manpage
.read(false)
.map(|text| crate::manpages::ManPages::remove_markup(&text).unwrap_or(text))
{
Ok(m) => self.rcv_event(UIEvent::Action(Tab(New(Some(Box::new(
Pager::from_string(
m,
Some(&self.context),
None,
None,
crate::conf::value(&self.context, "theme_default"),
),
)))))),
Err(err) => self.context.replies.push_back(UIEvent::Notification {
title: None,
body: "Encountered an error while retrieving manual page.".into(),
source: Some(err),
kind: Some(NotificationType::Error(ErrorKind::Bug)),
}),
},
v => {
self.rcv_event(UIEvent::Action(v));
}

View File

@ -1386,7 +1386,7 @@ impl Component for Tabbed {
self.dirty = true;
return true;
}
UIEvent::Action(Tab(New(ref mut e))) if e.is_some() => {
UIEvent::Action(Tab(New(ref mut e @ Some(_)))) => {
self.add_component(e.take().unwrap(), context);
self.children[self.cursor_pos]
.process_event(&mut UIEvent::VisibilityChange(false), context);

View File

@ -877,4 +877,12 @@ impl Component for Pager {
fn id(&self) -> ComponentId {
self.id
}
fn kill(&mut self, uuid: ComponentId, context: &mut Context) {
if self.id != uuid {
return;
}
context.replies.push_back(UIEvent::Action(Tab(Kill(uuid))));
}
}