]> code.delx.au - gnu-emacs-elpa/commitdiff
First commit
authorArtur Malabarba <bruce.connor.am@gmail.com>
Fri, 9 Oct 2015 22:08:39 +0000 (23:08 +0100)
committerArtur Malabarba <bruce.connor.am@gmail.com>
Fri, 9 Oct 2015 22:16:32 +0000 (23:16 +0100)
Readme.org [new file with mode: 0644]
spotlight.el [new file with mode: 0644]

diff --git a/Readme.org b/Readme.org
new file mode 100644 (file)
index 0000000..6587e12
--- /dev/null
@@ -0,0 +1,20 @@
+#+TITLE: Spotlight --- Never lose your cursor again
+
+This is a global minor-mode.  Turn it on everywhere with:
+#+BEGIN_SRC emacs-lisp
+(spotlight-mode 1)
+#+END_SRC
+
+Whenever point moves a long distance (as defined by
+~spotlight-minimum-distance~) a light will shine on top of your cursor
+to make sure you see where it is.
+
+- To customize the appearance of the spotlight, configure
+  ~spotlight-size~ and ~spotlight-brightness~.
+
+- To customize how long it lasts, configure ~spotlight-blink-duration~
+  and ~spotlight-blink-delay~.
+
+- To customize when it is used at all, configure
+  ~spotlight-minimum-distance~ and
+  ~spotlight-blink-when-buffer-changes~.
diff --git a/spotlight.el b/spotlight.el
new file mode 100644 (file)
index 0000000..5bc13a5
--- /dev/null
@@ -0,0 +1,201 @@
+;;; spotlight.el --- Highlight the cursor whenever it moves long distances  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2015  Artur Malabarba
+
+;; Author: Artur Malabarba <emacs@endlessparentheses.com>
+;; Keywords: convenience
+;; Version: 0.1
+;; Package-Requires: ((cl-lib "0.5"))
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This is a global minor-mode.  Turn it on everywhere with
+;;     (spotlight-mode 1)
+
+;;; Code:
+
+(require 'cl-lib)
+
+(defgroup spotlight nil
+  "Customization group for spotlight."
+  :group 'emacs
+  :prefix "spotlight-")
+
+(defvar spotlight--timer nil)
+
+(defcustom spotlight-minimum-distance 15
+  "Minimum movement distance in lines to blink the spotlight."
+  :type 'integer)
+
+(defcustom spotlight-push-mark nil
+  "Should the mark be pushed before long movements?"
+  :type 'boolean)
+
+(defcustom spotlight-blink-when-buffer-changes t
+  "Should the spotlight blink when changing buffer?"
+  :type 'boolean)
+
+(defcustom spotlight-blink-duration 0.3
+  "Time, in seconds, that the blink should last."
+  :type 'number)
+
+(defcustom spotlight-blink-delay 0.3
+  "Time, in seconds, before starting to fade the spotlight."
+  :type 'number)
+
+(defcustom spotlight-size 15
+  "Size of the spotlight in characters."
+  :type 'number)
+
+(defcustom spotlight-brightness 0.5
+  "Brightness as a float between 0 and 1."
+  :type 'number)
+
+\f
+;;; Overlays
+(defvar spotlight--ovs nil)
+
+(defun spotlight--colored-overlay (color)
+  "Put an overlay at point with background COLOR."
+  (let ((ov (make-overlay (point) (1+ (point)))))
+    (overlay-put ov 'face (list :background color))
+    (overlay-put ov 'spotlight t)
+    (push ov spotlight--ovs)))
+
+(defun spotlight--ov-at-point ()
+  (car (cl-member-if (lambda (o) (overlay-get o 'spotlight))
+                     (overlays-at (point)))))
+
+(defun spotlight--vanish ()
+  "Turn off the spotlight."
+  (when (timerp spotlight--timer)
+    (cancel-timer spotlight--timer))
+  (mapc #'delete-overlay spotlight--ovs)
+  (setq spotlight--ovs nil))
+
+\f
+;;; Colors
+(defun spotlight--int-range (a b)
+  "Return a list of integers between A inclusive and B exclusive.
+Only returns `spotlight-size' elements."
+  (let ((d (/ (- b a) spotlight-size))
+        (out (list a)))
+    (dotimes (_ (1- spotlight-size))
+      (push (+ (car out) d) out))
+    (nreverse out)))
+
+(defun spotlight--color-range ()
+  "Return a list of background colors for the spotlight."
+  (let ((bg (color-values (face-attribute 'default :background))))
+    (apply #'cl-mapcar (lambda (r g b) (format "#%04x%04x%04x" r g b))
+           (if (< (color-distance "black" bg)
+                  (color-distance "white" bg))
+               (mapcar (lambda (n) (butlast (spotlight--int-range (* spotlight-brightness 65535) n))) bg)
+             (mapcar (lambda (n) (cdr (spotlight--int-range (* (- 1 spotlight-brightness) 65535) n))) bg)))))
+
+\f
+;;; Blinking
+(defun spotlight--shine ()
+  "Shine a spotlight at point."
+  (let ((colors (spotlight--color-range)))
+    (save-excursion
+      (while colors
+        (if (looking-at "$")
+            (progn
+              ;; (spotlight--after-string)
+              (setq colors nil))
+          (spotlight--colored-overlay (pop colors))
+          (forward-char 1))))))
+
+(defun spotlight--dec ()
+  "Decrease the spotlight brightness by one."
+  (let ((o (spotlight--ov-at-point)))
+    (if (not o)
+        (spotlight--vanish)
+      (delete-overlay o)
+      (save-excursion
+        (while (progn (forward-char 1)
+                      (setq o (spotlight--ov-at-point)))
+          (move-overlay o (1- (point)) (point)))))))
+
+(defun spotlight-blink ()
+  "Blink the spotlight at the position of the cursor."
+  (interactive)
+  (spotlight--vanish)
+  (spotlight--shine)
+  (setq spotlight--timer
+        (run-at-time spotlight-blink-delay
+                     (/ spotlight-blink-duration 1.0 (length spotlight--colors))
+                     #'spotlight--dec)))
+
+\f
+;;; Movement detection
+(defvar spotlight--previous-place nil)
+(defvar spotlight--previous-mark-head nil)
+
+(defun spotlight--maybe-push-mark ()
+  "Push mark if it seems to be safe."
+  (when (and spotlight-push-mark
+             (not mark-active))
+    (let ((head (car mark-ring)))
+      (when (and (eq spotlight--previous-mark-head head)
+                 (not (equal head spotlight--previous-place)))
+        (push-mark spotlight--previous-place)))))
+
+(defun spotlight--post-command ()
+  "Blink if point moved very far."
+  (cond
+   ((not (markerp spotlight--previous-place))
+    (spotlight--vanish))
+   ;; Blink because we changed buffer.
+   ((not (equal (marker-buffer spotlight--previous-place)
+                (current-buffer)))
+    (when spotlight-blink-when-buffer-changes
+      (unless (window-minibuffer-p)
+        (spotlight-blink))))
+   ;; Blink for distance movement
+   ((and (> (abs (- (point) spotlight--previous-place))
+            spotlight-minimum-distance)
+         (> (count-screen-lines (min (point) spotlight--previous-place)
+                                (max (point) spotlight--previous-place))
+            spotlight-minimum-distance))
+    (spotlight--maybe-push-mark)
+    (spotlight-blink))
+   ;; Even if we don't blink, vanish any previous spotlight.
+   (t (spotlight--vanish)))
+  (unless (window-minibuffer-p)
+    (setq spotlight--previous-mark-head (car mark-ring))
+    (setq spotlight--previous-place (point-marker))))
+
+\f
+;;; Minor-mode
+(defcustom spotlight-lighter (cond
+                     ((char-displayable-p ?💡) "💡")
+                     ((char-displayable-p ?Λ) "Λ")
+                     (t "*"))
+  "Lighter string used on the mode-line."
+  :type 'string)
+
+;;;###autoload
+(define-minor-mode spotlight-mode
+  nil nil spotlight-lighter nil
+  :global t
+  (if spotlight-mode
+      (add-hook 'post-command-hook #'spotlight--post-command)
+    (remove-hook 'post-command-hook #'spotlight--post-command)))
+
+(provide 'spotlight)
+;;; spotlight.el ends here