|
;;; highlight-indentation.el --- Minor modes for highlighting indentation
|
|
;; Author: Anton Johansson <anton.johansson@gmail.com> - http://antonj.se
|
|
;; Created: Dec 15 23:42:04 2010
|
|
;; Version: 0.7.0
|
|
;; Package-Version: 20181204.839
|
|
;; URL: https://github.com/antonj/Highlight-Indentation-for-Emacs
|
|
;;
|
|
;; 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 2 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.
|
|
;;
|
|
;;; Commentary:
|
|
;; Customize `highlight-indentation-face', and
|
|
;; `highlight-indentation-current-column-face' to suit your theme.
|
|
|
|
;;; Code:
|
|
|
|
(defgroup highlight-indentation nil
|
|
"Highlight Indentation"
|
|
:prefix "highlight-indentation-"
|
|
:group 'basic-faces)
|
|
|
|
(defface highlight-indentation-face
|
|
;; Fringe has non intrusive color in most color-themes
|
|
'((t :inherit fringe))
|
|
"Basic face for highlighting indentation guides."
|
|
:group 'highlight-indentation)
|
|
|
|
(defcustom highlight-indentation-offset
|
|
(if (and (boundp 'standard-indent) standard-indent) standard-indent 2)
|
|
"Default indentation offset, used if no other can be found from
|
|
major mode. This value is always used by
|
|
`highlight-indentation-mode' if set buffer local. Set buffer
|
|
local with `highlight-indentation-set-offset'"
|
|
:type 'integer
|
|
:group 'highlight-indentation)
|
|
|
|
(defcustom highlight-indentation-blank-lines nil
|
|
"Show indentation guides on blank lines. Experimental.
|
|
|
|
Known issues:
|
|
- Doesn't work well with completion popups that use overlays
|
|
- Overlays on blank lines sometimes aren't cleaned up or updated perfectly
|
|
Can be refershed by scrolling
|
|
- Not yet implemented for highlight-indentation-current-column-mode
|
|
- May not work perfectly near the bottom of the screen
|
|
- Point appears after indent guides on blank lines"
|
|
:group 'highlight-indentation)
|
|
|
|
(defvar highlight-indentation-overlay-priority 1)
|
|
(defvar highlight-indentation-current-column-overlay-priority 2)
|
|
|
|
(defconst highlight-indentation-hooks
|
|
'((after-change-functions (lambda (start end length)
|
|
(highlight-indentation-redraw-region
|
|
start end
|
|
'highlight-indentation-overlay
|
|
'highlight-indentation-put-overlays-region))
|
|
t t)
|
|
(window-scroll-functions (lambda (win start)
|
|
(highlight-indentation-redraw-window
|
|
win
|
|
'highlight-indentation-overlay
|
|
'highlight-indentation-put-overlays-region
|
|
start))
|
|
nil t)))
|
|
|
|
(defun highlight-indentation-get-buffer-windows (&optional all-frames)
|
|
"Return a list of windows displaying the current buffer."
|
|
(get-buffer-window-list (current-buffer) 'no-minibuf all-frames))
|
|
|
|
(defun highlight-indentation-delete-overlays-buffer (overlay)
|
|
"Delete all overlays in the current buffer."
|
|
(save-restriction
|
|
(widen)
|
|
(highlight-indentation-delete-overlays-region (point-min) (point-max) overlay)))
|
|
|
|
(defun highlight-indentation-delete-overlays-region (start end overlay)
|
|
"Delete overlays between START and END."
|
|
(mapc #'(lambda (o)
|
|
(if (overlay-get o overlay) (delete-overlay o)))
|
|
(overlays-in start end)))
|
|
|
|
(defun highlight-indentation-redraw-window (win overlay func &optional start)
|
|
"Redraw win starting from START."
|
|
(highlight-indentation-redraw-region (or start (window-start win)) (window-end win t) overlay func))
|
|
|
|
(defun highlight-indentation-redraw-region (start end overlay func)
|
|
"Erease and read overlays between START and END."
|
|
(save-match-data
|
|
(save-excursion
|
|
(let ((inhibit-point-motion-hooks t)
|
|
(start (save-excursion (goto-char start) (beginning-of-line) (point)))
|
|
|
|
(end (save-excursion (goto-char end) (line-beginning-position 2))))
|
|
(highlight-indentation-delete-overlays-region start end overlay)
|
|
(funcall func start end overlay)))))
|
|
|
|
(defun highlight-indentation-redraw-all-windows (overlay func &optional all-frames)
|
|
"Redraw the all windows showing the current buffer."
|
|
(dolist (win (highlight-indentation-get-buffer-windows all-frames))
|
|
(highlight-indentation-redraw-window win overlay func)))
|
|
|
|
(defun highlight-indentation-put-overlays-region (start end overlay)
|
|
"Place overlays between START and END."
|
|
(goto-char end)
|
|
(let (o ;; overlay
|
|
(last-indent 0)
|
|
(last-char 0)
|
|
(pos (point))
|
|
(loop t))
|
|
(while (and loop
|
|
(>= pos start))
|
|
(save-excursion
|
|
(beginning-of-line)
|
|
(let ((c 0)
|
|
(cur-column (current-column)))
|
|
(while (and (setq c (char-after))
|
|
(integerp c)
|
|
(not (= 10 c)) ;; newline
|
|
(= 32 c)) ;; space
|
|
(when (= 0 (% cur-column highlight-indentation-offset))
|
|
(let ((p (point)))
|
|
(setq o (make-overlay p (+ p 1))))
|
|
(overlay-put o overlay t)
|
|
(overlay-put o 'priority highlight-indentation-overlay-priority)
|
|
(overlay-put o 'face 'highlight-indentation-face))
|
|
(forward-char)
|
|
(setq cur-column (current-column)))
|
|
(when (and highlight-indentation-blank-lines
|
|
(integerp c)
|
|
(or (= 10 c)
|
|
(= 13 c)))
|
|
(when (< cur-column last-indent)
|
|
(let ((column cur-column)
|
|
(s nil)
|
|
(show t)
|
|
num-spaces)
|
|
(while (< column last-indent)
|
|
(if (>= 0
|
|
(setq num-spaces
|
|
(%
|
|
(- last-indent column)
|
|
highlight-indentation-offset)))
|
|
(progn
|
|
(setq num-spaces (1- highlight-indentation-offset))
|
|
(setq show t))
|
|
(setq show nil))
|
|
(setq s (cons (concat
|
|
(if show
|
|
(propertize " "
|
|
'face
|
|
'highlight-indentation-face)
|
|
"")
|
|
(make-string num-spaces 32))
|
|
s))
|
|
(setq column (+ column num-spaces (if show 1 0))))
|
|
(setq s (apply 'concat (reverse s)))
|
|
(let ((p (point)))
|
|
(setq o (make-overlay p p)))
|
|
(overlay-put o overlay t)
|
|
(overlay-put o 'priority highlight-indentation-overlay-priority)
|
|
(overlay-put o 'after-string s))
|
|
(setq cur-column last-indent)))
|
|
(setq last-indent (* highlight-indentation-offset
|
|
(ceiling (/ (float cur-column)
|
|
highlight-indentation-offset))))))
|
|
(when (= pos start)
|
|
(setq loop nil))
|
|
(forward-line -1) ;; previous line
|
|
(setq pos (point)))))
|
|
|
|
(defun highlight-indentation-guess-offset ()
|
|
"Get indentation offset of current buffer."
|
|
(cond ((and (eq major-mode 'python-mode) (boundp 'python-indent))
|
|
python-indent)
|
|
((and (eq major-mode 'python-mode) (boundp 'py-indent-offset))
|
|
py-indent-offset)
|
|
((and (eq major-mode 'python-mode) (boundp 'python-indent-offset))
|
|
python-indent-offset)
|
|
((and (eq major-mode 'ruby-mode) (boundp 'ruby-indent-level))
|
|
ruby-indent-level)
|
|
((and (eq major-mode 'scala-mode) (boundp 'scala-indent:step))
|
|
scala-indent:step)
|
|
((and (eq major-mode 'scala-mode) (boundp 'scala-mode-indent:step))
|
|
scala-mode-indent:step)
|
|
((and (or (eq major-mode 'scss-mode) (eq major-mode 'css-mode)) (boundp 'css-indent-offset))
|
|
css-indent-offset)
|
|
((and (eq major-mode 'nxml-mode) (boundp 'nxml-child-indent))
|
|
nxml-child-indent)
|
|
((and (eq major-mode 'coffee-mode) (boundp 'coffee-tab-width))
|
|
coffee-tab-width)
|
|
((and (eq major-mode 'js-mode) (boundp 'js-indent-level))
|
|
js-indent-level)
|
|
((and (eq major-mode 'js2-mode) (boundp 'js2-basic-offset))
|
|
js2-basic-offset)
|
|
((and (fboundp 'derived-mode-class) (eq (derived-mode-class major-mode) 'sws-mode) (boundp 'sws-tab-width))
|
|
sws-tab-width)
|
|
((and (eq major-mode 'web-mode) (boundp 'web-mode-markup-indent-offset))
|
|
web-mode-markup-indent-offset) ; other similar vars: web-mode-{css-indent,scripts}-offset
|
|
((and (eq major-mode 'web-mode) (boundp 'web-mode-html-offset)) ; old var
|
|
web-mode-html-offset)
|
|
((and (local-variable-p 'c-basic-offset) (boundp 'c-basic-offset))
|
|
c-basic-offset)
|
|
((and (eq major-mode 'yaml-mode) (boundp 'yaml-indent-offset))
|
|
yaml-indent-offset)
|
|
((and (eq major-mode 'elixir-mode) (boundp 'elixir-smie-indent-basic))
|
|
elixir-smie-indent-basic)
|
|
(t
|
|
(default-value 'highlight-indentation-offset))))
|
|
|
|
;;;###autoload
|
|
(define-minor-mode highlight-indentation-mode
|
|
"Highlight indentation minor mode highlights indentation based on spaces"
|
|
:lighter " ||"
|
|
(when (not highlight-indentation-mode) ;; OFF
|
|
(highlight-indentation-delete-overlays-buffer 'highlight-indentation-overlay)
|
|
(dolist (hook highlight-indentation-hooks)
|
|
(remove-hook (car hook) (nth 1 hook) (nth 3 hook))))
|
|
|
|
(when highlight-indentation-mode ;; ON
|
|
(when (not (local-variable-p 'highlight-indentation-offset))
|
|
(set (make-local-variable 'highlight-indentation-offset)
|
|
(highlight-indentation-guess-offset)))
|
|
|
|
;; Setup hooks
|
|
(dolist (hook highlight-indentation-hooks)
|
|
(apply 'add-hook hook))
|
|
(highlight-indentation-redraw-all-windows 'highlight-indentation-overlay
|
|
'highlight-indentation-put-overlays-region)))
|
|
|
|
;;;###autoload
|
|
(defun highlight-indentation-set-offset (offset)
|
|
"Set indentation offset localy in buffer, will prevent
|
|
highlight-indentation from trying to guess indentation offset
|
|
from major mode"
|
|
(interactive
|
|
(if (and current-prefix-arg (not (consp current-prefix-arg)))
|
|
(list (prefix-numeric-value current-prefix-arg))
|
|
(list (read-number "Indentation offset: "))))
|
|
(set (make-local-variable 'highlight-indentation-offset) offset)
|
|
(when highlight-indentation-mode
|
|
(highlight-indentation-mode)))
|
|
|
|
;;; This minor mode will highlight the indentation of the current line
|
|
;;; as a vertical bar (grey background color) aligned with the column of the
|
|
;;; first character of the current line.
|
|
(defface highlight-indentation-current-column-face
|
|
;; Fringe has non intrusive color in most color-themes
|
|
'((t (:background "black")))
|
|
"Basic face for highlighting indentation guides."
|
|
:group 'highlight-indentation)
|
|
|
|
(defconst highlight-indentation-current-column-hooks
|
|
'((post-command-hook (lambda ()
|
|
(highlight-indentation-redraw-all-windows 'highlight-indentation-current-column-overlay
|
|
'highlight-indentation-current-column-put-overlays-region)) nil t)))
|
|
|
|
(defun highlight-indentation-current-column-put-overlays-region (start end overlay)
|
|
"Place overlays between START and END."
|
|
(let (o ;; overlay
|
|
(last-indent 0)
|
|
(indent (save-excursion (back-to-indentation) (current-column)))
|
|
(pos start))
|
|
(goto-char start)
|
|
;; (message "doing it %d" indent)
|
|
(while (< pos end)
|
|
(beginning-of-line)
|
|
(while (and (integerp (char-after))
|
|
(not (= 10 (char-after))) ;; newline
|
|
(= 32 (char-after))) ;; space
|
|
(when (= (current-column) indent)
|
|
(setq pos (point)
|
|
last-indent pos
|
|
o (make-overlay pos (+ pos 1)))
|
|
(overlay-put o overlay t)
|
|
(overlay-put o 'priority highlight-indentation-current-column-overlay-priority)
|
|
(overlay-put o 'face 'highlight-indentation-current-column-face))
|
|
(forward-char))
|
|
(forward-line) ;; Next line
|
|
(setq pos (point)))))
|
|
|
|
;;;###autoload
|
|
(define-minor-mode highlight-indentation-current-column-mode
|
|
"Hilight Indentation minor mode displays a vertical bar
|
|
corresponding to the indentation of the current line"
|
|
:lighter " |"
|
|
|
|
(when (not highlight-indentation-current-column-mode) ;; OFF
|
|
(highlight-indentation-delete-overlays-buffer 'highlight-indentation-current-column-overlay)
|
|
(dolist (hook highlight-indentation-current-column-hooks)
|
|
(remove-hook (car hook) (nth 1 hook) (nth 3 hook))))
|
|
|
|
(when highlight-indentation-current-column-mode ;; ON
|
|
(when (not (local-variable-p 'highlight-indentation-offset))
|
|
(set (make-local-variable 'highlight-indentation-offset)
|
|
(highlight-indentation-guess-offset)))
|
|
|
|
;; Setup hooks
|
|
(dolist (hook highlight-indentation-current-column-hooks)
|
|
(apply 'add-hook hook))
|
|
(highlight-indentation-redraw-all-windows 'highlight-indentation-current-column-overlay
|
|
'highlight-indentation-current-column-put-overlays-region)))
|
|
|
|
(provide 'highlight-indentation)
|
|
|
|
;;; highlight-indentation.el ends here
|