feat(calc): Convert to Rust w/ Qalc
This commit is contained in:
parent
dae8108cb1
commit
bc1fc717b1
11 changed files with 154 additions and 157 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
|
@ -799,6 +799,7 @@ dependencies = [
|
||||||
"new_mime_guess",
|
"new_mime_guess",
|
||||||
"pop-launcher",
|
"pop-launcher",
|
||||||
"postage",
|
"postage",
|
||||||
|
"regex",
|
||||||
"ron",
|
"ron",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
@ -1186,9 +1187,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.18"
|
version = "0.1.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052"
|
checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
3
Makefile
3
Makefile
|
|
@ -75,8 +75,7 @@ install:
|
||||||
ln -sf $(BIN) $(PLUGIN_DIR)/web/web
|
ln -sf $(BIN) $(PLUGIN_DIR)/web/web
|
||||||
|
|
||||||
# Calculator plugin
|
# Calculator plugin
|
||||||
install -Dm0755 plugins/src/calc/calc.js $(PLUGIN_DIR)/calc
|
ln -sf $(BIN) $(PLUGIN_DIR)/calc/calc
|
||||||
install -Dm0644 plugins/src/calc/math.js $(PLUGIN_DIR)/calc
|
|
||||||
|
|
||||||
# Files plugin
|
# Files plugin
|
||||||
install -Dm0755 plugins/src/files/files.js $(PLUGIN_DIR)/files
|
install -Dm0755 plugins/src/files/files.js $(PLUGIN_DIR)/files
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ fn main() {
|
||||||
let start = plugin.rfind('/').map(|v| v + 1).unwrap_or(0);
|
let start = plugin.rfind('/').map(|v| v + 1).unwrap_or(0);
|
||||||
let cmd = &plugin.as_str()[start..];
|
let cmd = &plugin.as_str()[start..];
|
||||||
match cmd {
|
match cmd {
|
||||||
|
"calc" => block_on(plugins::calc::main()),
|
||||||
"desktop-entries" => block_on(plugins::desktop_entries::main()),
|
"desktop-entries" => block_on(plugins::desktop_entries::main()),
|
||||||
"find" => block_on(plugins::find::main()),
|
"find" => block_on(plugins::find::main()),
|
||||||
"pop-launcher" => block_on(service::main()),
|
"pop-launcher" => block_on(service::main()),
|
||||||
|
|
|
||||||
2
debian/control
vendored
2
debian/control
vendored
|
|
@ -11,7 +11,7 @@ Homepage: https://github.com/pop-os/launcher
|
||||||
|
|
||||||
Package: pop-launcher
|
Package: pop-launcher
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
Depends: qalc, fd-find, ${misc:Depends}, ${shlibs:Depends}
|
||||||
Description: Modular IPC-based desktop launcher service
|
Description: Modular IPC-based desktop launcher service
|
||||||
|
|
||||||
Package: pop-launcher-system76-power
|
Package: pop-launcher-system76-power
|
||||||
|
|
|
||||||
1
debian/pop-launcher.links
vendored
1
debian/pop-launcher.links
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
|
/usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/calc/calc
|
||||||
/usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/desktop_entries/desktop-entries
|
/usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/desktop_entries/desktop-entries
|
||||||
/usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/find/find
|
/usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/find/find
|
||||||
/usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/pop_shell/pop-shell
|
/usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/pop_shell/pop-shell
|
||||||
|
|
|
||||||
|
|
@ -6,21 +6,22 @@ edition = "2018"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
fork = "0.1"
|
||||||
freedesktop-desktop-entry = "0.3"
|
freedesktop-desktop-entry = "0.3"
|
||||||
futures_codec = "0.4"
|
futures_codec = "0.4"
|
||||||
futures-lite = "1"
|
futures-lite = "1"
|
||||||
fork = "0.1"
|
|
||||||
new_mime_guess = "3"
|
new_mime_guess = "3"
|
||||||
pop-launcher = { path = "../" }
|
pop-launcher = { path = "../" }
|
||||||
|
postage = "0.4"
|
||||||
|
regex = "1"
|
||||||
|
ron = "0.6"
|
||||||
serde = "1"
|
serde = "1"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
slab = "0.4"
|
||||||
smol = "1"
|
smol = "1"
|
||||||
strsim = "0.10"
|
strsim = "0.10"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.2"
|
tracing-subscriber = "0.2"
|
||||||
|
urlencoding = "2"
|
||||||
zbus = "1"
|
zbus = "1"
|
||||||
zvariant = "=2.6" # Restrict for 1.47
|
zvariant = "=2.6" # Restrict for 1.47
|
||||||
ron = "0.6.4"
|
|
||||||
urlencoding = "2.1.0"
|
|
||||||
slab = "0.4.4"
|
|
||||||
postage = "0.4.1"
|
|
||||||
|
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
#!/usr/bin/gjs
|
|
||||||
|
|
||||||
const { GLib, Gio } = imports.gi;
|
|
||||||
|
|
||||||
/** The directory that this script is executed from. */
|
|
||||||
const SCRIPT_DIR = GLib.path_get_dirname(new Error().stack.split(':')[0].slice(1));
|
|
||||||
|
|
||||||
/** Add our directory so we can import modules from it. */
|
|
||||||
imports.searchPath.push(SCRIPT_DIR)
|
|
||||||
|
|
||||||
const math = imports.math.math;
|
|
||||||
math.config({number: 'BigNumber' });
|
|
||||||
|
|
||||||
const STDIN = new Gio.DataInputStream({ base_stream: new Gio.UnixInputStream({ fd: 0 }) })
|
|
||||||
const STDOUT = new Gio.DataOutputStream({ base_stream: new Gio.UnixOutputStream({ fd: 1 }) })
|
|
||||||
|
|
||||||
class App {
|
|
||||||
constructor() {
|
|
||||||
this.last_query = ""
|
|
||||||
this.last_value = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
search(input) {
|
|
||||||
this.last_query = input.substr(1)
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.last_value = math.evaluate(this.last_query).toString()
|
|
||||||
} catch (e) {
|
|
||||||
this.last_value = this.last_query + ` x = ?`
|
|
||||||
}
|
|
||||||
|
|
||||||
this.send({ "Append": {
|
|
||||||
id: 0,
|
|
||||||
name: this.last_value,
|
|
||||||
description: '',
|
|
||||||
icon: { Name: 'accessories-calculator' },
|
|
||||||
}})
|
|
||||||
|
|
||||||
this.send("Finished")
|
|
||||||
}
|
|
||||||
|
|
||||||
activate(_id) {
|
|
||||||
this.send({ "Fill": '= ' + this.last_value })
|
|
||||||
}
|
|
||||||
|
|
||||||
send(object) {
|
|
||||||
STDOUT.write_bytes(new GLib.Bytes(JSON.stringify(object) + "\n"), null)
|
|
||||||
STDOUT.flush(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function main() {
|
|
||||||
/** @type {null | ByteArray} */
|
|
||||||
let input_array
|
|
||||||
|
|
||||||
/** @type {string} */
|
|
||||||
let input_str
|
|
||||||
|
|
||||||
/** @type {null | LauncherRequest} */
|
|
||||||
let event
|
|
||||||
|
|
||||||
let app = new App()
|
|
||||||
|
|
||||||
mainloop:
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
[input_array,] = STDIN.read_line(null)
|
|
||||||
} catch (e) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
input_str = imports.byteArray.toString(input_array)
|
|
||||||
if ((event = parse_event(input_str)) !== null) {
|
|
||||||
if ("Search" in event) {
|
|
||||||
app.search(event.Search);
|
|
||||||
} else if ("Activate" in event) {
|
|
||||||
app.activate(event.Activate);
|
|
||||||
} else if ("Exit" === event) {
|
|
||||||
break mainloop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an IPC event received from STDIN
|
|
||||||
* @param {string} input
|
|
||||||
* @returns {null | LauncherRequest}
|
|
||||||
*/
|
|
||||||
function parse_event(input) {
|
|
||||||
try {
|
|
||||||
return JSON.parse(input)
|
|
||||||
} catch (e) {
|
|
||||||
log(`Input not valid JSON`)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
||||||
File diff suppressed because one or more lines are too long
139
plugins/src/calc/mod.rs
Normal file
139
plugins/src/calc/mod.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
use futures_lite::{AsyncBufReadExt, AsyncWriteExt, StreamExt};
|
||||||
|
use pop_launcher::*;
|
||||||
|
use regex::Regex;
|
||||||
|
use smol::{
|
||||||
|
process::{Command, Stdio},
|
||||||
|
Unblock,
|
||||||
|
};
|
||||||
|
use std::{borrow::Cow, io};
|
||||||
|
|
||||||
|
pub async fn main() {
|
||||||
|
let mut requests = json_input_stream(async_stdin());
|
||||||
|
|
||||||
|
let mut app = App::default();
|
||||||
|
|
||||||
|
while let Some(result) = requests.next().await {
|
||||||
|
match result {
|
||||||
|
Ok(request) => match request {
|
||||||
|
Request::Activate(_) => app.activate().await,
|
||||||
|
Request::ActivateContext { .. } => app.activate_context().await,
|
||||||
|
Request::Context(_) => app.context().await,
|
||||||
|
Request::Search(query) => app.search(&query).await,
|
||||||
|
Request::Exit => break,
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Err(why) => {
|
||||||
|
tracing::error!("malformed JSON input: {}", why);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct App {
|
||||||
|
out: Unblock<io::Stdout>,
|
||||||
|
outcome: Option<String>,
|
||||||
|
regex: Regex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for App {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
out: async_stdout(),
|
||||||
|
outcome: None,
|
||||||
|
regex: Regex::new("\\x1B\\[(?:;?[0-9]{1,3})+[mGK]").expect("bad regex for qalc"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub async fn activate(&mut self) {
|
||||||
|
if let Some(mut outcome) = self.outcome.take() {
|
||||||
|
outcome = ["= ", outcome.as_str()].concat();
|
||||||
|
crate::send(&mut self.out, PluginResponse::Fill(outcome)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn activate_context(&mut self) {
|
||||||
|
crate::xdg_open("https://qalculate.github.io/manual/qalc.html");
|
||||||
|
crate::send(&mut self.out, PluginResponse::Close).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn context(&mut self) {
|
||||||
|
let options = vec![ContextOption {
|
||||||
|
id: 0,
|
||||||
|
name: "Qalc Manual".into(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
crate::send(&mut self.out, PluginResponse::Context { id: 0, options }).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn search(&mut self, query: &str) {
|
||||||
|
if let Some(mut search) = query.strip_prefix("=") {
|
||||||
|
search = search.trim();
|
||||||
|
self.outcome = qcalc(&mut self.regex, search).await;
|
||||||
|
|
||||||
|
crate::send(
|
||||||
|
&mut self.out,
|
||||||
|
PluginResponse::Append(PluginSearchResult {
|
||||||
|
id: 0,
|
||||||
|
name: self
|
||||||
|
.outcome
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| [search, " x = ?"].concat()),
|
||||||
|
description: "Math expressions by Qalc".to_owned(),
|
||||||
|
icon: Some(IconSource::Name(Cow::Borrowed("accessories-calculator"))),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
crate::send(&mut self.out, PluginResponse::Finished).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn qcalc(regex: &mut Regex, expression: &str) -> Option<String> {
|
||||||
|
let mut child = Command::new("qalc")
|
||||||
|
.env("LANG", "C")
|
||||||
|
.arg("-t")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.spawn()
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if let Some(mut stdin) = child.stdin.take() {
|
||||||
|
let _ = stdin
|
||||||
|
.write_all([expression, "\n"].concat().as_bytes())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(stdout) = child.stdout.take() {
|
||||||
|
let mut reader = smol::io::BufReader::new(stdout).lines().skip(2);
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
|
while let Some(Ok(line)) = reader.next().await {
|
||||||
|
let line = line.trim();
|
||||||
|
|
||||||
|
if line.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let normalized = regex.replace_all(line, "");
|
||||||
|
|
||||||
|
if normalized.starts_with("error") {
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
if !output.is_empty() {
|
||||||
|
output.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push_str(normalized.as_ref());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,6 @@
|
||||||
help: "= ",
|
help: "= ",
|
||||||
isolate: true,
|
isolate: true,
|
||||||
),
|
),
|
||||||
bin: (path: "calc.js"),
|
bin: (path: "calc"),
|
||||||
icon: Name("x-office-spreadsheet")
|
icon: Name("x-office-spreadsheet")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod calc;
|
||||||
pub mod desktop_entries;
|
pub mod desktop_entries;
|
||||||
pub mod find;
|
pub mod find;
|
||||||
pub mod pop_shell;
|
pub mod pop_shell;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue