Example for plugin reading from stding or subcommand
carrascomj opened this issue · 3 comments
Hello!
I am writing a plugin that emulates promptSearch from Xmonad's prompt. Basically, the user can configure urls (say duckduckgo or the rust std documentation) to look up to then bind a key for each of the urls.
I have it implemented for duckduckgo, but I'd like to read from stdin or a subcommand to change between different urls. For instance:
dmenu --lookup --where=D
;"D" | dmenu --lookup
;- or maybe
dmenu --lookup=D
would translate to https://duckduckgo.com/ + dmenu's prompt input.
The problem is that I am struggling to understand how to read stdin (or to add a subcommand). When I set a format_stdin function and set nostdin to false, it gets stuck.This is what I have working for now:
use overrider::*;
use std::io::Write;
use std::process::Command;
use crate::drw::Drw;
use crate::result::*;
#[override_flag(flag = lookup)]
impl Drw {
// I know I need to implement format_stdin and put the logic to select the url there,
// but I don't know how to access it on dispose
pub fn format_input(&self) -> CompResult<String> {
Ok(format!("[Search DDG]: {}",self.input))
}
pub fn dispose(&mut self, _output: String, recommendation: bool) -> CompResult<bool> {
let eval = format!("https://duckduckgo.com/{}", self.input);
self.input = "".to_owned();
self.pseudo_globals.cursor = 0;
if eval.len() > 0 {
let mut child = Command::new("xdg-open")
.arg(eval.clone())
.spawn()
.map_err(|_| Die::Stderr("Failed to spawn child process".to_owned()))?;
child.stdin.as_mut().ok_or(Die::Stderr("Failed to open stdin of child process"
.to_owned()))?
.write_all(eval.as_bytes())
.map_err(|_| Die::Stderr("Failed to write to stdin of child process"
.to_owned()))?;
}
self.draw()?;
Ok(!recommendation)
}
}
use crate::config::{ConfigDefault, DefaultWidth};
#[override_flag(flag = lookup)]
impl ConfigDefault {
pub fn nostdin() -> bool {
// setting this to false makes it get stuck
true
}
pub fn render_flex() -> bool {
true
}
pub fn render_default_width() -> DefaultWidth {
DefaultWidth::Custom(25)
}
}
Hey there, glad to see you interested in this project. If you get this plugin working well, totally submit a PR! I'd actually use this plugin as a daily driver.
As for the solution, I've made a slight edit to the source code -- turns out there was some unintended behavior with format_stdin
. It doesn't break anything, but I feel like it should run whether nostdin
is selected or not. This fix is on the develop branch and is needed for the solution below, so I recommend testing from there.
Here is a bare bones setup that should help you figure out what to do:
# plugin.yml
about: |
DESCRIPTION
entry: main.rs
args:
- lookup:
help: DESCRIPTION
long: lookup
- engine:
help: DESCRIPTION
long: engine
takes_value: true
requires: lookup
The above provides the following functionality:
echo D | dmenu --lookup
to search DDGdmenu --lookup --engine D
for the same. I changedwhere
toengine
, aswhere
is a rust keyword
This works with the following main.rs
in the plugin directory. The important part is having two overrides, one for --lookup
and one for --engine
:
use overrider::*;
use crate::clapflags::CLAP_FLAGS;
use crate::drw::Drw;
use crate::result::*;
use crate::config::{ConfigDefault};
// Turns short description into a prompt
// eg "D" -> "[Search DDG]"
fn create_search_input(engine: &str) -> CompResult<String> {
Ok(format!("[Search {}]", match engine {
"D" => "DDG",
// add more engines here if you want
_ => return Err(Die::Stderr("invalid engine".to_string()))
}))
}
// Takes the output of create_search_input as prompt
// It's not very clean but hey it works
fn do_dispose(output: &str, prompt: &str) -> CompResult<()> {
// Extract "ENGINE_LONG" from "[Search ENGINE_LONG]"
let mut engine: String = prompt.chars().skip("[Search ".len()).collect();
engine.pop();
println!("engine: {}, searchterm: {}", engine, output);
// xdg logic goes here
Ok(())
}
// Important: engine must become before lookup. It's a bug in overrider.
#[override_flag(flag = engine, priority = 2)]
impl Drw {
pub fn dispose(&mut self, output: String, recommendation: bool) -> CompResult<bool> {
do_dispose(&output, &self.config.prompt)?;
Ok(recommendation)
}
pub fn format_stdin(&mut self, _lines: Vec<String>) -> CompResult<Vec<String>> {
self.config.prompt = create_search_input(CLAP_FLAGS.value_of("engine").unwrap())?;
Ok(vec![]) // turns into prompt
}
}
#[override_flag(flag = engine, priority = 2)]
impl ConfigDefault {
pub fn nostdin() -> bool {
true // if called with --engine ENGINE, takes no stdin
}
}
#[override_flag(flag = lookup, priority = 1)]
impl Drw {
pub fn dispose(&mut self, output: String, recommendation: bool) -> CompResult<bool> {
do_dispose(&output, &self.config.prompt)?;
Ok(recommendation)
}
pub fn format_stdin(&mut self, lines: Vec<String>) -> CompResult<Vec<String>> {
self.config.prompt = create_search_input(&lines[0])?;
Ok(vec![]) // turns into prompt
}
}
#[override_flag(flag = lookup, priority = 1)]
impl ConfigDefault {
pub fn nostdin() -> bool {
false // if called without --engine, takes stdin
}
}
If I understand your problem, that should be everything you need to get this plugin up and running. Let me know if you run into any other issues.
Thank you so much! I'll give it a try