├── .travis.yml ├── Makefile ├── README.md ├── clipmon.el ├── clipmon.wav ├── script └── make-readme.el └── test └── clipmon-test.el /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | # .travis.yml 3 | # build script for travis continuous integration 4 | # -------------------------------------------------------------------------------- 5 | 6 | # see https://travis-ci.org/bburns/clipmon 7 | # adapted from https://github.com/magnars/dash.el/blob/master/.travis.yml 8 | # and https://github.com/company-mode/company-mode/blob/master/.travis.yml 9 | 10 | # the emacs snapshot is from here, and should be the latest version (ie 25.1): 11 | # https://launchpad.net/~ubuntu-elisp/+archive/ubuntu/ppa 12 | 13 | # validate this file here: http://lint.travis-ci.org/ 14 | 15 | 16 | # travis-lint no longer permits emacs-lisp 17 | language: generic 18 | 19 | # test on linux and osx (windows not available on travis ci yet). 20 | # this os key also multiplies the build matrix (set below). 21 | os: 22 | - linux 23 | - osx 24 | 25 | # build matrix - test on different emacs versions 26 | env: 27 | matrix: 28 | - EMACS=emacs24 29 | - EMACS=emacs25 30 | 31 | install: 32 | 33 | # linux 34 | - if [[ "$TRAVIS_OS_NAME-$EMACS" == "linux-emacs24" ]]; then 35 | sudo add-apt-repository -y ppa:cassou/emacs && 36 | sudo apt-get update -qq && 37 | sudo apt-get install -qq emacs24 emacs24-el; 38 | fi 39 | - if [[ "$TRAVIS_OS_NAME-$EMACS" == "linux-emacs25" ]]; then 40 | sudo add-apt-repository -y ppa:ubuntu-elisp/ppa && 41 | sudo apt-get update -qq && 42 | sudo apt-get -qq -f install && 43 | sudo apt-get install -qq emacs-snapshot; 44 | fi 45 | 46 | # osx 47 | # build from source - per INSTALL instructions [slow] 48 | # wget http://mirrors.kernel.org/gnu/emacs/emacs-24.5.tar.gz 49 | # tar xf emacs-24.5.tar.gz 50 | # cd emacs-24.5 51 | # .configure --with-ns 52 | # install with brew [builds from source, took ~15mins] 53 | # brew install emacs ; 54 | # even using the .dmg files takes ~6mins each (due to including so many binaries in them?) 55 | - if [[ "$TRAVIS_OS_NAME-$EMACS" == "osx-emacs24" ]]; then 56 | wget https://emacsformacosx.com/emacs-builds/Emacs-24.5-universal.dmg && 57 | hdiutil attach Emacs-24.5-universal.dmg && 58 | sudo ln -s /Volumes/Emacs/Emacs.app/Contents/MacOS/Emacs /usr/local/bin/emacs; 59 | fi 60 | - if [[ "$TRAVIS_OS_NAME-$EMACS" == "osx-emacs25" ]]; then 61 | wget https://emacsformacosx.com/emacs-builds/Emacs-25.1-universal.dmg && 62 | hdiutil attach Emacs-25.1-universal.dmg && 63 | sudo ln -s /Volumes/Emacs/Emacs.app/Contents/MacOS/Emacs /usr/local/bin/emacs; 64 | fi 65 | 66 | 67 | script: 68 | make test 69 | 70 | # By default, a build email is sent to the committer and the author, 71 | # but only if they have access to the repository the commit was pushed to. 72 | notifications: 73 | email: 74 | on_success: always 75 | on_failure: always 76 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # makefile for emacs .tar packages - partially working 3 | # switch to cask, eventually 4 | 5 | 6 | # name of package 7 | PACKAGE = clipmon 8 | 9 | # any extra files or folders for the .tar file 10 | CONTENTS := clipmon.wav 11 | 12 | # ------------------------------------------------------------------------------ 13 | 14 | EMACS = emacs 15 | SOURCE = ${PACKAGE}.el 16 | TEST = test/${PACKAGE}-test.el 17 | MAKE-README = script/make-readme.el 18 | 19 | # parse package metadata 20 | # currently handles only one keyword, and no dependencies 21 | # eg need :keywords '("speed" "convenience")) 22 | #. better to call an elisp fn to parse this stuff and make -pkg file, 23 | # or use cask. 24 | # note: grep -P for perl regexp, -o to just output match 25 | # ?<= is the look-behind operator - match is not included in output 26 | DESCRIPTION != grep ";;; ${SOURCE} --- " ${SOURCE} | grep -Po "(?<= --- ).+" 27 | VERSION != grep ";; Version:" ${SOURCE} | grep -Po [0-9]+ 28 | HOMEPAGE != grep ";; URL:" ${SOURCE} | grep -Po "(?<=;; URL: ).+" 29 | KEYWORDS != grep ";; Keywords:" ${SOURCE} | grep -Po "(?<=;; Keywords: ).+" 30 | KEYWORDS := "\"${KEYWORDS}\"" 31 | DEPENDENCIES = "nil" 32 | 33 | # for tar 34 | PKG = ${PACKAGE}-pkg.el 35 | PACKAGE_DIR := ${PACKAGE}-${VERSION} 36 | PACKAGE_TAR := ${PACKAGE}-${VERSION}.tar 37 | TAR_DIR := tar 38 | 39 | # ------------------------------------------------------------------------------ 40 | 41 | 42 | help: 43 | @echo "" 44 | @echo "make info show package info extracted from .el" 45 | @echo "make test run unit tests in -test.el file" 46 | @echo "make all clean, compile, pkg, readme, tar" 47 | @echo "make compile compile .el files to .elc" 48 | @echo "make pkg make -pkg.el file for multi-file packages" 49 | @echo "make readme make README.md from .el commentary section" 50 | @echo "make tar make -.tar package file" 51 | @echo "make clean delete readme, -pkg, .elc, .tar" 52 | @echo "make clean-elc delete .elc" 53 | @echo "" 54 | 55 | 56 | info: 57 | @echo "Package: ${PACKAGE}" 58 | @echo "Description: ${DESCRIPTION}" 59 | @echo "Homepage: ${HOMEPAGE}" 60 | @echo "Version: ${VERSION}" 61 | @echo "Keywords: ${KEYWORDS}" 62 | @echo "Dependencies: ${DEPENDENCIES}" 63 | @echo "" 64 | 65 | 66 | # compile to check for bugs, and build tarfile for install test 67 | test: compile tar 68 | @echo "" 69 | @emacs -nw --version 70 | @echo "" 71 | ${EMACS} -Q -batch -L . -l ${TEST} -f ert-run-tests-batch-and-exit 72 | 73 | 74 | all: info clean compile test pkg readme tar 75 | 76 | 77 | # make sure everything compiles, then remove .elc files 78 | # need -L . so test can (require 'clipmon) 79 | compile: 80 | ${EMACS} -Q --batch -L . -f batch-byte-compile clipmon.el test/clipmon-test.el 81 | rm -f *.elc test/*.elc 82 | 83 | 84 | pkg: 85 | @echo "(define-package \"${PACKAGE}\" \"${VERSION}\"" > ${PKG} 86 | @echo " \"${DESCRIPTION}\"" >> ${PKG} 87 | @echo " ${DEPENDENCIES}" >> ${PKG} 88 | @echo " :url \"${HOMEPAGE}\"" >> ${PKG} 89 | @echo " :keywords '(${KEYWORDS}))" >> ${PKG} 90 | cat ${PKG} 91 | 92 | # cask is asynchronous though 93 | # pkg0: 94 | # @echo "Running cask - hit Enter when done..." 95 | # ${CASK} pkg-file 96 | # read 97 | # cat ${PKG} 98 | 99 | 100 | # generate a readme file from the commentary in the source file. 101 | # emacs currently generating dos line endings - fix that. 102 | # had to comment out dos2unix as travis build system didn't seem to have access to it. 103 | readme: 104 | rm -f README.md 105 | ${EMACS} --script ${MAKE-README} <${SOURCE} >README.md 106 | # dos2unix README.md 107 | attrib +r README.md 108 | head -5 README.md 109 | tail -9 README.md 110 | 111 | 112 | # melpa does this automatically 113 | tar: pkg 114 | rm -rf ${PACKAGE_DIR} 115 | mkdir ${PACKAGE_DIR} 116 | cp ${SOURCE} ${PACKAGE_DIR} 117 | cp ${PKG} ${PACKAGE_DIR} 118 | cp -r ${CONTENTS} ${PACKAGE_DIR} 119 | tar -cvf ${PACKAGE_TAR} ${PACKAGE_DIR} 120 | rm -rf ${PACKAGE_DIR} 121 | tar -tvf ${PACKAGE_TAR} 122 | mkdir -p ${TAR_DIR} 123 | mv ${PACKAGE_TAR} ${TAR_DIR} 124 | ls -l ${TAR_DIR} 125 | 126 | 127 | clean: 128 | rm -f *.elc 129 | rm -f ${PKG} 130 | rm -f ${PACKAGE_TAR} 131 | rm -rdf ${PACKAGE_DIR} 132 | 133 | 134 | clean-elc: 135 | rm -f *.elc 136 | 137 | 138 | .PHONY: help info test all compile pkg readme tar clean clean-elc 139 | 140 | 141 | # end 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | clipmon.el [![Travis build status](https://secure.travis-ci.org/bburns/clipmon.png?branch=master)](http://travis-ci.org/bburns/clipmon) [![melpa.org](http://melpa.org/packages/clipmon-badge.svg)](http://melpa.org/#/clipmon) [![GPL-3.0](http://img.shields.io/:license-gpl-blue.svg)](http://opensource.org/licenses/GPL-3.0) 3 | ---------------------------------------------------------------------------- 4 | 5 | 6 | Description 7 | ---------------------------------------------------------------------------- 8 | 9 | Clipmon is a clipboard monitor - it watches the system clipboard and can 10 | automatically insert any new text into the current location in Emacs. 11 | 12 | It also adds changes to the system clipboard to the kill ring, making Emacs 13 | into a clipboard manager for text - you can then use a package like 14 | browse-kill-ring or helm-ring to view and manage your clipboard history. 15 | 16 | **Warning (2015-12-24): in an X-windows system with clipmon-mode on, bringing 17 | up a graphical menu (e.g. Shift+Mouse-1) will cause Emacs to hang. See 18 | http://debbugs.gnu.org/cgi/bugreport.cgi?bug=22214. 19 | X-windows starts a timer when checking the contents of the clipboard, which 20 | interferes with the clipmon timer.** 21 | 22 | Update (2016-01-27): in an X-windows system, Clipmon now uses the clipboard 23 | instead of the primary selection - see https://github.com/bburns/clipmon/issues/4. 24 | 25 | You can use Clipmon for taking notes from a webpage, for example - just copy the 26 | text you want to save and it will be added to Emacs. It helps to have an 27 | autocopy feature or addon for the browser, e.g. AutoCopy 2 for Firefox - then 28 | you can just select text to add it to Emacs. 29 | 30 | Here's a diagram - text flows from the top to the bottom: 31 | 32 | +---------------------+ 33 | | Other programs |+ 34 | +---------------------+| 35 | +---------------------+ 36 | / 37 | +-----------+ 38 | | System | 39 | | clipboard | 40 | +-----------+ 41 | OS / 42 | --------------------------------------------------- 43 | Emacs / 44 | / 45 | +--------------+ +---------------+ 46 | | clipmon-mode |......| autoinsert | 47 | +--------------+ +---------------+ 48 | | . 49 | +-----------+ . 50 | | Emacs ++ . 51 | | kill ring ++ +--------------+ 52 | +-----------+|+ | transforms | 53 | +-----------+| +--------------+ 54 | +-----------+ . 55 | | . 56 | | yank . autoinsert 57 | +--------------------------+ 58 | | Emacs buffer | 59 | +--------------------------+ 60 | 61 | 62 | The solid line is turned on and off with `clipmon-mode`, while the dotted 63 | line is turned on and off with `clipmon-autoinsert-toggle`, usually bound to a key. 64 | There are also various transformations you can perform on the text, e.g. 65 | adding newlines to the end. 66 | 67 | (Emacs's kill-ring is like the system clipboard but with multiple items in 68 | it. If you copy a bunch of things in another program, Emacs normally only 69 | knows about the last one copied, but with clipmon mode on, it will monitor 70 | the system clipboard and add any new text it sees to the kill ring.) 71 | 72 | 73 | Installation 74 | ---------------------------------------------------------------------------- 75 | 76 | It's simplest to use the package manager: 77 | 78 | M-: (package-install 'clipmon) 79 | 80 | It will then be ready to use, and will also be available the next time you 81 | start Emacs. 82 | 83 | 84 | Usage 85 | ---------------------------------------------------------------------------- 86 | 87 | To give it a try, do M-: (clipmon-autoinsert-toggle) - this will turn on 88 | autoinsert. Then go to another application and copy some text to the 89 | clipboard - clipmon should detect it after a second or two and make a beep. 90 | If you switch back to Emacs, the text should be there in your buffer. 91 | 92 | Note that you can still yank and pull text in Emacs as usual while autoinsert 93 | is on, since it only monitors the system clipboard. 94 | 95 | You can turn off autoinsert with the same command - to add a keybinding to it 96 | add something like this to your init file: 97 | 98 | (global-set-key (kbd "") 'clipmon-autoinsert-toggle) 99 | 100 | You can also turn it on and off from the Options menu. 101 | 102 | Also, if no change is detected after a certain number of minutes, autoinsert will 103 | turn itself off automatically with another beep. This is so you don't forget 104 | that autoinsert is on and accidentally add text to your buffer. 105 | 106 | And note: if you happen to copy the same text to the clipboard twice, clipmon 107 | won't know about the second time, as it only detects changes. And if you copy 108 | text faster than the timer interval is set it may miss some changes, but you 109 | can adjust the interval. 110 | 111 | 112 | Using as a clipboard manager 113 | ---------------------------------------------------------------------------- 114 | 115 | To try out clipmon as a clipboard manager, make sure clipmon-mode is on by 116 | doing M-: (clipmon-mode 1) (also accessible from the Options menu) and that 117 | autoinsert is off, then copy a few pieces of text from another program (more 118 | slowly than the default timer interval of 2 seconds though). Switch back to 119 | Emacs, and see that you can yank any of the text back with C-y, M-y, M-y... 120 | 121 | Note that when you turn on autoinsert, it also turns on clipmon-mode, to 122 | capture text to the kill ring, but if you'd like to turn on clipmon-mode 123 | automatically, you can add this to your init file: 124 | 125 | ;; monitor the system clipboard and add any changes to the kill ring 126 | (add-to-list 'after-init-hook 'clipmon-mode-start) 127 | 128 | You can also use the package browse-kill-ring to manage the kill ring - you 129 | can install it with M-: (package-install 'browse-kill-ring), then call 130 | `browse-kill-ring` to see the contents of the kill ring, insert from it, 131 | delete items, etc. Helm also has a package called helm-ring, with the 132 | function `helm-show-kill-ring`. 133 | 134 | You can persist the kill ring between sessions if you'd like (though note 135 | that this might involve writing sensitive information like passwords to the 136 | disk - although you could always delete such text from the kill ring with 137 | `browse-kill-ring-delete`). To do so, add this to your init file: 138 | 139 | ;; persist the kill ring between sessions 140 | (add-to-list 'after-init-hook 'clipmon-persist) 141 | 142 | This will use Emacs's savehist library to save the kill ring, both at the end 143 | of the session and at set intervals. However, savehist also saves various 144 | other settings by default, including the minibuffer history - see 145 | `savehist-mode` for more details. To change the autosave interval, add 146 | something like this: 147 | 148 | (setq savehist-autosave-interval (* 5 60)) ; save every 5 minutes (default) 149 | 150 | The kill ring has a fixed number of entries which you can set, depending on 151 | how much history you want to save between sessions: 152 | 153 | (setq kill-ring-max 500) ; default is 60 in Emacs 24.4 154 | 155 | To see how much space the kill-ring is taking up, you can call this function: 156 | 157 | (clipmon-kill-ring-total) 158 | => 29670 characters 159 | 160 | 161 | Options 162 | ---------------------------------------------------------------------------- 163 | 164 | There are various options you can set with customize: 165 | 166 | (customize-group 'clipmon) 167 | 168 | or set them in your init file - these are the default values: 169 | 170 | (setq clipmon-timer-interval 2) ; check system clipboard every n secs 171 | (setq clipmon-autoinsert-sound t) ; t for included beep, or path or nil 172 | (setq clipmon-autoinsert-color "red") ; color of cursor when autoinsert is on 173 | (setq clipmon-autoinsert-timeout 5) ; stop autoinsert after n mins inactivity 174 | 175 | before inserting the text, transformations are performed on it in this order: 176 | 177 | (setq clipmon-transform-trim t) ; remove leading whitespace 178 | (setq clipmon-transform-remove ; remove text matching this regexp 179 | "\\[[0-9][0-9]?[0-9]?\\]\\|\\[citation needed\\]\\|\\[by whom?\\]") 180 | (setq clipmon-transform-prefix "") ; add to start of text 181 | (setq clipmon-transform-suffix "\n\n") ; add to end of text 182 | (setq clipmon-transform-function nil) ; additional transform function 183 | 184 | 185 | Todo 186 | ---------------------------------------------------------------------------- 187 | 188 | - Prefix with C-u to set a target point, then allow cut/copy/pasting from 189 | within Emacs, eg to take notes from another buffer, or move text elsewhere. 190 | 191 | 192 | ---- 193 | 194 | Author: Brian Burns 195 | URL: https://github.com/bburns/clipmon 196 | Version: 20160925 197 | 198 | This file was generated from commentary in clipmon.el - do not edit! 199 | 200 | ---- 201 | 202 | -------------------------------------------------------------------------------- /clipmon.el: -------------------------------------------------------------------------------- 1 | ;;; clipmon.el --- Clipboard monitor - watch system clipboard, add changes to kill ring/autoinsert 2 | ;; 3 | ;; Copyright (c) 2015-2016 Brian Burns 4 | ;; 5 | ;; Author: Brian Burns 6 | ;; URL: https://github.com/bburns/clipmon 7 | ;; Keywords: convenience 8 | ;; Version: 20160925 9 | ;; 10 | ;; This package is NOT part of GNU Emacs. 11 | ;; 12 | ;; This program is free software; you can redistribute it and/or modify 13 | ;; it under the terms of the GNU General Public License as published by 14 | ;; the Free Software Foundation, either version 3 of the License, or 15 | ;; (at your option) any later version. 16 | ;; 17 | ;; This program is distributed in the hope that it will be useful, 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ;; GNU General Public License for more details. 21 | ;; 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with this program. If not, see . 24 | ;; 25 | ;; 26 | ;;; Commentary: 27 | ;; 28 | ;;;; Description 29 | ;; ---------------------------------------------------------------------------- 30 | ;; 31 | ;; Clipmon is a clipboard monitor - it watches the system clipboard and can 32 | ;; automatically insert any new text into the current location in Emacs. 33 | ;; 34 | ;; It also adds changes to the system clipboard to the kill ring, making Emacs 35 | ;; into a clipboard manager for text - you can then use a package like 36 | ;; browse-kill-ring or helm-ring to view and manage your clipboard history. 37 | ;; 38 | ;; **Warning (2015-12-24): in an X-windows system with clipmon-mode on, bringing 39 | ;; up a graphical menu (e.g. Shift+Mouse-1) will cause Emacs to hang. See 40 | ;; http://debbugs.gnu.org/cgi/bugreport.cgi?bug=22214. 41 | ;; X-windows starts a timer when checking the contents of the clipboard, which 42 | ;; interferes with the clipmon timer.** 43 | ;; 44 | ;; Update (2016-01-27): in an X-windows system, Clipmon now uses the clipboard 45 | ;; instead of the primary selection - see https://github.com/bburns/clipmon/issues/4. 46 | ;; 47 | ;; You can use Clipmon for taking notes from a webpage, for example - just copy the 48 | ;; text you want to save and it will be added to Emacs. It helps to have an 49 | ;; autocopy feature or addon for the browser, e.g. AutoCopy 2 for Firefox - then 50 | ;; you can just select text to add it to Emacs. 51 | ;; 52 | ;; Here's a diagram - text flows from the top to the bottom: 53 | ;; 54 | ;; +---------------------+ 55 | ;; | Other programs |+ 56 | ;; +---------------------+| 57 | ;; +---------------------+ 58 | ;; / 59 | ;; +-----------+ 60 | ;; | System | 61 | ;; | clipboard | 62 | ;; +-----------+ 63 | ;; OS / 64 | ;; --------------------------------------------------- 65 | ;; Emacs / 66 | ;; / 67 | ;; +--------------+ +---------------+ 68 | ;; | clipmon-mode |......| autoinsert | 69 | ;; +--------------+ +---------------+ 70 | ;; | . 71 | ;; +-----------+ . 72 | ;; | Emacs ++ . 73 | ;; | kill ring ++ +--------------+ 74 | ;; +-----------+|+ | transforms | 75 | ;; +-----------+| +--------------+ 76 | ;; +-----------+ . 77 | ;; | . 78 | ;; | yank . autoinsert 79 | ;; +--------------------------+ 80 | ;; | Emacs buffer | 81 | ;; +--------------------------+ 82 | ;; 83 | ;; 84 | ;; The solid line is turned on and off with `clipmon-mode', while the dotted 85 | ;; line is turned on and off with `clipmon-autoinsert-toggle', usually bound to a key. 86 | ;; There are also various transformations you can perform on the text, e.g. 87 | ;; adding newlines to the end. 88 | ;; 89 | ;; (Emacs's kill-ring is like the system clipboard but with multiple items in 90 | ;; it. If you copy a bunch of things in another program, Emacs normally only 91 | ;; knows about the last one copied, but with clipmon mode on, it will monitor 92 | ;; the system clipboard and add any new text it sees to the kill ring.) 93 | ;; 94 | ;; 95 | ;;;; Installation 96 | ;; ---------------------------------------------------------------------------- 97 | ;; 98 | ;; It's simplest to use the package manager: 99 | ;; 100 | ;; M-: (package-install 'clipmon) 101 | ;; 102 | ;; It will then be ready to use, and will also be available the next time you 103 | ;; start Emacs. 104 | ;; 105 | ;; 106 | ;;;; Usage 107 | ;; ---------------------------------------------------------------------------- 108 | ;; 109 | ;; To give it a try, do M-: (clipmon-autoinsert-toggle) - this will turn on 110 | ;; autoinsert. Then go to another application and copy some text to the 111 | ;; clipboard - clipmon should detect it after a second or two and make a beep. 112 | ;; If you switch back to Emacs, the text should be there in your buffer. 113 | ;; 114 | ;; Note that you can still yank and pull text in Emacs as usual while autoinsert 115 | ;; is on, since it only monitors the system clipboard. 116 | ;; 117 | ;; You can turn off autoinsert with the same command - to add a keybinding to it 118 | ;; add something like this to your init file: 119 | ;; 120 | ;; (global-set-key (kbd "") 'clipmon-autoinsert-toggle) 121 | ;; 122 | ;; You can also turn it on and off from the Options menu. 123 | ;; 124 | ;; Also, if no change is detected after a certain number of minutes, autoinsert will 125 | ;; turn itself off automatically with another beep. This is so you don't forget 126 | ;; that autoinsert is on and accidentally add text to your buffer. 127 | ;; 128 | ;; And note: if you happen to copy the same text to the clipboard twice, clipmon 129 | ;; won't know about the second time, as it only detects changes. And if you copy 130 | ;; text faster than the timer interval is set it may miss some changes, but you 131 | ;; can adjust the interval. 132 | ;; 133 | ;; 134 | ;;;; Using as a clipboard manager 135 | ;; ---------------------------------------------------------------------------- 136 | ;; 137 | ;; To try out clipmon as a clipboard manager, make sure clipmon-mode is on by 138 | ;; doing M-: (clipmon-mode 1) (also accessible from the Options menu) and that 139 | ;; autoinsert is off, then copy a few pieces of text from another program (more 140 | ;; slowly than the default timer interval of 2 seconds though). Switch back to 141 | ;; Emacs, and see that you can yank any of the text back with C-y, M-y, M-y... 142 | ;; 143 | ;; Note that when you turn on autoinsert, it also turns on clipmon-mode, to 144 | ;; capture text to the kill ring, but if you'd like to turn on clipmon-mode 145 | ;; automatically, you can add this to your init file: 146 | ;; 147 | ;; ;; monitor the system clipboard and add any changes to the kill ring 148 | ;; (add-to-list 'after-init-hook 'clipmon-mode-start) 149 | ;; 150 | ;; You can also use the package browse-kill-ring to manage the kill ring - you 151 | ;; can install it with M-: (package-install 'browse-kill-ring), then call 152 | ;; `browse-kill-ring' to see the contents of the kill ring, insert from it, 153 | ;; delete items, etc. Helm also has a package called helm-ring, with the 154 | ;; function `helm-show-kill-ring'. 155 | ;; 156 | ;; You can persist the kill ring between sessions if you'd like (though note 157 | ;; that this might involve writing sensitive information like passwords to the 158 | ;; disk - although you could always delete such text from the kill ring with 159 | ;; `browse-kill-ring-delete'). To do so, add this to your init file: 160 | ;; 161 | ;; ;; persist the kill ring between sessions 162 | ;; (add-to-list 'after-init-hook 'clipmon-persist) 163 | ;; 164 | ;; This will use Emacs's savehist library to save the kill ring, both at the end 165 | ;; of the session and at set intervals. However, savehist also saves various 166 | ;; other settings by default, including the minibuffer history - see 167 | ;; `savehist-mode' for more details. To change the autosave interval, add 168 | ;; something like this: 169 | ;; 170 | ;; (setq savehist-autosave-interval (* 5 60)) ; save every 5 minutes (default) 171 | ;; 172 | ;; The kill ring has a fixed number of entries which you can set, depending on 173 | ;; how much history you want to save between sessions: 174 | ;; 175 | ;; (setq kill-ring-max 500) ; default is 60 in Emacs 24.4 176 | ;; 177 | ;; To see how much space the kill-ring is taking up, you can call this function: 178 | ;; 179 | ;; (clipmon-kill-ring-total) 180 | ;; => 29670 characters 181 | ;; 182 | ;; 183 | ;;;; Options 184 | ;; ---------------------------------------------------------------------------- 185 | ;; 186 | ;; There are various options you can set with customize: 187 | ;; 188 | ;; (customize-group 'clipmon) 189 | ;; 190 | ;; or set them in your init file - these are the default values: 191 | ;; 192 | ;; (setq clipmon-timer-interval 2) ; check system clipboard every n secs 193 | ;; (setq clipmon-autoinsert-sound t) ; t for included beep, or path or nil 194 | ;; (setq clipmon-autoinsert-color "red") ; color of cursor when autoinsert is on 195 | ;; (setq clipmon-autoinsert-timeout 5) ; stop autoinsert after n mins inactivity 196 | ;; 197 | ;; before inserting the text, transformations are performed on it in this order: 198 | ;; 199 | ;; (setq clipmon-transform-trim t) ; remove leading whitespace 200 | ;; (setq clipmon-transform-remove ; remove text matching this regexp 201 | ;; "\\[[0-9][0-9]?[0-9]?\\]\\|\\[citation needed\\]\\|\\[by whom?\\]") 202 | ;; (setq clipmon-transform-prefix "") ; add to start of text 203 | ;; (setq clipmon-transform-suffix "\n\n") ; add to end of text 204 | ;; (setq clipmon-transform-function nil) ; additional transform function 205 | ;; 206 | ;; 207 | ;;;; Todo 208 | ;; ---------------------------------------------------------------------------- 209 | ;; 210 | ;; - Prefix with C-u to set a target point, then allow cut/copy/pasting from 211 | ;; within Emacs, eg to take notes from another buffer, or move text elsewhere. 212 | ;; 213 | ;; 214 | ;;; Code: 215 | 216 | ;;;; Renamings 217 | ;; ---------------------------------------------------------------------------- 218 | 219 | ;; just renaming some things here - must come before defcustoms. 220 | ;; could remove after some time though. 221 | 222 | (eval-when-compile ; because it's a macro 223 | (defalias 'clipmon--rename 'define-obsolete-variable-alias)) 224 | 225 | ;; rename old to new 226 | (clipmon--rename 'clipmon-interval 'clipmon-timer-interval "20150211") 227 | (clipmon--rename 'clipmon-cursor-color 'clipmon-autoinsert-color "20150211") 228 | (clipmon--rename 'clipmon-sound 'clipmon-autoinsert-sound "20150211") 229 | (clipmon--rename 'clipmon-timeout 'clipmon-autoinsert-timeout "20150211") 230 | (clipmon--rename 'clipmon-trim 'clipmon-transform-trim "20150211") 231 | (clipmon--rename 'clipmon-remove-regexp 'clipmon-transform-remove "20150211") 232 | (clipmon--rename 'clipmon-prefix 'clipmon-transform-prefix "20150211") 233 | (clipmon--rename 'clipmon-suffix 'clipmon-transform-suffix "20150211") 234 | 235 | ;; FIXME how just mark as obsolete/removed? 236 | (clipmon--rename 'clipmon-action 'clipmon-action-obsolete "20150211") 237 | 238 | 239 | ;;;; Public settings 240 | ;; ---------------------------------------------------------------------------- 241 | 242 | (defgroup clipmon nil 243 | "Clipboard monitor - add clipboard contents to kill ring and automatically insert." 244 | :group 'convenience 245 | :group 'killing) 246 | 247 | (defcustom clipmon-timer-interval 2 248 | "Interval for checking system clipboard for changes, in seconds." 249 | :group 'clipmon 250 | :type 'integer) 251 | 252 | (defcustom clipmon-autoinsert-color "red" 253 | "Color to set cursor when clipmon autoinsert is on. Set to nil for no change." 254 | :group 'clipmon 255 | :type 'color) 256 | 257 | (defcustom clipmon-autoinsert-sound t 258 | "Path to sound file to play on autoinsert, t for included file, or nil. 259 | Use t for the included sound file (see 260 | `clipmon--included-sound-file'), nil for no sound, or path to an 261 | audio file - Emacs can play .wav or .au files." 262 | ; Note: can't use `ding' here because it doesn't make a sound when Emacs 263 | ; doesn't have focus. 264 | :group 'clipmon 265 | :type '(radio 266 | (string :tag "Audio file (.wav or .au)") 267 | (boolean :tag "Included sound file"))) 268 | 269 | (defcustom clipmon-autoinsert-timeout 5 270 | "Stop autoinsert if no system clipboard activity after this many minutes. 271 | Set to nil for no timeout." 272 | :group 'clipmon 273 | :type 'integer) 274 | 275 | 276 | ;; transforms on text - these are performed in this order 277 | 278 | (defcustom clipmon-transform-trim t 279 | "If non-nil, remove leading whitespace from string before autoinserting. 280 | Often it's hard to select text without grabbing a leading space, 281 | so this will remove it." 282 | :group 'clipmon 283 | :type 'boolean) 284 | 285 | (defcustom clipmon-transform-remove 286 | "\\[[0-9][0-9]?[0-9]?\\]\\|\\[citation needed\\]\\|\\[by whom?\\]" 287 | "Any text matching this regexp will be removed before autoinserting. 288 | e.g. Wikipedia-style references with 1-3 digits - [3], [115]." 289 | :group 'clipmon 290 | :type 'regexp) 291 | 292 | (defcustom clipmon-transform-prefix "" 293 | "String to add to start of clipboard contents before autoinserting." 294 | :group 'clipmon 295 | :type 'string) 296 | 297 | (defcustom clipmon-transform-suffix "\n\n" 298 | "String to add to end of clipboard contents before autoinserting. 299 | Default is two newlines, which leaves a blank line between clips. 300 | \(To add a newline in the customize interface, type \\[quoted-insert] C-j)." 301 | :group 'clipmon 302 | :type 'string) 303 | 304 | (defcustom clipmon-transform-function nil 305 | "Function to perform additional transformations on text before autoinserting. 306 | Receives one argument, the clipboard text - should return the changed text. 307 | E.g. to make the text lowercase before pasting, 308 | (setq clipmon-transform-function (lambda (s) (downcase s)))" 309 | :group 'clipmon 310 | :type 'function 311 | :risky t) 312 | 313 | 314 | ;;;; Initialize 315 | ;; ---------------------------------------------------------------------------- 316 | 317 | ; add items to Options menu 318 | ;;;###autoload 319 | (define-key-after global-map [menu-bar options clipmon-separator] ; path to new item 320 | '(menu-item "---") 321 | 'highlight-paren-mode) ; add after this 322 | 323 | ;;;###autoload 324 | (define-key-after global-map [menu-bar options clipmon-killring] ; path to new item 325 | '(menu-item "Clipboard Monitor (Add to Kill Ring)" 326 | clipmon-mode ; function to call on click 327 | :help "Add changes to the system clipboard to Emacs's kill ring." 328 | :button (:toggle . clipmon-mode)) ; show checkmark on/off 329 | 'clipmon-separator) ; add after this 330 | 331 | ;;;###autoload 332 | (define-key-after global-map [menu-bar options clipmon-autoinsert] ; path to new item 333 | '(menu-item "Clipboard Monitor Autoinsert" 334 | clipmon-autoinsert-toggle ; function to call on click 335 | :help "Automatically insert changes to the system clipboard at the current location." 336 | :button (:toggle . clipmon--autoinsert)) ; show checkmark on/off 337 | 'clipmon-killring) ; add after this 338 | 339 | 340 | ;;;; Private variables 341 | ;; ---------------------------------------------------------------------------- 342 | 343 | (defvar clipmon--timer nil 344 | "Timer handle for clipboard monitor to watch system clipboard.") 345 | 346 | (defvar clipmon--autoinsert nil 347 | "Non-nil if autoinsert is on.") 348 | 349 | (defvar clipmon--autoinsert-timeout-start nil 350 | "Time that autoinsert timeout timer was started.") 351 | 352 | (defvar clipmon--previous-contents nil 353 | "Last contents of the system clipboard.") 354 | 355 | (defvar clipmon--cursor-color-original nil 356 | "Original cursor color.") 357 | 358 | (defconst clipmon--folder 359 | (file-name-directory (or load-file-name (file-name-directory (buffer-file-name)))) 360 | "Path to clipmon install folder, or current buffer's location.") 361 | 362 | (defconst clipmon--included-sound-file 363 | (expand-file-name "clipmon.wav" clipmon--folder) 364 | "Path to included audio file.") 365 | 366 | 367 | 368 | ;;;; Public functions 369 | ;; ---------------------------------------------------------------------------- 370 | 371 | ;;;###autoload 372 | (define-minor-mode clipmon-mode 373 | "Start/stop clipboard monitor - watch system clipboard, add changes to kill ring. 374 | 375 | To also insert the changes to the system clipboard at the current 376 | location, call `clipmon-autoinsert-toggle' to turn autoinsert on 377 | and off. See commentary in source file for more information - 378 | M-: (find-library 'clipmon). 379 | 380 | Upgrade note (2015-02-11): you'll need to bind your shortcut key to 381 | `clipmon-autoinsert-toggle' instead of `clipmon-mode'." 382 | :global t 383 | :lighter "" 384 | ; value of clipmon-mode is toggled before this implicitly 385 | (if clipmon-mode (clipmon-mode-start) (clipmon-mode-stop))) 386 | 387 | 388 | ;;;###autoload 389 | (defun clipmon-mode-start () 390 | "Start clipboard monitor - watch system clipboard, add changes to kill ring." 391 | (interactive) 392 | (setq clipmon-mode t) ; in case called outside of clipmon-mode fn 393 | (unless clipmon--timer 394 | (setq clipmon--previous-contents (clipmon--clipboard-contents)) 395 | (setq clipmon--timer 396 | (run-at-time nil clipmon-timer-interval 'clipmon--check-clipboard)) 397 | (message "Clipboard monitor started - watching system clipboard, adding changes to kill ring.") 398 | )) 399 | 400 | 401 | (defun clipmon-mode-stop () 402 | "Stop clipboard monitor and autoinsert modes." 403 | (interactive) 404 | (setq clipmon-mode nil) ; in case called outside of clipmon-mode fn 405 | (when clipmon--timer 406 | (cancel-timer clipmon--timer) 407 | (setq clipmon--timer nil) 408 | (if clipmon--autoinsert (clipmon-autoinsert-stop)) ; turn off autoinsert also 409 | (message "Clipboard monitor stopped.") 410 | )) 411 | 412 | 413 | ;;;###autoload 414 | (defun clipmon-autoinsert-toggle () 415 | "Turn autoinsert on/off - watch system clipboard and insert changes. 416 | Will change cursor color and play a sound. Text will be 417 | transformed before insertion according to various settings - see 418 | `clipmon--transform-text'." 419 | (interactive) 420 | ;; note: a minor mode would toggle the value here rather than in the fns 421 | (if clipmon--autoinsert (clipmon-autoinsert-stop) (clipmon-autoinsert-start))) 422 | 423 | 424 | (defun clipmon-autoinsert-start () 425 | "Turn on autoinsert - change cursor color, play sound, insert changes." 426 | (interactive) 427 | (if (null clipmon--timer) (clipmon-mode-start)) ; make sure clipmon is on 428 | (if clipmon--autoinsert 429 | (message "Clipboard monitor autoinsert already on.") 430 | (setq clipmon--autoinsert-timeout-start (current-time)) 431 | (when clipmon-autoinsert-color 432 | (setq clipmon--cursor-color-original (face-background 'cursor)) 433 | (set-face-background 'cursor clipmon-autoinsert-color)) 434 | (message 435 | "Clipboard monitor autoinsert started with timer interval %g seconds. Stop with %s." 436 | clipmon-timer-interval 437 | (substitute-command-keys "\\[clipmon-autoinsert-toggle]")) ; eg "" 438 | (clipmon--play-sound) 439 | (setq clipmon--autoinsert t) 440 | )) 441 | 442 | 443 | (defun clipmon-autoinsert-stop (&optional msg) 444 | "Turn off autoinsert - restore cursor color and play sound. 445 | Show optional message MSG, or default message." 446 | (interactive) 447 | (if (null clipmon--autoinsert) 448 | (message "Clipboard monitor autoinsert already off.") 449 | (if clipmon--cursor-color-original 450 | (set-face-background 'cursor clipmon--cursor-color-original)) 451 | (message (or msg "Clipboard monitor autoinsert stopped.")) 452 | (clipmon--play-sound) 453 | (setq clipmon--autoinsert nil) 454 | )) 455 | 456 | 457 | ;;;###autoload 458 | (defun clipmon-persist () 459 | "Persist the kill ring to disk using Emacs's savehist library. 460 | Will save the kill ring at the end of the session and at various 461 | intervals as specified by variable `savehist-autosave-interval'. 462 | Note that savehist also includes various other Emacs settings by 463 | default, including the minibuffer history - see function 464 | `savehist-mode' for more details." 465 | (require 'savehist) 466 | (defvar savehist-additional-variables) ; for compiler warning 467 | (add-to-list 'savehist-additional-variables 'kill-ring) 468 | (savehist-mode 1)) 469 | 470 | 471 | ;;;; Private functions 472 | ;; ---------------------------------------------------------------------------- 473 | 474 | (defun clipmon--check-clipboard () 475 | "Check the clipboard and call `clipmon--on-clipboard-change' if changed. 476 | Otherwise check autoinsert idle timer and stop if it's been idle a while." 477 | (let ((s (clipmon--clipboard-contents))) ; s may actually be nil here 478 | (if (and s (not (string-equal s clipmon--previous-contents))) ; if not nil, and changed 479 | (clipmon--on-clipboard-change s) ; add to kill-ring, autoinsert 480 | ;; otherwise stop autoinsert if clipboard has been idle a while 481 | (if (and clipmon--autoinsert clipmon-autoinsert-timeout) 482 | (let ((idle-seconds (clipmon--seconds-since clipmon--autoinsert-timeout-start))) 483 | (when (>= idle-seconds (* 60 clipmon-autoinsert-timeout)) 484 | (clipmon-autoinsert-stop (format 485 | "Clipboard monitor autoinsert stopped after %g minutes of inactivity." 486 | clipmon-autoinsert-timeout)) 487 | )))))) 488 | 489 | 490 | (defun clipmon--on-clipboard-change (s) 491 | "Clipboard changed - add text S to kill ring, and optionally insert it." 492 | (setq clipmon--previous-contents s) ; save contents 493 | (kill-new s) ; add to kill ring 494 | (when clipmon--autoinsert 495 | (setq s (clipmon--autoinsert-transform-text s)) 496 | (insert s) 497 | (undo-boundary) ; mark a boundary between undo units 498 | (clipmon--play-sound) 499 | (setq clipmon--autoinsert-timeout-start (current-time)))) ; reset timeout 500 | 501 | 502 | (defun clipmon--autoinsert-transform-text (s) 503 | "Apply autoinsert transformations to clipboard text S." 504 | (if clipmon-transform-trim (setq s (clipmon--trim-left s))) 505 | (if clipmon-transform-remove 506 | (setq s (replace-regexp-in-string clipmon-transform-remove "" s))) 507 | (if clipmon-transform-prefix (setq s (concat clipmon-transform-prefix s))) 508 | (if clipmon-transform-suffix (setq s (concat s clipmon-transform-suffix))) 509 | (if clipmon-transform-function (setq s (funcall clipmon-transform-function s))) 510 | s) 511 | 512 | 513 | (defun clipmon--play-sound () 514 | "Play a user-specified sound file, the included sound file, or nothing." 515 | (if clipmon-autoinsert-sound 516 | (if (stringp clipmon-autoinsert-sound) 517 | ;; play-sound-file can throw an error if no sound device found 518 | (ignore-errors (play-sound-file clipmon-autoinsert-sound)) 519 | (ignore-errors (play-sound-file clipmon--included-sound-file))))) 520 | 521 | 522 | 523 | ;;;; Library functions 524 | ;; ---------------------------------------------------------------------------- 525 | 526 | (defun clipmon-kill-ring-total () 527 | "Get total size of kill ring, in characters." 528 | (let ((sum 0)) 529 | (mapc (lambda (s) (setq sum (+ sum (length s)))) kill-ring) sum)) 530 | 531 | 532 | (defun clipmon--remove-properties (s) 533 | "Remove formatting properties from string S." 534 | (if (null s) nil 535 | (substring-no-properties s))) 536 | 537 | 538 | (defun clipmon--get-selection () 539 | "Get the clipboard contents" 540 | ;; Note: When the OS is first started these functions will throw 541 | ;; (error "No selection is available"), so need to ignore errors. 542 | (cond ((boundp 'mac-carbon-version-string) ; emacs mac port 543 | ;; Need to have this before emacs25 since `gui-get-selection' is also 544 | ;; bound in the mac port, but it doesn't work right 545 | (ignore-errors (funcall interprogram-paste-function))) 546 | ((fboundp 'gui-get-selection) ; emacs25 547 | ; better to (setq selection-coding-system 'utf-8) to handle chinese, 548 | ; which is the default value for gui-get-selection etc 549 | ; because windows needs STRING. same below. 550 | ; (ignore-errors (gui-get-selection 'CLIPBOARD 'UTF8_STRING))) 551 | (ignore-errors (gui-get-selection 'CLIPBOARD))) ; for windows needs STRING 552 | ((eq window-system 'w32) ; windows/emacs24 553 | ;; Note: (x-get-selection 'CLIPBOARD) doesn't work on Windows. 554 | (ignore-errors (x-get-selection-value))) ; can be nil 555 | (t ; linux+osx/emacs24 556 | ; (ignore-errors (x-get-selection 'CLIPBOARD 'UTF8_STRING))))) 557 | (ignore-errors (x-get-selection 'CLIPBOARD))))) 558 | 559 | 560 | (defun clipmon--clipboard-contents () 561 | "Get current contents of system clipboard - returns a string, or nil." 562 | ;;. do we really need to remove the text properties? 563 | (let ((text (clipmon--remove-properties (clipmon--get-selection))) 564 | (top-of-kill-ring (clipmon--remove-properties (car kill-ring)))) 565 | (cond ((null text) nil) 566 | ;; don't return the text if user just copied/cut it from emacs 567 | ((string= text top-of-kill-ring) nil) 568 | (t text)))) 569 | 570 | 571 | (defun clipmon--trim-left (s) 572 | "Remove leading spaces from string S." 573 | (replace-regexp-in-string "^[ \t]+" "" s)) 574 | 575 | 576 | (defun clipmon--seconds-since (time) 577 | "Return number of seconds elapsed since the given TIME. 578 | TIME should be in Emacs time format (see `current-time'). 579 | Includes approximate number of milliseconds also. 580 | Valid for up to 2**16 seconds = 65536 secs ~ 18hrs." 581 | (let* ((elapsed (time-subtract (current-time) time)) 582 | (seconds (nth 1 elapsed)) 583 | (microseconds (nth 2 elapsed)) ; accurate to milliseconds on my system, anyway 584 | (total (+ seconds (/ microseconds 1.0e6)))) 585 | total)) 586 | 587 | 588 | ;;;; Footer 589 | ;; ---------------------------------------------------------------------------- 590 | 591 | (provide 'clipmon) 592 | 593 | ;; Local Variables: 594 | ;; indent-tabs-mode: nil 595 | ;; End: 596 | ;;; clipmon.el ends here 597 | -------------------------------------------------------------------------------- /clipmon.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bburns/clipmon/95dc56c7ed84a654ec90f4740eb6df1050de8cf1/clipmon.wav -------------------------------------------------------------------------------- /script/make-readme.el: -------------------------------------------------------------------------------- 1 | ;;; make-readme.el --- Convert elisp docs to markdown 2 | 3 | ; this file needs lots of refactoring 4 | ; or try the other packages 5 | 6 | 7 | ;; Copyright (C) 2011, Mitchel Humpherys 8 | ;; Copyright (C) 2013, Justine Tunney 9 | ;; Copyright (C) 2014 Brian Burns 10 | 11 | 12 | ;; Author: Mitchel Humpherys 13 | ;; Keywords: tools, convenience 14 | ;; Version: 0.1.20141227 15 | 16 | ;; This program is free software; you can redistribute it and/or modify 17 | ;; it under the terms of the GNU General Public License as published by 18 | ;; the Free Software Foundation, either version 3 of the License, or 19 | ;; (at your option) any later version. 20 | ;; 21 | ;; This program is distributed in the hope that it will be useful, 22 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | ;; GNU General Public License for more details. 25 | ;; 26 | ;; You should have received a copy of the GNU General Public License 27 | ;; along with this program. If not, see . 28 | 29 | ;;; Commentary: 30 | 31 | ;; This tool will let you easily convert elisp file headers to markdown text so 32 | ;; long as the file comments and documentation follow standard conventions 33 | ;; (like this file). This is because when you're writing an elisp module, the 34 | ;; module itself should be the canonical source of documentation. But it's not 35 | ;; very user-friendly or good marketing for your project to have an empty 36 | ;; README.md that refers people to your source code, and it's even worse if you 37 | ;; have to maintain two separate files that say the same thing. 38 | 39 | ;;; Installation: 40 | 41 | ;; None 42 | 43 | ;;; Usage: 44 | 45 | ;; The recommended way to use this tool is by putting the following code in 46 | ;; your Makefile and running `make README.md` (You don't even have to clone the 47 | ;; repository!): 48 | ;; 49 | ;; README.md: make-readme.el YOUR-MODULE.el 50 | ;; emacs --script $< $@ 2>/dev/null 51 | ;; 52 | ;; make-readme.el: 53 | ;; wget -q -O $@ https://raw.github.com/mgalgs/make-readme/master/make-readme.el 54 | ;; 55 | ;; .INTERMEDIATE: make-readme.el 56 | ;; 57 | ;; You can also invoke it directly with `emacs --script`: 58 | ;; 59 | ;; $ emacs --script make-readme.el /dev/null 60 | ;; 61 | ;; All functions and macros in your module with docstrings will be documented 62 | ;; in the output unless they've been marked as private. Convention dictates 63 | ;; that private elisp functions have two hypens, like `cup--noodle`. 64 | 65 | ;;; Syntax: 66 | 67 | ;; In order for this module to do you any good, you should write your 68 | ;; file header comments in a way that make-readme.el 69 | ;; understands. An attempt has been made to support the most common 70 | ;; file header comment style, so hopefully you shouldn't have to do 71 | ;; anything... The following patterns at the beginning of a line are 72 | ;; special: 73 | ;; 74 | ;; o `;;; My Header` :: Creates a header 75 | ;; o `;; o My list item` :: Creates a list item 76 | ;; o `;; * My list item` :: Also creates a list item 77 | ;; o `;; - My list item` :: Also creates a list item 78 | ;; 79 | ;; Everything else is stripped of its leading semicolons and first 80 | ;; space and is passed directly out. Note that you can embed markdown 81 | ;; syntax directly in your comments. This means that you can embed 82 | ;; blocks of code in your comments by leading the line with 4 spaces 83 | ;; (in addition to the first space directly following the last 84 | ;; semicolon). For example: 85 | ;; 86 | ;; (defun strip-comments (line) 87 | ;; "Stip elisp comments from line" 88 | ;; (replace-regexp-in-string "^;+ ?" "" line)) 89 | ;; 90 | ;; We parse everything between `;;; Commentary:` and `;;; Code`. See 91 | ;; make-readme.el for an example (you might already be 92 | ;; looking at it... whoa, this is really getting meta...). 93 | ;; 94 | ;; If there's some more syntax you would like to see supported, submit 95 | ;; an issue at https://github.com/mgalgs/make-readme/issues 96 | 97 | ;;; Code: 98 | 99 | (setq case-fold-search t) ;; Ignore case in regexps. 100 | 101 | (defun strip-comments (line) 102 | "Strip elisp comments from LINE." 103 | (replace-regexp-in-string "^;+ ?" "" line)) 104 | 105 | (defun trim-string (line) 106 | "Trim spaces from beginning and end of string LINE." 107 | (replace-regexp-in-string " +$" "" 108 | (replace-regexp-in-string "^ +" "" line))) 109 | 110 | (defun fix-symbol-references (line) 111 | "Fix refs like `this' so they don't turn adjacent text into code." 112 | (replace-regexp-in-string "`[^`\t ]+\\('\\)" "`" line nil nil 1)) 113 | 114 | (defun make-section (line level) 115 | "Makes a markdown section using the `#' syntax." 116 | (setq line (replace-regexp-in-string ":?[ \t]*$" "" line)) 117 | (setq line (replace-regexp-in-string " --- " " – " line)) 118 | (format "%s %s" (make-string level ?#) line)) 119 | 120 | (defun print-section (line level) 121 | "Prints a section made with `make-section'." 122 | (princ (make-section line level)) 123 | (princ "\n")) 124 | 125 | (defun slurp () 126 | "Read all text from stdin as a list of lines." 127 | (let (line lines) 128 | (condition-case nil 129 | (while (setq line (read-from-minibuffer "")) 130 | (setq lines (cons line lines))) 131 | (error nil)) 132 | (reverse lines))) 133 | 134 | (defun print-formatted-line (line) 135 | "Prints a line formatted as markdown." 136 | (setq line (fix-symbol-references line)) 137 | (let ((stripped-line (strip-comments line))) 138 | (cond 139 | 140 | ;; Header line (starts with ";;; ") 141 | ((string-match "^;;; " line) 142 | (print-section stripped-line 3)) 143 | 144 | ;; list line (starts with " o ") 145 | ((string-match "^ *o " stripped-line) 146 | (let ((line (replace-regexp-in-string "^ *\o" "*" stripped-line))) 147 | (princ line))) 148 | 149 | ;; default (just print it) 150 | (t 151 | (princ stripped-line)))) 152 | 153 | ;; and a newline 154 | (princ "\n")) 155 | ;; eo print-formatted-line 156 | 157 | (defun document-a-function () 158 | "Search for next defun/macro and print markdown documentation." 159 | (unless (search-forward-regexp 160 | "^(\\(defun\\|defmacro\\) \\([^ ]+\\) " nil t) 161 | (throw 'no-more-funcs nil)) 162 | (let ((func (buffer-substring-no-properties 163 | (match-beginning 2) 164 | (match-end 2)))) 165 | (when (not (string-match "--" func)) 166 | (move-beginning-of-line 1) 167 | (let ((start (point))) 168 | (forward-sexp) 169 | (eval-region start (point))) 170 | (let ((text (describe-function 171 | (eval (read (format "(function %s)" func)))))) 172 | (if (and (not (string-match "Not documented\\." text)) 173 | (string-match "(" text)) 174 | (with-temp-buffer 175 | (insert text) 176 | (goto-char (match-beginning 0)) 177 | (forward-line) 178 | (let* ((title-txt (replace-regexp-in-string "\n" 179 | "" 180 | (buffer-substring (point) 181 | (progn (forward-sexp) (point))))) 182 | (rest (buffer-substring (point) 183 | (point-max))) 184 | (cleaned-rest (fix-symbol-references rest)) 185 | (printable (concat (make-section (format "`%s`" title-txt) 4) 186 | cleaned-rest 187 | "\n\n"))) 188 | (princ printable)))))))) 189 | 190 | 191 | 192 | (defun get-meta (name) 193 | (save-excursion 194 | (goto-char 0) 195 | (re-search-forward (concat "^;; \\(" name ".*\\)") nil t) 196 | (match-string-no-properties 1))) 197 | 198 | (defun println (&rest args) 199 | (princ (apply 'concat args)) 200 | (princ "\n")) 201 | 202 | 203 | ; ---------------------------------------- 204 | 205 | (let* ((line nil) 206 | (title nil) 207 | (title-lines) 208 | (lines (slurp)) 209 | (started-output nil) 210 | (code (concat "(progn\n" (mapconcat 'identity lines "\n") "\n)")) 211 | title-parts title-name title-description) 212 | 213 | ;; The first line should be like ";;; lol.el --- does stuff". 214 | ; (while (if (string-match "^;;;" (car lines)) 215 | ; (setq title-lines (cons (strip-comments (car lines)) title-lines) 216 | ; lines (cdr lines)))) 217 | ; (setq title (mapconcat 'identity (reverse title-lines) " ")) 218 | 219 | (if (string-match "^;;;" (car lines)) 220 | (setq title (strip-comments (car lines)))) 221 | 222 | (unless (string= title "") 223 | (setq title-parts (split-string title " --- ") 224 | title-name (car title-parts) 225 | title-description (cadr title-parts)) 226 | (princ "\n") 227 | 228 | ; (princ "## ") 229 | ; (princ title-name) 230 | ; (princ "\n") 231 | ; (princ (concat "## " title-name " ")) 232 | (princ title-name) 233 | (princ " ") 234 | (princ "[![Travis build status](https://secure.travis-ci.org/bburns/clipmon.png?branch=master)](http://travis-ci.org/bburns/clipmon)") 235 | (princ " ") 236 | (princ "[![melpa.org](http://melpa.org/packages/clipmon-badge.svg)](http://melpa.org/#/clipmon)") 237 | (princ " ") 238 | (princ "[![GPL-3.0](http://img.shields.io/:license-gpl-blue.svg)](http://opensource.org/licenses/GPL-3.0)") 239 | (princ "\n") 240 | (princ (make-string 76 ?-)) 241 | 242 | (princ "\n") 243 | (princ "\n") 244 | ; (when (cdr title-parts) 245 | ; (princ (format "*%s*\n\n" (cadr title-parts)))) 246 | ; (princ "---\n") 247 | ) 248 | 249 | ;; Process everything else. 250 | (catch 'break 251 | (while (setq line (car lines)) 252 | (cond 253 | 254 | ;; Wait until we reach the commentary section. 255 | ((string-match "^;;; Commentary:?$" line) 256 | (setq started-output t)) 257 | 258 | ;; Once we hit code, attempt to document functions/macros. 259 | ((string-match "^;;; Code:?$" line) 260 | ; (print-section "Function Documentation" 3) 261 | ; (princ "\n\n") 262 | (with-temp-buffer 263 | (insert code) 264 | ; (goto-char 0) 265 | ; (lisp-mode) 266 | ; (catch 'no-more-funcs 267 | ; (while t 268 | ; (condition-case exc 269 | ; (document-a-function) 270 | ; (error (princ (format "\n\n" exc))))))) 271 | (println "----") 272 | (println) 273 | ; (println (get-meta "Author") " ") 274 | (println "Author: Brian Burns ") 275 | (println (get-meta "URL") " ") 276 | (println (get-meta "Version") " ") 277 | (println) 278 | (println "This file was generated from commentary in " title-name " - do not edit!") 279 | (println) 280 | (println "----") 281 | (println) 282 | ) 283 | (throw 'break nil)) 284 | 285 | ;; Otherwise print out all the documentation. 286 | (started-output 287 | (print-formatted-line line))) 288 | 289 | (setq lines (cdr lines)))) 290 | 291 | ) 292 | 293 | 294 | ; (princ "----- 295 | 296 | ;
297 | ; Markdown README file generated by 298 | ; make-readme.el 299 | ;
\n") 300 | 301 | ;;; make-readme.el ends here 302 | -------------------------------------------------------------------------------- /test/clipmon-test.el: -------------------------------------------------------------------------------- 1 | ;;; clipmon-test.el --- tests for clipmon.el 2 | ;;; Commentary: 3 | 4 | ; Just test the complicated things. 5 | 6 | ; Run standalone with 7 | ; $ emacs -batch -L . -l clipmon-test.el -f ert-run-tests-batch 8 | ; 9 | ; Running 2 tests (2014-12-27 01:01:14-0600) 10 | ; passed 1/2 clipmon-test-all-transforms 11 | ; passed 2/2 clipmon-test-no-transforms 12 | ; Ran 2 tests, 2 results as expected (2014-12-27 01:01:14-0600) 13 | ; 14 | ; 15 | ; or internally with (ert-run) 16 | ; or (ert-run-tests-interactively "regexp") 17 | 18 | 19 | ;;; Code: 20 | 21 | (require 'ert) 22 | (require 'clipmon) 23 | 24 | 25 | (ert-deftest clipmon-test-no-transforms () 26 | "Try with no transforms on text." 27 | 28 | (let ((clipmon-transform-trim nil) 29 | (clipmon-transform-remove nil) 30 | (clipmon-transform-prefix nil) 31 | (clipmon-transform-suffix nil) 32 | (clipmon-transform-function nil)) 33 | 34 | (should (equal 35 | (clipmon--autoinsert-transform-text 36 | " Marbled murrelets use old-growth forest stands for nesting.[2][3] ") 37 | " Marbled murrelets use old-growth forest stands for nesting.[2][3] ")) 38 | )) 39 | 40 | 41 | (ert-deftest clipmon-test-all-transforms () 42 | "Try all text transforms." 43 | 44 | (let ((clipmon-transform-trim t) 45 | (clipmon-transform-remove "stands \\|\\[[0-9]+\\]\\|\\[citation needed\\]") 46 | (clipmon-transform-prefix "<<") 47 | (clipmon-transform-suffix ">>") 48 | (clipmon-transform-function (lambda (s) (downcase s)))) 49 | 50 | (should (equal 51 | (clipmon--autoinsert-transform-text 52 | " Marbled murrelets use old-growth forest stands for nesting.[2][3] ") 53 | "<>")) 54 | )) 55 | 56 | 57 | (ert-deftest clipmon-test-remove-regexp () 58 | "Try the remove-regexp for Wikipedia references." 59 | 60 | (let ((clipmon-transform-trim nil) 61 | ; use the default remove-regexp 62 | (clipmon-transform-prefix nil) 63 | (clipmon-transform-suffix nil) 64 | (clipmon-transform-function nil)) 65 | 66 | (should (equal 67 | (clipmon--autoinsert-transform-text 68 | " Marbled [1 2] murrelets[115] use [old-growth][99] stands [1984] for nesting.[2] ") 69 | " Marbled [1 2] murrelets use [old-growth] stands [1984] for nesting. ")) 70 | )) 71 | 72 | 73 | (ert-deftest clipmon-test-on-and-off () 74 | "Try turning mode and autoinsert on and off." 75 | (let ((clipmon-autoinsert-sound nil)) 76 | 77 | ; clipmon-mode 78 | ; off 79 | (clipmon-mode-stop) 80 | (should (null clipmon-mode)) 81 | (clipmon-mode-stop) 82 | (should (null clipmon-mode)) 83 | 84 | ; on 85 | (clipmon-mode-start) 86 | (should clipmon-mode) 87 | (clipmon-mode-start) 88 | (should clipmon-mode) 89 | 90 | ; off 91 | (clipmon-mode 'toggle) 92 | (should (null clipmon-mode)) 93 | 94 | ; on 95 | (clipmon-mode 'toggle) 96 | (should clipmon-mode) 97 | 98 | ; off 99 | (clipmon-mode 0) 100 | (should (null clipmon-mode)) 101 | 102 | 103 | ; autoinsert 104 | ; off 105 | (clipmon-autoinsert-stop) 106 | (should (null clipmon--autoinsert)) 107 | (should (null clipmon-mode)) ; ie should still be off 108 | 109 | ; on 110 | (clipmon-autoinsert-toggle) 111 | (should clipmon--autoinsert) 112 | (should clipmon-mode) ; should automatically turn the mode on 113 | 114 | ; off 115 | (clipmon-autoinsert-toggle) 116 | (should (null clipmon--autoinsert)) 117 | (should clipmon-mode) ; should stay on 118 | 119 | (clipmon-mode 0) 120 | (should (null clipmon-mode)) 121 | )) 122 | 123 | 124 | (ert-deftest clipmon-test-timeout () 125 | "Let clock timeout." 126 | (let ((clipmon-timer-interval 0.1) ; secs 127 | (clipmon-autoinsert-timeout (/ 0.2 60.0)) ; 0.2 secs in mins 128 | (sleep-amount 0.4) ; secs 129 | (clipmon-autoinsert-sound nil)) 130 | 131 | (clipmon-autoinsert-stop) 132 | (clipmon-autoinsert-start) 133 | (should clipmon--autoinsert) 134 | (should clipmon-mode) ; should turn this on also 135 | (sleep-for sleep-amount) ; wait for timeout 136 | (should (null clipmon--autoinsert)) 137 | (should clipmon-mode) ; should still be on 138 | )) 139 | 140 | 141 | ; (defun get-tar-file (folder) 142 | ; "Get the latest tar file from the folder." 143 | ; (let* ((files (directory-files folder t "\\.tar$")) ; in sorted order 144 | ; (tar-file (car (last files)))) ; latest tar 145 | ; tar-file)) 146 | ; ; (get-tar-file "../tar") 147 | ; ; (get-tar-file "./tar") 148 | 149 | 150 | 151 | ; (ert-deftest clipmon-test-install () 152 | ; "Test installing from latest tar file to a local elpa folder." 153 | 154 | ; ;; We can make a new elpa folder, call it elpa-test, and install clipmon there, 155 | ; ;; just to see that the install is working correctly. 156 | ; (let* ((folder-elpa-test (expand-file-name "elpa-test" ".")) 157 | ; (package-user-dir folder-elpa-test) ; temporarily bind this 158 | ; (tar-file (get-tar-file "tar"))) 159 | 160 | ; ;; delete elpa-test folder to start new 161 | ; (when (file-exists-p folder-elpa-test) 162 | ; (message "deleting %s..." folder-elpa-test) 163 | ; (delete-directory folder-elpa-test t)) 164 | 165 | ; ;; a typical setup 166 | ; (global-set-key (kbd "C-0") 'clipmon-autoinsert-toggle) 167 | ; (setq clipmon-interval 1) ; an old name 168 | ; (setq clipmon-transform-clip nil) ; a new name 169 | ; (add-to-list 'after-init-hook 'clipmon-mode-start) ; capture to kill ring 170 | ; (add-to-list 'after-init-hook 'clipmon-persist) ; persist kill ring to disk 171 | 172 | ; ;; Note: this file is run in batch mode, which inhibits packages from being initialized, 173 | ; ;; so we must simulate it ourselves. 174 | 175 | ; ;; install package from tar file - this also requires/loads the package. 176 | ; ;; this is akin to what package-initialize does, after reading the init file, 177 | ; ;; though package-initialize just loads the autoloads, not the whole file. 178 | ; ;;> so, should add another test to check an existing installation.. somehow. 179 | ; (package-install-file tar-file) 180 | 181 | ; ; this hook would be run after package-initialize, so run it here 182 | ; (run-hooks 'after-init-hook) 183 | 184 | ; ;; make sure menu is there 185 | ; (should (key-binding [menu-bar options clipmon-killring])) 186 | 187 | ; ;; and settings 188 | ; (should (boundp 'clipmon-timer-interval)) 189 | 190 | ; ;; see if files got there okay - .el, .wav 191 | ; ;; (dired folder-elpa-test) 192 | ; ;; (assert (member "clipmon.el" (directory-files latestclipmonfolder))) 193 | 194 | ; ;; check renamings 195 | ; (should (eq 1 clipmon-timer-interval)) 196 | ; (should (eq clipmon-timeout clipmon-autoinsert-timeout)) 197 | 198 | ; ;; turn on and off 199 | ; (let ((clipmon-autoinsert-sound nil)) ; quiet 200 | ; (clipmon-autoinsert-toggle) 201 | ; (clipmon-mode 0) 202 | ; (should (null clipmon--autoinsert)) 203 | ; (should (null clipmon-mode))) 204 | 205 | ; ;; other things to check 206 | ; ;; (find-library "clipmon") 207 | ; ;; (describe-package "clipmon") 208 | ; ;; (customize-group 'clipmon) 209 | 210 | ; )) 211 | 212 | 213 | ;;; clipmon-test.el ends here 214 | --------------------------------------------------------------------------------