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.
This commit is contained in:
William Carroll 2020-01-16 14:49:50 +00:00
parent 47faf8d6f0
commit a3077677f9
2 changed files with 134 additions and 149 deletions

View file

@ -1,149 +0,0 @@
;;; clipmenu.el --- Emacs client for clipmenu -*- lexical-binding: t -*-
;; Author: William Carroll <wpcarro@gmail.com>
;;; 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

View file

@ -0,0 +1,134 @@
;;; ivy-clipmenu.el --- Emacs client for clipmenu -*- lexical-binding: t -*-
;; Author: William Carroll <wpcarro@gmail.com>
;;; 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