You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

370 lines
15 KiB

  1. /*
  2. * meli - ui crate.
  3. *
  4. * Copyright 2017-2018 Manos Pitsidianakis
  5. *
  6. * This file is part of meli.
  7. *
  8. * meli is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * meli is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with meli. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. /*! A parser module for user commands passed through the Ex mode.
  22. */
  23. use melib::backends::FolderOperation;
  24. pub use melib::thread::{SortField, SortOrder};
  25. use nom::{digit, not_line_ending};
  26. use std;
  27. pub mod actions;
  28. pub mod history;
  29. pub use crate::actions::Action::{self, *};
  30. pub use crate::actions::ComposeAction::{self, *};
  31. pub use crate::actions::ListingAction::{self, *};
  32. pub use crate::actions::MailingListAction::{self, *};
  33. pub use crate::actions::PagerAction::{self, *};
  34. pub use crate::actions::TabAction::{self, *};
  35. use std::str::FromStr;
  36. /* Create a const table with every command part that can be auto-completed and its description */
  37. macro_rules! define_commands {
  38. ( [$({ tags: [$( $tags:literal),*], desc: $desc:literal, parser: ($parser:item)}),*]) => {
  39. pub const COMMAND_COMPLETION: &[(&str, &str)] = &[$($( ($tags, $desc ) ),*),* ];
  40. $( $parser )*
  41. };
  42. }
  43. define_commands!([
  44. { tags: ["set"],
  45. desc: "set [seen/unseen], toggles message's Seen flag.",
  46. parser:
  47. ( named!(
  48. envelope_action<Action>,
  49. alt_complete!(
  50. preceded!(
  51. ws!(tag!("set")),
  52. alt_complete!(
  53. map!(ws!(tag!("seen")), |_| Listing(SetSeen))
  54. | map!(ws!(tag!("unseen")), |_| Listing(SetUnseen))
  55. )
  56. ) | map!(ws!(tag!("delete")), |_| Listing(Delete))
  57. )
  58. ); )
  59. },
  60. { tags: ["close"],
  61. desc: "close non-sticky tabs",
  62. parser: (
  63. named!(close<Action>, map!(ws!(tag!("close")), |_| Tab(Close)));
  64. )
  65. },
  66. { tags: ["goto"],
  67. desc: "goto [n], switch to nth mailbox in this account",
  68. parser: (
  69. named!(
  70. goto<Action>,
  71. preceded!(tag!("go "), map!(call!(usize_c), Action::ViewMailbox))
  72. );
  73. )
  74. },
  75. { tags: ["subsort"],
  76. desc: "subsort [date/subject] [asc/desc], sorts first level replies in threads.",
  77. parser: (
  78. named!(
  79. subsort<Action>,
  80. do_parse!(tag!("subsort ") >> p: pair!(sortfield, sortorder) >> (SubSort(p.0, p.1)))
  81. );
  82. )
  83. },
  84. { tags: ["sort"],
  85. desc: "sort [date/subject] [asc/desc], sorts threads.",
  86. parser: (
  87. named!(
  88. sort<Action>,
  89. do_parse!(
  90. tag!("sort ") >> p: separated_pair!(sortfield, tag!(" "), sortorder) >> (Sort(p.0, p.1))
  91. )
  92. );
  93. )
  94. },
  95. { tags: ["set", "set plain", "set threaded", "set compact"],
  96. desc: "set [plain/threaded/compact/conversations], changes the mail listing view",
  97. parser: (
  98. named!(
  99. toggle<Action>,
  100. preceded!(tag!("set "), alt_complete!(threaded | plain | compact | conversations))
  101. );
  102. )
  103. },
  104. { tags: ["toggle_thread_snooze"],
  105. desc: "turn off new notifications for this thread",
  106. parser: (
  107. named!(toggle_thread_snooze<Action>,
  108. map!(ws!(tag!("toggle_thread_snooze")), |_| ToggleThreadSnooze)
  109. );
  110. )
  111. },
  112. { tags: ["filter"],
  113. desc: "filter <TERM>, filters list with given term",
  114. parser:(
  115. named!(filter<Action>,
  116. do_parse!(
  117. ws!(tag!("filter"))
  118. >> string: map_res!(call!(not_line_ending), std::str::from_utf8)
  119. >> (Listing(Filter(String::from(string))))
  120. )
  121. );
  122. )
  123. },
  124. { tags: ["list-archive", "list-post", "list-unsubscribe", "list-"],
  125. desc: "list-[unsubscribe/post/archive]",
  126. parser: (
  127. named!(
  128. mailinglist<Action>,
  129. alt_complete!(
  130. map!(ws!(tag!("list-post")), |_| MailingListAction(ListPost))
  131. | map!(ws!(tag!("list-unsubscribe")), |_| MailingListAction(
  132. ListUnsubscribe
  133. ))
  134. | map!(ws!(tag!("list-archive")), |_| MailingListAction(
  135. ListArchive
  136. ))
  137. )
  138. );
  139. )
  140. },
  141. { tags: ["setenv "],
  142. desc:"setenv VAR=VALUE",
  143. parser: (
  144. named!( setenv<Action>,
  145. do_parse!(
  146. ws!(tag!("setenv"))
  147. >> key: map_res!(take_until1!("="), std::str::from_utf8)
  148. >> ws!(tag!("="))
  149. >> val: map_res!(call!(not_line_ending), std::str::from_utf8)
  150. >> (SetEnv(key.to_string(), val.to_string()))
  151. )
  152. );
  153. )
  154. },
  155. { tags: ["printenv "],
  156. desc: "printenv VAR",
  157. parser:(
  158. named!( printenv<Action>,
  159. do_parse!(
  160. ws!(tag!("env"))
  161. >> key: map_res!(call!(not_line_ending), std::str::from_utf8)
  162. >> (PrintEnv(key.to_string()))
  163. )
  164. );
  165. )
  166. },
  167. /* Pipe pager contents to binary */
  168. { tags: ["pipe "],
  169. desc: "pipe EXECUTABLE ARGS",
  170. parser:(
  171. named!( pipe<Action>,
  172. alt_complete!(
  173. do_parse!(
  174. ws!(tag!("pipe"))
  175. >> bin: map_res!(is_not!(" "), std::str::from_utf8)
  176. >> is_a!(" ")
  177. >> args: separated_list!(is_a!(" "), is_not!(" "))
  178. >> ({
  179. Pager(Pipe(bin.to_string(), args.into_iter().map(|v| String::from_utf8(v.to_vec()).unwrap()).collect::<Vec<String>>()))
  180. })) | do_parse!(
  181. ws!(tag!("pipe"))
  182. >> bin: ws!(map_res!(is_not!(" "), std::str::from_utf8))
  183. >> ({
  184. Pager(Pipe(bin.to_string(), Vec::new()))
  185. })
  186. ))
  187. );
  188. )
  189. },
  190. { tags: ["add-attachment "],
  191. desc: "add-attachment PATH",
  192. parser:(
  193. named!( add_attachment<Action>,
  194. do_parse!(
  195. ws!(tag!("add-attachment"))
  196. >> path: map_res!(call!(not_line_ending), std::str::from_utf8)
  197. >> (Compose(AddAttachment(path.to_string())))
  198. )
  199. );
  200. )
  201. },
  202. { tags: ["remove-attachment "],
  203. desc: "remove-attachment INDEX",
  204. parser:(
  205. named!( remove_attachment<Action>,
  206. do_parse!(
  207. ws!(tag!("remove-attachment"))
  208. >> idx: map_res!(map_res!(call!(not_line_ending), std::str::from_utf8), usize::from_str)
  209. >> (Compose(RemoveAttachment(idx)))
  210. )
  211. );
  212. )
  213. },
  214. { tags: ["toggle sign "],
  215. desc: "switch between sign/unsign for this draft",
  216. parser:(
  217. named!( toggle_sign<Action>,
  218. do_parse!(
  219. ws!(tag!("toggle sign"))
  220. >> (Compose(ToggleSign))
  221. )
  222. );
  223. )
  224. },
  225. { tags: ["create-folder "],
  226. desc: "create-folder ACCOUNT FOLDER_PATH",
  227. parser:(
  228. named!( create_folder<Action>,
  229. do_parse!(
  230. ws!(tag!("create-folder"))
  231. >> account: map_res!(is_not!(" "), std::str::from_utf8)
  232. >> is_a!(" ")
  233. >> path: map_res!(call!(not_line_ending), std::str::from_utf8)
  234. >> (Folder(account.to_string(), path.to_string(), FolderOperation::Create))
  235. )
  236. );
  237. )
  238. },
  239. { tags: ["subscribe-folder "],
  240. desc: "subscribe-folder ACCOUNT FOLDER_PATH",
  241. parser:(
  242. named!( sub_folder<Action>,
  243. do_parse!(
  244. ws!(tag!("subscribe-folder"))
  245. >> account: map_res!(is_not!(" "), std::str::from_utf8)
  246. >> is_a!(" ")
  247. >> path: map_res!(call!(not_line_ending), std::str::from_utf8)
  248. >> (Folder(account.to_string(), path.to_string(), FolderOperation::Subscribe))
  249. )
  250. );
  251. )
  252. },
  253. { tags: ["unsubscribe-folder "],
  254. desc: "unsubscribe-folder ACCOUNT FOLDER_PATH",
  255. parser:(
  256. named!( unsub_folder<Action>,
  257. do_parse!(
  258. ws!(tag!("unsubscribe-folder"))
  259. >> account: map_res!(is_not!(" "), std::str::from_utf8)
  260. >> is_a!(" ")
  261. >> path: map_res!(call!(not_line_ending), std::str::from_utf8)
  262. >> (Folder(account.to_string(), path.to_string(), FolderOperation::Unsubscribe))
  263. )
  264. );
  265. )
  266. },
  267. { tags: ["rename-folder "],
  268. desc: "rename-folder ACCOUNT FOLDER_PATH_SRC FOLDER_PATH_DEST",
  269. parser:(
  270. named!( rename_folder<Action>,
  271. do_parse!(
  272. ws!(tag!("rename-folder"))
  273. >> account: map_res!(is_not!(" "), std::str::from_utf8)
  274. >> is_a!(" ")
  275. >> src: map_res!(is_not!(" "), std::str::from_utf8)
  276. >> is_a!(" ")
  277. >> dest: map_res!(call!(not_line_ending), std::str::from_utf8)
  278. >> (Folder(account.to_string(), src.to_string(), FolderOperation::Rename(dest.to_string())))
  279. )
  280. );
  281. )
  282. },
  283. { tags: ["delete-folder "],
  284. desc: "delete-folder ACCOUNT FOLDER_PATH",
  285. parser:(
  286. named!( delete_folder<Action>,
  287. do_parse!(
  288. ws!(tag!("delete-folder"))
  289. >> account: map_res!(is_not!(" "), std::str::from_utf8)
  290. >> is_a!(" ")
  291. >> path: map_res!(call!(not_line_ending), std::str::from_utf8)
  292. >> (Folder(account.to_string(), path.to_string(), FolderOperation::Delete))
  293. )
  294. );
  295. )
  296. },
  297. { tags: ["open-in-tab"],
  298. desc: "opens envelope view in new tab",
  299. parser:(
  300. named!( open_in_new_tab<Action>,
  301. do_parse!(
  302. ws!(tag!("open-in-tab"))
  303. >> (Listing(OpenInNewTab))
  304. )
  305. );
  306. )
  307. }
  308. ]);
  309. named!(
  310. usize_c<usize>,
  311. map_res!(
  312. map_res!(ws!(digit), std::str::from_utf8),
  313. std::str::FromStr::from_str
  314. )
  315. );
  316. named!(
  317. sortfield<SortField>,
  318. map_res!(
  319. map_res!(take_until_s!(" "), std::str::from_utf8),
  320. std::str::FromStr::from_str
  321. )
  322. );
  323. named!(
  324. sortorder<SortOrder>,
  325. map_res!(
  326. map_res!(call!(not_line_ending), std::str::from_utf8),
  327. std::str::FromStr::from_str
  328. )
  329. );
  330. named!(
  331. threaded<Action>,
  332. map!(ws!(tag!("threaded")), |_| Listing(SetThreaded))
  333. );
  334. named!(
  335. plain<Action>,
  336. map!(ws!(tag!("plain")), |_| Listing(SetPlain))
  337. );
  338. named!(
  339. compact<Action>,
  340. map!(ws!(tag!("compact")), |_| Listing(SetCompact))
  341. );
  342. named!(
  343. conversations<Action>,
  344. map!(ws!(tag!("conversations")), |_| Listing(SetConversations))
  345. );
  346. named!(
  347. listing_action<Action>,
  348. alt_complete!(toggle | envelope_action | filter | toggle_thread_snooze | open_in_new_tab)
  349. );
  350. named!(
  351. compose_action<Action>,
  352. alt_complete!(add_attachment | remove_attachment | toggle_sign)
  353. );
  354. named!(pub parse_command<Action>,
  355. alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | pipe | compose_action | create_folder | sub_folder | unsub_folder | delete_folder | rename_folder)
  356. );