diff options
author | mattkae <mattkae@protonmail.com> | 2022-05-17 07:07:37 -0400 |
---|---|---|
committer | mattkae <mattkae@protonmail.com> | 2022-05-17 07:07:37 -0400 |
commit | becff06c71d277647eda4378203d03ab36e141eb (patch) | |
tree | a1f73bba3676f34e0faf76764f5de963321f5576 /elpa/auctex-13.1.3/tex-fold.el | |
parent | 3f4a0d5370ae6c34afe180df96add3b8522f4af1 (diff) |
Evil mode and latex support
Diffstat (limited to 'elpa/auctex-13.1.3/tex-fold.el')
-rw-r--r-- | elpa/auctex-13.1.3/tex-fold.el | 948 |
1 files changed, 948 insertions, 0 deletions
diff --git a/elpa/auctex-13.1.3/tex-fold.el b/elpa/auctex-13.1.3/tex-fold.el new file mode 100644 index 0000000..6d50f31 --- /dev/null +++ b/elpa/auctex-13.1.3/tex-fold.el @@ -0,0 +1,948 @@ +;;; tex-fold.el --- Fold TeX macros. -*- lexical-binding: t; -*- + +;; Copyright (C) 2004-2022 Free Software Foundation, Inc. + +;; Author: Ralf Angeli <angeli@caeruleus.net> +;; Maintainer: auctex-devel@gnu.org +;; Created: 2004-07-04 +;; Keywords: tex, wp + +;; This file is part of AUCTeX. + +;; AUCTeX 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, or (at your option) +;; any later version. + +;; AUCTeX 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 AUCTeX; see the file COPYING. If not, write to the Free +;; Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +;; 02110-1301, USA. + +;;; Commentary: + +;; This file provides support for hiding and unhiding TeX, LaTeX, +;; ConTeXt, Texinfo and similar macros and environments inside of +;; AUCTeX. +;; +;; Caveats: +;; +;; The display string of content which should display part of itself +;; is made by copying the text from the buffer together with its text +;; properties. If fontification has not happened when this is done +;; (e.g. because of lazy or just-in-time font locking) the intended +;; fontification will not show up. Maybe this could be improved by +;; using some sort of "lazy folding" or refreshing the window upon +;; scrolling. As a workaround fontification of the whole buffer +;; currently is forced before folding it. + +;;; Code: + +(eval-when-compile + (require 'cl-lib)) + +(require 'tex) +(autoload 'LaTeX-forward-paragraph "latex") +(autoload 'LaTeX-backward-paragraph "latex") +(autoload 'LaTeX-find-matching-begin "latex") +(autoload 'LaTeX-find-matching-end "latex") +(autoload 'ConTeXt-find-matching-start "context") +(autoload 'ConTeXt-find-matching-stop "context") +(autoload 'Texinfo-find-env-start "tex-info") +(autoload 'Texinfo-find-env-end "tex-info") + +(defgroup TeX-fold nil + "Fold TeX macros." + :group 'AUCTeX) + +(defcustom TeX-fold-type-list '(env macro math) + "List of item types to consider when folding. +Valid items are the symbols `env' for environments, `macro' for +macros, `math' for math macros and `comment' for comments." + :type '(set (const :tag "Environments" env) + (const :tag "Macros" macro) + (const :tag "Math Macros" math) + (const :tag "Comments" comment))) + +(defcustom TeX-fold-macro-spec-list + '(("[f]" ("footnote" "marginpar")) + ("[c]" ("cite")) + ("[l]" ("label")) + ("[r]" ("ref" "pageref" "eqref" "footref")) + ("[i]" ("index" "glossary")) + ("[1]:||*" ("item")) + ("..." ("dots")) + ("(C)" ("copyright")) + ("(R)" ("textregistered")) + ("TM" ("texttrademark")) + (1 ("part" "chapter" "section" "subsection" "subsubsection" + "paragraph" "subparagraph" + "part*" "chapter*" "section*" "subsection*" "subsubsection*" + "paragraph*" "subparagraph*" + "emph" "textit" "textsl" "textmd" "textrm" "textsf" "texttt" + "textbf" "textsc" "textup"))) + "List of replacement specifiers and macros to fold. + +The first element of each item can be a string, an integer or a +function symbol. The second element is a list of macros to fold +without the leading backslash. + +If the first element is a string, it will be used as a display +replacement for the whole macro. Numbers in braces, brackets, +parens or angle brackets will be replaced by the respective macro +argument. For example \"{1}\" will be replaced by the first +mandatory argument of the macro. One can also define +alternatives within the specifier which are used if an argument +is not found. Alternatives are separated by \"||\". They are +most useful with optional arguments. As an example, the default +specifier for \\item is \"[1]:||*\" which means that if there is +an optional argument, its value is shown followed by a colon. If +there is no optional argument, only an asterisk is used as the +display string. + +If the first element is an integer, the macro will be replaced by +the respective macro argument. + +If the first element is a function symbol, the function will be +called with all mandatory arguments of the macro and the result +of the function call will be used as a replacement for the macro. + +Setting this variable does not take effect immediately. Use +Customize or reset the mode." + :type '(repeat (group (choice (string :tag "Display String") + (integer :tag "Number of argument" :value 1) + (function :tag "Function to execute")) + (repeat :tag "Macros" (string))))) + +(defvar TeX-fold-macro-spec-list-internal nil + "Internal list of display strings and macros to fold. +Is updated when the TeX Fold mode is being activated and then +contains all constructs to fold for the given buffer or mode +respectively, that is, contents of both `TeX-fold-macro-spec-list' +and <mode-prefix>-fold-macro-spec-list.") +(make-variable-buffer-local 'TeX-fold-macro-spec-list-internal) + +(defcustom TeX-fold-env-spec-list + '(("[comment]" ("comment"))) + "List of display strings and environments to fold." + :type '(repeat (group (choice (string :tag "Display String") + (integer :tag "Number of argument" :value 1)) + (repeat :tag "Environments" (string))))) + +(defvar TeX-fold-env-spec-list-internal nil + "Internal list of display strings and environments to fold. +Is updated when the TeX Fold mode is being activated and then +contains all constructs to fold for the given buffer or mode +respectively, that is, contents of both `TeX-fold-env-spec-list' +and <mode-prefix>-fold-env-spec-list.") +(make-variable-buffer-local 'TeX-fold-env-spec-list-internal) + +(defcustom TeX-fold-math-spec-list nil + "List of display strings and math macros to fold." + :type '(repeat (group (choice (string :tag "Display String") + (integer :tag "Number of argument" :value 1)) + (repeat :tag "Math Macros" (string))))) + +(defvar TeX-fold-math-spec-list-internal nil + "Internal list of display strings and math macros to fold. +Is updated when the TeX Fold mode is being activated and then +contains all constructs to fold for the given buffer or mode +respectively, that is, contents of both `TeX-fold-math-spec-list' +and <mode-prefix>-fold-math-spec-list.") +(make-variable-buffer-local 'TeX-fold-math-spec-list-internal) + +(defcustom TeX-fold-unspec-macro-display-string "[m]" + "Display string for unspecified macros. +This string will be displayed if a single macro is being hidden +which is not specified in `TeX-fold-macro-spec-list'." + :type '(string)) + +(defcustom TeX-fold-unspec-env-display-string "[env]" + "Display string for unspecified environments. +This string will be displayed if a single environment is being +hidden which is not specified in `TeX-fold-env-spec-list'." + :type '(string)) + +(defcustom TeX-fold-unspec-use-name t + "If non-nil use the name of an unspecified item as display string. +Set it to nil if you want to use the values of the variables +`TeX-fold-unspec-macro-display-string' or +`TeX-fold-unspec-env-display-string' respectively as a display +string for any unspecified macro or environment." + :type 'boolean) + +(defcustom TeX-fold-preserve-comments nil + "If non-nil do not fold in comments." + :type 'boolean) + +(defcustom TeX-fold-unfold-around-mark t + "Unfold text around the mark, if active." + :type 'boolean) + +(defcustom TeX-fold-help-echo-max-length 70 + "Maximum length of help echo message for folded overlays. +Set it to zero in order to disable help echos." + :type 'integer) + +(defcustom TeX-fold-force-fontify t + "Force the buffer to be fully fontified by folding it." + :type 'boolean) + +(defcustom TeX-fold-auto nil + "If non-nil, fold macros automatically after `TeX-insert-macro'." + :type 'boolean) + +(defface TeX-fold-folded-face + '((((class color) (background light)) + (:foreground "SlateBlue")) + (((class color) (background dark)) + (:foreground "SlateBlue1")) + (((class grayscale) (background light)) + (:foreground "DimGray")) + (((class grayscale) (background dark)) + (:foreground "LightGray")) + (t (:slant italic))) + "Face for the display string of folded content.") + +(defvar TeX-fold-folded-face 'TeX-fold-folded-face + "Face for the display string of folded content.") + +(defface TeX-fold-unfolded-face + '((((class color) (background light)) + (:background "#f2f0fd")) + (((class color) (background dark)) + (:background "#38405d")) + (((class grayscale) (background light)) + (:background "LightGray")) + (((class grayscale) (background dark)) + (:background "DimGray")) + (t (:inverse-video t))) + "Face for folded content when it is temporarily opened.") + +(defvar TeX-fold-unfolded-face 'TeX-fold-unfolded-face + "Face for folded content when it is temporarily opened.") + +(defvar TeX-fold-ellipsis "..." + "String used as display string for overlays instead of a zero-length string.") + +(defvar TeX-fold-open-spots nil) +(make-variable-buffer-local 'TeX-fold-open-spots) + +(defcustom TeX-fold-command-prefix "\C-c\C-o" + "Prefix key to use for commands in TeX Fold mode. +The value of this variable is checked as part of loading TeX Fold mode. +After that, changing the prefix key requires manipulating keymaps." + :type 'string) + +(defvar TeX-fold-keymap + (let ((map (make-sparse-keymap))) + (define-key map "\C-o" #'TeX-fold-dwim) + (define-key map "\C-b" #'TeX-fold-buffer) + (define-key map "\C-r" #'TeX-fold-region) + (define-key map "\C-p" #'TeX-fold-paragraph) + (define-key map "\C-m" #'TeX-fold-macro) + (define-key map "\C-e" #'TeX-fold-env) + (define-key map "\C-c" #'TeX-fold-comment) + (define-key map "b" #'TeX-fold-clearout-buffer) + (define-key map "r" #'TeX-fold-clearout-region) + (define-key map "p" #'TeX-fold-clearout-paragraph) + (define-key map "i" #'TeX-fold-clearout-item) + map)) + + +;;; Folding + +(defun TeX-fold-dwim () + "Hide or show items according to the current context. +If there is folded content, unfold it. If there is a marked +region, fold all configured content in this region. If there is +no folded content but a macro or environment, fold it." + (interactive) + (cond ((TeX-fold-clearout-item)) + ((TeX-active-mark) (TeX-fold-region (mark) (point))) + ((TeX-fold-item 'macro)) + ((TeX-fold-item 'math)) + ((TeX-fold-item 'env)) + ((TeX-fold-comment)))) + +(defun TeX-fold-buffer () + "Hide all configured macros and environments in the current buffer. +The relevant macros are specified in the variable `TeX-fold-macro-spec-list' +and `TeX-fold-math-spec-list', and environments in `TeX-fold-env-spec-list'." + (interactive) + (TeX-fold-clearout-region (point-min) (point-max)) + (when (and TeX-fold-force-fontify + (boundp 'jit-lock-mode) + jit-lock-mode + (fboundp 'jit-lock-fontify-now)) + ;; We force fontification here only because it should rarely be + ;; needed for the other folding commands. + (jit-lock-fontify-now)) + (TeX-fold-region (point-min) (point-max))) + +(defun TeX-fold-paragraph () + "Hide all configured macros and environments in the current paragraph. +The relevant macros are specified in the variable `TeX-fold-macro-spec-list' +and `TeX-fold-math-spec-list', and environments in `TeX-fold-env-spec-list'." + (interactive) + (save-excursion + (let ((end (progn (LaTeX-forward-paragraph) (point))) + (start (progn (LaTeX-backward-paragraph) (point)))) + (TeX-fold-clearout-region start end) + (TeX-fold-region start end)))) + +(defun TeX-fold-region (start end) + "Fold all items in region from START to END." + (interactive "r") + (when (and (memq 'env TeX-fold-type-list) + (not (eq major-mode 'plain-tex-mode))) + (TeX-fold-region-macro-or-env start end 'env)) + (when (memq 'macro TeX-fold-type-list) + (TeX-fold-region-macro-or-env start end 'macro)) + (when (memq 'math TeX-fold-type-list) + (TeX-fold-region-macro-or-env start end 'math)) + (when (memq 'comment TeX-fold-type-list) + (TeX-fold-region-comment start end))) + +(defun TeX-fold-region-macro-or-env (start end type) + "Fold all items of type TYPE in region from START to END. +TYPE can be one of the symbols 'env for environments, 'macro +for macros and 'math for math macros." + (save-excursion + (let (fold-list item-list regexp) + (dolist (item (cond ((eq type 'env) TeX-fold-env-spec-list-internal) + ((eq type 'math) TeX-fold-math-spec-list-internal) + (t TeX-fold-macro-spec-list-internal))) + (dolist (i (cadr item)) + (cl-pushnew (list i (car item)) fold-list :test #'equal) + (cl-pushnew i item-list :test #'equal))) + (when item-list + (setq regexp (cond ((and (eq type 'env) + (eq major-mode 'context-mode)) + (concat (regexp-quote TeX-esc) + "start" (regexp-opt item-list t))) + ((and (eq type 'env) + (eq major-mode 'texinfo-mode)) + (concat (regexp-quote TeX-esc) + (regexp-opt item-list t))) + ((eq type 'env) + (concat (regexp-quote TeX-esc) + "begin[ \t]*{" + (regexp-opt item-list t) "}")) + (t + (concat (regexp-quote TeX-esc) + (regexp-opt item-list t))))) + (save-restriction + (narrow-to-region start end) + ;; Start from the bottom so that it is easier to prioritize + ;; nested macros. + (goto-char (point-max)) + (let ((case-fold-search nil) + item-name) + (while (re-search-backward regexp nil t) + (setq item-name (match-string 1)) + (unless (or (and TeX-fold-preserve-comments + (TeX-in-commented-line)) + ;; Make sure no partially matched macros are + ;; folded. For macros consisting of letters + ;; this means there should be none of the + ;; characters [A-Za-z@*] after the matched + ;; string. Single-char non-letter macros like + ;; \, don't have this requirement. + (and (memq type '(macro math)) + (save-match-data + (string-match "[A-Za-z]" item-name)) + (save-match-data + (string-match "[A-Za-z@*]" + (string (char-after + (match-end 0))))))) + (let* ((item-start (match-beginning 0)) + (display-string-spec (cadr (assoc item-name + fold-list))) + (item-end (TeX-fold-item-end item-start type)) + (ov (TeX-fold-make-overlay item-start item-end type + display-string-spec))) + (TeX-fold-hide-item ov)))))))))) + +(defun TeX-fold-region-comment (start end) + "Fold all comments in region from START to END." + (save-excursion + (goto-char start) + (let (beg) + (while (setq beg (TeX-search-forward-comment-start end)) + (goto-char beg) + ;; Determine the start of the region to be folded just behind + ;; the comment starter. + (looking-at TeX-comment-start-regexp) + (setq beg (match-end 0)) + ;; Search for the end of the comment. + (while (TeX-comment-forward)) + (end-of-line 0) + ;; Hide the whole region. + (TeX-fold-hide-item (TeX-fold-make-overlay beg (point) 'comment + TeX-fold-ellipsis)))))) + +(defun TeX-fold-macro () + "Hide the macro on which point currently is located." + (interactive) + (unless (TeX-fold-item 'macro) + (message "No macro found"))) + +(defun TeX-fold-math () + "Hide the math macro on which point currently is located." + (interactive) + (unless (TeX-fold-item 'math) + (message "No macro found"))) + +(defun TeX-fold-env () + "Hide the environment on which point currently is located." + (interactive) + (unless (TeX-fold-item 'env) + (message "No environment found"))) + +(defun TeX-fold-comment () + "Hide the comment on which point currently is located." + (interactive) + (unless (TeX-fold-comment-do) + (message "No comment found"))) + +(defun TeX-fold-item (type) + "Hide the item on which point currently is located. +TYPE specifies the type of item and can be one of the symbols +`env' for environments, `macro' for macros or `math' for math +macros. +Return non-nil if an item was found and folded, nil otherwise." + (if (and (eq type 'env) + (eq major-mode 'plain-tex-mode)) + (message + "Folding of environments is not supported in current mode") + (let ((item-start (cond ((and (eq type 'env) + (eq major-mode 'context-mode)) + (save-excursion + (ConTeXt-find-matching-start) (point))) + ((and (eq type 'env) + (eq major-mode 'texinfo-mode)) + (save-excursion + (Texinfo-find-env-start) (point))) + ((eq type 'env) + (condition-case nil + (save-excursion + (LaTeX-find-matching-begin) (point)) + (error nil))) + (t + (TeX-find-macro-start))))) + (when item-start + (let* ((item-name (save-excursion + (goto-char item-start) + (looking-at + (cond ((and (eq type 'env) + (eq major-mode 'context-mode)) + (concat (regexp-quote TeX-esc) + "start\\([A-Za-z]+\\)")) + ((and (eq type 'env) + (eq major-mode 'texinfo-mode)) + (concat (regexp-quote TeX-esc) + "\\([A-Za-z]+\\)")) + ((eq type 'env) + (concat (regexp-quote TeX-esc) + "begin[ \t]*{" + "\\([A-Za-z*]+\\)}")) + (t + (concat (regexp-quote TeX-esc) + "\\([A-Za-z@*]+\\)")))) + (match-string-no-properties 1))) + (fold-list (cond ((eq type 'env) TeX-fold-env-spec-list-internal) + ((eq type 'math) + TeX-fold-math-spec-list-internal) + (t TeX-fold-macro-spec-list-internal))) + fold-item + (display-string-spec + (or (catch 'found + (while fold-list + (setq fold-item (car fold-list)) + (setq fold-list (cdr fold-list)) + (when (member item-name (cadr fold-item)) + (throw 'found (car fold-item))))) + ;; Item is not specified. + (if TeX-fold-unspec-use-name + (concat "[" item-name "]") + (if (eq type 'env) + TeX-fold-unspec-env-display-string + TeX-fold-unspec-macro-display-string)))) + (item-end (TeX-fold-item-end item-start type)) + (ov (TeX-fold-make-overlay item-start item-end type + display-string-spec))) + (TeX-fold-hide-item ov)))))) + +(defun TeX-fold-comment-do () + "Hide the comment on which point currently is located. +This is the function doing the work for `TeX-fold-comment'. It +is an internal function communicating with return values rather +than with messages for the user. +Return non-nil if a comment was found and folded, nil otherwise." + (if (and (not (TeX-in-comment)) (not (TeX-in-line-comment))) + nil + (let (beg) + (save-excursion + (while (progn + (beginning-of-line 0) + (and (TeX-in-line-comment) + (not (bobp))))) + (goto-char (TeX-search-forward-comment-start (line-end-position 2))) + (looking-at TeX-comment-start-regexp) + (setq beg (match-end 0)) + (while (TeX-comment-forward)) + (end-of-line 0) + (when (> (point) beg) + (TeX-fold-hide-item (TeX-fold-make-overlay beg (point) 'comment + TeX-fold-ellipsis))))))) + + +;;; Utilities + +(defun TeX-fold-make-overlay (ov-start ov-end type display-string-spec) + "Make a TeX-fold overlay extending from OV-START to OV-END. +TYPE is a symbol which is used to describe the content to hide +and may be `macro' for macros, `math' for math macro and `env' for +environments. +DISPLAY-STRING-SPEC is the original specification of the display +string in the variables `TeX-fold-macro-spec-list' or +`TeX-fold-env-spec-list' and may be a string or an integer." + ;; Calculate priority before the overlay is instantiated. We don't + ;; want `TeX-overlay-prioritize' to pick up a non-prioritized one. + (let ((priority (TeX-overlay-prioritize ov-start ov-end)) + (ov (make-overlay ov-start ov-end (current-buffer) t nil))) + (overlay-put ov 'category 'TeX-fold) + (overlay-put ov 'priority priority) + (overlay-put ov 'evaporate t) + (overlay-put ov 'TeX-fold-type type) + (overlay-put ov 'TeX-fold-display-string-spec display-string-spec) + ov)) + +(defun TeX-fold-item-end (start type) + "Return the end of an item of type TYPE starting at START. +TYPE can be either `env' for environments, `macro' for macros or +`math' for math macros." + (save-excursion + (cond ((and (eq type 'env) + (eq major-mode 'context-mode)) + (goto-char start) + (ConTeXt-find-matching-stop) + (point)) + ((and (eq type 'env) + (eq major-mode 'texinfo-mode)) + (goto-char (1+ start)) + (Texinfo-find-env-end) + (point)) + ((eq type 'env) + (goto-char (1+ start)) + (LaTeX-find-matching-end) + (point)) + (t + (goto-char start) + (TeX-find-macro-end))))) + +(defun TeX-fold-overfull-p (ov-start ov-end display-string) + "Return t if an overfull line will result after adding an overlay. +The overlay extends from OV-START to OV-END and will display the +string DISPLAY-STRING." + (and + (save-excursion + (goto-char ov-end) + (search-backward "\n" ov-start t)) + (not (string-match "\n" display-string)) + (> (+ (- ov-start + (save-excursion + (goto-char ov-start) + (line-beginning-position))) + (length display-string) + (- (save-excursion + (goto-char ov-end) + (line-end-position)) + ov-end)) + (current-fill-column)))) + +(defun TeX-fold-macro-nth-arg (n macro-start &optional macro-end delims) + "Return a property list of the argument number N of a macro. +The start of the macro to examine is given by MACRO-START, its +end optionally by MACRO-END. With DELIMS the type of delimiters +can be specified as a cons cell containing the opening char as +the car and the closing char as the cdr. The chars have to have +opening and closing syntax as defined in +`TeX-search-syntax-table'. + +The first item in the returned list is the string specified in +the argument, with text properties. The second item is for +backward compatibility and always nil." + (save-excursion + (let* ((macro-end (or macro-end + (save-excursion (goto-char macro-start) + (TeX-find-macro-end)))) + (open-char (if delims (car delims) ?{)) + (open-string (char-to-string open-char)) + (close-char (if delims (cdr delims) ?})) + ;; (close-string (char-to-string close-char)) + content-start content-end) + (goto-char macro-start) + (if (condition-case nil + (progn + (while (> n 0) + (skip-chars-forward (concat "^" open-string) macro-end) + (when (= (point) macro-end) + (error nil)) + (setq content-start (progn + (skip-chars-forward + (concat open-string " \t")) + (point))) + (goto-char + (if delims + (with-syntax-table + (TeX-search-syntax-table open-char close-char) + (scan-lists (point) 1 1)) + (TeX-find-closing-brace))) + (setq content-end (save-excursion + (backward-char) + (skip-chars-backward " \t") + (point))) + (setq n (1- n))) + t) + (error nil)) + (list (TeX-fold-buffer-substring content-start content-end)) + nil)))) + +(defun TeX-fold-buffer-substring (start end) + "Return the contents of buffer from START to END as a string. +Like `buffer-substring' but copy overlay display strings as well." + ;; Swap values of `start' and `end' if necessary. + (when (> start end) (let ((tmp start)) (setq start end end tmp))) + (let ((overlays (overlays-in start end)) + result) + ;; Get rid of overlays not under our control or not completely + ;; inside the specified region. + (dolist (ov overlays) + (when (or (not (eq (overlay-get ov 'category) 'TeX-fold)) + (< (overlay-start ov) start) + (> (overlay-end ov) end)) + (setq overlays (remove ov overlays)))) + (if (null overlays) + (buffer-substring start end) + ;; Sort list according to ascending starts. + (setq overlays (sort (copy-sequence overlays) + (lambda (a b) + (< (overlay-start a) (overlay-start b))))) + ;; Get the string from the start of the region up to the first overlay. + (setq result (buffer-substring start (overlay-start (car overlays)))) + (let (ov) + (while overlays + (setq ov (car overlays) + overlays (cdr overlays)) + ;; Add the display string of the overlay. + (setq result (concat result (overlay-get ov 'display))) + ;; Remove overlays contained in the current one. + (dolist (elt overlays) + (when (< (overlay-start elt) (overlay-end ov)) + (setq overlays (remove elt overlays)))) + ;; Add the string from the end of the current overlay up to + ;; the next overlay or the end of the specified region. + (setq result (concat result (buffer-substring (overlay-end ov) + (if overlays + (overlay-start + (car overlays)) + end)))))) + result))) + +(defun TeX-fold-make-help-echo (start end) + "Return a string to be used as the help echo of folded overlays. +The text between START and END will be used for this but cropped +to the length defined by `TeX-fold-help-echo-max-length'. Line +breaks will be replaced by spaces." + (let* ((spill (+ start TeX-fold-help-echo-max-length)) + (lines (split-string (buffer-substring start (min end spill)) "\n")) + (result (pop lines))) + (dolist (line lines) + ;; Strip leading whitespace + (when (string-match "^[ \t]+" line) + (setq line (replace-match "" nil nil line))) + ;; Strip trailing whitespace + (when (string-match "[ \t]+$" line) + (setq line (replace-match "" nil nil line))) + (setq result (concat result " " line))) + (when (> end spill) (setq result (concat result "..."))) + result)) + +(defun TeX-fold-update-at-point () + "Update all TeX-fold overlays at point displaying computed content." + (let (overlays) + ;; Get all overlays at point under our control. + (dolist (ov (overlays-at (point))) + (when (and (eq (overlay-get ov 'category) 'TeX-fold) + (numberp (overlay-get ov 'TeX-fold-display-string-spec))) + (cl-pushnew ov overlays))) + (when overlays + ;; Sort list according to descending starts. + (setq overlays (sort (copy-sequence overlays) + (lambda (a b) + (> (overlay-start a) (overlay-start b))))) + (dolist (ov overlays) + (TeX-fold-hide-item ov))))) + + +;;; Removal + +(defun TeX-fold-clearout-buffer () + "Permanently show all macros in the buffer." + (interactive) + (TeX-fold-clearout-region (point-min) (point-max))) + +(defun TeX-fold-clearout-paragraph () + "Permanently show all macros in the paragraph point is located in." + (interactive) + (save-excursion + (let ((end (progn (LaTeX-forward-paragraph) (point))) + (start (progn (LaTeX-backward-paragraph) (point)))) + (TeX-fold-clearout-region start end)))) + +(defun TeX-fold-clearout-region (start end) + "Permanently show all macros in region starting at START and ending at END." + (interactive "r") + (let ((overlays (overlays-in start end))) + (TeX-fold-remove-overlays overlays))) + +(defun TeX-fold-clearout-item () + "Permanently show the macro on which point currently is located." + (interactive) + (let ((overlays (overlays-at (point)))) + (TeX-fold-remove-overlays overlays))) + +(defun TeX-fold-remove-overlays (overlays) + "Remove all overlays set by TeX-fold in OVERLAYS. +Return non-nil if a removal happened, nil otherwise." + (let (found) + (while overlays + (when (eq (overlay-get (car overlays) 'category) 'TeX-fold) + (delete-overlay (car overlays)) + (setq found t)) + (setq overlays (cdr overlays))) + found)) + + +;;; Toggling + +(defun TeX-fold-expand-spec (spec ov-start ov-end) + "Expand instances of {<num>}, [<num>], <<num>>, and (<num>). +Replace them with the respective macro argument." + (let ((spec-list (split-string spec "||")) + (delims '((?\{ . ?\}) (?\[ . ?\]) (?< . ?>) (?\( . ?\)))) + index success) + (catch 'success + ;; Iterate over alternatives. + (dolist (elt spec-list) + (setq spec elt + index nil) + ;; Find and expand every placeholder. + (while (and (string-match "\\([[{<]\\)\\([1-9]\\)\\([]}>]\\)" elt index) + ;; Does the closing delim match the opening one? + (string-equal + (match-string 3 elt) + (char-to-string + (cdr (assq (string-to-char (match-string 1 elt)) + delims))))) + (setq index (match-end 0)) + (let ((arg (car (save-match-data + ;; Get the argument. + (TeX-fold-macro-nth-arg + (string-to-number (match-string 2 elt)) + ov-start ov-end + (assoc (string-to-char (match-string 1 elt)) + delims)))))) + (when arg (setq success t)) + ;; Replace the placeholder in the string. + (setq elt (replace-match (or arg TeX-fold-ellipsis) nil t elt) + index (+ index (- (length elt) (length spec))) + spec elt))) + (when success (throw 'success nil)))) + spec)) + +(defun TeX-fold-hide-item (ov) + "Hide a single macro or environment. +That means, put respective properties onto overlay OV." + (let* ((ov-start (overlay-start ov)) + (ov-end (overlay-end ov)) + (spec (overlay-get ov 'TeX-fold-display-string-spec)) + (computed (cond + ((stringp spec) + (TeX-fold-expand-spec spec ov-start ov-end)) + ((functionp spec) + (let (arg arg-list + (n 1)) + (while (setq arg (TeX-fold-macro-nth-arg + n ov-start ov-end)) + (unless (member (car arg) arg-list) + (setq arg-list (append arg-list (list (car arg))))) + (setq n (1+ n))) + (or (condition-case nil + (apply spec arg-list) + (error nil)) + "[Error: No content or function found]"))) + (t (or (TeX-fold-macro-nth-arg spec ov-start ov-end) + "[Error: No content found]")))) + (display-string (if (listp computed) (car computed) computed)) + ;; (face (when (listp computed) (cadr computed))) + ) + ;; Do nothing if the overlay is empty. + (when (and ov-start ov-end) + ;; Cater for zero-length display strings. + (when (string= display-string "") (setq display-string TeX-fold-ellipsis)) + ;; Add a linebreak to the display string and adjust the overlay end + ;; in case of an overfull line. + (when (TeX-fold-overfull-p ov-start ov-end display-string) + (setq display-string (concat display-string "\n")) + (move-overlay ov ov-start (save-excursion + (goto-char ov-end) + (skip-chars-forward " \t") + (point)))) + (overlay-put ov 'mouse-face 'highlight) + (when font-lock-mode + ;; Add raise adjustment for superscript and subscript. + ;; (bug#42209) + (setq display-string + (propertize display-string + 'display (get-text-property ov-start 'display)))) + (overlay-put ov 'display display-string) + (when font-lock-mode + (overlay-put ov 'face TeX-fold-folded-face)) + (unless (zerop TeX-fold-help-echo-max-length) + (overlay-put ov 'help-echo (TeX-fold-make-help-echo + (overlay-start ov) (overlay-end ov))))))) + +(defun TeX-fold-show-item (ov) + "Show a single LaTeX macro or environment. +Remove the respective properties from the overlay OV." + (overlay-put ov 'mouse-face nil) + (overlay-put ov 'display nil) + (overlay-put ov 'help-echo nil) + (when font-lock-mode + (overlay-put ov 'face TeX-fold-unfolded-face))) + +;; Copy and adaption of `reveal-post-command' from reveal.el in GNU +;; Emacs on 2004-07-04. +(defun TeX-fold-post-command () + ;; `with-local-quit' is not supported in XEmacs. + (condition-case nil + (let ((inhibit-quit nil)) + (condition-case err + (let* ((spots (TeX-fold-partition-list + (lambda (x) + ;; We refresh any spot in the current + ;; window as well as any spots associated + ;; with a dead window or a window which + ;; does not show this buffer any more. + (or (eq (car x) (selected-window)) + (not (window-live-p (car x))) + (not (eq (window-buffer (car x)) + (current-buffer))))) + TeX-fold-open-spots)) + (old-ols (mapcar #'cdr (car spots)))) + (setq TeX-fold-open-spots (cdr spots)) + (when (or disable-point-adjustment + global-disable-point-adjustment + ;; See preview.el on how to make this configurable. + (memq this-command + (list (key-binding [left]) (key-binding [right]) + #'backward-char #'forward-char + #'mouse-set-point))) + ;; Open new overlays. + (dolist (ol (nconc (when (and TeX-fold-unfold-around-mark + mark-active) + (overlays-at (mark))) + (overlays-at (point)))) + (when (eq (overlay-get ol 'category) 'TeX-fold) + (push (cons (selected-window) ol) TeX-fold-open-spots) + (setq old-ols (delq ol old-ols)) + (TeX-fold-show-item ol)))) + ;; Close old overlays. + (dolist (ol old-ols) + (when (and (eq (current-buffer) (overlay-buffer ol)) + (not (rassq ol TeX-fold-open-spots))) + (if (and (>= (point) (overlay-start ol)) + (<= (point) (overlay-end ol))) + ;; Still near the overlay: keep it open. + (push (cons (selected-window) ol) TeX-fold-open-spots) + ;; Really close it. + (TeX-fold-hide-item ol))))) + (error (message "TeX-fold: %s" err)))) + (quit (setq quit-flag t)))) + + +;;; Misc + +;; Copy and adaption of `cvs-partition' from pcvs-util.el in GNU Emacs +;; on 2004-07-05 to make tex-fold.el mainly self-contained. +(defun TeX-fold-partition-list (p l) + "Partition a list L into two lists based on predicate P. +The function returns a `cons' cell where the `car' contains +elements of L for which P is true while the `cdr' contains +the other elements. The ordering among elements is maintained." + (let (car cdr) + (dolist (x l) + (if (funcall p x) (push x car) (push x cdr))) + (cons (nreverse car) (nreverse cdr)))) + + +;;; The mode + +;;;###autoload +(define-minor-mode TeX-fold-mode + "Minor mode for hiding and revealing macros and environments. + +Called interactively, with no prefix argument, toggle the mode. +With universal prefix ARG (or if ARG is nil) turn mode on. +With zero or negative ARG turn mode off." + :init-value nil + :lighter nil + :keymap (list (cons TeX-fold-command-prefix TeX-fold-keymap)) + (if TeX-fold-mode + (progn + ;; The value t causes problem when body text is hidden in + ;; outline-minor-mode. (bug#36651) + ;; In addition, it's better not to override user preference + ;; without good reason. + ;; (set (make-local-variable 'search-invisible) t) + (add-hook 'post-command-hook #'TeX-fold-post-command nil t) + (add-hook 'LaTeX-fill-newline-hook #'TeX-fold-update-at-point nil t) + (add-hook 'TeX-after-insert-macro-hook + (lambda () + (when (and TeX-fold-mode TeX-fold-auto) + (save-excursion + (backward-char) + (or (TeX-fold-item 'macro) + (TeX-fold-item 'math) + (TeX-fold-item 'env)))))) + ;; Update the `TeX-fold-*-spec-list-internal' variables. + (dolist (elt '("macro" "env" "math")) + (set (intern (format "TeX-fold-%s-spec-list-internal" elt)) + ;; Append the value of `TeX-fold-*-spec-list' to the + ;; mode-specific `<mode-prefix>-fold-*-spec-list' variable. + (append (symbol-value (intern (format "TeX-fold-%s-spec-list" + elt))) + (let ((symbol (intern (format "%s-fold-%s-spec-list" + (TeX-mode-prefix) elt)))) + (when (boundp symbol) + (symbol-value symbol))))))) + ;; (kill-local-variable 'search-invisible) + (remove-hook 'post-command-hook #'TeX-fold-post-command t) + (remove-hook 'LaTeX-fill-newline-hook #'TeX-fold-update-at-point t) + (TeX-fold-clearout-buffer)) + (TeX-set-mode-name)) + +;;;###autoload +(defalias 'tex-fold-mode #'TeX-fold-mode) + +(provide 'tex-fold) + +;;; tex-fold.el ends here |