;;; show-sentence.el --- visually mark sentence boundaries -*- lexical-binding: t; -*- ;; Copyright (C) 2025 John Ankarström ;; Author: John Ankarström ;; Created: 8 Feb 2025 ;; Version: 0.1 ;; Keywords: navigation ;; URL: http://ankarstrom.se/~john/emacs/show-sentence.el ;; This file is not part of GNU Emacs. ;; Permission to use, copy, modify and/or distribute this software for ;; any purpose with or without fee is hereby granted. ;;; Commentary: ;; `show-sentence' provides `show-sentence-mode', which visually ;; highlights sentences and the boundaries between them. ;; To install, place show-sentence.el in ~/.emacs.d/lisp and put the ;; following in your configuration: ;; (add-to-list 'load-path (concat user-emacs-directory "lisp")) ;; (autoload 'show-sentence-mode "show-sentence.el" ;; "Visually highlight the beginnings of sentences." t) ;; To change the appearance, customize `show-sentence-boundary', ;; `show-sentence-first-word', `show-sentence-first-letter'. ;; By default, sentence boundaries are highlighted with a gray ;; background, and the first word and letter are not highlighted. ;; The following customization makes the first word of every sentence ;; highlighted with a yellow background: ;; (custom-set-faces ;; '(show-sentence-boundary ((t nil))) ;; '(show-sentence-first-word ;; ((t ( :background "#dfdf88" ;; :underline (:color "#afaf88" :style line :position t)))))) ;;; Code: (defconst show-sentence--font-lock-keywords '((show-sentence--find-boundary (2 (list 'face 'show-sentence-boundary)) (3 (list 'face 'show-sentence-first-letter)) (4 (list 'face 'show-sentence-first-word))) ("[^\n.]\n\n+\\([[:upper:]]\\)\\([[:alpha:]-]*\\)" (1 (list 'face 'show-sentence-first-letter)) (2 (list 'face 'show-sentence-first-word))))) ;;;###autoload (define-minor-mode show-sentence-mode "Visually highlight the beginnings of sentences. To configure the appearance, customize `show-sentence-boundary', `show-sentence-first-word', `show-sentence-first-letter'." :lighter " S" (if show-sentence-mode (progn (font-lock-add-keywords nil show-sentence--font-lock-keywords t) (add-to-list 'font-lock-extend-region-functions 'show-sentence--font-lock-extend-region t) (font-lock-flush)) (font-lock-remove-keywords nil show-sentence--font-lock-keywords) (setq font-lock-extend-region-functions (delq 'show-sentence--font-lock-extend-region font-lock-extend-region-functions)) (font-lock-flush))) ;;;###autoload (defgroup show-sentence nil "Visually highlight the beginnings of sentences." :group 'text) ;;;###autoload (define-globalized-minor-mode global-show-sentence-mode show-sentence-mode show-sentence-mode :group 'show-sentence) ;;;###autoload (defface show-sentence-boundary '((t :background "grey70")) "Face for sentence boundaries. See `show-sentence-mode'." :group 'show-sentence) ;;;###autoload (defface show-sentence-first-letter '((t :inherit show-sentence-first-word)) "Face for the first letter in a sentence. See `show-sentence-mode'.") ;;;###autoload (defface show-sentence-first-word '((t)) "Face for the first word in a sentence. See `show-sentence-mode'.") (defun show-sentence--find-boundary (limit) "Find next sentence boundary before LIMIT." (re-search-forward (concat (sentence-end) "\\([^\n]\\)\\([[:alpha:]-]*\\)") limit t)) (defun show-sentence--font-lock-extend-region () "Extend font lock region such that sentences are recognized at the beginning of paragraphs. For performance reasons, only paragraphs separated by two newlines are supported." (eval-when-compile (defvar font-lock-beg) (defvar font-lock-end)) (save-excursion (goto-char font-lock-beg) (when-let ((beg (re-search-backward "[^\n]\n+" (- font-lock-beg 3) t))) (setq font-lock-beg beg)) (goto-char font-lock-end) (when-let ((end (re-search-forward "\n+[^\n]" (+ font-lock-end 3) t))) (setq font-lock-end end)))) (provide 'show-sentence) ;;; show-sentence.el ends here