aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiguel Ángel Moreno <mail@migalmoreno.com>2022-02-23 20:42:07 +0100
committerMiguel Ángel Moreno <mail@migalmoreno.com>2022-02-23 20:42:07 +0100
commit44cb065a2d0915227bec2a4f48c71ca220b206b0 (patch)
tree92fcee828c444fb6b64e4c5580e9fd8fa483c066
feat: Adds initial commit
-rw-r--r--README.org24
-rw-r--r--fdroid.el165
2 files changed, 189 insertions, 0 deletions
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..0402042
--- /dev/null
+++ b/README.org
@@ -0,0 +1,24 @@
+=fdroid.el= is an Emacs interface to [[https://github.com/mvdan/fdroidcl][fdroidcl]]. Its purpose is to aid the management of F-Droid repository packages to be installed in an Android device from the comfort of Emacs.
+
+To install it, simply put the =fdroid.el= file in your =load-path=, or optionally manually download the source and point to it.
+
+#+begin_src elisp
+(add-to-list 'load-path "path/to/source/fdroid.el")
+#+end_src
+
+This package makes use of the [[https://github.com/oantolin/embark][Embark]] and [[https://github.com/minad/consult][consult]] packages to provide a more coherent interface to the interactive commands, so ensure these are installed too. An example configuration might look like what follows.
+
+#+begin_src elisp
+(global-set-key (kbd "C-c -") #'fdroid-list-packages)
+(with-eval-after-load 'fdroid
+ (custom-set-variables
+ '(fdroid-log-events t) ; Whether messages should be logged after an operation
+ '(fdroid-program (executable-find "fdroidcl")))) ; Path to `fdroidcl'
+#+end_src
+
+In the above configuration, we only set one keybinding because =fdroid-list-packages= shows all packages available for the current F-Droid repository, and from then one can use the following Embark actions on a particular package:
+
+- ~i~ :: install the current package.
+- ~u~ :: uninstall the current package.
+- ~d~ :: download the current package.
+- ~s~ :: show more information about the current package.
diff --git a/fdroid.el b/fdroid.el
new file mode 100644
index 0000000..3c6caf4
--- /dev/null
+++ b/fdroid.el
@@ -0,0 +1,165 @@
+;; -*- lexical-binding: t; -*-
+(require 'consult)
+(require 'embark)
+
+(defgroup fdroid nil
+ "Manage F-Droid packages through `fdroidcl'."
+ :group 'external
+ :prefix "fdroid-"
+ :tag "F-Droid")
+
+(defcustom fdroid-program (executable-find "fdroidcl")
+ "Holds the executable path for `fdroidcl'."
+ :group 'froid
+ :type 'string)
+
+(defcustom fdroid-log-events nil
+ "Selects whether to log the exectuion of events."
+ :group 'fdroid
+ :type 'boolean)
+
+(cl-defmacro with--fdroidcl (commands &body body)
+ "Executes `fdroidcl' with COMMANDS and performs operations set by BODY."
+ `(with-temp-buffer
+ (apply #'call-process fdroid-program nil t nil ,commands)
+ (goto-char (point-min))
+ ,@body))
+
+(cl-defun fdroid--list-packages (&optional keywords)
+ "Lists all packages in current F-Droid repository. Optionally, filter packages by KEYWORDS
+ and returns a list of matching results."
+ (with--fdroidcl
+ (if keywords
+ `("search" ,keywords)
+ '("search"))
+ (let ((results (make-hash-table :test 'equal)))
+ (while (not (eobp))
+ (when (re-search-forward (rx (: bol (group (+ (or alpha punct)))
+ (+ blank)
+ (or "- " (group (* anychar)))
+ " - " (group (+ any))
+ "\n" (+ blank) (group (+ any))))
+ (point-at-eol 2) t)
+ (puthash (match-string 1) (list
+ :name (match-string 2)
+ :version (match-string 3)
+ :description (match-string 4))
+ results))
+ (forward-line 1))
+ results)))
+
+(defun fdroid--format-package (key value table)
+ "Embellishes package entry with KEY and VALUE from TABLE for user completion."
+ (let ((name (cl-getf value :name))
+ (version (cl-getf value :version))
+ (description (cl-getf value :description)))
+ (if (not (string-empty-p name))
+ (puthash (format "%s - %s (%s): %s" name version key description)
+ key table)
+ (puthash (format "%s - %s: %s" key version description)
+ key table))))
+
+(defun fdroid--build-candidate-list (&optional keywords)
+ "Builds candidate list with KEYWORDS to be leveraged on completion functions."
+ (let ((completion-hash (make-hash-table :test 'equal)))
+ (cl-loop for k being the hash-keys
+ in (fdroid--list-packages (or keywords)) using (hash-value v)
+ collect (fdroid--format-package k v completion-hash))
+ completion-hash))
+
+(cl-defun fdroid--prompt-completion (&key (multiple nil) (keywords nil))
+ "Prompts the user for a package from the full list of packages, or
+from the filtered list drawn from KEYWORDS if provided. If specified, prompt the user
+for a MULTIPLE package selection."
+ (interactive)
+ (let ((candidates (if keywords
+ (fdroid--build-candidate-list)
+ (fdroid--build-candidate-list keywords))))
+ (if multiple
+ (consult-completing-read-multiple
+ "Package(s): "
+ candidates)
+ (consult--read
+ candidates
+ :prompt "Package: "
+ :sort nil
+ :category 'fdroid))))
+
+;;;###autoload
+(defun fdroid-list-packages ()
+ "Main point of entry to the user facing interactive commands."
+ (interactive)
+ (fdroid--prompt-completion))
+
+;;;###autoload
+(defun fdroid-update ()
+ "Updates current F-Droid repository package index."
+ (interactive)
+ (with--fdroidcl
+ '("update")
+ (when fdroid-log-events
+ (message "Repository updated!"))))
+
+;;;###autoload
+(defun fdroid-search (keywords)
+ "Searches for packages with KEYWORDS in current F-Droid repository."
+ (interactive "sKeywords: ")
+ (fdroid--prompt-completion keywords))
+
+;;;###autoload
+(defun fdroid-install (package)
+ "Installs or upgrades a single PACKAGE on the device."
+ (interactive
+ (list (gethash (fdroid--prompt-completion) (fdroid--build-candidate-list))))
+ (with--fdroidcl
+ `("install" ,package)
+ (when fdroid-log-events
+ (message "Package \"%s\" successfully installed on device." package))))
+
+;;;###autoload
+(defun fdroid-install-multiple (packages)
+ "Installs or upgrades multiple PACKAGES on the device."
+ (interactive
+ (list (mapcar (lambda (e)
+ (gethash e (fdroid--build-candidate-list)))
+ (fdroid--prompt-completion :multiple t))))
+ (with--fdroidcl
+ `("install" ,@packages)
+ (when fdroid-log-events
+ (message "Packages \"%s\" successfully installed on device." (mapconcat #'identity packages " ")))))
+
+;;;###autoload
+(defun fdroid-uninstall (package)
+ "Uninstalls PACKAGE from device."
+ (interactive
+ (list (gethash (fdroid--prompt-completion) (fdroid--build-candidate-list))))
+ (with--fdroidcl
+ `("uninstall" ,package)
+ (when fdroid-log-events
+ (message "Package \"%s\" successfully uninstalled from device." package))))
+
+;;;###autoload
+(defun fdroid-download (package)
+ "Downloads PACKAGE to the device."
+ (interactive
+ (list (gethash (fdroid--prompt-completion) (fdroid--build-candidate-list))))
+ (with--fdroidcl
+ `("download" ,package)
+ (when fdroid-log-events
+ (message "Package \"%s\" successfully downloaded to device." package))))
+
+;;;###autoload
+(defun fdroid-show (package)
+ "Shows detailed information about PACKAGE."
+ (interactive))
+
+(embark-define-keymap embark-fdroid-actions
+ "Keymap for `fdroidcl' actions which take F-Droid package identifiers."
+ ("i" fdroid-install)
+ ("d" fdroid-download)
+ ("u" fdroid-uninstall)
+ ("s" fdroid-show))
+
+(add-to-list 'embark-keymap-alist '(fdroid . embark-fdroid-actions))
+
+(provide 'fdroid)