;;; isearch-olc.el --- In-buffer overlay for Isearch's lazy count -*- lexical-binding: t -*- ;; Copyright (C) 2022 Free Software Foundation, Inc. ;; Author: Jai Flack ;; Version: 2022-03-24 ;; URL: https://git.disroot.org/jflack/isearch-olc ;; Package-Requires: ((emacs "28.1")) ;; 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 . ;;; 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)