docs(toolkit): add a plugin example
This commit is contained in:
parent
8293392969
commit
3f8253ad76
4 changed files with 155 additions and 1 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1148,10 +1148,12 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"async-trait",
|
||||
"dirs 4.0.0",
|
||||
"fork",
|
||||
"futures",
|
||||
"pop-launcher",
|
||||
"pop-launcher-plugins",
|
||||
"pop-launcher-service",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -14,4 +14,12 @@ async-trait = "0.1.53"
|
|||
tracing = "0.1.32"
|
||||
tracing-subscriber = { version = "0.3.9", default-features = false, features = ["std", "fmt", "env-filter"] }
|
||||
dirs = "4.0.0"
|
||||
futures = "0.3.21"
|
||||
futures = "0.3.21"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = [ "rt" ] }
|
||||
fork = "0.1.19"
|
||||
|
||||
[[example]]
|
||||
name = "man-pages-plugin"
|
||||
path = "examples/man-pages-plugin.rs"
|
||||
132
toolkit/examples/man-pages-plugin.rs
Normal file
132
toolkit/examples/man-pages-plugin.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
// Copyright © 2021 System76
|
||||
|
||||
use fork::{daemon, Fork};
|
||||
use pop_launcher::{Indice, PluginResponse, PluginSearchResult};
|
||||
use pop_launcher_toolkit::plugin_trait::{async_trait, PluginExt};
|
||||
use std::io;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{exit, Command};
|
||||
|
||||
// This example demonstrate how to write a pop-launcher plugin using the `PluginExt` helper trait.
|
||||
// We are going to build a plugin to display man pages descriptions and open them on activation.
|
||||
// To do that we will use `whatis`, a command that searches the manual page names and displays their descriptions.
|
||||
|
||||
// For instance running `whatis git` would output the following :
|
||||
// ```
|
||||
// git (1) - the stupid content tracker
|
||||
// Git (3pm) - Perl interface to the Git version control system
|
||||
// ```
|
||||
|
||||
// Run `whatis` and split the output line to get a man page name and its description
|
||||
fn run_whatis(arg: &str) -> io::Result<Vec<(String, String)>> {
|
||||
let output = Command::new("whatis").arg(arg).output()?.stdout;
|
||||
|
||||
Ok(String::from_utf8_lossy(&output)
|
||||
.lines()
|
||||
.filter_map(|entry| entry.split_once('-'))
|
||||
.map(|(man_page, description)| {
|
||||
(man_page.trim().to_string(), description.trim().to_string())
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
// Open a new terminal and run `man` with the provided man page name
|
||||
fn open_man_page(arg: &str) -> io::Result<()> {
|
||||
//
|
||||
let (terminal, targ) = detect_terminal();
|
||||
|
||||
if let Ok(Fork::Child) = daemon(true, false) {
|
||||
Command::new(terminal).args(&[targ, "man", arg]).exec();
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// A helper function to detect the user default terminal.
|
||||
// If the terminal is not found, fallback to `gnome-termninal
|
||||
fn detect_terminal() -> (PathBuf, &'static str) {
|
||||
use std::fs::read_link;
|
||||
|
||||
const SYMLINK: &str = "/usr/bin/x-terminal-emulator";
|
||||
|
||||
if let Ok(found) = read_link(SYMLINK) {
|
||||
return (read_link(&found).unwrap_or(found), "-e");
|
||||
}
|
||||
|
||||
(PathBuf::from("/usr/bin/gnome-terminal"), "--")
|
||||
}
|
||||
|
||||
// Our plugin struct, holding the search results.
|
||||
#[derive(Default)]
|
||||
pub struct WhatIsPlugin {
|
||||
entries: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
// This is the main part of our plugin, defining how it will react to pop-launcher requests.
|
||||
#[async_trait]
|
||||
impl PluginExt for WhatIsPlugin {
|
||||
// Define the name of our plugin, this is mainly used to write log
|
||||
// emitted by tracing macros to `$HOME/.local/state/pop-launcher/wathis.log.
|
||||
fn name(&self) -> &str {
|
||||
"whatis"
|
||||
}
|
||||
|
||||
// Define how the plugin will react to pop-launcher search requests.
|
||||
// Note that we need to send `PluginResponse::Finished` once we are done,
|
||||
// otherwise pop-launcher will not display our search results and wait forever.
|
||||
async fn search(&mut self, query: &str) {
|
||||
// pop-launcher will only dispatch query matching the regex defined in our `plugin.ron`
|
||||
// file, can safely strip it out.
|
||||
let query = query.strip_prefix("whatis ");
|
||||
|
||||
if let Some(query) = query {
|
||||
// Whenever we get a new query, pass the query to the `whatis` helper function
|
||||
// and update our plugin entries with the result.
|
||||
match run_whatis(query) {
|
||||
Ok(entries) => self.entries = entries,
|
||||
// If we need to produce log, we use the tracing macros.
|
||||
Err(err) => tracing::error!("Error while running 'whatis' command: {err}"),
|
||||
}
|
||||
|
||||
// Now we send our entries back to the launcher. We also need a way to find our entry on activation
|
||||
// requests, here we use the entry index as an idendifier.
|
||||
for (idx, (cmd, description)) in self.entries.iter().enumerate() {
|
||||
self.respond_with(PluginResponse::Append(PluginSearchResult {
|
||||
id: idx as u32,
|
||||
name: format!("{cmd} - {description}"),
|
||||
keywords: None,
|
||||
description: description.clone(),
|
||||
icon: None,
|
||||
exec: None,
|
||||
window: None,
|
||||
}))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
// Tell pop-launcher we are done with this search request.
|
||||
self.respond_with(PluginResponse::Finished).await;
|
||||
}
|
||||
|
||||
// pop-launcher is asking for an entry activation.
|
||||
async fn activate(&mut self, id: Indice) {
|
||||
// First we try to find the requested entry in the plugin struct
|
||||
if let Some((command, _description)) = self.entries.get(id as usize) {
|
||||
// Open a new terminal with the requested man page and exit the plugin.
|
||||
if let Err(err) = open_man_page(command) {
|
||||
tracing::error!("Failed to open man page for '{command}': {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we just need to call the `run` function to start our plugin.
|
||||
// You can test it by writing request to its stdin.
|
||||
// For instance issuing a search request : `{ "Search": "whatis git" }`,
|
||||
// or activate one of the search results : `{ "Activate": 0 }`
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
WhatIsPlugin::default().run().await
|
||||
}
|
||||
12
toolkit/examples/plugin.ron
Normal file
12
toolkit/examples/plugin.ron
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
(
|
||||
name: "Find man pages",
|
||||
description: "Syntax: { whatis }\nExample: whatis git",
|
||||
query: (
|
||||
regex: "^(whatis ).+",
|
||||
help: "whatis",
|
||||
isolate: true,
|
||||
no_sort: true,
|
||||
),
|
||||
bin: (path: "man-pages-plugin"),
|
||||
icon: Name("org.gnome.Documents-symbolic"),
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue