;;; ivy-clipmenu.el --- Emacs client for clipmenu -*- lexical-binding: t -*- ;; Author: William Carroll ;; Version: 0.0.1 ;; Package-Requires: ((emacs "25.1")) ;;; 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, X, 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