isearch-olc/isearch-olc.el

150 lines
4.4 KiB
EmacsLisp

;;; isearch-olc.el --- In-buffer overlay for Isearch's lazy count -*- lexical-binding: t -*-
;; Copyright (C) 2022 Free Software Foundation, Inc.
;; Author: Jai Flack <jflack@disroot.org>
;; Version: 2022-03-24
;; URL: https://git.disroot.org/jflack/isearch-olc
;; Package-Requires: ((emacs "29"))
;; Keywords: Isearch matching
;; This file is NOT part of GNU 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 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 <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This package provides an in-buffer overlay of Isearch's lazy counting
;; facility. This count is displayed at the end of the current matches
;; line with configurable formatting similar to
;; `lazy-count-suffix-format'.
;; To use the package, activate `isearch-olc-mode'. Requires both
;; `isearch-lazy-highlight' and `isearch-lazy-count' to be non-nil.
;;; Configuration:
;; The defaults should be acceptable for most users however it can be
;; customised for aesthetic purposes.
;; Available options:
;; - `isearch-olc-format'
;; Available faces:
;; - `isearch-olc'
;;; Code:
(require 'isearch)
(defgroup isearch-olc nil
"In-buffer overlay for Isearch's lazy count."
:group 'isearch)
(defcustom isearch-olc-format " %s/%s"
"Format of the current and total number of matches for the
in-buffer display. Requirements are the same as
`lazy-count-suffix-format'.
See `isearch-olc-mode'."
:type 'string
:group 'isearch-olc)
(defface isearch-olc
'((t (:inherit isearch)))
"Face for the in-buffer count overlay.
See `isearch-olc-mode'."
:group 'isearch-olc)
(defvar isearch-olc--overlay nil)
(defun isearch-olc--format ()
(if (and isearch-olc-format
isearch-lazy-count
isearch-lazy-count-current
(not isearch-error)
(not isearch-suspended))
(format isearch-olc-format
(if isearch-lazy-highlight-forward
isearch-lazy-count-current
(if (eq isearch-lazy-count-current 0)
0
(- isearch-lazy-count-total
isearch-lazy-count-current
-1)))
(or isearch-lazy-count-total "?"))
""))
(defun isearch-olc--display ()
;; Try to reuse the overlay as much as possible to remove flickering.
(unless isearch-olc--overlay
(let ((ov (make-overlay 0 0)))
(setq isearch-olc--overlay ov)
;; see `isearch-lazy-highlight-match'
(overlay-put ov 'priority 1001)
(overlay-put ov 'face 'isearch-olc)
(unless isearch-lazy-highlight-buffer
(overlay-put ov 'window (selected-window)))))
;; TODO: test more thoroughly with RTL text
(let ((ov isearch-olc--overlay)
(pae
(if isearch-match-data
(save-excursion
(goto-char (car isearch-match-data))
(point-at-eol))
(point-at-eol)))
(count (propertize (isearch-olc--format)
'cursor t)))
(unless (and (= (overlay-end ov)
pae)
(eq (overlay-buffer ov)
(current-buffer)))
(move-overlay ov pae pae (current-buffer)))
;; TODO: could this be eq?
(unless (string= (overlay-get ov 'after-string)
count)
(overlay-put ov 'after-string count))))
(defun isearch-olc--cleanup ()
(when isearch-olc--overlay
(delete-overlay isearch-olc--overlay)
(setq isearch-olc--overlay nil)))
(defun isearch-olc--setup ()
(add-hook 'lazy-count-update-hook #'isearch-olc--display)
(add-hook 'isearch-mode-end-hook #'isearch-olc--cleanup))
(defun isearch-olc--packup ()
(remove-hook 'lazy-count-update-hook #'isearch-olc--display)
(remove-hook 'isearch-mode-end-hook #'isearch-olc--cleanup))
(define-minor-mode isearch-olc-mode
"Display the current isearch's match number and total number of
matches just after the currently matched line.
Requires both `isearch-lazy-highlight' and `isearch-lazy-count'
to be non-nil."
:global t
(if isearch-olc-mode
(isearch-olc--setup)
(isearch-olc--packup)))
(provide 'isearch-olc)
;;; isearch-olc.el ends here