diff options
Diffstat (limited to 'lisp/textmodes')
33 files changed, 1813 insertions, 659 deletions
diff --git a/lisp/textmodes/artist.el b/lisp/textmodes/artist.el index e6fddd216d7..2cf9ded04bf 100644 --- a/lisp/textmodes/artist.el +++ b/lisp/textmodes/artist.el @@ -184,7 +184,6 @@ ;; Variables -(defconst artist-version "1.2.6") (defconst artist-maintainer-address "tab@lysator.liu.se, bug-gnu-emacs@gnu.org") (defvar x-pointer-crosshair) @@ -338,7 +337,8 @@ Example: (defvar artist-pointer-shape (if (eq window-system 'x) x-pointer-crosshair nil) "If in X Windows, use this pointer shape while drawing with the mouse.") -(defvaralias 'artist-text-renderer 'artist-text-renderer-function) +(define-obsolete-variable-alias 'artist-text-renderer + 'artist-text-renderer-function "29.1") (defcustom artist-text-renderer-function 'artist-figlet "Function for doing text rendering." @@ -474,60 +474,57 @@ This variable is initialized by the `artist-make-prev-next-op-alist' function.") (defvar artist-arrow-point-1 nil) (defvar artist-arrow-point-2 nil) -(defvar artist-mode-map - (let ((map (make-sparse-keymap))) - (setq artist-mode-map (make-sparse-keymap)) - (define-key map [down-mouse-1] 'artist-down-mouse-1) - (define-key map [S-down-mouse-1] 'artist-down-mouse-1) - (define-key map [down-mouse-2] 'artist-mouse-choose-operation) - (define-key map [S-down-mouse-2] 'artist-mouse-choose-operation) - (define-key map [down-mouse-3] 'artist-down-mouse-3) - (define-key map [S-down-mouse-3] 'artist-down-mouse-3) - (define-key map [C-mouse-4] 'artist-select-prev-op-in-list) - (define-key map [C-mouse-5] 'artist-select-next-op-in-list) - (define-key map "\r" 'artist-key-set-point) ; return - (define-key map [up] 'artist-previous-line) - (define-key map "\C-p" 'artist-previous-line) - (define-key map [down] 'artist-next-line) - (define-key map "\C-n" 'artist-next-line) - (define-key map [left] 'artist-backward-char) - (define-key map "\C-b" 'artist-backward-char) - (define-key map [right] 'artist-forward-char) - (define-key map "\C-f" 'artist-forward-char) - (define-key map "<" 'artist-toggle-first-arrow) - (define-key map ">" 'artist-toggle-second-arrow) - (define-key map "\C-c\C-a\C-e" 'artist-select-erase-char) - (define-key map "\C-c\C-a\C-f" 'artist-select-fill-char) - (define-key map "\C-c\C-a\C-l" 'artist-select-line-char) - (define-key map "\C-c\C-a\C-o" 'artist-select-operation) - (define-key map "\C-c\C-a\C-r" 'artist-toggle-rubber-banding) - (define-key map "\C-c\C-a\C-t" 'artist-toggle-trim-line-endings) - (define-key map "\C-c\C-a\C-s" 'artist-toggle-borderless-shapes) - (define-key map "\C-c\C-c" 'artist-mode-off) - (define-key map "\C-c\C-al" 'artist-select-op-line) - (define-key map "\C-c\C-aL" 'artist-select-op-straight-line) - (define-key map "\C-c\C-ar" 'artist-select-op-rectangle) - (define-key map "\C-c\C-aR" 'artist-select-op-square) - (define-key map "\C-c\C-as" 'artist-select-op-square) - (define-key map "\C-c\C-ap" 'artist-select-op-poly-line) - (define-key map "\C-c\C-aP" 'artist-select-op-straight-poly-line) - (define-key map "\C-c\C-ae" 'artist-select-op-ellipse) - (define-key map "\C-c\C-ac" 'artist-select-op-circle) - (define-key map "\C-c\C-at" 'artist-select-op-text-see-thru) - (define-key map "\C-c\C-aT" 'artist-select-op-text-overwrite) - (define-key map "\C-c\C-aS" 'artist-select-op-spray-can) - (define-key map "\C-c\C-az" 'artist-select-op-spray-set-size) - (define-key map "\C-c\C-a\C-d" 'artist-select-op-erase-char) - (define-key map "\C-c\C-aE" 'artist-select-op-erase-rectangle) - (define-key map "\C-c\C-av" 'artist-select-op-vaporize-line) - (define-key map "\C-c\C-aV" 'artist-select-op-vaporize-lines) - (define-key map "\C-c\C-a\C-k" 'artist-select-op-cut-rectangle) - (define-key map "\C-c\C-a\M-w" 'artist-select-op-copy-rectangle) - (define-key map "\C-c\C-a\C-y" 'artist-select-op-paste) - (define-key map "\C-c\C-af" 'artist-select-op-flood-fill) - (define-key map "\C-c\C-a\C-b" 'artist-submit-bug-report) - map) - "Keymap for `artist-mode'.") +(defvar-keymap artist-mode-map + :doc "Keymap for `artist-mode'." + "<down-mouse-1>" #'artist-down-mouse-1 + "S-<down-mouse-1>" #'artist-down-mouse-1 + "<down-mouse-2>" #'artist-mouse-choose-operation + "S-<down-mouse-2>" #'artist-mouse-choose-operation + "<down-mouse-3>" #'artist-down-mouse-3 + "S-<down-mouse-3>" #'artist-down-mouse-3 + "C-<mouse-4>" #'artist-select-prev-op-in-list + "C-<mouse-5>" #'artist-select-next-op-in-list + "RET" #'artist-key-set-point ; return + "<up>" #'artist-previous-line + "C-p" #'artist-previous-line + "<down>" #'artist-next-line + "C-n" #'artist-next-line + "<left>" #'artist-backward-char + "C-b" #'artist-backward-char + "<right>" #'artist-forward-char + "C-f" #'artist-forward-char + "<" #'artist-toggle-first-arrow + ">" #'artist-toggle-second-arrow + "C-c C-a C-e" #'artist-select-erase-char + "C-c C-a C-f" #'artist-select-fill-char + "C-c C-a C-l" #'artist-select-line-char + "C-c C-a C-o" #'artist-select-operation + "C-c C-a C-r" #'artist-toggle-rubber-banding + "C-c C-a C-t" #'artist-toggle-trim-line-endings + "C-c C-a C-s" #'artist-toggle-borderless-shapes + "C-c C-c" #'artist-mode-off + "C-c C-a l" #'artist-select-op-line + "C-c C-a L" #'artist-select-op-straight-line + "C-c C-a r" #'artist-select-op-rectangle + "C-c C-a R" #'artist-select-op-square + "C-c C-a s" #'artist-select-op-square + "C-c C-a p" #'artist-select-op-poly-line + "C-c C-a P" #'artist-select-op-straight-poly-line + "C-c C-a e" #'artist-select-op-ellipse + "C-c C-a c" #'artist-select-op-circle + "C-c C-a t" #'artist-select-op-text-see-thru + "C-c C-a T" #'artist-select-op-text-overwrite + "C-c C-a S" #'artist-select-op-spray-can + "C-c C-a z" #'artist-select-op-spray-set-size + "C-c C-a C-d" #'artist-select-op-erase-char + "C-c C-a E" #'artist-select-op-erase-rectangle + "C-c C-a v" #'artist-select-op-vaporize-line + "C-c C-a V" #'artist-select-op-vaporize-lines + "C-c C-a C-k" #'artist-select-op-cut-rectangle + "C-c C-a M-w" #'artist-select-op-copy-rectangle + "C-c C-a C-y" #'artist-select-op-paste + "C-c C-a f" #'artist-select-op-flood-fill + "C-c C-a C-b" #'artist-submit-bug-report) (easy-menu-define artist-menu-map artist-mode-map "Menu for `artist-mode'." @@ -1370,8 +1367,11 @@ Keymap summary (t ;; Turn mode on (artist-mode-init) - (let ((font (face-attribute 'default :font))) - (when (and (fontp font) (not (font-get font :spacing))) + (let* ((font (face-attribute 'default :font)) + (spacing-prop (if (fontp font) + (font-get font :spacing) + t))) + (when (or (null spacing-prop) (eq spacing-prop 0)) (message "The default font isn't monospaced, so the drawings in this buffer may look odd")))))) ;; Init and exit @@ -2840,9 +2840,8 @@ Returns a list of strings." (if (memq system-type '(windows-nt ms-dos)) (artist-figlet-get-font-list-windows) (artist-figlet-get-font-list))) - (font (completing-read (concat "Select font (default " - artist-figlet-default-font - "): ") + (font (completing-read (format-prompt "Select font" + artist-figlet-default-font) (mapcar (lambda (font) (cons font font)) avail-fonts)))) @@ -4916,7 +4915,7 @@ The event, EV, is the mouse event." (arrow-set-fn (artist-go-get-arrow-set-fn-from-symbol op)) (ev-start (event-start ev)) (initial-win (posn-window ev-start)) - (ev-start-pos (artist-coord-win-to-buf (posn-col-row ev-start))) + (ev-start-pos (artist-coord-win-to-buf (posn-col-row ev-start t))) (x1 (artist--adjust-x (car ev-start-pos))) (y1 (cdr ev-start-pos)) (timer nil)) @@ -4932,7 +4931,7 @@ The event, EV, is the mouse event." (while (or (mouse-movement-p ev) (member 'down (event-modifiers ev))) (setq ev-start-pos (artist-coord-win-to-buf - (posn-col-row (event-start ev)))) + (posn-col-row (event-start ev) t))) (setq x1 (artist--adjust-x (car ev-start-pos))) (setq y1 (cdr ev-start-pos)) @@ -5012,7 +5011,7 @@ The event, EV, is the mouse event." (arrow-set-fn (artist-go-get-arrow-set-fn-from-symbol op)) (ev-start (event-start ev)) (initial-win (posn-window ev-start)) - (ev-start-pos (artist-coord-win-to-buf (posn-col-row ev-start))) + (ev-start-pos (artist-coord-win-to-buf (posn-col-row ev-start t))) (x1-last (artist--adjust-x (car ev-start-pos))) (y1-last (cdr ev-start-pos)) (x2 x1-last) @@ -5104,7 +5103,7 @@ The event, EV, is the mouse event." ;; set x2 and y2 ;; (setq ev-start-pos (artist-coord-win-to-buf - (posn-col-row (event-start ev)))) + (posn-col-row (event-start ev) t))) (setq x2 (artist--adjust-x (car ev-start-pos))) (setq y2 (cdr ev-start-pos)) @@ -5131,7 +5130,7 @@ The event, EV, is the mouse event." ;; ;; set x2 and y2 (setq ev-start-pos (artist-coord-win-to-buf - (posn-col-row (event-start ev)))) + (posn-col-row (event-start ev) t))) (setq x2 (artist--adjust-x (car ev-start-pos))) (setq y2 (cdr ev-start-pos)) @@ -5215,7 +5214,8 @@ Operation is done once. The event, EV, is the mouse event." (arrow-pred (artist-go-get-arrow-pred-from-symbol op)) (arrow-set-fn (artist-go-get-arrow-set-fn-from-symbol op)) (ev-start (event-start ev)) - (ev-start-pos (artist-coord-win-to-buf (posn-col-row ev-start))) + (ev-start-pos (artist-coord-win-to-buf + (posn-col-row ev-start t))) (x1 (artist--adjust-x (car ev-start-pos))) (y1 (cdr ev-start-pos))) (select-window (posn-window ev-start)) @@ -5249,7 +5249,8 @@ The event, EV, is the mouse event." (arrow-set-fn (artist-go-get-arrow-set-fn-from-symbol op)) (ev-start (event-start ev)) (initial-win (posn-window ev-start)) - (ev-start-pos (artist-coord-win-to-buf (posn-col-row ev-start))) + (ev-start-pos (artist-coord-win-to-buf + (posn-col-row ev-start t))) (x1 (artist--adjust-x (car ev-start-pos))) (y1 (cdr ev-start-pos)) (x2) @@ -5263,7 +5264,7 @@ The event, EV, is the mouse event." (while (or (mouse-movement-p ev) (member 'down (event-modifiers ev))) (setq ev-start-pos (artist-coord-win-to-buf - (posn-col-row (event-start ev)))) + (posn-col-row (event-start ev) t))) (setq x2 (artist--adjust-x (car ev-start-pos))) (setq y2 (cdr ev-start-pos)) @@ -5359,7 +5360,7 @@ The event, EV, is the mouse event." (setq vars (delq x vars)))) vars) (reporter-submit-bug-report artist-maintainer-address - (concat "artist.el " artist-version) + (concat "artist.el in Emacs " emacs-version) vars nil nil (concat "Hello Tomas,\n\n" @@ -5367,6 +5368,9 @@ The event, EV, is the mouse event." (define-obsolete-function-alias 'artist-uniq #'seq-uniq "28.1") +(defconst artist-version "1.2.6") +(make-obsolete-variable 'artist-version 'emacs-version "29.1") + (provide 'artist) diff --git a/lisp/textmodes/bibtex.el b/lisp/textmodes/bibtex.el index e4df28d03de..6763da046ff 100644 --- a/lisp/textmodes/bibtex.el +++ b/lisp/textmodes/bibtex.el @@ -316,8 +316,6 @@ If parsing fails, try to set this variable to nil." (option (choice :tag "Alternative" :value nil (const nil) integer))))))) -(define-obsolete-variable-alias 'bibtex-entry-field-alist - 'bibtex-BibTeX-entry-alist "24.1") (defcustom bibtex-BibTeX-entry-alist '(("Article" "Article in Journal" (("author") @@ -764,6 +762,20 @@ for a new entry." ("eprint") ("eprintclass" nil nil 4) ("primaryclass" nil nil -4) ("eprinttype" nil nil 5) ("archiveprefix" nil nil -5) ("url") ("urldate"))) + ("Conference" "Article in Conference Proceedings" ; same as InProceedings + (("author") + ("title" "Title of the article in proceedings (BibTeX converts it to lowercase)")) + (("booktitle" "Name of the conference proceedings") + ("year")) + (("editor") + ("volume" "Volume of the conference proceedings in the series") + ("number" "Number of the conference proceedings in a small series (overwritten by volume)") + ("series" "Series in which the conference proceedings appeared") + ("pages" "Pages in the conference proceedings") + ("month") ("address") + ("organization" "Sponsoring organization of the conference") + ("publisher" "Publishing company, its location") + ("note"))) ("Reference" "Single-Volume Work of Reference" ; same as @collection (("editor") ("title") ("date" nil nil 1) ("year" nil nil -1)) nil @@ -839,6 +851,33 @@ for a new entry." ("eprint") ("eprintclass" nil nil 4) ("primaryclass" nil nil -4) ("eprinttype" nil nil 5) ("archiveprefix" nil nil -5) ("url") ("urldate"))) + ("PhdThesis" "PhD Thesis" + (("author") + ("title" "Title of the PhD thesis") + ("school" "School where the PhD thesis was written") + ("year")) + nil + (("type" "Type of the PhD thesis") + ("address" "Address of the school (if not part of field \"school\") or country") + ("month") ("note"))) + ("MastersThesis" "Master's Thesis" + (("author") + ("title" "Title of the master's thesis (BibTeX converts it to lowercase)") + ("school" "School where the master's thesis was written") + ("year")) + nil + (("type" "Type of the master's thesis (if other than \"Master's thesis\")") + ("address" "Address of the school (if not part of field \"school\") or country") + ("month") ("note"))) + ("TechReport" "Technical Report" + (("author") + ("title" "Title of the technical report (BibTeX converts it to lowercase)") + ("institution" "Sponsoring institution of the report") + ("year")) + nil + (("type" "Type of the report (if other than \"technical report\")") + ("number" "Number of the technical report") + ("address") ("month") ("note"))) ("Unpublished" "Unpublished" (("author") ("title") ("date" nil nil 1) ("year" nil nil -1)) nil @@ -1193,8 +1232,8 @@ See `bibtex-generate-autokey' for details." :type '(repeat (cons (regexp :tag "Old") (string :tag "New")))) -(defvaralias 'bibtex-autokey-name-case-convert - 'bibtex-autokey-name-case-convert-function) +(define-obsolete-variable-alias 'bibtex-autokey-name-case-convert + 'bibtex-autokey-name-case-convert-function "29.1") (defcustom bibtex-autokey-name-case-convert-function #'downcase "Function called for each name to perform case conversion. @@ -1268,8 +1307,8 @@ Case is significant. See `bibtex-generate-autokey' for details." :group 'bibtex-autokey :type '(repeat regexp)) -(defvaralias 'bibtex-autokey-titleword-case-convert - 'bibtex-autokey-titleword-case-convert-function) +(define-obsolete-variable-alias 'bibtex-autokey-titleword-case-convert + 'bibtex-autokey-titleword-case-convert-function "29.1") (defcustom bibtex-autokey-titleword-case-convert-function #'downcase "Function called for each titleword to perform case conversion. @@ -2257,11 +2296,17 @@ is non-nil, FUN is not called for @String entries." (set-marker-insertion-type end-marker t) (save-excursion (goto-char (point-min)) - (while (setq found (bibtex-skip-to-valid-entry)) - (set-marker end-marker (cdr found)) - (looking-at bibtex-any-entry-maybe-empty-head) - (funcall fun (bibtex-key-in-head "") (car found) end-marker) - (goto-char end-marker))))) + (let ((prev nil)) + (while (setq found (bibtex-skip-to-valid-entry)) + ;; If we have invalid entries, ensure that we have forward + ;; progress so that we don't infloop. + (if (equal (point) prev) + (forward-line 1) + (setq prev (point)) + (set-marker end-marker (cdr found)) + (looking-at bibtex-any-entry-maybe-empty-head) + (funcall fun (bibtex-key-in-head "") (car found) end-marker) + (goto-char end-marker))))))) (defun bibtex-progress-message (&optional flag interval) "Echo a message about progress of current buffer. @@ -3626,14 +3671,6 @@ if that value is non-nil. (if (not (consp (nth 1 (car entry-alist)))) ;; new format entry-alist - ;; Convert old format of `bibtex-entry-field-alist' - (unless (get var 'entry-list-format) - (put var 'entry-list-format "pre-24") - (message "Old format of `%s' (pre GNU Emacs 24). -Please convert to the new format." - (if (eq (indirect-variable 'bibtex-entry-field-alist) var) - 'bibtex-entry-field-alist var)) - (sit-for 3)) (let (lst) (dolist (entry entry-alist) (let ((fl (nth 1 entry)) req xref opt) @@ -4101,11 +4138,11 @@ Optional arg POS is the position of the BibTeX entry to use." (goto-char pnt))))) (defun bibtex-mark-entry () - "Put mark at beginning, point at end of current BibTeX entry. + "Put mark at end, point at beginning of current BibTeX entry. Activate mark in Transient Mark mode." (interactive) - (push-mark (bibtex-beginning-of-entry) t t) - (bibtex-end-of-entry)) + (push-mark (bibtex-end-of-entry) t t) + (bibtex-beginning-of-entry)) (defun bibtex-count-entries (&optional count-string-entries) "Count number of entries in current buffer or region. @@ -4317,8 +4354,6 @@ for a crossref key, t otherwise." (eqb (goto-char pos)) (t (set-buffer buffer) (goto-char pos))) pos)) -;; backward compatibility -(defalias 'bibtex-find-crossref 'bibtex-search-crossref) (defun bibtex-dist (pos beg end) "Return distance between POS and region delimited by BEG and END." @@ -4381,8 +4416,6 @@ A prefix arg negates the value of `bibtex-search-entry-globally'." (if display (bibtex-reposition-window))) (display (message "Key `%s' not found" key))) pnt))) -;; backward compatibility -(defalias 'bibtex-find-entry 'bibtex-search-entry) (defun bibtex-prepare-new-entry (index) "Prepare a new BibTeX entry with index INDEX. @@ -4996,7 +5029,7 @@ on the value of `bibtex-entry-format'. If the reference key of the entry is empty or a prefix argument is given, calculate a new reference key. (Note: this works only if fields in entry begin on separate lines prior to calling `bibtex-clean-entry' or if -'realign is contained in `bibtex-entry-format'.) +`realign' is contained in `bibtex-entry-format'.) Don't call `bibtex-clean-entry' on @Preamble entries. At end of the cleaning process, the functions in `bibtex-clean-entry-hook' are called with region narrowed to entry." @@ -5275,7 +5308,6 @@ entries from minibuffer." (goto-char (point-max)) (message "Buffer is now parsable. Please save it."))) -(define-obsolete-function-alias 'bibtex-complete #'completion-at-point "24.1") (defun bibtex-completion-at-point-function () (let ((pnt (point)) (case-fold-search t) @@ -5608,5 +5640,8 @@ If APPEND is non-nil, append ENTRIES to those already displayed." (setq buffer-read-only t) (goto-char (point-min))) +(define-obsolete-function-alias 'bibtex-find-crossref #'bibtex-search-crossref "29.1") +(define-obsolete-function-alias 'bibtex-find-entry #'bibtex-search-entry "29.1") + (provide 'bibtex) ;;; bibtex.el ends here diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el index 1139fd1976e..a2a7774aba7 100644 --- a/lisp/textmodes/css-mode.el +++ b/lisp/textmodes/css-mode.el @@ -269,6 +269,10 @@ ("resize" "none" "both" "horizontal" "vertical") ("text-overflow" "clip" "ellipsis" string) + ;; CSS Cascading and Inheritance Level 3 + ;; (https://www.w3.org/TR/css-cascade-3/#property-index) + ("all") + ;; CSS Color Module Level 3 ;; (https://www.w3.org/TR/css3-color/#property) ("color" color) @@ -304,27 +308,27 @@ ;; CSS Box Alignment Module Level 3 ;; (https://www.w3.org/TR/css-align-3/#property-index) - ("align-content" - baseline-position content-distribution overflow-position content-position) - ("align-items" - "normal" "stretch" baseline-position overflow-position self-position) - ("align-self" - "auto" "normal" "stretch" - baseline-position overflow-position self-position) - ("justify-content" "normal" - content-distribution overflow-position content-position "left" "right") - ("justify-items" - "normal" "stretch" baseline-position overflow-position self-position - "left" "right" "legacy") - ("justify-self" - "auto" "normal" "stretch" baseline-position overflow-position self-position - "left" "right") + ("align-content" baseline-position content-distribution + overflow-position content-position) + ("align-items" "normal" "stretch" baseline-position + overflow-position self-position) + ("align-self" "auto" "normal" "stretch" baseline-position + overflow-position self-position) + ("column-gap" "normal" length-percentage) + ("gap" row-gap column-gap) + ("justify-content" "normal" content-distribution overflow-position + content-position "left" "right") + ("justify-items" "normal" "stretch" baseline-position + overflow-position self-position "left" "right" "legacy" "center") + ("justify-self" "auto" "normal" "stretch" baseline-position + overflow-position self-position "left" "right") ("place-content" align-content justify-content) ("place-items" align-items justify-items) ("place-self" justify-self align-self) + ("row-gap" "normal" length-percentage) - ;; CSS Flexible Box Layout Module Level 2 - ;; (https://www.w3.org/TR/css-flexbox-2/#property-index) + ;; CSS Flexible Box Layout Module Level 1 + ;; (https://www.w3.org/TR/css-flexbox-1/#property-index) ("flex" "none" flex-grow flex-shrink flex-basis) ("flex-basis" "auto" "content" width) ("flex-direction" "row" "row-reverse" "column" "column-reverse") @@ -413,21 +417,20 @@ ("mask-type" "luminance" "alpha") ("clip" "rect()" "auto") - ;; CSS Multi-column Layout Module + ;; CSS Multi-column Layout Module Level 1 ;; (https://www.w3.org/TR/css3-multicol/#property-index) ;; "break-after", "break-before", and "break-inside" are left out ;; below, because they're already included in CSS Fragmentation ;; Module Level 3. - ("column-count" integer "auto") - ("column-fill" "auto" "balance") - ("column-gap" length "normal") + ("column-count" "auto" integer) + ("column-fill" "auto" "balance" "balance-all") ("column-rule" column-rule-width column-rule-style - column-rule-color "transparent") + column-rule-color) ("column-rule-color" color) - ("column-rule-style" border-style) - ("column-rule-width" border-width) + ("column-rule-style" line-style) + ("column-rule-width" line-width) ("column-span" "none" "all") - ("column-width" length "auto") + ("column-width" "auto" length) ("columns" column-width column-count) ;; CSS Overflow Module Level 3 @@ -925,6 +928,32 @@ cannot be completed sensibly: `custom-ident', (defface css-proprietary-property '((t :inherit (css-property italic))) "Face to use for vendor-specific properties.") +(defun css--selector-regexp (sassy) + (concat + "\\(?:" + (if (not sassy) + "[-_%*#.>[:alnum:]]+" + ;; Same as for non-sassy except we do want to allow { and } + ;; chars in selectors in the case of #{$foo} + ;; variable interpolation! + (concat "\\(?:[-_%*#.>&+~[:alnum:]]*" scss--hash-re + "\\|[-_%*#.>&+~[:alnum:]]+\\)")) + ;; Even though pseudo-elements should be prefixed by ::, a + ;; single colon is accepted for backward compatibility. + "\\(?:\\(:" (regexp-opt (append css-pseudo-class-ids + css-pseudo-element-ids) + t) + "\\|::" (regexp-opt css-pseudo-element-ids t) "\\)\\)?" + ;; Braces after selectors. + "\\(?:\\[[^]\n]+\\]\\)?" + ;; Parentheses after selectors. + "\\(?:([^)]+)\\)?" + ;; Main bit over. But perhaps just [target]? + "\\|\\[[^]\n]+\\]" + ;; :root, ::marker and the like. + "\\|::?[[:alnum:]]+\\(?:([^)]+)\\)?" + "\\)")) + (defun css--font-lock-keywords (&optional sassy) `((,(concat "!\\s-*" (regexp-opt css--bang-ids)) (0 font-lock-builtin-face)) @@ -945,28 +974,16 @@ cannot be completed sensibly: `custom-ident', ;; selector between [...] should simply not be highlighted. (,(concat "^[ \t]*\\(" - (if (not sassy) - ;; We don't allow / as first char, so as not to - ;; take a comment as the beginning of a selector. - "[^@/:{}() \t\n][^:{}()]*" - ;; Same as for non-sassy except we do want to allow { and } - ;; chars in selectors in the case of #{$foo} - ;; variable interpolation! - (concat "\\(?:" scss--hash-re - "\\|[^@/:{}() \t\n#]\\)" - "[^:{}()#]*\\(?:" scss--hash-re "[^:{}()#]*\\)*")) - ;; Even though pseudo-elements should be prefixed by ::, a - ;; single colon is accepted for backward compatibility. - "\\(?:\\(:" (regexp-opt (append css-pseudo-class-ids - css-pseudo-element-ids) - t) - "\\|::" (regexp-opt css-pseudo-element-ids t) "\\)" - "\\(?:([^)]+)\\)?" - (if (not sassy) - "[^:{}()\n]*" - (concat "[^:{}()\n#]*\\(?:" scss--hash-re "[^:{}()\n#]*\\)*")) + ;; We have at least one selector. + (css--selector-regexp sassy) + ;; And then possibly more. + "\\(?:" + ;; Separators between selectors. + "[ \n\t,+~>]+" + (css--selector-regexp sassy) "\\)*" - "\\)\\(?:\n[ \t]*\\)*{") + ;; And then a brace. + "\\)[ \n\t]*{") (1 'css-selector keep)) ;; In the above rule, we allow the open-brace to be on some subsequent ;; line. This will only work if we properly mark the intervening text diff --git a/lisp/textmodes/dns-mode.el b/lisp/textmodes/dns-mode.el index d4acbe24ebb..42d547504c1 100644 --- a/lisp/textmodes/dns-mode.el +++ b/lisp/textmodes/dns-mode.el @@ -110,11 +110,11 @@ "26.1" 'set) (defcustom dns-mode-font-lock-keywords - `((,(concat "^\\$" (regexp-opt dns-mode-control-entities)) + `((,(concat "^\\$" (regexp-opt dns-mode-control-entities) "\\>") 0 ,dns-mode-control-entity-face) ("^\\$[a-z0-9A-Z]+" 0 ,dns-mode-bad-control-entity-face) - (,(regexp-opt dns-mode-classes) 0 ,dns-mode-class-face) - (,(regexp-opt dns-mode-types) 0 ,dns-mode-type-face)) + (,(regexp-opt dns-mode-classes 'words) 0 ,dns-mode-class-face) + (,(regexp-opt dns-mode-types 'words) 0 ,dns-mode-type-face)) "Font lock keywords used to highlight text in DNS master file mode." :version "26.1" :type 'sexp) diff --git a/lisp/textmodes/emacs-news-mode.el b/lisp/textmodes/emacs-news-mode.el new file mode 100644 index 00000000000..af0aa2ddeab --- /dev/null +++ b/lisp/textmodes/emacs-news-mode.el @@ -0,0 +1,269 @@ +;;; emacs-news-mode.el --- major mode to edit and view the NEWS file -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; Keywords: tools + +;; This file is part of GNU Emacs. + +;; GNU Emacs 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. + +;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: + +(eval-when-compile (require 'cl-lib)) +(require 'outline) + +(defgroup emacs-news-mode nil + "Major mode for editing and viewing the Emacs NEWS file." + :group 'lisp) + +(defface emacs-news-is-documented + '((t :inherit font-lock-type-face)) + "Face used for displaying the \"is documented\" tag." + :version "29.1") + +(defface emacs-news-does-not-need-documentation + '((t :inherit font-lock-preprocessor-face)) + "Face used for displaying the \"does not need documentation\" tag." + :version "29.1") + +(defvar-keymap emacs-news-common-map + ;; Navigation like `org-mode'/`outline-minor-mode'. + "C-c C-f" #'outline-forward-same-level + "C-c C-b" #'outline-backward-same-level + "C-c C-n" #'outline-next-visible-heading + "C-c C-p" #'outline-previous-visible-heading + "C-c C-u" #'outline-up-heading) + +(defvar-keymap emacs-news-mode-map + :parent emacs-news-common-map + "C-c C-s" #'emacs-news-next-untagged-entry + "C-c C-r" #'emacs-news-previous-untagged-entry + "C-c C-t" #'emacs-news-toggle-tag + "C-c C-g" #'emacs-news-goto-section + "C-c C-j" #'emacs-news-find-heading + "C-c C-e" #'emacs-news-count-untagged-entries + "<remap> <open-line>" #'emacs-news-open-line) + +(defvar-keymap emacs-news-view-mode-map + :parent emacs-news-common-map) + +(defvar emacs-news-mode-font-lock-keywords + `(("^---$" 0 'emacs-news-does-not-need-documentation) + ("^\\+\\+\\+$" 0 'emacs-news-is-documented))) + +(defun emacs-news--mode-common () + (setq-local font-lock-defaults '(emacs-news-mode-font-lock-keywords t)) + (setq-local outline-regexp "\\*+ " + outline-minor-mode-cycle t + ;; We subtract one from the level, because we have a + ;; space after the asterisks. + outline-level (lambda () (1- (length (match-string 0)))) + outline-minor-mode-highlight 'append) + (outline-minor-mode)) + +;;;###autoload +(define-derived-mode emacs-news-mode text-mode "NEWS" + "Major mode for editing the Emacs NEWS file." + (setq-local fill-paragraph-function #'emacs-news--fill-paragraph) + (emacs-news--mode-common)) + +;;;###autoload +(define-derived-mode emacs-news-view-mode special-mode "NEWS" + "Major mode for viewing the Emacs NEWS file." + (setq buffer-read-only t) + (emacs-news--buttonize) + (button-mode) + (emacs-news--mode-common)) + +(defun emacs-news--fill-paragraph (&optional justify) + (cond + ;; We're in a heading -- do nothing. + ((save-excursion + (beginning-of-line) + (looking-at "\\*+ ")) + ) + ;; We're in a news item -- exclude the heading before filling. + ((and (save-excursion + (re-search-backward (concat "^\\(?:" paragraph-start "\\|\\*+ \\)") + nil t)) + (= (char-after (match-beginning 0)) ?*)) + (save-restriction + (narrow-to-region (save-excursion + (goto-char (match-beginning 0)) + (forward-line 1) + (point)) + (point-max)) + (fill-paragraph justify))) + ;; Fill normally. + (t + (fill-paragraph justify)))) + +(defun emacs-news-next-untagged-entry (&optional reverse) + "Go to the next untagged NEWS entry. +If REVERSE (interactively, the prefix), go to the previous +untagged NEWS entry." + (interactive "P" emacs-news-mode) + (let ((start (point)) + (found nil)) + ;; Don't consider the current line, because that would stop + ;; progress if calling this command repeatedly. + (unless reverse + (forward-line 1)) + (while (and (not found) + (funcall (if reverse #'re-search-backward + #'re-search-forward) + "^\\(\\*+\\) " nil t)) + (when (and (not (save-excursion + (forward-line -1) + (looking-at "---$\\|\\+\\+\\+$"))) + ;; We have an entry without a tag before it, but + ;; check whether it's a heading (which we can + ;; determine if the next entry has more asterisks). + (not (emacs-news--heading-p))) + ;; It wasn't a sub-heading, so we've found one. + (setq found t))) + (if found + (progn + (push-mark start) + (message "Untagged entry") + (beginning-of-line) + t) + (message "No further untagged entries") + (goto-char start) + nil))) + +(defun emacs-news--heading-p () + (save-excursion + (beginning-of-line) + ;; A heading starts with * characters, and then a blank line, and + ;; then paragraphs with more * characters than in the heading. + (and (looking-at "\\(\\*+\\) ") + (let ((level (length (match-string 1)))) + (forward-line 1) + (and (looking-at "$") + (re-search-forward "^\\(\\*+\\) " nil t) + (> (length (match-string 1)) level)))))) + +(defun emacs-news-previous-untagged-entry () + "Go to the previous untagged NEWS entry." + (interactive nil emacs-news-mode) + (emacs-news-next-untagged-entry t)) + +(defun emacs-news-toggle-tag () + "Toggle documentation tag of current headline in the Emacs NEWS file." + (interactive nil emacs-news-mode) + (save-excursion + (goto-char (line-beginning-position)) + (cond ((or (looking-at (rx bol (or "---" "+++") eol))) + (forward-line 2)) + ((or (looking-at (rx bol "*** "))) + (forward-line 1))) + (outline-previous-visible-heading 1) + (forward-line -1) + (cond ((not (looking-at (rx bol (or "---" "+++") eol))) + (insert "\n---")) + ((looking-at (rx bol "---" eol)) + (delete-char 3) + (insert "+++")) + ((looking-at (rx bol "+++" eol)) + (delete-char 4)) + (t (user-error "Invalid headline tag; can't toggle"))))) + +(defun emacs-news-count-untagged-entries () + "Say how many untagged entries there are in the current NEWS buffer." + (interactive nil emacs-news-mode) + (save-excursion + (goto-char (point-min)) + (let ((i 0)) + (while (emacs-news-next-untagged-entry) + (setq i (1+ i))) + (message (if (= i 1) + "There's 1 untagged entry" + (format "There are %s untagged entries" i)))))) + +(defun emacs-news--buttonize () + "Make manual and symbol references into buttons." + (save-excursion + (with-silent-modifications + (let ((inhibit-read-only t)) + ;; Do functions and variables. + (goto-char (point-min)) + (search-forward "\f" nil t) + (while (re-search-forward "'\\([^-][^ \t\n]+\\)'" nil t) + ;; Filter out references to key sequences. + (let ((string (match-string 1))) + (when-let ((symbol (intern-soft string))) + (when (or (boundp symbol) + (fboundp symbol)) + (buttonize-region (match-beginning 1) (match-end 1) + (lambda (symbol) + (describe-symbol symbol)) + symbol))))) + ;; Do manual references. + (goto-char (point-min)) + (search-forward "\f" nil t) + (while (re-search-forward "\"\\(([a-z0-9]+)[ \n][^\"]\\{1,80\\}\\)\"" + nil t) + (buttonize-region (match-beginning 1) (match-end 1) + (lambda (node) (info node)) + (match-string 1))))))) + +(defun emacs-news--sections (regexp) + (let ((sections nil)) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward (concat "^" regexp "\\(.*\\)") nil t) + (when (save-match-data (emacs-news--heading-p)) + (push (buffer-substring-no-properties + (match-beginning 1) (match-end 1)) + sections)))) + (nreverse sections))) + +(defun emacs-news-goto-section (section) + "Go to SECTION in the Emacs NEWS file." + (interactive (list + (completing-read "Goto section: " (emacs-news--sections "\\* ") + nil t)) + emacs-news-mode) + (goto-char (point-min)) + (when (search-forward (concat "\n* " section) nil t) + (beginning-of-line))) + +(defun emacs-news-find-heading (heading) + "Go to HEADING in the Emacs NEWS file." + (interactive (list + (completing-read "Goto heading: " + (emacs-news--sections "\\*\\*\\*? ") + nil t)) + emacs-news-mode) + (goto-char (point-min)) + (when (re-search-forward (concat "^*+ " (regexp-quote heading)) nil t) + (beginning-of-line))) + +(defun emacs-news-open-line (n) + "Open a new line in a NEWS file. +This is like `open-line', but skips any temporary NEWS-style +documentation marks on the previous line." + (interactive "*p" emacs-news-mode) + (when (save-excursion (forward-line -1) + (looking-at (rx bol (or "---" "+++") eol))) + (forward-line -1)) + (open-line n)) + +(provide 'emacs-news-mode) + +;;; emacs-news-mode.el ends here diff --git a/lisp/textmodes/enriched.el b/lisp/textmodes/enriched.el index 50ff668a9ff..935be06812f 100644 --- a/lisp/textmodes/enriched.el +++ b/lisp/textmodes/enriched.el @@ -539,6 +539,30 @@ the range of text to assign text property SYMBOL with value VALUE." (list start end 'display prop) (list start end 'display (list 'disable-eval prop))))) +(defvar enriched--markup-shown) +(defun enriched-toggle-markup () + "Toggle whether to see markup in the current buffer." + (interactive) + (save-excursion + (save-restriction + (widen) + (with-silent-modifications + (if (bound-and-true-p enriched--markup-shown) + (progn + (setq-local enriched--markup-shown nil) + ;; Remove any faces, because they will be decoded, too. + (goto-char (point-min)) + (let (match) + (while (setq match (text-property-search-forward 'face)) + (put-text-property (prop-match-beginning match) + (prop-match-end match) + 'face nil))) + (enriched-decode (point-min) (point-max)) + (enriched-mode 1)) + (setq-local enriched--markup-shown t) + (enriched-encode (point-min) (point-max) (current-buffer)) + (enriched-mode -1)))))) + (provide 'enriched) ;;; enriched.el ends here diff --git a/lisp/textmodes/etc-authors-mode.el b/lisp/textmodes/etc-authors-mode.el index 3912b829d20..7eabdd4c2b8 100644 --- a/lisp/textmodes/etc-authors-mode.el +++ b/lisp/textmodes/etc-authors-mode.el @@ -115,12 +115,10 @@ With a prefix arg ARG, move point that many authors backward." (interactive "p" etc-authors-mode) (etc-authors-next-author (- arg))) -(defvar etc-authors-mode-map - (let ((map (make-sparse-keymap))) - (define-key map "n" #'etc-authors-next-author) - (define-key map "p" #'etc-authors-prev-author) - map) - "Keymap for `etc-authors-mode'.") +(defvar-keymap etc-authors-mode-map + :doc "Keymap for `etc-authors-mode'." + "n" #'etc-authors-next-author + "p" #'etc-authors-prev-author) ;;;###autoload (define-derived-mode etc-authors-mode special-mode "Authors View" diff --git a/lisp/textmodes/fill.el b/lisp/textmodes/fill.el index ff84c353aa8..23ba1a24f1f 100644 --- a/lisp/textmodes/fill.el +++ b/lisp/textmodes/fill.el @@ -29,6 +29,8 @@ ;;; Code: +(eval-when-compile (require 'subr-x)) + (defgroup fill nil "Indenting and filling text." :link '(custom-manual "(emacs)Filling") @@ -44,8 +46,8 @@ A value of nil means that any change in indentation starts a new paragraph." (defcustom colon-double-space nil "Non-nil means put two spaces after a colon when filling." - :type 'boolean) -(put 'colon-double-space 'safe-local-variable #'booleanp) + :type 'boolean + :safe #'booleanp) (defcustom fill-separate-heterogeneous-words-with-space nil "Non-nil means to use a space to separate words of a different kind. @@ -396,12 +398,8 @@ and `fill-nobreak-invisible'." (save-excursion (skip-chars-backward " ") (and (eq (preceding-char) ?.) - (looking-at " \\([^ ]\\|$\\)")))) - ;; Another approach to the same problem. - (save-excursion - (skip-chars-backward " ") - (and (eq (preceding-char) ?.) - (not (progn (forward-char -1) (looking-at (sentence-end)))))) + ;; There's something more after the space. + (looking-at " [^ \n]")))) ;; Don't split a line if the rest would look like a new paragraph. (unless use-hard-newlines (save-excursion @@ -716,7 +714,10 @@ space does not end a sentence, so don't break a line there." (goto-char from-plus-indent)) (if (not (> to (point))) - nil ;; There is no paragraph, only whitespace: exit now. + ;; There is no paragraph, only whitespace: exit now. + (progn + (set-marker to nil) + nil) (or justify (setq justify (current-justification))) @@ -792,6 +793,7 @@ space does not end a sentence, so don't break a line there." ;; Leave point after final newline. (goto-char to) (unless (eobp) (forward-char 1)) + (set-marker to nil) ;; Return the fill-prefix we used fill-prefix))) @@ -839,75 +841,67 @@ region, instead of just filling the current paragraph." (interactive (progn (barf-if-buffer-read-only) (list (if current-prefix-arg 'full) t))) - (let ((hash (and (not (buffer-modified-p)) - (buffer-hash)))) - (prog1 - (or - ;; 1. Fill the region if it is active when called interactively. - (and region transient-mark-mode mark-active - (not (eq (region-beginning) (region-end))) - (or (fill-region (region-beginning) (region-end) justify) t)) - ;; 2. Try fill-paragraph-function. - (and (not (eq fill-paragraph-function t)) - (or fill-paragraph-function - (and (minibufferp (current-buffer)) - (= 1 (point-min)))) - (let ((function (or fill-paragraph-function - ;; In the minibuffer, don't count - ;; the width of the prompt. - 'fill-minibuffer-function)) - ;; If fill-paragraph-function is set, it probably - ;; takes care of comments and stuff. If not, it - ;; will have to set fill-paragraph-handle-comment - ;; back to t explicitly or return nil. - (fill-paragraph-handle-comment nil) - (fill-paragraph-function t)) - (funcall function justify))) - ;; 3. Try our syntax-aware filling code. - (and fill-paragraph-handle-comment - ;; Our code only handles \n-terminated comments right now. - comment-start (equal comment-end "") - (let ((fill-paragraph-handle-comment nil)) - (fill-comment-paragraph justify))) - ;; 4. If it all fails, default to the good ol' text paragraph filling. - (let ((before (point)) - (paragraph-start paragraph-start) - ;; Fill prefix used for filling the paragraph. - fill-pfx) - ;; Try to prevent code sections and comment sections from being - ;; filled together. - (when (and fill-paragraph-handle-comment comment-start-skip) - (setq paragraph-start - (concat paragraph-start "\\|[ \t]*\\(?:" - comment-start-skip "\\)"))) - (save-excursion - ;; To make sure the return value of forward-paragraph is - ;; meaningful, we have to start from the beginning of - ;; line, otherwise skipping past the last few chars of a - ;; paragraph-separator would count as a paragraph (and - ;; not skipping any chars at EOB would not count as a - ;; paragraph even if it is). - (move-to-left-margin) - (if (not (zerop (fill-forward-paragraph 1))) - ;; There's no paragraph at or after point: give up. - (setq fill-pfx "") - (let ((end (point)) - (beg (progn (fill-forward-paragraph -1) (point)))) - (goto-char before) - (setq fill-pfx - (if use-hard-newlines - ;; Can't use fill-region-as-paragraph, since this - ;; paragraph may still contain hard newlines. See - ;; fill-region. - (fill-region beg end justify) - (fill-region-as-paragraph beg end justify)))))) - fill-pfx)) - ;; If we didn't change anything in the buffer (and the buffer - ;; was previously unmodified), then flip the modification status - ;; back to "unchanged". - (when (and hash - (equal hash (buffer-hash))) - (set-buffer-modified-p nil))))) + (with-buffer-unmodified-if-unchanged + (or + ;; 1. Fill the region if it is active when called interactively. + (and region transient-mark-mode mark-active + (not (eq (region-beginning) (region-end))) + (or (fill-region (region-beginning) (region-end) justify) t)) + ;; 2. Try fill-paragraph-function. + (and (not (eq fill-paragraph-function t)) + (or fill-paragraph-function + (and (minibufferp (current-buffer)) + (= 1 (point-min)))) + (let ((function (or fill-paragraph-function + ;; In the minibuffer, don't count + ;; the width of the prompt. + 'fill-minibuffer-function)) + ;; If fill-paragraph-function is set, it probably + ;; takes care of comments and stuff. If not, it + ;; will have to set fill-paragraph-handle-comment + ;; back to t explicitly or return nil. + (fill-paragraph-handle-comment nil) + (fill-paragraph-function t)) + (funcall function justify))) + ;; 3. Try our syntax-aware filling code. + (and fill-paragraph-handle-comment + ;; Our code only handles \n-terminated comments right now. + comment-start (equal comment-end "") + (let ((fill-paragraph-handle-comment nil)) + (fill-comment-paragraph justify))) + ;; 4. If it all fails, default to the good ol' text paragraph filling. + (let ((before (point)) + (paragraph-start paragraph-start) + ;; Fill prefix used for filling the paragraph. + fill-pfx) + ;; Try to prevent code sections and comment sections from being + ;; filled together. + (when (and fill-paragraph-handle-comment comment-start-skip) + (setq paragraph-start + (concat paragraph-start "\\|[ \t]*\\(?:" + comment-start-skip "\\)"))) + (save-excursion + ;; To make sure the return value of forward-paragraph is + ;; meaningful, we have to start from the beginning of + ;; line, otherwise skipping past the last few chars of a + ;; paragraph-separator would count as a paragraph (and + ;; not skipping any chars at EOB would not count as a + ;; paragraph even if it is). + (move-to-left-margin) + (if (not (zerop (fill-forward-paragraph 1))) + ;; There's no paragraph at or after point: give up. + (setq fill-pfx "") + (let ((end (point)) + (beg (progn (fill-forward-paragraph -1) (point)))) + (goto-char before) + (setq fill-pfx + (if use-hard-newlines + ;; Can't use fill-region-as-paragraph, since this + ;; paragraph may still contain hard newlines. See + ;; fill-region. + (fill-region beg end justify) + (fill-region-as-paragraph beg end justify)))))) + fill-pfx)))) (declare-function comment-search-forward "newcomment" (limit &optional noerror)) (declare-function comment-string-strip "newcomment" (str beforep afterp)) diff --git a/lisp/textmodes/flyspell.el b/lisp/textmodes/flyspell.el index 21612cd5e38..2c5e30fecd8 100644 --- a/lisp/textmodes/flyspell.el +++ b/lisp/textmodes/flyspell.el @@ -1942,9 +1942,7 @@ before point that's highlighted as misspelled." 'face 'flyspell-incorrect string)) (setq pos (cdr pos))) - (if (fboundp 'display-message) - (display-message 'no-log string) - (message "%s" string)))) + (message "%s" string))) ;;*---------------------------------------------------------------------*/ ;;* flyspell-abbrev-table ... */ @@ -2273,17 +2271,8 @@ If OPOINT is non-nil, restore point there after adjusting it for replacement." ;;*---------------------------------------------------------------------*/ (defun flyspell-emacs-popup (event poss word) "The Emacs popup menu." - (if (and (not event) - (display-mouse-p)) - (let* ((mouse-pos (mouse-position)) - (mouse-pos (if (nth 1 mouse-pos) - mouse-pos - (set-mouse-position (car mouse-pos) - (/ (frame-width) 2) 2) - (mouse-position)))) - (setq event (list (list (car (cdr mouse-pos)) - (1+ (cdr (cdr mouse-pos)))) - (car mouse-pos))))) + (unless event + (setq event (popup-menu-normalize-position (point)))) (let* ((corrects (flyspell-sort (car (cdr (cdr poss))) word)) (cor-menu (if (consp corrects) (mapcar (lambda (correct) diff --git a/lisp/textmodes/glyphless-mode.el b/lisp/textmodes/glyphless-mode.el new file mode 100644 index 00000000000..4d48d90b562 --- /dev/null +++ b/lisp/textmodes/glyphless-mode.el @@ -0,0 +1,68 @@ +;;; glyphless-mode.el --- minor mode for displaying glyphless characters -*- lexical-binding: t; -*- + +;; Copyright (C) 2021-2022 Free Software Foundation, Inc. + +;; Maintainer: emacs-devel@gnu.org + +;; This file is part of GNU Emacs. + +;; GNU Emacs 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. + +;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: + +(defcustom glyphless-mode-types '(all) + "Which glyphless characters to display. +The value can be any of the groups supported by +`glyphless-char-display-control' (which see), and in addition +`all', for all glyphless characters." + :version "29.1" + :type '(repeat (choice (const :tag "All" all) + (const :tag "No font" no-font) + (const :tag "C0 Control" c0-control) + (const :tag "C1 Control" c1-control) + (const :tag "Format Control" format-control) + (const :tag "Bidirectional Control" bidi-control) + (const :tag "Variation Selectors" variation-selectors) + (const :tag "No Font" no-font))) + :group 'display) + +;;;###autoload +(define-minor-mode glyphless-display-mode + "Minor mode for displaying glyphless characters in the current buffer. +If enabled, all glyphless characters will be displayed as boxes +that display their acronyms." + :lighter " Glyphless" + (if glyphless-display-mode + (progn + (setq-local glyphless-char-display + (let ((table (make-display-table))) + (set-char-table-parent table glyphless-char-display) + table)) + (glyphless-mode--setup)) + (kill-local-variable 'glyphless-char-display))) + +(defun glyphless-mode--setup () + (let ((types (if (memq 'all glyphless-mode-types) + '(c0-control c1-control format-control + variation-selectors no-font) + glyphless-mode-types))) + (when types + (update-glyphless-char-display + nil (mapcar (lambda (e) (cons e 'acronym)) types))))) + +(provide 'glyphless-mode) + +;;; glyphless-mode.el ends here diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el index a4bf454fdcb..8c8522a6e5e 100644 --- a/lisp/textmodes/ispell.el +++ b/lisp/textmodes/ispell.el @@ -296,7 +296,8 @@ The following values are supported: "Non-nil means suppress messages in `ispell-word'." :type 'boolean) -(defvaralias 'ispell-format-word 'ispell-format-word-function) +(define-obsolete-variable-alias 'ispell-format-word + 'ispell-format-word-function "29.1") (defcustom ispell-format-word-function (function upcase) "Formatting function for displaying word being spell checked. @@ -796,6 +797,9 @@ See `ispell-buffer-with-debug' for an example of use." "An alist of parsed Aspell dicts and associated parameters. Internal use.") +(defvar ispell--aspell-found-dictionaries nil + "An alist of identified aspell dictionaries.") + (defun ispell-find-aspell-dictionaries () "Find Aspell's dictionaries, and record in `ispell-aspell-dictionary-alist'." (let* ((dictionaries @@ -809,7 +813,8 @@ Internal use.") (mapcar #'ispell-aspell-find-dictionary dictionaries)))) ;; Ensure aspell's alias dictionary will override standard ;; definitions. - (setq found (ispell-aspell-add-aliases found)) + (setq found (ispell-aspell-add-aliases found) + ispell--aspell-found-dictionaries (copy-sequence found)) ;; Merge into FOUND any elements from the standard ispell-dictionary-base-alist ;; which have no element in FOUND at all. (dolist (dict ispell-dictionary-base-alist) @@ -1377,9 +1382,11 @@ The variable `ispell-library-directory' defines their location." (if (and name (or ;; Include all for Aspell (we already know existing dicts) - ispell-really-aspell + (and ispell-really-aspell + (assoc name ispell--aspell-found-dictionaries)) ;; Include all if `ispell-library-directory' is nil (Hunspell) - (not ispell-library-directory) + (and (not ispell-really-aspell) + (not ispell-library-directory)) ;; If explicit (-d with an absolute path) and existing dict. (and dict-explt (file-name-absolute-p dict-explt) @@ -1672,14 +1679,13 @@ Valid forms include: ("\\\\bibliographystyle" ispell-tex-arg-end) ("\\\\makebox" ispell-tex-arg-end 0) ("\\\\e?psfig" ispell-tex-arg-end) - ("\\\\document\\(class\\|style\\)" . - "\\\\begin[ \t\n]*{[ \t\n]*document[ \t\n]*}")) + ("\\\\document\\(class\\|style\\)" . "\\\\begin[ \t\n]*{document}")) (;; delimited with \begin. In ispell: displaymath, eqnarray, eqnarray*, ;; equation, minipage, picture, tabular, tabular* (ispell) ("\\(figure\\|table\\)\\*?" ispell-tex-arg-end 0) ("list" ispell-tex-arg-end 2) - ("program" . "\\\\end[ \t\n]*{[ \t\n]*program[ \t\n]*}") - ("verbatim\\*?" . "\\\\end[ \t\n]*{[ \t\n]*verbatim\\*?[ \t\n]*}")))) + ("program" . "\\\\end[ \t]*{program}") + ("verbatim\\*?" . "\\\\end[ \t]*{verbatim\\*?}")))) "Lists of regions to be skipped in TeX mode. First list is used raw. Second list has key placed inside \\begin{}. @@ -2398,24 +2404,24 @@ Global `ispell-quit' set to start location to continue spell session." Selections are: -DIGIT: Replace the word with a digit offered in the *Choices* buffer. -SPC: Accept word this time. -`i': Accept word and insert into private dictionary. -`a': Accept word for this session. -`A': Accept word and place in `buffer-local dictionary'. -`r': Replace word with typed-in value. Rechecked. -`R': Replace word with typed-in value. Query-replaced in buffer. Rechecked. -`?': Show these commands. -`x': Exit spelling buffer. Move cursor to original point. -`X': Exit spelling buffer. Leaves cursor at the current point, and permits +\\`0'..\\`9' Replace the word with a digit offered in the *Choices* buffer. +\\`SPC' Accept word this time. +\\`i' Accept word and insert into private dictionary. +\\`a' Accept word for this session. +\\`A' Accept word and place in `buffer-local dictionary'. +\\`r' Replace word with typed-in value. Rechecked. +\\`R' Replace word with typed-in value. Query-replaced in buffer. Rechecked. +\\`?' Show these commands. +\\`x' Exit spelling buffer. Move cursor to original point. +\\`X' Exit spelling buffer. Leaves cursor at the current point, and permits the aborted check to be completed later. -`q': Quit spelling session (Kills ispell process). -`l': Look up typed-in replacement in alternate dictionary. Wildcards okay. -`u': Like `i', but the word is lower-cased first. -`m': Place typed-in value in personal dictionary, then recheck current word. -`C-l': Redraw screen. -`C-r': Recursive edit. -`C-z': Suspend Emacs or iconify frame." +\\`q' Quit spelling session (Kills ispell process). +\\`l' Look up typed-in replacement in alternate dictionary. Wildcards okay. +\\`u' Like \\`i', but the word is lower-cased first. +\\`m' Place typed-in value in personal dictionary, then recheck current word. +\\`C-l' Redraw screen. +\\`C-r' Recursive edit. +\\`C-z' Suspend Emacs or iconify frame." (if (equal ispell-help-in-bufferp 'electric) (progn @@ -2428,26 +2434,28 @@ SPC: Accept word this time. ;;(if (< (window-height) 15) ;; (enlarge-window ;; (- 15 (ispell-adjusted-window-height)))) - (princ "Selections are: - -DIGIT: Replace the word with a digit offered in the *Choices* buffer. -SPC: Accept word this time. -`i': Accept word and insert into private dictionary. -`a': Accept word for this session. -`A': Accept word and place in `buffer-local dictionary'. -`r': Replace word with typed-in value. Rechecked. -`R': Replace word with typed-in value. Query-replaced in buffer. Rechecked. -`?': Show these commands. -`x': Exit spelling buffer. Move cursor to original point. -`X': Exit spelling buffer. Leaves cursor at the current point, and permits - the aborted check to be completed later. -`q': Quit spelling session (Kills ispell process). -`l': Look up typed-in replacement in alternate dictionary. Wildcards okay. -`u': Like `i', but the word is lower-cased first. -`m': Place typed-in value in personal dictionary, then recheck current word. -`C-l': Redraw screen. -`C-r': Recursive edit. -`C-z': Suspend Emacs or iconify frame.") + (princ + (substitute-command-keys + "Selections are: + +\\`0'..\\`9' Replace the word with a digit offered in the *Choices* buffer. +\\`SPC' Accept word this time. +\\`i' Accept word and insert into private dictionary. +\\`a' Accept word for this session. +\\`A' Accept word and place in `buffer-local dictionary'. +\\`r' Replace word with typed-in value. Rechecked. +\\`R' Replace word with typed-in value. Query-replaced in buffer. Rechecked. +\\`?' Show these commands. +\\`x' Exit spelling buffer. Move cursor to original point. +\\`X' Exit spelling buffer. Leaves cursor at the current point, and permits + the aborted check to be completed later. +\\`q' Quit spelling session (Kills ispell process). +\\`l' Look up typed-in replacement in alternate dictionary. Wildcards okay. +\\`u' Like \\`i', but the word is lower-cased first. +\\`m' Place typed-in value in personal dictionary, then recheck current word. +\\`C-l' Redraw screen. +\\`C-r' Recursive edit. +\\`C-z' Suspend Emacs or iconify frame.")) nil))) @@ -2607,15 +2615,18 @@ Optional REFRESH will unhighlighted then highlight, using block cursor (text (buffer-substring-no-properties start end)) ; Save highlight region. (inhibit-quit t) ; inhibit interrupt processing here. - (buffer-undo-list t)) ; don't clutter the undo list. + (buffer-undo-list t) ; don't clutter the undo list. + (end1 (if (markerp end) (marker-position end) end))) (goto-char end) (delete-region start end) - (insert-char ? (- end start)) ; minimize amount of redisplay + (insert-char ? (- end1 start)) ; minimize amount of redisplay (sit-for 0) ; update display (if highlight (setq inverse-video (not inverse-video))) ; toggle video - (delete-region start end) ; delete whitespace + (delete-region start end1) ; delete whitespace (insert text) ; insert text in inverse video. (sit-for 0) ; update display showing inverse video. + (if (markerp end) + (set-marker end end1)) ; restore marker position (if (not highlight) (goto-char end) (setq inverse-video (not inverse-video)) ; toggle video @@ -2984,8 +2995,7 @@ By just answering RET you can find out what the current dictionary is." (interactive (list (completing-read "Use new dictionary (RET for current, SPC to complete): " - (and (fboundp 'ispell-valid-dictionary-list) - (mapcar #'list (ispell-valid-dictionary-list))) + (mapcar #'list (ispell-valid-dictionary-list)) nil t) current-prefix-arg)) (ispell-set-spellchecker-params) ; Initialize variables and dicts alists @@ -3045,6 +3055,8 @@ when needed." ;;;###autoload (defun ispell-region (reg-start reg-end &optional recheckp shift) "Interactively check a region for spelling errors. +Leave the mark at the last misspelled word that the user was queried about. + Return nil if spell session was terminated, otherwise returns shift offset amount for last line processed." (interactive "r") ; Don't flag errors on read-only bufs. @@ -3056,7 +3068,8 @@ amount for last line processed." (region-type (if (and (= reg-start (point-min)) (= reg-end (point-max))) (buffer-name) "region")) (program-basename (file-name-nondirectory ispell-program-name)) - (dictionary (or ispell-current-dictionary "default"))) + (dictionary (or ispell-current-dictionary "default")) + max-word) (unwind-protect (save-excursion (message "Spell-checking %s using %s with %s dictionary..." @@ -3152,10 +3165,14 @@ ispell-region: Search for first region to skip after (ispell-begin-skip-region-r ;; Reset `in-comment' (and indirectly `add-comment') for new line in-comment nil)) (setq ispell-end (point)) ; "end" tracks region retrieved. - (if string ; there is something to spell check! - ;; (special start end) - (setq shift (ispell-process-line string - (and recheckp shift)))) + ;; There is something to spell check! + (when string + ;; (special start end) + (let ((res (ispell-process-line string + (and recheckp shift)))) + (setq shift (car res)) + (when (cdr res) + (setq max-word (cdr res))))) (goto-char ispell-end))))) (if ispell-quit nil @@ -3166,6 +3183,9 @@ ispell-region: Search for first region to skip after (ispell-begin-skip-region-r (kill-buffer ispell-choices-buffer)) (set-marker skip-region-start nil) (set-marker rstart nil) + ;; Allow the user to pop back to the last position. + (when max-word + (push-mark max-word t)) (if ispell-quit (progn ;; preserve or clear the region for ispell-continue. @@ -3400,9 +3420,12 @@ Returns a string with the line data." This will modify the buffer for spelling errors. Requires variables ISPELL-START and ISPELL-END to be defined in its dynamic scope. -Returns the sum SHIFT due to changes in word replacements." + +Returns a cons cell where the `car' is sum SHIFT due to changes +in word replacements, and the `cdr' is the location of the final +word that was queried about." ;;(declare special ispell-start ispell-end) - (let (poss accept-list) + (let (poss accept-list max-word) (if (not (numberp shift)) (setq shift 0)) ;; send string to spell process and get input. @@ -3456,6 +3479,7 @@ Returns the sum SHIFT due to changes in word replacements." (error (concat "Ispell misalignment: word " "`%s' point %d; probably incompatible versions") ispell-pipe-word actual-point))) + (setq max-word (marker-position word-start)) ;; ispell-cmd-loop can go recursive & change buffer (if ispell-keep-choices-win (setq replace (ispell-command-loop @@ -3552,7 +3576,7 @@ Returns the sum SHIFT due to changes in word replacements." (set-marker line-end nil))) ;; Finished with misspelling! (setq ispell-filter (cdr ispell-filter))) - shift)) + (cons shift max-word))) ;;;###autoload @@ -3593,7 +3617,8 @@ to limit the check." ;;;###autoload (defun ispell-buffer () - "Check the current buffer for spelling errors interactively." + "Check the current buffer for spelling errors interactively. +Leave the mark at the last misspelled word that the user was queried about." (interactive) (ispell-region (point-min) (point-max))) @@ -3883,8 +3908,8 @@ Don't check spelling of message headers except the Subject field. Don't check included messages. To abort spell checking of a message region and send the message anyway, -use the `x' command. (Any subsequent regions will be checked.) -The `X' command aborts sending the message so that you can edit the buffer. +use the \\`x' command. (Any subsequent regions will be checked.) +The \\`X' command aborts sending the message so that you can edit the buffer. To spell-check whenever a message is sent, include the appropriate lines in your init file: @@ -3975,7 +4000,7 @@ You can bind this to the key C-c i in GNUS or mail by adding to (if (re-search-forward "^Subject: *" end-of-headers t) (progn (goto-char (match-end 0)) - (if (and (not (looking-at ".*Re\\>")) + (if (and (not (looking-at ".*\\<Re\\>")) (not (looking-at "\\["))) (progn (setq case-fold-search old-case-fold-search) diff --git a/lisp/textmodes/page-ext.el b/lisp/textmodes/page-ext.el index 24149f9afb8..6b71f26e4f2 100644 --- a/lisp/textmodes/page-ext.el +++ b/lisp/textmodes/page-ext.el @@ -515,13 +515,12 @@ resets the page-delimiter to the original value." (defvar pages-buffer-original-position) (defvar pages-buffer-original-page) -(defun pages-directory - (pages-list-all-headers-p count-lines-p &optional regexp) +(defun pages-directory (pages-list-all-headers-p count-lines-p &optional regexp) "Display a directory of the page headers in a temporary buffer. A header is the first non-blank line after the `page-delimiter'. -\\[pages-directory-mode] +\\<pages-directory-mode-map> You may move point to one of the lines in the temporary buffer, -then use \\<pages-directory-goto> to go to the same line in the pages buffer. +then use \\[pages-directory-goto] to go to the same line in the pages buffer. In interactive use: @@ -587,7 +586,9 @@ directory for only the accessible portion of the buffer." (pages-directory-mode) (setq buffer-read-only nil) (insert - "==== Pages Directory: use `C-c C-c' to go to page under cursor. ====" ?\n) + (substitute-command-keys + "==== Pages Directory: use \\<pages-directory-mode-map>\ +\\[pages-directory-goto] to go to page under cursor. ====") "\n") (setq pages-buffer pages-target-buffer) (setq pages-pos-list nil)) @@ -772,7 +773,9 @@ directory." (goto-char (point-min)) (delete-region (point) (line-end-position)) (insert - "=== Address List Directory: use `C-c C-c' to go to page under cursor. ===") + (substitute-command-keys + "=== Address List Directory: use \\<pages-directory-mode-map>\ +\\[pages-directory-goto] to go to page under cursor. ===")) (set-buffer-modified-p nil) )) (error "No addresses file found!"))) diff --git a/lisp/textmodes/page.el b/lisp/textmodes/page.el index 3fc18323349..5d6f017eb99 100644 --- a/lisp/textmodes/page.el +++ b/lisp/textmodes/page.el @@ -35,11 +35,18 @@ A page boundary is any line whose beginning matches the regexp (interactive "p") (or count (setq count 1)) (while (and (> count 0) (not (eobp))) - ;; In case the page-delimiter matches the null string, - ;; don't find a match without moving. - (if (bolp) (forward-char 1)) - (unless (re-search-forward page-delimiter nil t) - (goto-char (point-max))) + (if (and (looking-at page-delimiter) + (> (match-end 0) (point))) + ;; If we're standing at the page delimiter, then just skip to + ;; the end of it. (But only if it's not a zero-length + ;; delimiter, because then we wouldn't have forward progress.) + (goto-char (match-end 0)) + ;; In case the page-delimiter matches the null string, + ;; don't find a match without moving. + (when (bolp) + (forward-char 1)) + (unless (re-search-forward page-delimiter nil t) + (goto-char (point-max)))) (setq count (1- count))) (while (and (< count 0) (not (bobp))) ;; In case the page-delimiter matches the null string, diff --git a/lisp/textmodes/paragraphs.el b/lisp/textmodes/paragraphs.el index 29804c3bfd2..cd726ad4776 100644 --- a/lisp/textmodes/paragraphs.el +++ b/lisp/textmodes/paragraphs.el @@ -96,8 +96,8 @@ lines that start paragraphs from lines that separate them. If the variable `use-hard-newlines' is non-nil, then only lines following a hard newline are considered to match." - :type 'regexp) -(put 'paragraph-start 'safe-local-variable #'stringp) + :type 'regexp + :safe #'stringp) ;; paragraph-start requires a hard newline, but paragraph-separate does not: ;; It is assumed that paragraph-separate is distinctive enough to be believed @@ -113,8 +113,8 @@ This is matched against the text at the left margin, which is not necessarily the beginning of the line, so it should not use \"^\" as an anchor. This ensures that the paragraph functions will work equally within a region of text indented by a margin setting." - :type 'regexp) -(put 'paragraph-separate 'safe-local-variable #'stringp) + :type 'regexp + :safe #'stringp) (defcustom sentence-end-double-space t "Non-nil means a single space does not end a sentence. @@ -125,8 +125,8 @@ This value is used by the function `sentence-end' to construct the regexp describing the end of a sentence, when the value of the variable `sentence-end' is nil. See Info node `(elisp)Standard Regexps'." :type 'boolean + :safe #'booleanp :group 'fill) -(put 'sentence-end-double-space 'safe-local-variable #'booleanp) (defcustom sentence-end-without-period nil "Non-nil means a sentence will end without a period. @@ -137,8 +137,8 @@ This value is used by the function `sentence-end' to construct the regexp describing the end of a sentence, when the value of the variable `sentence-end' is nil. See Info node `(elisp)Standard Regexps'." :type 'boolean + :safe #'booleanp :group 'fill) -(put 'sentence-end-without-period 'safe-local-variable #'booleanp) (defcustom sentence-end-without-space "。.?!" @@ -147,8 +147,8 @@ regexp describing the end of a sentence, when the value of the variable This value is used by the function `sentence-end' to construct the regexp describing the end of a sentence, when the value of the variable `sentence-end' is nil. See Info node `(elisp)Standard Regexps'." - :type 'string) -(put 'sentence-end-without-space 'safe-local-variable #'stringp) + :type 'string + :safe #'stringp) (defcustom sentence-end nil "Regexp describing the end of a sentence. @@ -158,14 +158,14 @@ All paragraph boundaries also end sentences, regardless. The value nil means to use the default value defined by the function `sentence-end'. You should always use this function to obtain the value of this variable." - :type '(choice regexp (const :tag "Use default value" nil))) -(put 'sentence-end 'safe-local-variable #'string-or-null-p) + :type '(choice regexp (const :tag "Use default value" nil)) + :safe #'string-or-null-p) (defcustom sentence-end-base "[.?!…‽][]\"'”’)}»›]*" "Regexp matching the basic end of a sentence, not including following space." :type 'regexp + :safe #'stringp :version "25.1") -(put 'sentence-end-base 'safe-local-variable #'stringp) (defun sentence-end () "Return the regexp describing the end of a sentence. @@ -192,14 +192,14 @@ in between. See Info node `(elisp)Standard Regexps'." (defcustom page-delimiter "^\014" "Regexp describing line-beginnings that separate pages." - :type 'regexp) -(put 'page-delimiter 'safe-local-variable #'stringp) + :type 'regexp + :safe #'stringp) (defcustom paragraph-ignore-fill-prefix nil "Non-nil means the paragraph commands are not affected by `fill-prefix'. This is desirable in modes where blank lines are the paragraph delimiters." - :type 'boolean) -(put 'paragraph-ignore-fill-prefix 'safe-local-variable #'booleanp) + :type 'boolean + :safe #'booleanp) ;; Silence the compiler. (defun forward-paragraph (&optional arg) @@ -477,20 +477,60 @@ sentences. Also, every paragraph boundary terminates sentences as well." (skip-chars-backward " \t\n") (goto-char par-end))) (setq arg (1- arg))) - (constrain-to-field nil opoint t))) - -(defun repunctuate-sentences (&optional no-query) + (let ((npoint (constrain-to-field nil opoint t))) + (not (= npoint opoint))))) + +(defun count-sentences (start end) + "Count sentences in current buffer from START to END." + (let ((sentences 0) + (inhibit-field-text-motion t)) + (save-excursion + (save-restriction + (narrow-to-region start end) + (goto-char (point-min)) + (while (ignore-errors (forward-sentence)) + (setq sentences (1+ sentences))) + ;; Remove last possibly empty sentence + (when (/= (skip-chars-backward " \t\n") 0) + (setq sentences (1- sentences))) + sentences)))) + +(defun repunctuate-sentences-filter (_start _end) + "Search filter used by `repunctuate-sentences' to skip unneeded spaces. +By default, it skips occurrences that already have two spaces." + (/= 2 (- (point) (save-excursion (skip-chars-backward " ") (point))))) + +(defvar repunctuate-sentences-filter #'repunctuate-sentences-filter + "The default filter used by `repunctuate-sentences'. +It is advised to use `add-function' on this to add more filters, +for example, `(looking-back (rx (or \"e.g.\" \"i.e.\") \" \") 5)' +with a set of predefined abbreviations to skip from adding two spaces.") + +(defun repunctuate-sentences (&optional no-query start end) "Put two spaces at the end of sentences from point to the end of buffer. -It works using `query-replace-regexp'. -If optional argument NO-QUERY is non-nil, make changes without -asking for confirmation." - (interactive) +It works using `query-replace-regexp'. In Transient Mark mode, +if the mark is active, operate on the contents of the region. +Second and third arg START and END specify the region to operate on. +If optional argument NO-QUERY is non-nil, make changes without asking +for confirmation. You can use `repunctuate-sentences-filter' to add +filters to skip occurrences of spaces that don't need to be replaced." + (interactive (list nil + (if (use-region-p) (region-beginning)) + (if (use-region-p) (region-end)))) (let ((regexp "\\([]\"')]?\\)\\([.?!]\\)\\([]\"')]?\\) +") (to-string "\\1\\2\\3 ")) (if no-query - (while (re-search-forward regexp nil t) - (replace-match to-string)) - (query-replace-regexp regexp to-string)))) + (progn + (when start (goto-char start)) + (while (re-search-forward regexp end t) + (replace-match to-string))) + (unwind-protect + (progn + (add-function :after-while isearch-filter-predicate + repunctuate-sentences-filter) + (query-replace-regexp regexp to-string nil start end)) + (remove-function isearch-filter-predicate + repunctuate-sentences-filter))))) (defun backward-sentence (&optional arg) diff --git a/lisp/textmodes/pixel-fill.el b/lisp/textmodes/pixel-fill.el new file mode 100644 index 00000000000..e47653e734a --- /dev/null +++ b/lisp/textmodes/pixel-fill.el @@ -0,0 +1,240 @@ +;;; pixel-fill.el --- variable pitch filling functions -*- lexical-binding: t; -*- + +;; Copyright (C) 2021-2022 Free Software Foundation, Inc. + +;; Maintainer: emacs-devel@gnu.org +;; Keywords: filling + +;; This file is part of GNU Emacs. + +;; GNU Emacs 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. + +;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; The main entry point is `pixel-fill-region', but +;; `pixel-fill-find-fill-point' can also be useful by itself. + +;;; Code: + +(require 'kinsoku) + +(defgroup pixel-fill nil + "Filling based on pixel widths." + :group 'fill + :version "29.1") + +(defcustom pixel-fill-respect-kinsoku t + "If nil, fill even if we can't find a good kinsoku point. +Kinsoku is a Japanese word meaning a rule that should not be violated. +In Emacs, it is a term used for characters, e.g. punctuation marks, +parentheses, and so on, that should not be placed in the beginning +of a line or the end of a line." + :type 'boolean + :version "29.1") + +(defun pixel-fill-width (&optional columns window) + "Return the pixel width corresponding to COLUMNS in WINDOW. +If COLUMNS is nil or omitted, use the entire window width. + +If WINDOW is nil or omitted, this defaults to the selected window." + (unless window + (setq window (selected-window))) + (let ((frame (window-frame window))) + (if columns + (* (frame-char-width frame) columns) + (- (window-body-width nil t) + (* 2 (frame-char-width frame)) + ;; We need to adjust the available width for when the user + ;; disables the fringes, which will cause the display + ;; engine usurp one column for the continuation glyph. + (if (and (fboundp 'fringe-columns) + (or (not (zerop (fringe-columns 'right))) + (not (zerop (fringe-columns 'left))))) + 0 + (* (frame-char-width frame) 2)) + 1)))) + +(defun pixel-fill-region (start end pixel-width) + "Fill the region between START and END. +This will attempt to reformat the text in the region to have no +lines that are visually wider than PIXEL-WIDTH. + +If START isn't at the start of a line, the horizontal position of +START, converted to pixel units, will be used as the indentation +prefix on subsequent lines." + (save-excursion + (goto-char start) + (let ((indentation + (car (window-text-pixel-size nil (line-beginning-position) + (point)))) + (newline-end nil)) + (when (> indentation pixel-width) + (error "The indentation (%s) is wider than the fill width (%s)" + indentation pixel-width)) + (save-restriction + (narrow-to-region start end) + (goto-char (point-max)) + (when (looking-back "\n[ \t]*" (point-min)) + (setq newline-end t)) + (goto-char (point-min)) + ;; First replace all whitespace with space. + (while (re-search-forward "[ \t\n]+" nil t) + (cond + ((or (= (match-beginning 0) start) + (= (match-end 0) end)) + (delete-region (match-beginning 0) (match-end 0))) + ;; If there's just a single space here, don't replace. + ((not (and (= (- (match-end 0) (match-beginning 0)) 1) + (= (char-after (match-beginning 0)) ?\s))) + (replace-match + ;; We need to use a space that has an appropriate width. + (propertize " " 'face + (get-text-property (match-beginning 0) 'face)))))) + (goto-char start) + (pixel-fill--fill-line pixel-width indentation) + (goto-char (point-max)) + (when newline-end + (insert "\n")))))) + +(defun pixel-fill--goto-pixel (width) + (vertical-motion (cons (/ width (frame-char-width)) 0))) + +(defun pixel-fill--fill-line (width &optional indentation) + (let ((start (point))) + (pixel-fill--goto-pixel width) + (while (not (eolp)) + ;; We have to do some folding. First find the first previous + ;; point suitable for folding. + (when (or (not (pixel-fill-find-fill-point (line-beginning-position))) + (= (point) start)) + ;; We had unbreakable text (for this width), so just go to + ;; the first space and carry on. + (beginning-of-line) + (skip-chars-forward " ") + (search-forward " " (line-end-position) 'move)) + (when (= (preceding-char) ?\s) + (delete-char -1)) + (unless (eobp) + (insert ?\n) + (when (> indentation 0) + (insert (propertize " " 'display + (list 'space :align-to (list indentation)))))) + (setq start (point)) + (unless (eobp) + (pixel-fill--goto-pixel width))))) + +(define-inline pixel-fill--char-breakable-p (char) + "Return non-nil if a line can be broken before and after CHAR." + (inline-quote (aref fill-find-break-point-function-table ,char))) + +(define-inline pixel-fill--char-nospace-p (char) + "Return non-nil if no space is required before and after CHAR." + (inline-quote (aref fill-nospace-between-words-table ,char))) + +(define-inline pixel-fill--char-kinsoku-bol-p (char) + "Return non-nil if a line ought not to begin with CHAR." + (inline-letevals (char) + (inline-quote (and (not (eq ,char ?')) + (aref (char-category-set ,char) ?>))))) + +(define-inline pixel-fill--char-kinsoku-eol-p (char) + "Return non-nil if a line ought not to end with CHAR." + (inline-quote (aref (char-category-set ,char) ?<))) + +(defun pixel-fill-find-fill-point (start) + "Find a place suitable for breaking the current line. +START should be the earliest buffer position that should be considered +(typically the start of the line), and this function will search +backward in the current buffer from the current position." + (let ((bp (point)) + (end (point)) + failed) + (while (not + (or (setq failed (<= (point) start)) + (eq (preceding-char) ?\s) + (eq (following-char) ?\s) + (pixel-fill--char-breakable-p (preceding-char)) + (pixel-fill--char-breakable-p (following-char)) + (and (pixel-fill--char-kinsoku-bol-p (preceding-char)) + (pixel-fill--char-breakable-p (following-char)) + (not (pixel-fill--char-kinsoku-bol-p (following-char)))) + (pixel-fill--char-kinsoku-eol-p (following-char)) + (bolp))) + (backward-char 1)) + (if failed + ;; There's no breakable point, so we give it up. + (let (found) + (goto-char bp) + ;; Don't overflow the window edge, even if + ;; `pixel-fill-respect-kinsoku' is t. + (when pixel-fill-respect-kinsoku + (while (setq found (re-search-forward + "\\(\\c>\\)\\| \\|\\c<\\|\\c|" + (line-end-position) 'move))) + (if (and found + (not (match-beginning 1))) + (goto-char (match-beginning 0))))) + (or + (eolp) + ;; Don't put kinsoku-bol characters at the beginning of a line, + ;; or kinsoku-eol characters at the end of a line. + (cond + ;; Don't overflow the window edge, even if `pixel-fill-respect-kinsoku' + ;; is t. + ((not pixel-fill-respect-kinsoku) + (while (and (not (eq (preceding-char) ?\s)) + (or (pixel-fill--char-kinsoku-eol-p (preceding-char)) + (pixel-fill--char-kinsoku-bol-p (following-char)))) + (backward-char 1)) + (when (setq failed (<= (point) start)) + ;; There's no breakable point that doesn't violate kinsoku, + ;; so we look for the second best position. + (while (and (progn + (forward-char 1) + (<= (point) end)) + (progn + (setq bp (point)) + (pixel-fill--char-kinsoku-eol-p (following-char))))) + (goto-char bp))) + ((pixel-fill--char-kinsoku-eol-p (preceding-char)) + ;; Find backward the point where kinsoku-eol characters begin. + (let ((count 4)) + (while + (progn + (backward-char 1) + (and (> (setq count (1- count)) 0) + (not (eq (preceding-char) ?\s)) + (or (pixel-fill--char-kinsoku-eol-p (preceding-char)) + (pixel-fill--char-kinsoku-bol-p (following-char))))))) + (when (setq failed (<= (point) start)) + ;; There's no breakable point that doesn't violate kinsoku, + ;; so we go to the second best position. + (if (looking-at "\\(\\c<+\\)\\c<") + (goto-char (match-end 1)) + (forward-char 1)))) + ((pixel-fill--char-kinsoku-bol-p (following-char)) + ;; Find forward the point where kinsoku-bol characters end. + (let ((count 4)) + (while (progn + (forward-char 1) + (and (>= (setq count (1- count)) 0) + (pixel-fill--char-kinsoku-bol-p (following-char)) + (pixel-fill--char-breakable-p (following-char)))))))) + (when (eq (following-char) ?\s) + (forward-char 1)))) + (not failed))) + +(provide 'pixel-fill) + +;;; pixel-fill.el ends here diff --git a/lisp/textmodes/reftex-cite.el b/lisp/textmodes/reftex-cite.el index 4e487d745c2..26b14ebc79e 100644 --- a/lisp/textmodes/reftex-cite.el +++ b/lisp/textmodes/reftex-cite.el @@ -360,7 +360,7 @@ The name of the first different author/editor is used." ;; Parse the bibliography environment (defun reftex-extract-bib-entries-from-thebibliography (files) - "Extract bib-entries from the \begin{thebibliography} environment. + "Extract bib-entries from the \\begin{thebibliography} environment. Parsing is not as good as for the BibTeX database stuff. The environment should be located in FILES." (let* (start end buf entries re re-list file default) @@ -580,7 +580,7 @@ If FORMAT is non-nil `format' entry accordingly." (concat key "\n " authors " " year " " extra "\n " title "\n\n"))) (defun reftex-parse-bibitem (item) - "Parse a \bibitem entry in ITEM." + "Parse a \\bibitem entry in ITEM." (let ((key "") (text "")) (when (string-match "\\`{\\([^}]+\\)}\\([^\000]*\\)" item) (setq key (match-string 1 item) @@ -596,7 +596,7 @@ If FORMAT is non-nil `format' entry accordingly." (cons "&entry" (concat key " " text))))) (defun reftex-format-bibitem (item) - "Format a \bibitem entry in ITEM so that it is (relatively) nice to look at." + "Format a \\bibitem entry in ITEM so that it is (relatively) nice to look at." (let ((text (reftex-get-bib-field "&text" item)) (key (reftex-get-bib-field "&key" item)) (lines nil)) diff --git a/lisp/textmodes/reftex-global.el b/lisp/textmodes/reftex-global.el index c7e34b4b90a..062cea9c505 100644 --- a/lisp/textmodes/reftex-global.el +++ b/lisp/textmodes/reftex-global.el @@ -154,8 +154,10 @@ No active TAGS table is required." (erase-buffer) (insert " MULTIPLE LABELS IN CURRENT DOCUMENT:\n") (insert - " Move point to label and type `r' to run a query-replace on the label\n" - " and its references. Type `q' to exit this buffer.\n\n") + (substitute-command-keys + " Move point to label and type \\`r' to run a query-replace on the label\n") + (substitute-command-keys + " and its references. Type \\`q' to exit this buffer.\n\n")) (insert " LABEL FILE\n") (insert " -------------------------------------------------------------\n") (use-local-map (make-sparse-keymap)) @@ -188,8 +190,8 @@ No active TAGS table is required." default)))) (if (string= from "") (setq from default)) (unless to - (setq to (read-string (format "Replace label %s with: " - from)))) + (setq to (read-string (format "Replace label %s with: " from) + nil nil from))) (reftex-query-replace-document (concat "{" (regexp-quote from) "}") (format "{%s}" to)))) diff --git a/lisp/textmodes/reftex-index.el b/lisp/textmodes/reftex-index.el index c28f31d5647..b517cc16634 100644 --- a/lisp/textmodes/reftex-index.el +++ b/lisp/textmodes/reftex-index.el @@ -29,9 +29,7 @@ (require 'reftex) -;; START remove for XEmacs release (defvar TeX-master) -;; END remove for XEmacs release ;;;###autoload (defun reftex-index-selection-or-word (&optional arg phrase) @@ -271,8 +269,6 @@ will prompt for other arguments." (and newtag (cdr cell) (not (member newtag (cdr cell))) (push newtag (cdr cell))))) -(define-obsolete-variable-alias - 'reftex-index-map 'reftex-index-mode-map "24.1") (defvar reftex-index-mode-map (let ((map (make-sparse-keymap))) ;; Index map @@ -1200,8 +1196,6 @@ This gets refreshed in every phrases command.") '((reftex-index-phrases-font-lock-keywords) nil t nil beginning-of-line) "Font lock defaults for `reftex-index-phrases-mode'.") -(define-obsolete-variable-alias - 'reftex-index-phrases-map 'reftex-index-phrases-mode-map "24.1") (defvar reftex-index-phrases-mode-map (let ((map (make-sparse-keymap))) ;; Keybindings and Menu for phrases buffer @@ -1274,10 +1268,11 @@ This gets refreshed in every phrases command.") ;;;###autoload (defun reftex-index-phrase-selection-or-word (arg) "Add current selection or word at point to the phrases buffer. +\\<reftex-index-phrases-mode-map> When you are in transient-mark-mode and the region is active, the selection will be used - otherwise the word at point. You get a chance to edit the entry in the phrases buffer - finish with -`C-c C-c'." +\\[reftex-index-phrases-save-and-return]." (interactive "P") (set-marker reftex-index-return-marker (point)) (reftex-index-selection-or-word arg 'phrase) @@ -1375,7 +1370,7 @@ If the buffer is non-empty, delete the old header first." ;;;###autoload (define-derived-mode reftex-index-phrases-mode fundamental-mode "Phrases" "Major mode for managing the Index phrases of a LaTeX document. -This buffer was created with RefTeX. +This buffer was created with RefTeX. \\<reftex-index-phrases-mode-map> To insert new phrases, use - `C-c \\' in the LaTeX document to copy selection or word @@ -1686,8 +1681,8 @@ this function repeatedly." (defun reftex-index-phrases-set-macro-key () "Change the macro key for the current line. Prompts for a macro key and insert is at the beginning of the line. -If you reply with SPACE, the macro keyn will be removed, so that the -default macro will be used. If you reply with `RET', just prints +If you reply with \\`SPC', the macro key will be removed, so that the +default macro will be used. If you reply with \\`RET', just prints information about the currently selected macro." (interactive) (reftex-index-phrases-parse-header) diff --git a/lisp/textmodes/reftex-parse.el b/lisp/textmodes/reftex-parse.el index e34c45178b4..49cef297882 100644 --- a/lisp/textmodes/reftex-parse.el +++ b/lisp/textmodes/reftex-parse.el @@ -345,7 +345,17 @@ of master file." ;; Find external document specifications (goto-char 1) - (while (re-search-forward "[\n\r][ \t]*\\\\externaldocument\\(\\[\\([^]]*\\)\\]\\)?{\\([^}]+\\)}" nil t) + (while (re-search-forward + (concat "[\n\r][ \t]*" + ;; Support \externalcitedocument macro + "\\\\external\\(?:cite\\)?document" + ;; The optional prefix + "\\(\\[\\([^]]*\\)\\]\\)?" + ;; The 2nd opt. arg can only be nocite + "\\(?:\\[nocite\\]\\)?" + ;; Mandatory file argument + "{\\([^}]+\\)}") + nil t) (push (list 'xr-doc (reftex-match-string 2) (reftex-match-string 3)) docstruct)) @@ -360,13 +370,18 @@ of master file." docstruct)) (defun reftex-using-biblatex-p () - "Return non-nil if we are using biblatex rather than bibtex." + "Return non-nil if we are using biblatex or other specific cite package. +biblatex and other similar packages like multibib allow multiple macro +calls to load a bibliography file. This function should be able to +detect those packages." (if (boundp 'TeX-active-styles) ;; the sophisticated AUCTeX way - (member "biblatex" TeX-active-styles) + (or (member "biblatex" TeX-active-styles) + (member "multibib" TeX-active-styles)) ;; poor-man's check... (save-excursion - (re-search-forward "^[^%\n]*?\\\\usepackage.*{biblatex}" nil t)))) + (re-search-forward + "^[^%\n]*?\\\\usepackage\\(\\[[^]]*\\]\\)?{biblatex\\|multibib}" nil t)))) ;;;###autoload (defun reftex-locate-bibliography-files (master-dir &optional files) @@ -374,7 +389,7 @@ of master file." (unless files (save-excursion (goto-char (point-min)) - ;; when biblatex is used, multiple \bibliography or + ;; when biblatex or multibib are used, multiple \bibliography or ;; \addbibresource macros are allowed. With plain bibtex, only ;; the first is used. (let ((using-biblatex (reftex-using-biblatex-p)) @@ -382,7 +397,7 @@ of master file." (while (and again (re-search-forward (concat - ;; "\\(\\`\\|[\n\r]\\)[^%]*\\\\\\(" + ;; "\\(\\`\\|[\n\r]\\)[^%]*\\\\\\(" "\\(^\\)[^%\n\r]*\\\\\\(" (mapconcat #'identity reftex-bibliography-commands "\\|") "\\)\\(\\[.+?\\]\\)?{[ \t]*\\([^}]+\\)") @@ -405,7 +420,7 @@ of master file." ;; find the file (reftex-locate-file x "bib" master-dir))) files)) - (delq nil files))) + (delq nil (delete-dups files)))) (defun reftex-replace-label-list-segment (old insert &optional entirely) "Replace the segment in OLD which corresponds to INSERT. diff --git a/lisp/textmodes/reftex-sel.el b/lisp/textmodes/reftex-sel.el index d77411483f7..5942801a8a9 100644 --- a/lisp/textmodes/reftex-sel.el +++ b/lisp/textmodes/reftex-sel.el @@ -59,8 +59,6 @@ (define-key map [follow-link] 'mouse-face) map)) -(define-obsolete-variable-alias - 'reftex-select-label-map 'reftex-select-label-mode-map "24.1") (defvar reftex-select-label-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map reftex-select-shared-map) @@ -109,8 +107,6 @@ During a selection process, these are the local bindings. ;; We do not set a local map - reftex-select-item does this. ) -(define-obsolete-variable-alias - 'reftex-select-bib-map 'reftex-select-bib-mode-map "24.1") (defvar reftex-select-bib-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map reftex-select-shared-map) diff --git a/lisp/textmodes/reftex-toc.el b/lisp/textmodes/reftex-toc.el index 4ba3c2193ee..5599eaee024 100644 --- a/lisp/textmodes/reftex-toc.el +++ b/lisp/textmodes/reftex-toc.el @@ -28,7 +28,6 @@ (require 'reftex) ;;; -(define-obsolete-variable-alias 'reftex-toc-map 'reftex-toc-mode-map "24.1") (defvar reftex-toc-mode-map (let ((map (make-sparse-keymap))) @@ -157,22 +156,22 @@ Here are all local bindings. (defconst reftex-toc-help " AVAILABLE KEYS IN TOC BUFFER ============================ -n / p next-line / previous-line -SPC Show the corresponding location of the LaTeX document. -TAB Goto the location and keep the TOC window. -RET Goto the location and hide the TOC window (also on mouse-2). -< / > Promote / Demote section, or all sections in region. -C-c > Display Index. With prefix arg, restrict index to current section. -q / k Hide/Kill *toc* buffer, return to position of reftex-toc command. -l i c F Toggle display of [l]abels, [i]ndex, [c]ontext, [F]ile borders. -t Change maximum toc depth (e.g. `3 t' hides levels greater than 3). -f / g Toggle follow mode / Refresh *toc* buffer. -a / d Toggle auto recenter / Toggle dedicated frame -r / C-u r Reparse the LaTeX document / Reparse entire LaTeX document. -. In other window, show position from where `reftex-toc' was called. -M-% Global search and replace to rename label at point. -x Switch to TOC of external document (with LaTeX package `xr'). -z Jump to a specific section (e.g. '3 z' goes to section 3).") +\\`n' / \\`p' `next-line' / `previous-line' +\\`SPC' Show the corresponding location of the LaTeX document. +\\`TAB' Goto the location and keep the TOC window. +\\`RET' Goto the location and hide the TOC window (also on `mouse-2'). +\\`<' / \\`>' Promote / Demote section, or all sections in region. +\\`C-c >' Display Index. With prefix arg, restrict index to current section. +\\`q' / \\`k' Hide/Kill *toc* buffer, return to position of reftex-toc command. +\\`l' \\`i' \\`c' \\`F' Toggle display of [l]abels, [i]ndex, [c]ontext, [F]ile borders. +\\`t' Change maximum toc depth (e.g. `3 t' hides levels greater than 3). +\\`f' / \\`g' Toggle follow mode / Refresh *toc* buffer. +\\`a' / \\`d' Toggle auto recenter / Toggle dedicated frame +\\`r' / \\`C-u r' Reparse the LaTeX document / Reparse entire LaTeX document. +\\`.' In other window, show position from where `reftex-toc' was called. +\\`M-%' Global search and replace to rename label at point. +\\`x' Switch to TOC of external document (with LaTeX package `xr'). +\\`z' Jump to a specific section (e.g. \\`3 z' goes to section 3).") (defvar reftex--rebuilding-toc nil) @@ -381,7 +380,7 @@ SPC=view TAB=goto RET=goto+hide [q]uit [r]escan [l]abels [f]ollow [x]r [?]Help (- (or reftex-last-window-height (window-height)) (window-height))))) (when (> count 0) - (with-demoted-errors ;E.g. the window might be the root window! + (with-demoted-errors "Enlarge window error: %S" (enlarge-window count reftex-toc-split-windows-horizontally))))) (defun reftex-toc-dframe-p (&optional frame error) @@ -394,7 +393,9 @@ SPC=view TAB=goto RET=goto+hide [q]uit [r]escan [l]abels [f]ollow [x]r [?]Help (frame-parameter frame 'name)) "RefTeX TOC Frame"))) (if (and res error) - (error "This frame is view-only. Use `C-c =' to create TOC window for commands")) + (error (substitute-command-keys + "This frame is view-only. Use \\[reftex-toc] \ +to create TOC window for commands"))) res)) (defun reftex-toc-show-help () @@ -402,7 +403,9 @@ SPC=view TAB=goto RET=goto+hide [q]uit [r]escan [l]abels [f]ollow [x]r [?]Help (interactive) (reftex-toc-dframe-p nil 'error) (with-output-to-temp-buffer "*RefTeX Help*" - (princ reftex-toc-help)) + (let ((help (substitute-command-keys reftex-toc-help))) + (with-current-buffer standard-output + (insert help)))) (reftex-enlarge-to-fit "*RefTeX Help*" t) ;; If follow mode is active, arrange to delay it one command (if reftex-toc-follow-mode diff --git a/lisp/textmodes/reftex-vars.el b/lisp/textmodes/reftex-vars.el index 36dd36c95ea..f9f09825fa0 100644 --- a/lisp/textmodes/reftex-vars.el +++ b/lisp/textmodes/reftex-vars.el @@ -70,12 +70,16 @@ ("tabwindow" ?f nil nil 1))) (rotating "Sidewaysfigure and table" - (("sidewaysfigure" ?f nil nil caption) - ("sidewaystable" ?t nil nil caption))) + (("sidewaysfigure" ?f nil nil caption) + ("sidewaysfigure*" ?f nil nil caption) + ("sidewaystable" ?t nil nil caption) + ("sidewaystable*" ?t nil nil caption))) - (sidecap "CSfigure and SCtable" - (("SCfigure" ?f nil nil caption) - ("SCtable" ?t nil nil caption))) + (sidecap "SCfigure and SCtable" + (("SCfigure" ?f nil nil caption) + ("SCfigure*" ?f nil nil caption) + ("SCtable" ?t nil nil caption) + ("SCtable*" ?t nil nil caption))) (subfigure "Subfigure environments/macro" (("subfigure" ?f nil nil caption) @@ -392,19 +396,19 @@ that the *toc* window fills half the frame." (defcustom reftex-toc-include-file-boundaries nil "Non-nil means, include file boundaries in *toc* buffer. -This flag can be toggled from within the *toc* buffer with the `F' key." +This flag can be toggled from within the *toc* buffer with the \\`F' key." :group 'reftex-table-of-contents-browser :type 'boolean) (defcustom reftex-toc-include-labels nil "Non-nil means, include labels in *toc* buffer. -This flag can be toggled from within the *toc* buffer with the `l' key." +This flag can be toggled from within the *toc* buffer with the \\`l' key." :group 'reftex-table-of-contents-browser :type 'boolean) (defcustom reftex-toc-include-index-entries nil "Non-nil means, include index entries in *toc* buffer. -This flag can be toggled from within the *toc* buffer with the `i' key." +This flag can be toggled from within the *toc* buffer with the \\`i' key." :group 'reftex-table-of-contents-browser :type 'boolean) @@ -422,14 +426,14 @@ changed." (defcustom reftex-toc-include-context nil "Non-nil means, include context with labels in the *toc* buffer. Context will only be shown when labels are visible as well. -This flag can be toggled from within the *toc* buffer with the `c' key." +This flag can be toggled from within the *toc* buffer with the \\`c' key." :group 'reftex-table-of-contents-browser :type 'boolean) (defcustom reftex-toc-follow-mode nil "Non-nil means, point in *toc* buffer will cause other window to follow. The other window will show the corresponding part of the document. -This flag can be toggled from within the *toc* buffer with the `f' key." +This flag can be toggled from within the *toc* buffer with the \\`f' key." :group 'reftex-table-of-contents-browser :type 'boolean) @@ -1314,7 +1318,7 @@ macro before insertion. For example, it will change \\cite[][]{Jones} -> \\cite{Jones} \\cite[][Chapter 1]{Jones} -> \\cite[Chapter 1]{Jones} \\cite[see][]{Jones} -> \\cite[see][]{Jones} - \\cite[see][Chapter 1]{Jones} -> \\cite{Jones} + \\cite[see][Chapter 1]{Jones} -> \\cite[see][Chapter 1]{Jones} It is possible that other packages have other conventions about which optional argument is interpreted how - that is why this cleaning up can be turned off." @@ -1627,14 +1631,14 @@ to that section." (defcustom reftex-index-include-context nil "Non-nil means, display the index definition context in the index buffer. -This flag may also be toggled from the index buffer with the `c' key." +This flag may also be toggled from the index buffer with the \\`c' key." :group 'reftex-index-support :type 'boolean) (defcustom reftex-index-follow-mode nil "Non-nil means, point in *Index* buffer will cause other window to follow. The other window will show the corresponding part of the document. -This flag can be toggled from within the *Index* buffer with the `f' key." +This flag can be toggled from within the *Index* buffer with the \\`f' key." :group 'reftex-table-of-contents-browser :type 'boolean) @@ -1863,10 +1867,11 @@ of the regular expressions in this list, that file is not parsed by RefTeX." (defcustom reftex-enable-partial-scans nil "Non-nil means, re-parse only 1 file when asked to re-parse. Re-parsing is normally requested with a \\[universal-argument] prefix to many RefTeX commands, -or with the `r' key in menus. When this option is t in a multifile document, +or with the \\`r' key in menus. When this option is t in a multifile document, we will only parse the current buffer, or the file associated with the label or section heading near point in a menu. Requesting re-parsing of an entire -multifile document then requires a \\[universal-argument] \\[universal-argument] prefix or the capital `R' key +multifile document then requires a \\[universal-argument] \ +\\[universal-argument] prefix or the capital \\`R' key in menus." :group 'reftex-optimizations-for-large-documents :type 'boolean) @@ -1912,7 +1917,7 @@ when new labels in its category are added. See the variable When a new label is defined with `reftex-label', all selection buffers associated with that label category are emptied, in order to force an update upon next use. When nil, the buffers are left alone and have to be -updated by hand, with the `g' key from the label selection process. +updated by hand, with the \\`g' key from the label selection process. The value of this variable will only have any effect when `reftex-use-multiple-selection-buffers' is non-nil." :group 'reftex-optimizations-for-large-documents @@ -1964,7 +1969,7 @@ instead or as well. The variable may have one of these values: both Both cursor and mouse trigger highlighting. Changing this variable requires rebuilding the selection and *toc* buffers -to become effective (keys `g' or `r')." +to become effective (keys \\`g' or \\`r')." :group 'reftex-fontification-configurations :type '(choice (const :tag "Never" nil) diff --git a/lisp/textmodes/reftex.el b/lisp/textmodes/reftex.el index 907d50889a1..e72576cdc74 100644 --- a/lisp/textmodes/reftex.el +++ b/lisp/textmodes/reftex.el @@ -2257,8 +2257,7 @@ IGNORE-WORDS List of words which should be removed from the string." ("Customize" ["Browse RefTeX Group" reftex-customize t] "--" - ["Build Full Customize Menu" reftex-create-customize-menu - (fboundp 'customize-menu-create)]) + ["Build Full Customize Menu" reftex-create-customize-menu]) ("Documentation" ["Info" reftex-info t] ["Commentary" reftex-show-commentary t]))) diff --git a/lisp/textmodes/remember.el b/lisp/textmodes/remember.el index d65aea62862..f7ebe04bcf5 100644 --- a/lisp/textmodes/remember.el +++ b/lisp/textmodes/remember.el @@ -296,7 +296,8 @@ With a prefix or a visible region, use the region as INITIAL." (insert "\n\n" annotation)) (setq remember-initial-contents nil) (goto-char (point-min))) - (message "Use C-c C-c to remember the data."))) + (message (substitute-command-keys + "Use \\[remember-finalize] to remember the data")))) ;;;###autoload (defun remember-other-frame (&optional initial) @@ -653,7 +654,7 @@ to turn the *scratch* buffer into your notes buffer." (remember-notes-mode 1) (current-buffer))))) (when switch-to - (switch-to-buffer buf)) + (pop-to-buffer-same-window buf)) buf)) (defun remember-notes--kill-buffer-query () diff --git a/lisp/textmodes/rst.el b/lisp/textmodes/rst.el index 104812f43cd..10313e99393 100644 --- a/lisp/textmodes/rst.el +++ b/lisp/textmodes/rst.el @@ -522,7 +522,7 @@ argument list for `rst-re'.") (defvar rst-re-alist) ; Forward declare to use it in `rst-re'. -;; FIXME: Use `sregex' or `rx' instead of re-inventing the wheel. +;; FIXME: Use `rx' instead of re-inventing the wheel. (rst-testcover-add-compose 'rst-re) (defun rst-re (&rest args) ;; testcover: ok. @@ -2351,7 +2351,7 @@ If user selects bullets or #, it's just added with position arranged by `rst-insert-list-new-tag'. If user selects enumerations, a further prompt is given. User need to -input a starting item, for example 'e' for 'A)' style. The position is +input a starting item, for example `e' for `A)' style. The position is also arranged by `rst-insert-list-new-tag'." (let* ((itemstyle (completing-read (format-prompt "Select preferred item style" "#.") @@ -3569,8 +3569,6 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Font lock -;; FIXME: The obsolete variables need to disappear. - ;; The following versions have been done inside Emacs and should not be ;; replaced by `:package-version' attributes until a change. @@ -3584,125 +3582,46 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." :version "24.1" :group 'rst-faces) -(defcustom rst-block-face 'rst-block - "All syntax marking up a special block." - :version "24.1" - :group 'rst-faces - :type '(face)) -(make-obsolete-variable 'rst-block-face - "customize the face `rst-block' instead." - "24.1") - (defface rst-external '((t :inherit font-lock-type-face)) "Face used for field names and interpreted text." :version "24.1" :group 'rst-faces) -(defcustom rst-external-face 'rst-external - "Field names and interpreted text." - :version "24.1" - :group 'rst-faces - :type '(face)) -(make-obsolete-variable 'rst-external-face - "customize the face `rst-external' instead." - "24.1") - (defface rst-definition '((t :inherit font-lock-function-name-face)) "Face used for all other defining constructs." :version "24.1" :group 'rst-faces) -(defcustom rst-definition-face 'rst-definition - "All other defining constructs." - :version "24.1" - :group 'rst-faces - :type '(face)) -(make-obsolete-variable 'rst-definition-face - "customize the face `rst-definition' instead." - "24.1") - (defface rst-directive '((t :inherit font-lock-builtin-face)) "Face used for directives and roles." :version "24.1" :group 'rst-faces) -(defcustom rst-directive-face 'rst-directive - "Directives and roles." - :group 'rst-faces - :type '(face)) -(make-obsolete-variable 'rst-directive-face - "customize the face `rst-directive' instead." - "24.1") - (defface rst-comment '((t :inherit font-lock-comment-face)) "Face used for comments." :version "24.1" :group 'rst-faces) -(defcustom rst-comment-face 'rst-comment - "Comments." - :version "24.1" - :group 'rst-faces - :type '(face)) -(make-obsolete-variable 'rst-comment-face - "customize the face `rst-comment' instead." - "24.1") - (defface rst-emphasis1 '((t :inherit italic)) "Face used for simple emphasis." :version "24.1" :group 'rst-faces) -(defcustom rst-emphasis1-face 'rst-emphasis1 - "Simple emphasis." - :version "24.1" - :group 'rst-faces - :type '(face)) -(make-obsolete-variable 'rst-emphasis1-face - "customize the face `rst-emphasis1' instead." - "24.1") - (defface rst-emphasis2 '((t :inherit bold)) "Face used for double emphasis." :version "24.1" :group 'rst-faces) -(defcustom rst-emphasis2-face 'rst-emphasis2 - "Double emphasis." - :group 'rst-faces - :type '(face)) -(make-obsolete-variable 'rst-emphasis2-face - "customize the face `rst-emphasis2' instead." - "24.1") - (defface rst-literal '((t :inherit font-lock-string-face)) "Face used for literal text." :version "24.1" :group 'rst-faces) -(defcustom rst-literal-face 'rst-literal - "Literal text." - :version "24.1" - :group 'rst-faces - :type '(face)) -(make-obsolete-variable 'rst-literal-face - "customize the face `rst-literal' instead." - "24.1") - (defface rst-reference '((t :inherit font-lock-variable-name-face)) "Face used for references to a definition." :version "24.1" :group 'rst-faces) -(defcustom rst-reference-face 'rst-reference - "References to a definition." - :version "24.1" - :group 'rst-faces - :type '(face)) -(make-obsolete-variable 'rst-reference-face - "customize the face `rst-reference' instead." - "24.1") - (defface rst-transition '((t :inherit font-lock-keyword-face)) "Face used for a transition." :package-version '(rst . "1.3.0") @@ -3794,23 +3713,23 @@ of your own." ;; `Bullet Lists`_ ;; FIXME: A bullet directly after a field name is not recognized. (,(rst-re 'lin-beg '(:grp bul-sta)) - 1 rst-block-face) + 1 'rst-block) ;; `Enumerated Lists`_ (,(rst-re 'lin-beg '(:grp enmany-sta)) - 1 rst-block-face) + 1 'rst-block) ;; `Definition Lists`_ ;; FIXME: missing. ;; `Field Lists`_ (,(rst-re 'lin-beg '(:grp fld-tag) 'bli-sfx) - 1 rst-external-face) + 1 'rst-external) ;; `Option Lists`_ (,(rst-re 'lin-beg '(:grp opt-tag (:shy optsep-tag opt-tag) "*") '(:alt "$" (:seq hws-prt "\\{2\\}"))) - 1 rst-block-face) + 1 'rst-block) ;; `Line Blocks`_ ;; Only for lines containing no more bar - to distinguish from tables. (,(rst-re 'lin-beg '(:grp "|" bli-sfx) "[^|\n]*$") - 1 rst-block-face) + 1 'rst-block) ;; `Tables`_ ;; FIXME: missing @@ -3818,22 +3737,22 @@ of your own." ;; All the `Explicit Markup Blocks`_ ;; `Footnotes`_ / `Citations`_ (,(rst-re 'lin-beg 'fnc-sta-2) - (1 rst-definition-face) - (2 rst-definition-face)) + (1 'rst-definition) + (2 'rst-definition)) ;; `Directives`_ / `Substitution Definitions`_ (,(rst-re 'lin-beg 'dir-sta-3) - (1 rst-directive-face) - (2 rst-definition-face) - (3 rst-directive-face)) + (1 'rst-directive) + (2 'rst-definition) + (3 'rst-directive)) ;; `Hyperlink Targets`_ (,(rst-re 'lin-beg '(:grp exm-sta "_" (:alt (:seq "`" ilcbkqdef-tag "`") (:seq (:alt "[^:\\\n]" "\\\\.") "+")) ":") 'bli-sfx) - 1 rst-definition-face) + 1 'rst-definition) (,(rst-re 'lin-beg '(:grp "__") 'bli-sfx) - 1 rst-definition-face) + 1 'rst-definition) ;; All `Inline Markup`_ ;; Most of them may be multiline though this is uninteresting. @@ -3841,16 +3760,16 @@ of your own." ;; FIXME: Condition 5 preventing fontification of e.g. "*" not implemented ;; `Strong Emphasis`_. (,(rst-re 'ilm-pfx '(:grp "\\*\\*" ilcast-tag "\\*\\*") 'ilm-sfx) - 1 rst-emphasis2-face) + 1 'rst-emphasis2) ;; `Emphasis`_ (,(rst-re 'ilm-pfx '(:grp "\\*" ilcast-tag "\\*") 'ilm-sfx) - 1 rst-emphasis1-face) + 1 'rst-emphasis1) ;; `Inline Literals`_ (,(rst-re 'ilm-pfx '(:grp "``" ilcbkq-tag "``") 'ilm-sfx) - 1 rst-literal-face) + 1 'rst-literal) ;; `Inline Internal Targets`_ (,(rst-re 'ilm-pfx '(:grp "_`" ilcbkq-tag "`") 'ilm-sfx) - 1 rst-definition-face) + 1 'rst-definition) ;; `Hyperlink References`_ ;; FIXME: `Embedded URIs and Aliases`_ not considered. ;; FIXME: Directly adjacent marked up words are not fontified correctly @@ -3858,28 +3777,28 @@ of your own." (,(rst-re 'ilm-pfx '(:grp (:alt (:seq "`" ilcbkq-tag "`") (:seq "\\sw" (:alt "\\sw" "-") "+\\sw")) "__?") 'ilm-sfx) - 1 rst-reference-face) + 1 'rst-reference) ;; `Interpreted Text`_ (,(rst-re 'ilm-pfx '(:grp (:shy ":" sym-tag ":") "?") '(:grp "`" ilcbkq-tag "`") '(:grp (:shy ":" sym-tag ":") "?") 'ilm-sfx) - (1 rst-directive-face) - (2 rst-external-face) - (3 rst-directive-face)) + (1 'rst-directive) + (2 'rst-external) + (3 'rst-directive)) ;; `Footnote References`_ / `Citation References`_ (,(rst-re 'ilm-pfx '(:grp fnc-tag "_") 'ilm-sfx) - 1 rst-reference-face) + 1 'rst-reference) ;; `Substitution References`_ ;; FIXME: References substitutions like |this|_ or |this|__ are not ;; fontified correctly. (,(rst-re 'ilm-pfx '(:grp sub-tag) 'ilm-sfx) - 1 rst-reference-face) + 1 'rst-reference) ;; `Standalone Hyperlinks`_ ;; FIXME: This takes it easy by using a whitespace as delimiter. (,(rst-re 'ilm-pfx '(:grp uri-tag ":\\S +") 'ilm-sfx) - 1 rst-definition-face) + 1 'rst-definition) (,(rst-re 'ilm-pfx '(:grp sym-tag "@" sym-tag ) 'ilm-sfx) - 1 rst-definition-face) + 1 'rst-definition) ;; Do all block fontification as late as possible so 'append works. @@ -3906,18 +3825,18 @@ of your own." ;; `Comments`_ ;; This is multiline. (,(rst-re 'lin-beg 'cmt-sta-1) - (1 rst-comment-face) + (1 'rst-comment) (rst-font-lock-find-unindented-line-match (rst-font-lock-find-unindented-line-limit (match-end 1)) nil - (0 rst-comment-face append))) + (0 'rst-comment append))) (,(rst-re 'lin-beg '(:grp exm-tag) '(:grp hws-tag) "$") - (1 rst-comment-face) - (2 rst-comment-face) + (1'rst-comment) + (2'rst-comment) (rst-font-lock-find-unindented-line-match (rst-font-lock-find-unindented-line-limit 'next) nil - (0 rst-comment-face append))) + (0 'rst-comment append))) ;; FIXME: This is not rendered as comment:: ;; .. .. list-table:: @@ -3941,11 +3860,11 @@ of your own." ;; `Indented Literal Blocks`_ ;; This is multiline. (,(rst-re 'lin-beg 'lit-sta-2) - (2 rst-block-face) + (2 'rst-block) (rst-font-lock-find-unindented-line-match (rst-font-lock-find-unindented-line-limit t) nil - (0 rst-literal-face append))) + (0 'rst-literal append))) ;; FIXME: `Quoted Literal Blocks`_ missing. ;; This is multiline. @@ -3972,8 +3891,8 @@ of your own." ;; ;; Indentation is not required for doctest blocks. (,(rst-re 'lin-beg '(:grp (:alt ">>>" ell-tag)) '(:grp ".+")) - (1 rst-block-face) - (2 rst-literal-face))) + (1 'rst-block) + (2 'rst-literal))) "Keywords to highlight in rst mode.") (defvar font-lock-beg) diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el index efebee0521b..8f9b603ef5f 100644 --- a/lisp/textmodes/sgml-mode.el +++ b/lisp/textmodes/sgml-mode.el @@ -75,7 +75,8 @@ a DOCTYPE or an XML declaration." :type 'boolean :version "22.1") -(defvaralias 'sgml-transformation 'sgml-transformation-function) +(define-obsolete-variable-alias 'sgml-transformation + 'sgml-transformation-function "29.1") (defcustom sgml-transformation-function 'identity "Default value for `skeleton-transformation-function' in SGML mode." @@ -418,11 +419,11 @@ These have to be run via `sgml-syntax-propertize'")) (defun sgml-syntax-propertize (start end &optional rules-function) "Syntactic keywords for `sgml-mode'." (setq sgml--syntax-propertize-ppss (cons start (syntax-ppss start))) - (cl-assert (>= (cadr sgml--syntax-propertize-ppss) 0)) - (sgml-syntax-propertize-inside end) - (funcall (or rules-function sgml--syntax-propertize) (point) end) - ;; Catch any '>' after the last quote. - (sgml--syntax-propertize-ppss end)) + (when (>= (cadr sgml--syntax-propertize-ppss) 0) + (sgml-syntax-propertize-inside end) + (funcall (or rules-function sgml--syntax-propertize) (point) end) + ;; Catch any '>' after the last quote. + (sgml--syntax-propertize-ppss end))) (defun sgml-syntax-propertize-inside (end) (let ((ppss (syntax-ppss))) @@ -440,7 +441,8 @@ These have to be run via `sgml-syntax-propertize'")) ;; internal (defvar sgml-face-tag-alist () - "Alist of face and tag name for facemenu.") + "Alist of face and tag name for facemenu. +The tag name can be a string or a list of strings.") (defvar sgml-tag-face-alist () "Tag names and face or list of faces to fontify with when invisible. @@ -478,8 +480,8 @@ The attribute alist is made up as ATTRIBUTERULE is a list of optionally t (no value when no input) followed by an optional alist of possible values." :type '(repeat (cons (string :tag "Tag Name") - (repeat :tag "Tag Rule" sexp)))) -(put 'sgml-tag-alist 'risky-local-variable t) + (repeat :tag "Tag Rule" sexp))) + :risky t) (defcustom sgml-tag-help '(("!" . "Empty declaration for comment") @@ -528,11 +530,13 @@ an optional alist of possible values." (comment-indent-new-line soft))) (defun sgml-mode-facemenu-add-face-function (face _end) - (let ((tag-face (cdr (assq face sgml-face-tag-alist)))) + "Add \"face\" tags with `facemenu-keymap' commands." + (let ((tag-face (ensure-list (cdr (assq face sgml-face-tag-alist))))) (cond (tag-face (setq tag-face (funcall skeleton-transformation-function tag-face)) - (setq facemenu-end-add-face (concat "</" tag-face ">")) - (concat "<" tag-face ">")) + (setq facemenu-end-add-face + (mapconcat (lambda (f) (concat "</" f ">")) (reverse tag-face) "")) + (mapconcat (lambda (f) (concat "<" f ">")) tag-face "")) ((and (consp face) (consp (car face)) (null (cdr face)) @@ -596,12 +600,11 @@ Do \\[describe-key] on the following bindings to discover what they do. (setq-local tildify-foreach-region-function (apply-partially 'tildify-foreach-ignore-environments - `((,(eval-when-compile - (concat - "<\\(" - (regexp-opt '("pre" "dfn" "code" "samp" "kbd" "var" - "PRE" "DFN" "CODE" "SAMP" "KBD" "VAR")) - "\\)\\>[^>]*>")) + `((,(concat + "<\\(" + (regexp-opt '("pre" "dfn" "code" "samp" "kbd" "var" + "PRE" "DFN" "CODE" "SAMP" "KBD" "VAR")) + "\\)\\>[^>]*>") . ("</" 1 ">")) ("<! *--" . "-- *>") ("<" . ">")))) @@ -620,6 +623,7 @@ Do \\[describe-key] on the following bindings to discover what they do. (setq-local comment-indent-function 'sgml-comment-indent) (setq-local comment-line-break-function 'sgml-comment-indent-new-line) (setq-local skeleton-further-elements '((completion-ignore-case t))) + (setq-local skeleton-end-newline nil) (setq-local skeleton-end-hook (lambda () (or (eolp) @@ -1868,6 +1872,7 @@ This takes effect when first loading the library.") (defvar html-face-tag-alist '((bold . "strong") (italic . "em") + (bold-italic . ("strong" "em")) (underline . "u") (mode-line . "rev")) "Value of `sgml-face-tag-alist' for HTML mode.") @@ -2403,6 +2408,7 @@ To work around that, do: (lambda () (char-before (match-end 0)))) (setq-local add-log-current-defun-function #'html-current-defun-name) (setq-local sentence-end-base "[.?!][]\"'”)}]*\\(<[^>]*>\\)*") + (add-hook 'completion-at-point-functions 'html-mode--complete-at-point nil t) (when (fboundp 'libxml-parse-html-region) (defvar css-class-list-function) @@ -2411,6 +2417,8 @@ To work around that, do: (setq-local css-id-list-function #'html-current-buffer-ids)) (setq imenu-create-index-function 'html-imenu-index) + (yank-media-handler 'text/html #'html-mode--html-yank-handler) + (yank-media-handler "image/.*" #'html-mode--image-yank-handler) (setq-local sgml-empty-tags ;; From HTML-4.01's loose.dtd, parsed with @@ -2426,6 +2434,60 @@ To work around that, do: ;; (setq imenu-sort-function nil) ; sorting the menu defeats the purpose ) +(defun html-mode--complete-at-point () + ;; Complete a tag like <colg etc. + (or + (when-let ((tag (save-excursion + (and (looking-back "<\\([^ \t\n]*\\)" + (line-beginning-position)) + (match-string 1))))) + (list (match-beginning 1) (point) + (mapcar #'car html-tag-alist))) + ;; Complete params like <colgroup ali etc. + (when-let ((tag (save-excursion (sgml-beginning-of-tag))) + (params (seq-filter #'consp (cdr (assoc tag html-tag-alist)))) + (param (save-excursion + (and (looking-back "[ \t\n]\\([^= \t\n]*\\)" + (line-beginning-position)) + (match-string 1))))) + (list (match-beginning 1) (point) + (mapcar #'car params))) + ;; Complete param values like <colgroup align=mi etc. + (when-let ((tag (save-excursion (sgml-beginning-of-tag))) + (params (seq-filter #'consp (cdr (assoc tag html-tag-alist)))) + (param (save-excursion + (and (looking-back + "[ \t\n]\\([^= \t\n]+\\)=\\([^= \t\n]*\\)" + (line-beginning-position)) + (match-string 1)))) + (values (cdr (assoc param params)))) + (list (match-beginning 2) (point) + (mapcar #'car values))))) + +(defun html-mode--html-yank-handler (_type html) + (save-restriction + (insert html) + (ignore-errors + (sgml-pretty-print (point-min) (point-max))))) + +(defun html-mode--image-yank-handler (type image) + (let ((file (read-file-name (format "Save %s image to: " type)))) + (when (file-directory-p file) + (user-error "%s is a directory")) + (when (and (file-exists-p file) + (not (yes-or-no-p (format "%s exists; overwrite?" file)))) + (user-error "%s exists")) + (with-temp-buffer + (set-buffer-multibyte nil) + (insert image) + (write-region (point-min) (point-max) file)) + (insert (format "<img src=%S>\n" (file-relative-name file))) + (insert-image + (create-image file (mailcap-mime-type-to-extension type) nil + :max-width 200 + :max-height 200) + " "))) + (defvar html-imenu-regexp "\\s-*<h\\([1-9]\\)[^\n<>]*>\\(<[^\n<>]*>\\)*\\s-*\\([^\n<>]*\\)" "A regular expression matching a head line to be added to the menu. diff --git a/lisp/textmodes/string-edit.el b/lisp/textmodes/string-edit.el new file mode 100644 index 00000000000..53850674ac0 --- /dev/null +++ b/lisp/textmodes/string-edit.el @@ -0,0 +1,136 @@ +;;; string-edit.el --- editing long strings -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; Maintainer: emacs-devel@gnu.org + +;; This file is part of GNU Emacs. + +;; GNU Emacs 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. + +;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defface string-edit-prompt + '((t (:inherit font-lock-comment-face))) + "Face used on `string-edit' help text." + :group 'text + :version "29.1") + +(defvar string-edit--success-callback) +(defvar string-edit--abort-callback) + +;;;###autoload +(cl-defun string-edit (prompt string success-callback + &key abort-callback) + "Switch to a new buffer to edit STRING. +When the user finishes editing (with \\<string-edit-mode-map>\\[string-edit-done]), SUCCESS-CALLBACK +is called with the resulting string. + +If the user aborts (with \\<string-edit-mode-map>\\[string-edit-abort]), ABORT-CALLBACK (if any) is +called with no parameters. + +PROMPT will be inserted at the start of the buffer, but won't be +included in the resulting string. If PROMPT is nil, no help text +will be inserted." + (with-current-buffer (generate-new-buffer "*edit string*") + (when prompt + (let ((inhibit-read-only t)) + (insert prompt) + (ensure-empty-lines 0) + (add-text-properties (point-min) (point) + (list 'intangible t + 'face 'string-edit-prompt + 'read-only t)) + (insert (propertize (make-separator-line) 'rear-nonsticky t)) + (add-text-properties (point-min) (point) + (list 'string-edit--prompt t)))) + (let ((start (point))) + (insert string) + (goto-char start)) + + ;; Use `fit-window-to-buffer' after the buffer is filled with text. + (pop-to-buffer (current-buffer) + '(display-buffer-below-selected + (window-height . (lambda (window) + (fit-window-to-buffer window nil 10))))) + + (set-buffer-modified-p nil) + (setq buffer-undo-list nil) + (string-edit-mode) + (setq-local string-edit--success-callback success-callback) + (when abort-callback + (setq-local string-edit--abort-callback abort-callback)) + (setq-local header-line-format + (substitute-command-keys + "Type \\<string-edit-mode-map>\\[string-edit-done] when you've finished editing or \\[string-edit-abort] to abort")) + (message "%s" (substitute-command-keys + "Type \\<string-edit-mode-map>\\[string-edit-done] when you've finished editing")))) + +;;;###autoload +(defun read-string-from-buffer (prompt string) + "Switch to a new buffer to edit STRING in a recursive edit. +The user finishes editing with \\<string-edit-mode-map>\\[string-edit-done], or aborts with \\<string-edit-mode-map>\\[string-edit-abort]). + +PROMPT will be inserted at the start of the buffer, but won't be +included in the resulting string. If nil, no prompt will be +inserted in the buffer." + (string-edit + prompt + string + (lambda (edited) + (setq string edited) + (exit-recursive-edit)) + :abort-callback (lambda () + (exit-recursive-edit) + (error "Aborted edit"))) + (recursive-edit) + string) + +(defvar-keymap string-edit-mode-map + "C-c C-c" #'string-edit-done + "C-c C-k" #'string-edit-abort) + +(define-derived-mode string-edit-mode text-mode "String" + "Mode for editing strings." + :interactive nil) + +(defun string-edit-done () + "Finish editing the string and call the callback function. +This will kill the current buffer." + (interactive) + (goto-char (point-min)) + ;; Skip past the help text. + (when-let ((match (text-property-search-forward + 'string-edit--prompt nil t))) + (goto-char (prop-match-beginning match))) + (let ((string (buffer-substring (point) (point-max))) + (callback string-edit--success-callback)) + (quit-window 'kill) + (funcall callback string))) + +(defun string-edit-abort () + "Abort editing the current string." + (interactive) + (let ((callback string-edit--abort-callback)) + (quit-window 'kill) + (when callback + (funcall callback)))) + +(provide 'string-edit) + +;;; string-edit.el ends here diff --git a/lisp/textmodes/table.el b/lisp/textmodes/table.el index 30a07cbefea..fc06c4c0da1 100644 --- a/lisp/textmodes/table.el +++ b/lisp/textmodes/table.el @@ -753,6 +753,18 @@ the cell contents dynamically." :type 'string :group 'table) +(defcustom table-latex-environment "tabular" + "Tabular-compatible environment to use when generating latex. +The value should be a string suitable for use as a LaTeX environment +that's compatible with the \"tabular\" protocol, such as \"tabular\" +and \"longtable\"." + :tag "Latex environment used to export tables" + :type '(choice + (const :tag "tabular" "tabular") + (const :tag "longtable" "longtable") + string) + :version "29.1") + (defcustom table-cals-thead-rows 1 "Number of top rows to become header rows in CALS table." :tag "CALS Header Rows" @@ -1195,6 +1207,21 @@ executing body forms.") (easy-menu-add-item (current-global-map) '("menu-bar" "tools") table-global-menu-map) +;;;###autoload +(define-minor-mode table-fixed-width-mode + "Cell width is fixed when this is non-nil. +Normally it should be nil for allowing automatic cell width expansion +that widens a cell when it is necessary. When non-nil, typing in a +cell does not automatically expand the cell width. A word that is too +long to fit in a cell is chopped into multiple lines. The chopped +location is indicated by `table-word-continuation-char'. This +variable's value can be toggled by \\[table-fixed-width-mode] at +run-time." + :tag "Fix Cell Width" + :group 'table + (table--finish-delayed-tasks) + (table--update-cell-face)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Macros @@ -1219,43 +1246,49 @@ original buffer's point is moved to the location that corresponds to the last cache point coordinate." (declare (debug (body)) (indent 0)) (let ((height-expansion (make-symbol "height-expansion-var-symbol")) - (width-expansion (make-symbol "width-expansion-var-symbol"))) - `(let (,height-expansion ,width-expansion) + (width-expansion (make-symbol "width-expansion-var-symbol")) + (fixed-width (make-symbol "fixed-width"))) + `(let ((,fixed-width table-fixed-width-mode) + ,height-expansion ,width-expansion) ;; make sure cache has valid data unless it is explicitly inhibited. (unless table-inhibit-update (table-recognize-cell)) (with-current-buffer (get-buffer-create table-cache-buffer-name) - ;; goto the cell coordinate based on `table-cell-cache-point-coordinate'. - (set-mark (table--goto-coordinate table-cell-cache-mark-coordinate)) - (table--goto-coordinate table-cell-cache-point-coordinate) - (table--untabify-line) - ;; always reset before executing body forms because auto-fill behavior is the default. - (setq table-inhibit-auto-fill-paragraph nil) - ;; do the body - ,@body - ;; fill paragraph unless the body does not want to by setting `table-inhibit-auto-fill-paragraph'. - (unless table-inhibit-auto-fill-paragraph - (if (and table-cell-info-justify - (not (eq table-cell-info-justify 'left))) - (table--fill-region (point-min) (point-max)) - (table--fill-region - (save-excursion (forward-paragraph -1) (point)) - (save-excursion (forward-paragraph 1) (point))))) - ;; keep the updated cell coordinate. - (setq table-cell-cache-point-coordinate (table--get-coordinate)) - ;; determine the cell width expansion. - (setq ,width-expansion (table--measure-max-width)) - (if (<= ,width-expansion table-cell-info-width) nil - (table--fill-region (point-min) (point-max) ,width-expansion) - ;; keep the updated cell coordinate. - (setq table-cell-cache-point-coordinate (table--get-coordinate))) - (setq ,width-expansion (- ,width-expansion table-cell-info-width)) - ;; determine the cell height expansion. - (if (looking-at "\\s *\\'") nil - (goto-char (point-min)) - (if (re-search-forward "\\(\\s *\\)\\'" nil t) - (goto-char (match-beginning 1)))) - (setq ,height-expansion (- (cdr (table--get-coordinate)) (1- table-cell-info-height)))) + (let ((table-fixed-width-mode ,fixed-width)) + ;; Go to the cell coordinate based on + ;; `table-cell-cache-point-coordinate'. + (set-mark (table--goto-coordinate table-cell-cache-mark-coordinate)) + (table--goto-coordinate table-cell-cache-point-coordinate) + (table--untabify-line) + ;; Always reset before executing body forms because + ;; auto-fill behavior is the default. + (setq table-inhibit-auto-fill-paragraph nil) + ;; Do the body + ,@body + ;; Fill paragraph unless the body does not want to by + ;; setting `table-inhibit-auto-fill-paragraph'. + (unless table-inhibit-auto-fill-paragraph + (if (and table-cell-info-justify + (not (eq table-cell-info-justify 'left))) + (table--fill-region (point-min) (point-max)) + (table--fill-region + (save-excursion (forward-paragraph -1) (point)) + (save-excursion (forward-paragraph 1) (point))))) + ;; Keep the updated cell coordinate. + (setq table-cell-cache-point-coordinate (table--get-coordinate)) + ;; Determine the cell width expansion. + (setq ,width-expansion (table--measure-max-width)) + (if (<= ,width-expansion table-cell-info-width) nil + (table--fill-region (point-min) (point-max) ,width-expansion) + ;; Keep the updated cell coordinate. + (setq table-cell-cache-point-coordinate (table--get-coordinate))) + (setq ,width-expansion (- ,width-expansion table-cell-info-width)) + ;; Determine the cell height expansion. + (if (looking-at "\\s *\\'") nil + (goto-char (point-min)) + (if (re-search-forward "\\(\\s *\\)\\'" nil t) + (goto-char (match-beginning 1)))) + (setq ,height-expansion (- (cdr (table--get-coordinate)) (1- table-cell-info-height))))) ;; now back to the table buffer. ;; expand the cell width in the table buffer if necessary. (if (> ,width-expansion 0) @@ -2823,21 +2856,6 @@ or `top', `middle', `bottom' or `none' for vertical." (table--justify-cell-contents justify)))))) ;;;###autoload -(define-minor-mode table-fixed-width-mode - "Cell width is fixed when this is non-nil. -Normally it should be nil for allowing automatic cell width expansion -that widens a cell when it is necessary. When non-nil, typing in a -cell does not automatically expand the cell width. A word that is too -long to fit in a cell is chopped into multiple lines. The chopped -location is indicated by `table-word-continuation-char'. This -variable's value can be toggled by \\[table-fixed-width-mode] at -run-time." - :tag "Fix Cell Width" - :group 'table - (table--finish-delayed-tasks) - (table--update-cell-face)) - -;;;###autoload (defun table-query-dimension (&optional where) "Return the dimension of the current cell and the current table. The result is a list (cw ch tw th c r cells) where cw is the cell @@ -3019,7 +3037,8 @@ CALS (DocBook DTD): ""))) ((eq language 'latex) (insert (format "%% This LaTeX table template is generated by emacs %s\n" emacs-version) - "\\begin{tabular}{|" (apply #'concat (make-list (length col-list) "l|")) "}\n" + "\\begin{" table-latex-environment "}{|" + (apply #'concat (make-list (length col-list) "l|")) "}\n" "\\hline\n")) ((eq language 'cals) (insert (format "<!-- This CALS table template is generated by emacs %s -->\n" emacs-version) @@ -3045,7 +3064,7 @@ CALS (DocBook DTD): ((eq language 'html) (insert "</table>\n")) ((eq language 'latex) - (insert "\\end{tabular}\n")) + (insert "\\end{" table-latex-environment "}\n")) ((eq language 'cals) (set-marker-insertion-type (table-get-source-info 'colspec-marker) t) ;; insert before (save-excursion diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el index 64e38ad6973..d34133f8564 100644 --- a/lisp/textmodes/tex-mode.el +++ b/lisp/textmodes/tex-mode.el @@ -248,9 +248,9 @@ Normally set to either `plain-tex-mode' or `latex-mode'." (defcustom tex-fontify-script t "If non-nil, fontify subscript and superscript strings." :type 'boolean + :safe #'booleanp :group 'tex :version "23.1") -(put 'tex-fontify-script 'safe-local-variable #'booleanp) (defcustom tex-font-script-display '(-0.2 0.2) "How much to lower and raise subscript and superscript content. @@ -505,7 +505,9 @@ An alternative value is \" . \", if you use a font with a narrow period." "documentstyle" "documentclass" "verbatiminput" "includegraphics" "includegraphics*") t)) - (verbish (regexp-opt '("url" "nolinkurl" "path") t)) + (verbish (regexp-opt '("url" "nolinkurl" "path" + "href" "ProvidesFile") + t)) ;; Miscellany. (slash "\\\\") (opt " *\\(\\[[^]]*\\] *\\)*") @@ -981,14 +983,13 @@ Inherits `shell-mode-map' with a few additions.") (when (and slash (not comment)) (setq mode (if (looking-at - (eval-when-compile - (concat - (regexp-opt '("documentstyle" "documentclass" - "begin" "subsection" "section" - "part" "chapter" "newcommand" - "renewcommand" "RequirePackage") - 'words) - "\\|NeedsTeXFormat{LaTeX"))) + (concat + (regexp-opt '("documentstyle" "documentclass" + "begin" "subsection" "section" + "part" "chapter" "newcommand" + "renewcommand" "RequirePackage") + 'words) + "\\|NeedsTeXFormat{LaTeX")) (if (and (looking-at "document\\(style\\|class\\)\\(\\[.*\\]\\)?{slides}") ;; SliTeX is almost never used any more nowadays. @@ -1176,12 +1177,7 @@ subshell is initiated, `tex-shell-hook' is run." (setq-local outline-regexp latex-outline-regexp) (setq-local outline-level #'latex-outline-level) (setq-local forward-sexp-function #'latex-forward-sexp) - (setq-local skeleton-end-hook nil) - (setq-local comment-region-function #'latex--comment-region) - (setq-local comment-style 'plain)) - -(defun latex--comment-region (beg end &optional arg) - (comment-region-default-1 beg end arg t)) + (setq-local skeleton-end-hook nil)) ;;;###autoload (define-derived-mode slitex-mode latex-mode "SliTeX" @@ -1245,11 +1241,10 @@ Entering SliTeX mode runs the hook `text-mode-hook', then the hook (apply-partially #'tildify-foreach-ignore-environments `(("\\\\\\\\" . "") ; do not remove this - (,(eval-when-compile - (concat "\\\\begin{\\(" - (regexp-opt '("verbatim" "math" "displaymath" - "equation" "eqnarray" "eqnarray*")) - "\\)}")) + (,(concat "\\\\begin{\\(" + (regexp-opt '("verbatim" "math" "displaymath" + "equation" "eqnarray" "eqnarray*")) + "\\)}") . ("\\\\end{" 1 "}")) ("\\\\verb\\*?\\(.\\)" . (1)) ("\\$\\$?" . (0)) @@ -2037,7 +2032,7 @@ In the tex shell buffer this command behaves like `comint-send-input'." (defun tex-display-shell () "Make the TeX shell buffer visible in a window." - (display-buffer (tex-shell-buf)) + (display-buffer (tex-shell-buf) display-comint-buffer-action) (tex-recenter-output-buffer nil)) (defun tex-shell-sentinel (proc _msg) @@ -2129,11 +2124,10 @@ If NOT-ALL is non-nil, save the `.dvi' file." (defvar tex-compile-history nil) (defvar tex-input-files-re - (eval-when-compile - (concat "\\." (regexp-opt '("tex" "texi" "texinfo" - "bbl" "ind" "sty" "cls") t) - ;; Include files with no dots (for directories). - "\\'\\|\\`[^.]+\\'"))) + (concat "\\." (regexp-opt '("tex" "texi" "texinfo" + "bbl" "ind" "sty" "cls") t) + ;; Include files with no dots (for directories). + "\\'\\|\\`[^.]+\\'")) (defcustom tex-use-reftex t "If non-nil, use RefTeX's list of files to determine what command to use." @@ -2441,7 +2435,7 @@ Only applies the FSPEC to the args part of FORMAT." (if cmds (tex-format-cmd (caar cmds) fspec)))))) (defun tex-cmd-doc-view (file) - (pop-to-buffer (find-file-noselect file))) + (pop-to-buffer (find-file-noselect file) display-comint-buffer-action)) (defun tex-compile (dir cmd) "Run a command CMD on current TeX buffer's file in DIR." @@ -2457,7 +2451,7 @@ Only applies the FSPEC to the args part of FORMAT." (default (tex-compile-default fspec))) (list default-directory (completing-read - (format "Command [%s]: " (tex-summarize-command default)) + (format-prompt "Command" (tex-summarize-command default)) (mapcar (lambda (x) (list (tex-format-cmd (eval (car x) t) fspec))) tex-compile-commands) @@ -2698,7 +2692,7 @@ line LINE of the window, or centered if LINE is nil." (window)) (if (null tex-shell) (message "No TeX output buffer") - (setq window (display-buffer tex-shell)) + (setq window (display-buffer tex-shell display-comint-buffer-action)) (with-selected-window window (bury-buffer tex-shell) (goto-char (point-max)) @@ -2987,13 +2981,7 @@ There might be text before point." (put-text-property (1- (match-beginning 1)) (match-beginning 1) 'syntax-table - (if (= (1+ (line-beginning-position)) (match-beginning 1)) - ;; The `%' is a single-char comment, which Emacs - ;; syntax-table can't deal with. We could turn it - ;; into a non-comment, or use `\n%' or `%^' as the comment. - ;; Instead, we include it in the ^^A comment. - (string-to-syntax "< b") - (string-to-syntax ">"))) + (string-to-syntax ">")) (let ((end (line-end-position))) (if (< end (point-max)) (put-text-property @@ -3016,8 +3004,9 @@ There might be text before point." (defconst doctex-syntax-propertize-rules (syntax-propertize-precompile-rules latex-syntax-propertize-rules - ;; For DocTeX comment-in-doc. - ("\\(\\^\\)\\^A" (1 (doctex-font-lock-^^A)))))) + ;; For DocTeX comment-in-doc (DocTeX ≥3 also allows ^^X). + ;; We make the comment start on the second char because of bug#35140. + ("\\^\\(\\^\\)[AX]" (1 (doctex-font-lock-^^A)))))) (defvar doctex-font-lock-keywords (append tex-font-lock-keywords @@ -3566,28 +3555,122 @@ There might be text before point." ("\\ordmasculine" . ?º) ("\\lambdabar" . ?ƛ) ("\\celsius" . ?℃) + ;; Text symbols formerly part of textcomp package: + ("\\textdollar" . ?$) + ("\\textborn" . ?*) + ("\\textless" . ?<) + ("\\textgreater" . ?>) + ("\\textbackslash" . ?\\) + ("\\textasciicircum" . ?^) + ("\\textunderscore" . ?_) + ("\\textbraceleft" . ?\{) + ("\\textbar" . ?|) + ("\\textbraceright" . ?\}) + ("\\textasciitilde" . ?~) + ("\\textexclamdown" . ?¡) + ("\\textcent" . ?¢) + ("\\textsterling" . ?£) + ("\\textcurrency" . ?¤) + ("\\textyen" . ?¥) + ("\\textbrokenbar" . ?¦) + ("\\textsection" . ?§) + ("\\textasciidieresis" . ?¨) + ("\\textcopyright" . ?©) + ("\\textordfeminine" . ?ª) + ("\\guillemetleft" . ?«) + ("\\guillemotleft" . ?«) + ("\\textlnot" . ?¬) + ("\\textregistered" . ?®) + ("\\textasciimacron" . ?¯) + ("\\textdegree" . ?°) + ("\\textpm" . ?±) + ("\\texttwosuperior" . ?²) + ("\\textthreesuperior" . ?³) + ("\\textasciiacute" . ?´) ("\\textmu" . ?µ) + ("\\textparagraph" . ?¶) + ("\\textpilcrow" . ?¶) + ("\\textperiodcentered" . ?·) + ("\\textonesuperior" . ?¹) + ("\\textordmasculine" . ?º) + ("\\guillemetright" . ?») + ("\\guillemotright" . ?») + ("\\textonequarter" . ?¼) + ("\\textonehalf" . ?½) + ("\\textthreequarters" . ?¾) + ("\\textquestiondown" . ?¿) + ("\\texttimes" . ?×) + ("\\textdiv" . ?÷) + ("\\textflorin" . ?ƒ) + ("\\textasciicaron" . ?ˇ) + ("\\textasciibreve" . ?˘) + ("\\textacutedbl" . ?˝) + ("\\textgravedbl" . 757) + ("\\texttildelow" . 759) + ("\\textbaht" . ?฿) + ("\\textendash" . ?–) + ("\\textemdash" . ?—) + ("\\textbardbl" . ?‖) + ("\\textquoteleft" . 8216) + ("\\textquoteright" . 8217) + ("\\quotesinglbase" . 8218) + ("\\textquotedblleft" . 8220) + ("\\textquotedblright" . 8221) + ("\\quotedblbase" . 8222) + ;; \textdagger and \textdied are replaced with DAGGER (#x2020) and + ;; not with LATIN CROSS (#x271d) + ("\\textdagger" . ?†) + ("\\textdied" . ?†) + ("\\textdaggerdbl" . ?‡) + ("\\textbullet" . ?•) + ("\\textellipsis" . ?…) + ("\\textperthousand" . ?‰) + ("\\textpertenthousand" . ?‱) + ("\\guilsinglleft" . ?‹) + ("\\guilsinglright" . ?›) + ("\\textreferencemark" . ?※) + ("\\textinterrobang" . ?‽) ("\\textfractionsolidus" . ?⁄) - ("\\textbigcircle" . ?⃝) - ("\\textmusicalnote" . ?♪) - ("\\textdied" . ?✝) + ("\\textlquill" . 8261) ; Literal ?⁅ breaks indentation + ("\\textrquill" . 8262) ; Literal ?⁆ breaks indentation + ("\\textdiscount" . ?⁒) ("\\textcolonmonetary" . ?₡) - ("\\textwon" . ?₩) + ("\\textlira" . ?₤) ("\\textnaira" . ?₦) + ("\\textwon" . ?₩) + ("\\textdong" . ?₫) + ("\\texteuro" . ?€) ("\\textpeso" . ?₱) - ("\\textlira" . ?₤) - ("\\textrecipe" . ?℞) - ("\\textinterrobang" . ?‽) - ("\\textpertenthousand" . ?‱) - ("\\textbaht" . ?฿) + ("\\textguarani" . ?₲) + ("\\textcelsius" . ?℃) ("\\textnumero" . ?№) - ("\\textdiscount" . ?⁒) + ("\\textcircledP" . ?℗) + ("\\textrecipe" . ?℞) + ("\\textservicemark" . ?℠) + ("\\texttrademark" . ?™) + ("\\textohm" . ?Ω) + ("\\textmho" . ?℧) ("\\textestimated" . ?℮) + ("\\textleftarrow" . ?←) + ("\\textuparrow" . ?↑) + ("\\textrightarrow" . ?→) + ("\\textdownarrow" . ?↓) + ("\\textminus" . ?−) + ("\\textsurd" . ?√) + ("\\textlangle" . 9001) ; Literal ?〈 breaks indentation + ("\\textrangle" . 9002) ; Literal ?〉 breaks indentation + ("\\textblank" . ?␢) + ("\\textvisiblespace" . ?␣) ("\\textopenbullet" . ?◦) - ("\\textlquill" . 8261) ; Literal ?⁅ breaks indentation. - ("\\textrquill" . 8262) ; Literal ?⁆ breaks indentation. - ("\\textcircledP" . ?℗) - ("\\textreferencemark" . ?※)) + ;; \textbigcircle is replaced with LARGE CIRCLE (#x25ef) and not + ;; with COMBINING ENCLOSING CIRCLE (#x20dd) + ("\\textbigcircle" . ?◯) + ("\\textmusicalnote" . ?♪) + ("\\textmarried" . ?⚭) + ("\\textdivorced" . ?⚮) + ("\\textlbrackdbl" . 10214) ; Literal ?⟦ breaks indentation + ("\\textrbrackdbl" . 10215) ; Literal ?⟧ breaks indentation + ("\\textinterrobangdown" . ?⸘)) "A `prettify-symbols-alist' usable for (La)TeX modes.") (defun tex--prettify-symbols-compose-p (_start end _match) diff --git a/lisp/textmodes/texinfo.el b/lisp/textmodes/texinfo.el index 81ac45eb6c4..5d6f5deae1b 100644 --- a/lisp/textmodes/texinfo.el +++ b/lisp/textmodes/texinfo.el @@ -4,7 +4,6 @@ ;; Foundation, Inc. ;; Author: Robert J. Chassell -;; Date: [See date below for texinfo-version] ;; Maintainer: emacs-devel@gnu.org ;; Keywords: maint, tex, docs @@ -32,6 +31,16 @@ ;;; Code: +(eval-when-compile (require 'cl-lib) + (require 'flymake) + (require 'rx)) +(declare-function flymake-diag-region "flymake" + (buffer line &optional col)) +(declare-function flymake-make-diagnostic "flymake" + ( locus beg end type text + &optional data overlay-properties)) +(declare-function flymake--log-1 "flymake" (level sublog msg &rest args)) + (eval-when-compile (require 'tex-mode)) (declare-function tex-buffer "tex-mode" ()) (declare-function tex-region "tex-mode" (beg end)) @@ -336,6 +345,69 @@ Subexpression 1 is what goes into the corresponding `@end' statement.") (if (re-search-backward "^@node[ \t]+\\([^,\n]+\\)" nil t) (match-string-no-properties 1)))) +;;; Flymake support +(defvar-local texinfo--flymake-proc nil) +(defun texinfo-flymake (report-fn &rest _) + "Texinfo checking for Flymake. + +REPORT-FN is the callback function." + (let ((executable (or (executable-find "makeinfo") + (executable-find "texi2any"))) + (source (current-buffer))) + + (unless executable + (error "Flymake for Texinfo requires `makeinfo' or `texi2any'")) + + (when (process-live-p texinfo--flymake-proc) + (kill-process texinfo--flymake-proc)) + + (save-restriction + (widen) + (setq texinfo--flymake-proc + (make-process + :name "texinfo-flymake" + :noquery t + :connection-type 'pipe + :buffer (generate-new-buffer " *texinfo-flymake*") + :command `(,executable "-o" ,null-device "-") + :sentinel + (lambda (proc _event) + (when (memq (process-status proc) '(exit signal)) + (unwind-protect + (if (eq (buffer-local-value 'texinfo--flymake-proc + source) + proc) + (with-current-buffer (process-buffer proc) + (goto-char (point-min)) + (cl-loop + while (search-forward-regexp + (rx line-start + "-:" + (group-n 1 (0+ digit)) ; Line + (optional ":" (group-n 2 (0+ digit))) ; col + ": " + (optional (group-n 3 "warning: ")) ; warn + (group-n 4 (0+ nonl)) ; Message + line-end) + nil t) + for msg = (match-string 4) + for (beg . end) = (flymake-diag-region + source + (string-to-number (match-string 1))) + for type = (if (match-string 3) + :warning + :error) + collect (flymake-make-diagnostic + source beg end type msg) + into diags + finally (funcall report-fn diags))) + (flymake-log :warning "Cancelling obsolete check %s" + proc)) + (kill-buffer (process-buffer proc))))))) + (process-send-region texinfo--flymake-proc (point-min) (point-max)) + (process-send-eof texinfo--flymake-proc)))) + + ;;; Texinfo mode ;;;###autoload @@ -411,13 +483,13 @@ value of `texinfo-mode-hook'." "\\)\\>")) (setq-local require-final-newline mode-require-final-newline) (setq-local indent-tabs-mode nil) - (setq-local paragraph-separate - (concat "@[a-zA-Z]*[ \n]\\|" - paragraph-separate)) (setq-local paragraph-start (concat "@[a-zA-Z]*[ \n]\\|" paragraph-start)) + (setq-local fill-paragraph-function 'texinfo--fill-paragraph) (setq-local sentence-end-base "\\(@\\(end\\)?dots{}\\|[.?!]\\)[]\"'”)}]*") (setq-local fill-column 70) + (setq-local beginning-of-defun-function #'texinfo--beginning-of-defun) + (setq-local end-of-defun-function #'texinfo--end-of-defun) (setq-local comment-start "@c ") (setq-local comment-start-skip "@c +\\|@comment +") (setq-local words-include-escapes t) @@ -455,8 +527,63 @@ value of `texinfo-mode-hook'." (let ((prevent-filling "^@\\(def\\|multitable\\)")) (if (null auto-fill-inhibit-regexp) prevent-filling - (concat auto-fill-inhibit-regexp "\\|" prevent-filling))))) - + (concat auto-fill-inhibit-regexp "\\|" prevent-filling)))) + + ;; Set up Flymake support. + (add-hook 'flymake-diagnostic-functions #'texinfo-flymake nil t)) + +(defvar texinfo-fillable-commands '("@noindent") + "A list of commands that can be filled.") + +(defun texinfo--fill-paragraph (justify) + "Function to fill a paragraph in `texinfo-mode'." + (let ((command-re "\\(@[a-zA-Z]+\\)[ \t\n]")) + (catch 'no-fill + (save-restriction + ;; First check whether we're on a command line that can be + ;; filled by itself. + (or + (save-excursion + (beginning-of-line) + (when (looking-at command-re) + (let ((command (match-string 1))) + (if (member command texinfo-fillable-commands) + (progn + (narrow-to-region (point) (progn (forward-line 1) (point))) + t) + (throw 'no-fill nil))))) + ;; We're not on such a line, so fill the region. + (save-excursion + (let ((regexp (concat command-re "\\|^[ \t]*$\\|\f"))) + (narrow-to-region + (if (re-search-backward regexp nil t) + (progn + (forward-line 1) + (point)) + (point-min)) + (if (re-search-forward regexp nil t) + (match-beginning 0) + (point-max))) + (goto-char (point-min))))) + ;; We've now narrowed to the region we want to fill. + (let ((fill-paragraph-function nil) + (adaptive-fill-mode nil)) + (fill-paragraph justify)))) + t)) + +(defun texinfo--beginning-of-defun (&optional arg) + "Go to the previous @node line." + (while (and (> arg 0) + (re-search-backward "^@node " nil t)) + (setq arg (1- arg)))) + +(defun texinfo--end-of-defun () + "Go to the start of the next @node line." + (when (looking-at-p "@node") + (forward-line)) + (if (re-search-forward "^@node " nil t) + (goto-char (match-beginning 0)) + (goto-char (point-max)))) ;;; Insert string commands diff --git a/lisp/textmodes/texnfo-upd.el b/lisp/textmodes/texnfo-upd.el index 5b468dc808b..e44aa06e3dd 100644 --- a/lisp/textmodes/texnfo-upd.el +++ b/lisp/textmodes/texnfo-upd.el @@ -1367,7 +1367,7 @@ left at the end of the node line." ;; There may be an @chapter or other such command between ;; the top node line and the next node line, as a title ;; for an `ifinfo' section. This @chapter command must - ;; must be skipped. So the procedure is to search for + ;; be skipped. So the procedure is to search for ;; the next `@node' line, and then copy its name. (if (re-search-forward "^@node" nil t) (progn diff --git a/lisp/textmodes/tildify.el b/lisp/textmodes/tildify.el index 9dcfb10d6df..2a7ad295ab7 100644 --- a/lisp/textmodes/tildify.el +++ b/lisp/textmodes/tildify.el @@ -494,9 +494,8 @@ variable will be set to the representation." (if (not (string-equal " " (or space tildify-space-string))) (when space (setq tildify-space-string space)) - (message (eval-when-compile - (concat "Hard space is a single space character, tildify-" - "mode won't have any effect, disabling."))) + (message (concat "Hard space is a single space character, tildify-" + "mode won't have any effect, disabling.")) (setq tildify-mode nil)))) (if tildify-mode (add-hook 'post-self-insert-hook #'tildify-space nil t) diff --git a/lisp/textmodes/word-wrap-mode.el b/lisp/textmodes/word-wrap-mode.el new file mode 100644 index 00000000000..c354fc773a7 --- /dev/null +++ b/lisp/textmodes/word-wrap-mode.el @@ -0,0 +1,80 @@ +;;; word-wrap-mode.el --- minor mode for `word-wrap' tweaks -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; Maintainer: emacs-devel@gnu.org + +;; This file is part of GNU Emacs. + +;; GNU Emacs 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. + +;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: + +;; The list below lists all characters that have a general-category of +;; Zs, but with the ones we don't want to add here commented out. +(defcustom word-wrap-whitespace-characters + '(;;?\N{SPACE} + ;;?\N{NO-BREAK SPACE} + ?\N{OGHAM SPACE MARK} + ?\N{EN QUAD} + ?\N{EM QUAD} + ?\N{EN SPACE} + ?\N{EM SPACE} + ?\N{THREE-PER-EM SPACE} + ?\N{FOUR-PER-EM SPACE} + ?\N{SIX-PER-EM SPACE} + ?\N{FIGURE SPACE} + ?\N{PUNCTUATION SPACE} + ?\N{THIN SPACE} + ?\N{HAIR SPACE} + ;;?\N{NARROW NO-BREAK SPACE} + ?\N{MEDIUM MATHEMATICAL SPACE} + ?\N{IDEOGRAPHIC SPACE} + ;; Not in the Zs category: + ?\N{ZERO WIDTH SPACE}) + "Characters that `word-wrap-whitespace-mode' should add to `word-wrap'." + :version "29.1" + :type '(repeat character) + :group 'display) + +(defvar word-wrap-mode--previous-state) + +;;;###autoload +(define-minor-mode word-wrap-whitespace-mode + "Allow `word-wrap' to fold on all breaking whitespace characters. +The characters to break on are defined by `word-wrap-whitespace-characters'." + :group 'display + (if word-wrap-whitespace-mode + (progn + (setq-local word-wrap-mode--previous-state + (cons (category-table) + (buffer-local-set-state + word-wrap-by-category t + word-wrap t))) + (set-category-table (copy-category-table)) + (dolist (char word-wrap-whitespace-characters) + (modify-category-entry char ?|))) + (set-category-table (car word-wrap-mode--previous-state)) + (buffer-local-restore-state (cdr word-wrap-mode--previous-state)))) + +;;;###autoload +(define-globalized-minor-mode global-word-wrap-whitespace-mode + word-wrap-whitespace-mode word-wrap-whitespace-mode + :group 'display) + +(provide 'word-wrap-mode) + +;;; word-wrap-mode.el ends here |