Focus/focus.el

156 lines
5.6 KiB
EmacsLisp
Raw Normal View History

2015-05-17 10:41:18 +00:00
;;; focus.el --- Dim the font color of text in surrounding sections -*- lexical-binding: t; -*-
2015-05-12 20:42:01 +00:00
;; Copyright (C) 2015 Lars Tveito
;; Author: Lars Tveito <larstvei@ifi.uio.no>
;; URL: http://github.com/larstvei/Focus
;; Created: 11th May 2015
;; Version: 0.0.1
2015-05-17 10:36:20 +00:00
;; Package-Requires: ((emacs "24") (cl-lib "0.5"))
2015-05-12 20:42:01 +00:00
;; 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:
2015-05-17 10:41:18 +00:00
;; Focus provides `focus-mode` that dims the text of surrounding sections,
2015-05-12 21:21:33 +00:00
;; similar to [iA Writer's](https://ia.net/writer) Focus Mode.
2015-05-12 20:42:01 +00:00
;;
2015-05-12 21:21:33 +00:00
;; Enable the mode with `M-x focus-mode'.
2015-05-12 20:42:01 +00:00
;;; Code:
(require 'cl-lib)
(require 'thingatpt)
2015-05-17 18:28:40 +00:00
(defgroup focus ()
2015-05-17 10:41:33 +00:00
"Dim the font color of text in surrounding sections."
2015-05-17 18:28:40 +00:00
:group 'font-lock
:prefix "focus-")
2015-05-17 10:41:33 +00:00
(defcustom focus-dimness 0
2015-05-17 10:36:20 +00:00
"Amount of dimness in out of focus sections is determined by this integer.
2015-05-18 21:03:39 +00:00
A positive value increases the dimness of the sections.
A negative value decreases the dimness.
2015-05-17 10:36:20 +00:00
2015-05-18 21:03:39 +00:00
The default is 0 which means a 50/50 mixture of the background
and foreground color."
:type '(integer)
:group 'focus)
(defcustom focus-mode-to-thing '((prog-mode . defun) (text-mode . sentence))
2015-05-17 10:36:20 +00:00
"An associated list between mode and thing.
A thing is defined in thingatpt.el; the thing determines the
narrowness of the focused section.
Note that the order of the list matters. The first mode that the
current mode is derived from is used, so more modes that have
many derivatives should be placed by the end of the list.
Things that are defined include `symbol', `list', `sexp',
`defun', `filename', `url', `email', `word', `sentence',
`whitespace', `line', and `page'."
:type '(repeat symbol)
:group 'focus)
2015-05-17 10:36:20 +00:00
(defvar focus-pre-overlay nil
2015-05-12 20:23:36 +00:00
"The overlay that dims the text prior to the current-point.")
2015-05-17 10:36:20 +00:00
(defvar focus-post-overlay nil
2015-05-12 20:23:36 +00:00
"The overlay that dims the text past the current-point.")
2015-05-11 22:48:09 +00:00
2015-05-17 10:36:20 +00:00
;; Use make-local-variable for backwards compatibility.
(dolist (var '(focus-pre-overlay
focus-post-overlay))
(make-local-variable var))
;; Changing major-mode should not affect Focus mode.
(dolist (var '(focus-pre-overlay
focus-post-overlay
post-command-hook))
(put var 'permanent-local t))
(defun focus-any (f lst)
2015-05-17 10:36:20 +00:00
"Apply F to each element of LST and return first NON-NIL."
(when lst
(let ((v (funcall f (car lst))))
(if v v (focus-any f (cdr lst))))))
(defun focus-bounds ()
2015-05-17 10:36:20 +00:00
"Return the current bounds, based on `focus-mode-to-thing'."
2015-05-16 23:31:44 +00:00
(let* ((modes (mapcar 'car focus-mode-to-thing))
(mode (focus-any 'derived-mode-p modes))
2015-05-16 23:31:44 +00:00
(thing (if mode (cdr (assoc mode focus-mode-to-thing)) 'sentence)))
(bounds-of-thing-at-point thing)))
(defun focus-average-colors (color &rest colors)
2015-05-17 10:36:20 +00:00
"Takes one or more COLORS and returns the average of the RGB-values."
(let* ((colors (cons color colors))
(colors (mapcar 'color-name-to-rgb colors))
(len (length colors))
(sums (apply 'cl-mapcar '+ colors))
(avg (mapcar (lambda (v) (/ v len)) sums)))
(apply 'color-rgb-to-hex avg)))
2015-05-11 22:48:09 +00:00
(defun focus-make-dim-color ()
2015-05-17 10:36:20 +00:00
"Return a dimmed color relative to the current theme."
(let ((background (face-attribute 'default :background))
(foreground (face-attribute 'default :foreground))
(backgrounds (if (> focus-dimness 0) focus-dimness 1))
(foregrounds (if (< focus-dimness 0) (- focus-dimness) 1)))
(apply 'focus-average-colors
(append (make-list backgrounds background)
(make-list foregrounds foreground)))))
2015-05-11 22:48:09 +00:00
(defun focus-move-focus ()
2015-05-17 10:36:20 +00:00
"Move `focus-pre-overlay' and `focus-post-overlay'.
If function `focus-mode' is enabled, this command fires after
each command."
(let* ((bounds (focus-bounds)))
(when bounds
(move-overlay focus-pre-overlay (point-min) (car bounds))
(move-overlay focus-post-overlay (cdr bounds) (point-max)))))
2015-05-11 22:48:09 +00:00
(defun focus-init ()
2015-05-17 10:36:20 +00:00
"This function is run when command `focus-mode' is enabled.
It sets the `focus-pre-overlay' and `focus-post-overlay' to
overlays; these are invisible until `focus-move-focus' is run. It
adds `focus-move-focus' to `post-command-hook'."
(setq focus-pre-overlay (make-overlay (point-min) (point-min))
focus-post-overlay (make-overlay (point-max) (point-max)))
(let ((color (focus-make-dim-color)))
(mapc (lambda (o) (overlay-put o 'face (cons 'foreground-color color)))
(list focus-pre-overlay focus-post-overlay)))
(add-hook 'post-command-hook 'focus-move-focus nil t))
(defun focus-terminate ()
2015-05-17 10:36:20 +00:00
"This function is run when command `focus-mode' is disabled.
The overlays pointed to by `focus-pre-overlay' and `focus-post-overlay' are
deleted, and `focus-move-focus' is removed from `post-command-hook'."
(progn (mapc 'delete-overlay (list focus-pre-overlay focus-post-overlay))
(remove-hook 'post-command-hook 'focus-move-focus t)))
2015-05-11 22:48:09 +00:00
;;;###autoload
(define-minor-mode focus-mode
2015-05-17 10:41:18 +00:00
"Dim the font color of text in surrounding sections."
2015-05-11 22:48:09 +00:00
:init-value nil
(if focus-mode (focus-init) (focus-terminate)))
2015-05-12 20:42:01 +00:00
(provide 'focus)
;;; focus.el ends here