From a3077677f967ad299f49f8898cb9af4072462ce9 Mon Sep 17 00:00:00 2001 From: William Carroll Date: Thu, 16 Jan 2020 14:49:50 +0000 Subject: [PATCH] Rename clipmenu.el to ivy-clipmenu.el TL;DR: Preparing ivy-clipmenu for publishing. Also: - Removes lingering TODO items. - Clarifies module and function documentation. - Defines groups for custom variables. - Supports history variable for ivy-read. --- configs/shared/.emacs.d/wpc/clipmenu.el | 149 -------------------- configs/shared/.emacs.d/wpc/ivy-clipmenu.el | 134 ++++++++++++++++++ 2 files changed, 134 insertions(+), 149 deletions(-) delete mode 100644 configs/shared/.emacs.d/wpc/clipmenu.el create mode 100644 configs/shared/.emacs.d/wpc/ivy-clipmenu.el diff --git a/configs/shared/.emacs.d/wpc/clipmenu.el b/configs/shared/.emacs.d/wpc/clipmenu.el deleted file mode 100644 index ba48a9091..000000000 --- a/configs/shared/.emacs.d/wpc/clipmenu.el +++ /dev/null @@ -1,149 +0,0 @@ -;;; clipmenu.el --- Emacs client for clipmenu -*- lexical-binding: t -*- -;; Author: William Carroll - -;;; Commentary: -;; Ivy integration with the excellent program, clipmenu. -;; -;; clipmenu is a simple clipboard manager xsel. Usually clipmenu integrates with -;; dmenu. This Emacs module integrates with ivy. Launch this when you want to -;; select a clip. -;; -;; The following environment variables allow you to customize clipmenu's -;; behavior: -;; -;; - CM_DIR: specify the base directory to store the cache dir in -;; (default: $XDG_RUNTIME_DIR, $TMPDIR, or /tmp) -;; - CM_HISTLENGTH: specify the number of lines to show in ivy. (default: 8) -;; -;; Other variables for customizing clipmenu are defined herein. -;; -;; For more information, see `clipmenu --help`. - -;;; Code: - -;; TODO: Support an ivy action of deleting an entry from clipmenu. - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Dependencies -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(require 'f) -(require 's) -(require 'dash) - -(prelude/assert - (prelude/executable-exists? "clipmenud")) - -(prelude/assert - (prelude/executable-exists? "clipmenu")) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Variables -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; TODO: Remove this if you're publishing it. -(defcustom clipmenu/install-kbds? t - "When t, install the keybindings defined herein") - -(defcustom clipmenu/directory - (or (getenv "XDG_RUNTIME_DIR") - (getenv "TMPDIR") - "/tmp") - "Base directory for clipmenu data.") - -(defconst clipmenu/major-version 5 - "The major version number for clipmenu.") - -(defconst clipmenu/cache-directory - (f-join clipmenu/directory - (format "clipmenu.%s.%s" - clipmenu/major-version - (getenv "USER"))) - "Directory where the clips are stored.") - -(defconst clipmenu/cache-file-pattern - (f-join clipmenu/cache-directory "line_cache_*") - "Glob pattern matching the locations on disk for clipmenu's labels.") - -(defcustom clipmenu/history-length - (or (getenv "CM_HISTLENGTH") 50) - "Limit the number of clips in the history. -This value defaults to 50.") - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Functions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun clipmenu/parse-line (x) - "Parse the entry in the clipmenu's line-cache." - (string-to-number - (list/join "" (parsec-with-input x (parsec-count 19 (parsec-digit)))))) - -(defun clipmenu/parse-content (x) - "Parse the label from the entry in clipmenu's line-cache." - (list/join - "" - (parsec-with-input x - (parsec-count 19 (parsec-digit)) - (parsec-str " ") - (parsec-many (parsec-any-ch))))) - -(defun clipmenu/list-clips () - "Return a list of the content of all of the clips." - (->> clipmenu/cache-file-pattern - f-glob - (-map (lambda (path) - (s-split "\n" (f-read path) t))) - -flatten - (-reject #'s-blank?) - (-sort (lambda (a b) - (< (clipmenu/parse-line a) - (clipmenu/parse-line b)))) - (-map #'clipmenu/parse-content) - list/dedupe-adjacent - (-take clipmenu/history-length))) - -;; TODO: Add tests. -(defun clipmenu/escape-quotes (x) - "Escape double and single quotes in X." - (->> x - (s-replace "\"" "\\\"") - (s-replace "'" "\\'"))) - -(defun clipmenu/line-to-clip (line) - "Map the chosen LINE to a clip stored on disk." - (->> line - clipmenu/checksum - (f-join clipmenu/cache-directory) - f-read - clipboard/copy)) - -;; TODO: Consider supporting :history keyword. - -;; TODO: Ensure you can handle special characters like: -;; r}_rh,pmj~kCR.<5w"PUk#Z^>. - -(defun clipmenu/ivy-copy () - "Use `ivy-read' to select and copy a clip." - (interactive) - (let ((ivy-sort-functions-alist nil)) - (ivy-read "Clipmenu: " - (clipmenu/list-clips) - :action #'clipmenu/line-to-clip))) - -(defun clipmenu/checksum (content) - "Return the CRC checksum of CONTENT." - (s-trim-right - (prelude/call-process-to-string - "/bin/bash" "-c" (string/format "/usr/bin/cksum <<<'%s'" content)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Keybindings -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(when clipmenu/install-kbds? - (exwm-input-set-key - (kbd "C-M-v") #'clipmenu/ivy-copy)) - -(provide 'clipmenu) -;;; clipmenu.el ends here diff --git a/configs/shared/.emacs.d/wpc/ivy-clipmenu.el b/configs/shared/.emacs.d/wpc/ivy-clipmenu.el new file mode 100644 index 000000000..f3896137b --- /dev/null +++ b/configs/shared/.emacs.d/wpc/ivy-clipmenu.el @@ -0,0 +1,134 @@ +;;; ivy-clipmenu.el --- Emacs client for clipmenu -*- lexical-binding: t -*- +;; Author: William Carroll + +;;; Commentary: +;; Ivy integration with the clipboard manager, clipmenu. Essentially, clipmenu +;; turns your system clipboard into a list. +;; +;; To use this module, you must first install clipmenu and ensure that the +;; clipmenud daemon is running. Refer to the installation instructions at +;; github.com/cdown/clipmenu for those details. +;; +;; This module intentionally does not define any keybindings since I'd prefer +;; not to presume my users' preferences. Personally, I use EXWM as my window +;; manager, so I call `exwm-input-set-key' and map it to `ivy-clipmenu/copy'. +;; +;; Usually clipmenu integrates with rofi or dmenu. This Emacs module integrates +;; with ivy. Launch this when you want to select a clip. +;; +;; Clipmenu itself supports a variety of environment variables that allow you to +;; customize its behavior. These variables are respected herein. If you'd +;; prefer to customize clipmenu's behavior from within Emacs, refer to the +;; variables defined in this module. +;; +;; For more information: +;; - See `clipmenu --help`. +;; - Visit github.com/cdown/clipmenu. + +;;; Code: + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Dependencies +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(require 'f) +(require 's) +(require 'dash) +(require 'ivy) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Variables +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defgroup ivy-clipmenu nil + "Ivy integration for clipmenu." + :group 'ivy) + +(defcustom ivy-clipmenu/directory + (or (getenv "XDG_RUNTIME_DIR") + (getenv "TMPDIR") + "/tmp") + "Base directory for clipmenu's data." + :type 'string + :group 'ivy-clipmenu) + +(defconst ivy-clipmenu/executable-version 5 + "The major version number for the clipmenu executable.") + +(defconst ivy-clipmenu/cache-directory + (f-join ivy-clipmenu/directory + (format "clipmenu.%s.%s" + ivy-clipmenu/executable-version + (getenv "USER"))) + "Directory where the clips are stored.") + +(defconst ivy-clipmenu/cache-file-pattern + (f-join ivy-clipmenu/cache-directory "line_cache_*") + "Glob pattern matching the locations on disk for clipmenu's labels.") + +(defcustom ivy-clipmenu/history-length + (or (getenv "CM_HISTLENGTH") 25) + "Limit the number of clips in the history. +This value defaults to 25.") + +(defvar ivy-clipmenu/history nil + "History for `ivy-clipmenu/copy'.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Functions +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun ivy-clipmenu/parse-content (x) + "Parse the label from the entry in clipmenu's line-cache." + (->> (s-split " " x) + (-drop 1) + (s-join " "))) + +(defun ivy-clipmenu/list-clips () + "Return a list of the content of all of the clips." + (->> ivy-clipmenu/cache-file-pattern + f-glob + (-map (lambda (path) + (s-split "\n" (f-read path) t))) + -flatten + (-reject #'s-blank?) + (-sort #'string>) + (-map #'ivy-clipmenu/parse-content) + delete-dups + (-take ivy-clipmenu/history-length))) + +(defun ivy-clipmenu/checksum (content) + "Return the CRC checksum of CONTENT." + (s-trim-right + (with-temp-buffer + (call-process "/bin/bash" nil (current-buffer) nil "-c" + (format "cksum <<<'%s'" content)) + (buffer-string)))) + +(defun ivy-clipmenu/line-to-content (line) + "Map the chosen LINE from the line cache its content from disk." + (->> line + ivy-clipmenu/checksum + (f-join ivy-clipmenu/cache-directory) + f-read)) + +(defun ivy-clipmenu/do-copy (x) + "Copy string, X, to the system clipboard." + (kill-new x) + (message "[ivy-clipmenu.el] Copied!")) + +(defun ivy-clipmenu/copy () + "Use `ivy-read' to select and copy a clip. +It's recommended to bind this function to a globally available keymap." + (interactive) + (let ((ivy-sort-functions-alist nil)) + (ivy-read "Clipmenu: " + (ivy-clipmenu/list-clips) + :history 'ivy-clipmenu/history + :action (lambda (line) + (->> line + ivy-clipmenu/line-to-content + ivy-clipmenu/do-copy))))) + +(provide 'ivy-clipmenu) +;;; ivy-clipmenu.el ends here