refactor(tazjin/gio-list-apps): refactor into dynamic Emacs module

Instead of producing a binary that gets called by Emacs, with
input/output serialisation, use a dynamic Emacs module that lets Emacs
more-or-less directly call the relevant GTK functions.

I'm doing this mostly as an experiment. Might be interesting to end up
with a dynamic module that I can dump some experimental code into that
improves my workflows.

To do this, I've exposed the emacs binary used by my Emacs
configuration in an additional `passthru` field. This ensures that the
module is linked against the right version of Emacs.

Change-Id: I1426994fe3455ed1b2a685c5a09705e29fa40950
Reviewed-on: https://cl.tvl.fyi/c/depot/+/9163
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
Autosubmit: tazjin <tazjin@tvl.su>
This commit is contained in:
Vincent Ambo 2023-08-29 15:43:13 +03:00 committed by clbot
parent d6bce3f83d
commit e5f7fe430d
7 changed files with 178 additions and 52 deletions

View file

@ -2,6 +2,8 @@
(require 'dash) (require 'dash)
(require 'map) (require 'map)
(require 'gio-list-apps) ;; native module!
(defun goto-line-with-feedback () (defun goto-line-with-feedback ()
"Show line numbers temporarily, while prompting for the line number input" "Show line numbers temporarily, while prompting for the line number input"
(interactive) (interactive)
@ -336,12 +338,7 @@ names, instead of only their names (which might change)."
"Use `//users/tazjin/gio-list-apps' to retrieve a list of "Use `//users/tazjin/gio-list-apps' to retrieve a list of
installed (and visible) XDG apps, and let users launch them." installed (and visible) XDG apps, and let users launch them."
(interactive) (interactive)
(let* ((apps-json (s-lines (s-trim (shell-command-to-string "gio-list-apps")))) (let* ((apps (taz-list-xdg-apps))
(apps (seq-map (lambda (app)
(let ((parsed (json-parse-string app :object-type 'alist)))
(cons (cdr (assoc 'name parsed))
(cdr (assoc 'commandline parsed)))))
apps-json))
;; Display the command that will be run as an annotation ;; Display the command that will be run as an annotation
(completion-extra-properties (completion-extra-properties

View file

@ -13,7 +13,6 @@ pkgs.makeOverridable
# $PATH for binaries that need to be available to Emacs # $PATH for binaries that need to be available to Emacs
emacsBinPath = lib.makeBinPath [ emacsBinPath = lib.makeBinPath [
depot.users.tazjin.gio-list-apps
(currentTelega pkgs.emacsPackages) (currentTelega pkgs.emacsPackages)
pkgs.libwebp # for dwebp, required by telega pkgs.libwebp # for dwebp, required by telega
]; ];
@ -100,6 +99,9 @@ pkgs.makeOverridable
tvlPackages.rcirc tvlPackages.rcirc
tvlPackages.term-switcher tvlPackages.term-switcher
tvlPackages.tvl tvlPackages.tvl
# Dynamic/native modules
depot.users.tazjin.gio-list-apps
]))); ])));
# Tired of telega.el runtime breakages through tdlib # Tired of telega.el runtime breakages through tdlib
@ -138,6 +140,12 @@ pkgs.makeOverridable
'').overrideAttrs '').overrideAttrs
(_: { (_: {
passthru = { passthru = {
# Expose original Emacs used for my configuration.
inherit emacs;
# Expose the pure emacs with all packages.
emacsWithPackages = tazjinsEmacs f;
# Call overrideEmacs with a function (pkgs -> pkgs) to modify the # Call overrideEmacs with a function (pkgs -> pkgs) to modify the
# packages that should be included in this Emacs distribution. # packages that should be included in this Emacs distribution.
overrideEmacs = f': self l f'; overrideEmacs = f': self l f';

View file

@ -2,6 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -24,12 +30,96 @@ dependencies = [
"target-lexicon", "target-lexicon",
] ]
[[package]]
name = "ctor"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
dependencies = [
"quote",
"syn 1.0.109",
]
[[package]]
name = "darling"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
dependencies = [
"darling_core",
"quote",
"syn 1.0.109",
]
[[package]]
name = "emacs"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6797a940189d353de79bec32abe717aeeecd79a08236e84404c888354e040665"
dependencies = [
"anyhow",
"ctor",
"emacs-macros",
"emacs_module",
"once_cell",
"rustc_version",
"thiserror",
]
[[package]]
name = "emacs-macros"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69656fdfe7c2608b87164964db848b5c3795de7302e3130cce7131552c6be161"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "emacs_module"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3067bc974045ed2c6db333bd4fc30d3bdaafa6421a9a889fa7b2826b6f7f2fa"
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.28" version = "0.3.28"
@ -116,8 +206,8 @@ dependencies = [
name = "gio-list-apps" name = "gio-list-apps"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"emacs",
"gio", "gio",
"serde_json",
] ]
[[package]] [[package]]
@ -203,6 +293,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.0.0" version = "2.0.0"
@ -213,12 +309,6 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.147" version = "0.2.147"
@ -308,10 +398,28 @@ dependencies = [
] ]
[[package]] [[package]]
name = "ryu" name = "rustc_version"
version = "1.0.15" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "serde" name = "serde"
@ -333,17 +441,6 @@ dependencies = [
"syn 2.0.29", "syn 2.0.29",
] ]
[[package]]
name = "serde_json"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.3" version = "0.6.3"
@ -368,6 +465,12 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "strsim"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -375,6 +478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote",
"unicode-ident", "unicode-ident",
] ]

View file

@ -3,8 +3,9 @@ name = "gio-list-apps"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib]
crate-type = ["cdylib"]
[dependencies] [dependencies]
emacs = "0.18.0"
gio = "0.18.1" gio = "0.18.1"
serde_json = "1.0.105"

View file

@ -1,9 +1,14 @@
{ pkgs, lib, ... }: { depot, pkgs, lib, ... }:
pkgs.rustPlatform.buildRustPackage { pkgs.rustPlatform.buildRustPackage {
name = "gio-list-apps"; name = "gio-list-apps";
src = lib.cleanSource ./.; src = lib.cleanSource ./.;
cargoLock.lockFile = ./Cargo.lock; cargoLock.lockFile = ./Cargo.lock;
nativeBuildInputs = [ pkgs.pkg-config ]; nativeBuildInputs = [ pkgs.pkg-config ];
buildInputs = [ pkgs.gtk3 ]; buildInputs = [ pkgs.gtk3 depot.users.tazjin.emacs.emacs ];
postInstall = ''
mkdir -p $out/share/emacs/site-lisp
ln -s $out/lib/libgio_list_apps.so $out/share/emacs/site-lisp/gio-list-apps.so
'';
} }

View file

@ -0,0 +1,31 @@
use emacs::{defun, Env, IntoLisp, Result, Value};
use gio::traits::AppInfoExt;
use gio::AppInfo;
emacs::plugin_is_GPL_compatible!();
#[emacs::module(defun_prefix = "taz", mod_in_name = false)]
fn init(_: &Env) -> Result<()> {
Ok(())
}
/// Returns an alist of the currently available XDG applications (through their
/// `.desktop' shortcuts), and the command line parameters needed to start them.
///
/// Hidden applications or applications without specified command-line
/// parameters are not included.
#[defun]
fn list_xdg_apps(env: &Env) -> Result<Value> {
let mut visible_apps: Vec<Value> = vec![];
for app in AppInfo::all().into_iter().filter(AppInfo::should_show) {
if let Some(cmd) = app
.commandline()
.and_then(|p| Some(p.to_str()?.to_string()))
{
visible_apps.push(env.cons(app.name().as_str().into_lisp(env)?, cmd.into_lisp(env)?)?);
}
}
env.list(&visible_apps)
}

View file

@ -1,20 +0,0 @@
use gio::traits::AppInfoExt;
use gio::AppInfo;
use serde_json::json;
fn main() {
for app in AppInfo::all() {
if app.should_show() {
if let Some(cmd) = app.commandline() {
println!(
"{}",
json!({
"name": app.name().as_str(),
"display_name": app.display_name().as_str(),
"commandline": cmd,
})
);
}
}
}
}