commit 6cfc312d7196d7c7c70e7030b344891ecea8c4f1 (HEAD, refs/remotes/origin/master) Author: Augusto Stoffel Date: Wed Sep 15 10:02:34 2021 +0200 Python shell: rearrange printing of newline before output * progmodes/python.el (python-shell-output-filter-in-progress) (python-shell-output-filter-buffer): Move defvars to avoid compiler warnings. (python-shell-eval-setup-code): Don't print a newline in __PYTHON_EL_eval. (python-shell-send-string): Insert newline before output when applicable (bug#50514). diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index d9fc5c5009..fae350dea2 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2189,6 +2189,9 @@ virtualenv." :type '(alist regexp) :group 'python) +(defvar python-shell-output-filter-in-progress nil) +(defvar python-shell-output-filter-buffer nil) + (defmacro python-shell--add-to-path-with-priority (pathvar paths) "Modify PATHVAR and ensure PATHS are added only once at beginning." `(dolist (path (reverse ,paths)) @@ -2821,7 +2824,6 @@ def __PYTHON_EL_eval(source, filename): from __builtin__ import compile, eval, globals else: from builtins import compile, eval, globals - sys.stdout.write('\\n') try: p, e = ast.parse(source, filename), None except SyntaxError: @@ -3162,6 +3164,11 @@ t when called interactively." (python-shell--encode-string string) (python-shell--encode-string (or (buffer-file-name) ""))))) + (unless python-shell-output-filter-in-progress + (with-current-buffer (process-buffer process) + (save-excursion + (goto-char (process-mark process)) + (insert-before-markers "\n")))) (if (or (null (process-tty-name process)) (<= (string-bytes code) (or (bound-and-true-p comint-max-line-length) @@ -3172,9 +3179,6 @@ t when called interactively." (file-name (or (buffer-file-name) temp-file-name))) (python-shell-send-file file-name process temp-file-name t))))) -(defvar python-shell-output-filter-in-progress nil) -(defvar python-shell-output-filter-buffer nil) - (defun python-shell-output-filter (string) "Filter used in `python-shell-send-string-no-output' to grab output. STRING is the output received to this point from the process. commit 3c2753a3b8e69e32524d1e1342be6ffc41aa1ec3 Author: Lars Ingebrigtsen Date: Wed Sep 15 09:58:41 2021 +0200 Make bookmark fringe marks evaporate * lisp/bookmark.el (bookmark--set-fringe-mark): Make the bookmark evaporate when a buffer is erased (like, for instance, when doing `revert-buffer' in a vc buffer). diff --git a/lisp/bookmark.el b/lisp/bookmark.el index 719fc98f76..0a079482ca 100644 --- a/lisp/bookmark.el +++ b/lisp/bookmark.el @@ -459,8 +459,9 @@ In other words, return all information but the name." (defun bookmark--set-fringe-mark () "Apply a colorized overlay to the bookmarked location. See user option `bookmark-set-fringe-mark'." - (let ((bm (make-overlay (point-at-bol) (point-at-bol)))) + (let ((bm (make-overlay (point-at-bol) (1+ (point-at-bol))))) (overlay-put bm 'category 'bookmark) + (overlay-put bm 'evaporate t) (overlay-put bm 'before-string (propertize "x" 'display commit fbef1ee018d9229be0c7d9af9eaa2774d430149c Author: Lars Ingebrigtsen Date: Tue Sep 14 14:58:37 2021 +0200 Mention `lisp-data-mode' in `emacs-lisp-mode' doc string * lisp/progmodes/elisp-mode.el (emacs-lisp-mode): Mention `lisp-data-mode'. diff --git a/etc/NEWS b/etc/NEWS index 5809716868..b92a5b6918 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2900,6 +2900,7 @@ This is a mode for searching a RFC 2229 dictionary server. the mouse in 'dictionary-tooltip-dictionary' (which must be customized first). +--- ** Lisp Data mode The new command 'lisp-data-mode' enables a major mode for buffers composed of Lisp symbolic expressions that do not form a computer diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index a4c8d8671d..f71718bed5 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -276,6 +276,9 @@ Commands: Delete converts tabs to spaces as it moves back. Blank lines separate paragraphs. Semicolons start comments. +When editing Lisp data (as opposed to code), `lisp-data-mode' can +be used instead. + \\{emacs-lisp-mode-map}" :group 'lisp (defvar project-vc-external-roots-function) commit ba28acf3b8fb81a95e9963fb986ec581969d237c Author: Juri Linkov Date: Wed Sep 15 10:20:42 2021 +0300 Adjust occur-context-menu and elisp-context-menu * lisp/progmodes/elisp-mode.el (elisp-context-menu): Add separator only when there is a symbol at mouse click. * lisp/replace.el (occur-context-menu): Insert items in the middle of the menu after mark-whole-buffer. diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 917a7ad9af..a4c8d8671d 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -154,9 +154,9 @@ All commands in `lisp-mode-shared-map' are inherited by this map.") :selected (bound-and-true-p eldoc-mode)])) (defun elisp-context-menu (menu click) - (define-key-after menu [elisp-separator] menu-bar-separator - 'mark-whole-buffer) (when (thing-at-mouse click 'symbol) + (define-key-after menu [elisp-separator] menu-bar-separator + 'mark-whole-buffer) (define-key-after menu [describe-symbol] '(menu-item "Describe Symbol" (lambda (click) (interactive "e") diff --git a/lisp/replace.el b/lisp/replace.el index 63b3e213ce..63b58c9b45 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -2385,13 +2385,16 @@ To be added to `context-menu-functions'." (let ((word (thing-at-mouse click 'word)) (sym (thing-at-mouse click 'symbol))) (when (or word sym) - (define-key-after menu [occur-separator] menu-bar-separator) - (when word - (define-key-after menu [occur-word-at-mouse] - '(menu-item "Occur Word" occur-word-at-mouse))) + (define-key-after menu [occur-separator] menu-bar-separator + 'mark-whole-buffer) (when sym (define-key-after menu [occur-symbol-at-mouse] - '(menu-item "Occur Symbol" occur-symbol-at-mouse))))) + '(menu-item "Occur Symbol" occur-symbol-at-mouse) + 'occur-separator)) + (when word + (define-key-after menu [occur-word-at-mouse] + '(menu-item "Occur Word" occur-word-at-mouse) + 'occur-separator)))) menu) commit 3eb80b78473b425cdbc251e48aec7cfd9afea2cc Author: Philip Kaludercic Date: Wed Sep 15 10:13:53 2021 +0300 Add occur-related context-menu operations (bug#50552) * replace.el (occur-word-at-mouse): Add new command. (occur-symbol-at-mouse): Add new command. (occur-context-menu): Add new function. diff --git a/lisp/replace.el b/lisp/replace.el index 69bdfe1331..63b3e213ce 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -2367,6 +2367,33 @@ See also `multi-occur'." ;; And the second element is the list of context after-lines. (if (> nlines 0) after-lines)))) +(defun occur-word-at-mouse (event) + "Display an occur buffer for the word at EVENT." + (interactive "e") + (let ((word (thing-at-mouse event 'word t))) + (occur (concat "\\<" (regexp-quote word) "\\>")))) + +(defun occur-symbol-at-mouse (event) + "Display an occur buffer for the symbol at EVENT." + (interactive "e") + (let ((symbol (thing-at-mouse event 'symbol t))) + (occur (concat "\\_<" (regexp-quote symbol) "\\_>")))) + +(defun occur-context-menu (menu click) + "Populate MENU with occur commands for CLICK. +To be added to `context-menu-functions'." + (let ((word (thing-at-mouse click 'word)) + (sym (thing-at-mouse click 'symbol))) + (when (or word sym) + (define-key-after menu [occur-separator] menu-bar-separator) + (when word + (define-key-after menu [occur-word-at-mouse] + '(menu-item "Occur Word" occur-word-at-mouse))) + (when sym + (define-key-after menu [occur-symbol-at-mouse] + '(menu-item "Occur Symbol" occur-symbol-at-mouse))))) + menu) + ;; It would be nice to use \\[...], but there is no reasonable way ;; to make that display both SPC and Y. commit 67ab890cde6c4c87ddf2913f4d476ea2e2f6b19e Author: Juri Linkov Date: Wed Sep 15 09:47:58 2021 +0300 * lisp/window.el (display-buffer-in-previous-window): Add symbolp (bug#50576) diff --git a/lisp/window.el b/lisp/window.el index 2960384e15..3b897eb0ba 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -8363,7 +8363,8 @@ indirectly called by the latter." (throw 'best t))))) ;; When ALIST has a `previous-window' entry, that entry may override ;; anything we found so far. - (when (and previous-window (boundp previous-window)) + (when (and previous-window (symbolp previous-window) + (boundp previous-window)) (setq previous-window (symbol-value previous-window))) (when (and (setq window previous-window) (window-live-p window) commit b172049717ee2dd55e389bfa96d33a1850fe41ef Author: Juri Linkov Date: Wed Sep 15 09:30:11 2021 +0300 * lisp/tab-bar.el (tab-bar-close-other-tabs): Add arg ‘tab-number’. (tab-bar-mouse-context-menu): Bind menu-item "Close other tabs" to tab-bar-close-other-tabs with arg ‘tab-number’. diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 89af306902..d6173d9ca4 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -316,7 +316,12 @@ that closes only when clicked on the close button." (define-key-after menu [close] `(menu-item "Close" (lambda () (interactive) (tab-bar-close-tab ,tab-number)) - :help "Close the tab")))) + :help "Close the tab")) + (define-key-after menu [close-other] + `(menu-item "Close other tabs" + (lambda () (interactive) + (tab-bar-close-other-tabs ,tab-number)) + :help "Close all other tabs")))) (popup-menu menu event))) @@ -1405,15 +1410,25 @@ for the last tab on a frame is determined by (funcall tab-bar-tabs-function))))) (tab-bar-close-tab (1+ (tab-bar--tab-index-by-name name)))) -(defun tab-bar-close-other-tabs () - "Close all tabs on the selected frame, except the selected one." +(defun tab-bar-close-other-tabs (&optional tab-number) + "Close all tabs on the selected frame, except TAB-NUMBER. +TAB-NUMBER counts from 1 and defaults to the current tab." (interactive) (let* ((tabs (funcall tab-bar-tabs-function)) - (current-tab (tab-bar--current-tab-find tabs)) + (current-index (tab-bar--current-tab-index tabs)) + (keep-index (if (integerp tab-number) + (1- (max 0 (min tab-number (length tabs)))) + current-index)) + (keep-tab (nth keep-index tabs)) (index 0)) - (when current-tab + + (when keep-tab + (unless (eq keep-index current-index) + (tab-bar-select-tab (1+ keep-index)) + (setq tabs (funcall tab-bar-tabs-function))) + (dolist (tab tabs) - (unless (or (eq tab current-tab) + (unless (or (eq tab keep-tab) (run-hook-with-args-until-success 'tab-bar-tab-prevent-close-functions tab ;; `last-tab-p' logically can't ever be true commit cc52f6d995ac73c66701f788852e9a8e82eea6f7 Author: Juri Linkov Date: Wed Sep 15 09:27:57 2021 +0300 * lisp/tab-bar.el: Rename args to consistent naming convention. Use the same naming scheme for function arguments. Use the term "index" when arguments count from 0, and the term "number" when arguments count from 1. * lisp/tab-bar.el (tab-bar-select-tab): Rename ‘arg’ to ‘tab-number’. (tab-bar-move-tab-to): Rename ‘from-index’ to ‘from-number’ and ‘to-index’ to ‘to-number’. (tab-bar-move-tab-to-frame): Rename ‘from-index’ to ‘from-number’ and ‘to-index’ to ‘to-number’. (tab-bar-new-tab-to): Rename ‘to-index’ to ‘tab-number’. (tab-bar-close-tab): Rename ‘arg’ to ‘tab-number’ and ‘to-index’ to ‘to-number’. (tab-bar-rename-tab): Rename ‘arg’ to ‘tab-number’. (tab-bar-change-tab-group): Rename ‘arg’ to ‘tab-number’. diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 72bce6e80d..89af306902 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -89,10 +89,10 @@ (defcustom tab-bar-select-tab-modifiers '() - "List of modifier keys for selecting tab-bar tabs by index numbers. + "List of modifier keys for selecting tab-bar tabs by their numbers. Possible modifier keys are `control', `meta', `shift', `hyper', `super' and -`alt'. Presiing one of the modifiers in the list and a digit selects -the tab whose index equals the digit. Negative numbers count from +`alt'. Pressing one of the modifiers in the list and a digit selects +the tab whose number equals the digit. Negative numbers count from the end of the tab bar. The digit 9 selects the last (rightmost) tab. For easier selection of tabs by their numbers, consider customizing `tab-bar-tab-hints', which will show tab numbers alongside the tab name." @@ -954,25 +954,25 @@ on the tab bar instead." tabs)))) -(defun tab-bar-select-tab (&optional arg) - "Switch to the tab by its absolute position ARG in the tab bar. +(defun tab-bar-select-tab (&optional tab-number) + "Switch to the tab by its absolute position TAB-NUMBER in the tab bar. When this command is bound to a numeric key (with a prefix or modifier key using `tab-bar-select-tab-modifiers'), calling it without an argument will translate its bound numeric key to the numeric argument. -ARG counts from 1. Negative ARG counts tabs from the end of the tab bar." +TAB-NUMBER counts from 1. Negative TAB-NUMBER counts tabs from the end of the tab bar." (interactive "P") - (unless (integerp arg) + (unless (integerp tab-number) (let ((key (event-basic-type last-command-event))) - (setq arg (if (and (characterp key) (>= key ?1) (<= key ?9)) - (- key ?0) - 0)))) + (setq tab-number (if (and (characterp key) (>= key ?1) (<= key ?9)) + (- key ?0) + 0)))) (let* ((tabs (funcall tab-bar-tabs-function)) (from-index (tab-bar--current-tab-index tabs)) - (to-index (cond ((< arg 0) (+ (length tabs) (1+ arg))) - ((zerop arg) (1+ from-index)) - (t arg))) - (to-index (1- (max 1 (min to-index (length tabs)))))) + (to-number (cond ((< tab-number 0) (+ (length tabs) (1+ tab-number))) + ((zerop tab-number) (1+ from-index)) + (t tab-number))) + (to-index (1- (max 1 (min to-number (length tabs)))))) (unless (eq from-index to-index) (let* ((from-tab (tab-bar--tab)) @@ -1085,20 +1085,20 @@ most recent, and so on." (defalias 'tab-bar-select-tab-by-name 'tab-bar-switch-to-tab) -(defun tab-bar-move-tab-to (to-index &optional from-index) - "Move tab from FROM-INDEX position to new position at TO-INDEX. -FROM-INDEX defaults to the current tab index. -FROM-INDEX and TO-INDEX count from 1. -Negative TO-INDEX counts tabs from the end of the tab bar. +(defun tab-bar-move-tab-to (to-number &optional from-number) + "Move tab from FROM-NUMBER position to new position at TO-NUMBER. +FROM-NUMBER defaults to the current tab number. +FROM-NUMBER and TO-NUMBER count from 1. +Negative TO-NUMBER counts tabs from the end of the tab bar. Argument addressing is absolute in contrast to `tab-bar-move-tab' where argument addressing is relative." (interactive "P") (let* ((tabs (funcall tab-bar-tabs-function)) - (from-index (or from-index (1+ (tab-bar--current-tab-index tabs)))) - (from-tab (nth (1- from-index) tabs)) - (to-index (if to-index (prefix-numeric-value to-index) 1)) - (to-index (if (< to-index 0) (+ (length tabs) (1+ to-index)) to-index)) - (to-index (max 0 (min (1- to-index) (1- (length tabs)))))) + (from-number (or from-number (1+ (tab-bar--current-tab-index tabs)))) + (from-tab (nth (1- from-number) tabs)) + (to-number (if to-number (prefix-numeric-value to-number) 1)) + (to-number (if (< to-number 0) (+ (length tabs) (1+ to-number)) to-number)) + (to-index (max 0 (min (1- to-number) (1- (length tabs)))))) (setq tabs (delq from-tab tabs)) (cl-pushnew from-tab (nthcdr to-index tabs)) (tab-bar-tabs-set tabs) @@ -1121,10 +1121,10 @@ Like `tab-bar-move-tab', but moves in the opposite direction." (interactive "p") (tab-bar-move-tab (- (or arg 1)))) -(defun tab-bar-move-tab-to-frame (arg &optional from-frame from-index to-frame to-index) - "Move tab from FROM-INDEX position to new position at TO-INDEX. -FROM-INDEX defaults to the current tab index. -FROM-INDEX and TO-INDEX count from 1. +(defun tab-bar-move-tab-to-frame (arg &optional from-frame from-number to-frame to-number) + "Move tab from FROM-NUMBER position to new position at TO-NUMBER. +FROM-NUMBER defaults to the current tab number. +FROM-NUMBER and TO-NUMBER count from 1. FROM-FRAME specifies the source frame and defaults to the selected frame. TO-FRAME specifies the target frame and defaults the next frame. Interactively, ARG selects the ARGth different frame to move to." @@ -1136,10 +1136,10 @@ Interactively, ARG selects the ARGth different frame to move to." (setq to-frame (next-frame to-frame)))) (unless (eq from-frame to-frame) (let* ((from-tabs (funcall tab-bar-tabs-function from-frame)) - (from-index (or from-index (1+ (tab-bar--current-tab-index from-tabs)))) - (from-tab (nth (1- from-index) from-tabs)) + (from-number (or from-number (1+ (tab-bar--current-tab-index from-tabs)))) + (from-tab (nth (1- from-number) from-tabs)) (to-tabs (funcall tab-bar-tabs-function to-frame)) - (to-index (max 0 (min (1- (or to-index 1)) (1- (length to-tabs)))))) + (to-index (max 0 (min (1- (or to-number 1)) (1- (length to-tabs)))))) (cl-pushnew (assq-delete-all 'wc (if (eq (car from-tab) 'current-tab) (tab-bar--tab from-frame) @@ -1148,7 +1148,7 @@ Interactively, ARG selects the ARGth different frame to move to." (with-selected-frame from-frame (let ((inhibit-message t) ; avoid message about deleted tab tab-bar-closed-tabs) - (tab-bar-close-tab from-index))) + (tab-bar-close-tab from-number))) (tab-bar-tabs-set to-tabs to-frame) (force-mode-line-update t)))) @@ -1177,11 +1177,11 @@ to the tab argument will be applied after all functions are called." :group 'tab-bar :version "27.1") -(defun tab-bar-new-tab-to (&optional to-index) - "Add a new tab at the absolute position TO-INDEX. -TO-INDEX counts from 1. If no TO-INDEX is specified, then add +(defun tab-bar-new-tab-to (&optional tab-number) + "Add a new tab at the absolute position TAB-NUMBER. +TAB-NUMBER counts from 1. If no TAB-NUMBER is specified, then add a new tab at the position specified by `tab-bar-new-tab-to'. -Negative TO-INDEX counts tabs from the end of the tab bar, +Negative TAB-NUMBER counts tabs from the end of the tab bar, and -1 means the new tab will become the last one. Argument addressing is absolute in contrast to `tab-bar-new-tab' where argument addressing is relative. @@ -1215,11 +1215,11 @@ After the tab is created, the hooks in (let* ((to-tab (tab-bar--current-tab-make (when (eq tab-bar-new-tab-group t) `((group . ,(alist-get 'group from-tab)))))) - (to-index (and to-index (prefix-numeric-value to-index))) - (to-index (or (if to-index - (if (< to-index 0) - (+ (length tabs) (1+ to-index)) - (1- to-index))) + (to-number (and tab-number (prefix-numeric-value tab-number))) + (to-index (or (if to-number + (if (< to-number 0) + (+ (length tabs) (1+ to-number)) + (1- to-number))) (pcase tab-bar-new-tab-to ('leftmost 0) ('rightmost (length tabs)) @@ -1320,15 +1320,15 @@ respectively." :group 'tab-bar :version "27.1") -(defun tab-bar-close-tab (&optional arg to-index) - "Close the tab specified by its absolute position ARG. -If no ARG is specified, then close the current tab and switch +(defun tab-bar-close-tab (&optional tab-number to-number) + "Close the tab specified by its absolute position TAB-NUMBER. +If no TAB-NUMBER is specified, then close the current tab and switch to the tab specified by `tab-bar-close-tab-select'. -ARG counts from 1. -Optional TO-INDEX could be specified to override the value of +TAB-NUMBER counts from 1. +Optional TO-NUMBER could be specified to override the value of `tab-bar-close-tab-select' programmatically with a position of an existing tab to select after closing the current tab. -TO-INDEX counts from 1. +TO-NUMBER counts from 1. The functions in `tab-bar-tab-prevent-close-functions' will be run to determine whether or not to close the tab. @@ -1339,7 +1339,7 @@ for the last tab on a frame is determined by (interactive "P") (let* ((tabs (funcall tab-bar-tabs-function)) (current-index (tab-bar--current-tab-index tabs)) - (close-index (if (integerp arg) (1- arg) current-index)) + (close-index (if (integerp tab-number) (1- tab-number) current-index)) (last-tab-p (= 1 (length tabs))) (prevent-close (run-hook-with-args-until-success 'tab-bar-tab-prevent-close-functions @@ -1367,7 +1367,7 @@ for the last tab on a frame is determined by ;; More than one tab still open (when (eq current-index close-index) ;; Select another tab before deleting the current tab - (let ((to-index (or (if to-index (1- to-index)) + (let ((to-index (or (if to-number (1- to-number)) (pcase tab-bar-close-tab-select ('left (1- (if (< current-index 1) 2 current-index))) ('right (if (> (length tabs) (1+ current-index)) @@ -1462,23 +1462,23 @@ for the last tab on a frame is determined by (message "No more closed tabs to undo"))) -(defun tab-bar-rename-tab (name &optional arg) - "Rename the tab specified by its absolute position ARG. -If no ARG is specified, then rename the current tab. -ARG counts from 1. +(defun tab-bar-rename-tab (name &optional tab-number) + "Rename the tab specified by its absolute position TAB-NUMBER. +If no TAB-NUMBER is specified, then rename the current tab. +TAB-NUMBER counts from 1. If NAME is the empty string, then use the automatic name function `tab-bar-tab-name-function'." (interactive (let* ((tabs (funcall tab-bar-tabs-function)) - (tab-index (or current-prefix-arg (1+ (tab-bar--current-tab-index tabs)))) - (tab-name (alist-get 'name (nth (1- tab-index) tabs)))) + (tab-number (or current-prefix-arg (1+ (tab-bar--current-tab-index tabs)))) + (tab-name (alist-get 'name (nth (1- tab-number) tabs)))) (list (read-from-minibuffer "New name for tab (leave blank for automatic naming): " nil nil nil nil tab-name) current-prefix-arg))) (let* ((tabs (funcall tab-bar-tabs-function)) - (tab-index (if arg - (1- (max 0 (min arg (length tabs)))) + (tab-index (if (integerp tab-number) + (1- (max 0 (min tab-number (length tabs)))) (tab-bar--current-tab-index tabs))) (tab-to-rename (nth tab-index tabs)) (tab-explicit-name (> (length name) 0)) @@ -1545,20 +1545,20 @@ The current tab is supplied as an argument." :group 'tab-bar :version "28.1") -(defun tab-bar-change-tab-group (group-name &optional arg) - "Add the tab specified by its absolute position ARG to GROUP-NAME. -If no ARG is specified, then set the GROUP-NAME for the current tab. -ARG counts from 1. +(defun tab-bar-change-tab-group (group-name &optional tab-number) + "Add the tab specified by its absolute position TAB-NUMBER to GROUP-NAME. +If no TAB-NUMBER is specified, then set the GROUP-NAME for the current tab. +TAB-NUMBER counts from 1. If GROUP-NAME is the empty string, then remove the tab from any group. While using this command, you might also want to replace `tab-bar-format-tabs' with `tab-bar-format-tabs-groups' in `tab-bar-format' to group tabs on the tab bar." (interactive (let* ((tabs (funcall tab-bar-tabs-function)) - (tab-index (or current-prefix-arg + (tab-number (or current-prefix-arg (1+ (tab-bar--current-tab-index tabs)))) (group-name (funcall tab-bar-tab-group-function - (nth (1- tab-index) tabs)))) + (nth (1- tab-number) tabs)))) (list (completing-read "Group name for tab (leave blank to remove group): " (delete-dups @@ -1568,8 +1568,8 @@ While using this command, you might also want to replace (funcall tab-bar-tabs-function)))))) current-prefix-arg))) (let* ((tabs (funcall tab-bar-tabs-function)) - (tab-index (if arg - (1- (max 0 (min arg (length tabs)))) + (tab-index (if tab-number + (1- (max 0 (min tab-number (length tabs)))) (tab-bar--current-tab-index tabs))) (tab (nth tab-index tabs)) (group (assq 'group tab)) commit b189b6f2564f903cf271a46910ad7a5df9da6918 Author: Stephen Gildea Date: Tue Sep 14 20:26:42 2021 -0700 * lisp/mh-e/mh-e.el: Simplify file commentary for a native package. diff --git a/lisp/mh-e/mh-e.el b/lisp/mh-e/mh-e.el index 375072b9d8..52fb11be6f 100644 --- a/lisp/mh-e/mh-e.el +++ b/lisp/mh-e/mh-e.el @@ -26,11 +26,8 @@ ;; MH-E is an Emacs interface to the MH mail system. -;; MH-E is supported in GNU Emacs 21 and higher, as well as XEmacs 21 -;; (except for versions 21.5.9-21.5.16). It is compatible with MH -;; versions 6.8.4 and higher, all versions of nmh, and GNU mailutils -;; 1.0 and higher. Gnus is also required; version 5.10 or higher is -;; recommended. +;; MH-E is compatible with MH versions 6.8.4 and higher, all versions +;; of nmh, and GNU mailutils 1.0 and higher. ;; MH (Message Handler) is a powerful mail reader. See ;; https://rand-mh.sourceforge.io/. @@ -49,12 +46,6 @@ ;; (global-set-key "\C-xm" 'mh-smail) ;; (global-set-key "\C-x4m" 'mh-smail-other-window) -;; If Emacs can't find mh-rmail or mh-smail, add the following to ~/.emacs: -;; (require 'mh-autoloads) - -;; If you want to customize MH-E before explicitly loading it, add this: -;; (require 'mh-cus-load) - ;; Mailing Lists: ;; mh-e-users@lists.sourceforge.net ;; mh-e-announce@lists.sourceforge.net commit c83f5e77dbc1614cbb6ae48ebe6961679854fd1b Author: Dmitry Gutov Date: Wed Sep 15 05:51:24 2021 +0300 ; Add a note diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index fef01eca9d..917a7ad9af 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -989,6 +989,8 @@ namespace but with lower confidence." ((setq generic (cl--generic symbol)) ;; FIXME: move this to elisp-xref-find-def-functions, in cl-generic.el + ;; XXX: How are we going to support using newer xref + ;; with older versions of Emacs, though? ;; A generic function. If there is a default method, it ;; will appear in the method table, with no commit 1f54c7aeeddba90458260fd0d9515db904f3b1ad Author: Dmitry Gutov Date: Wed Sep 15 05:33:06 2021 +0300 Localize namespace-filtering code To be able to filter results coming from elisp-xref-find-def-functions, and for general ease of understanding. * lisp/progmodes/elisp-mode.el (elisp--xref-find-definitions): Undo the previous change. (xref-backend-apropos): Update accordingly. (elisp--xref-filter-definitions): New function. (xref-backend-definitions): Use it to post-filter the results coming from elisp--xref-find-definitions. * test/lisp/progmodes/elisp-mode-tests.el (find-defs-minor-defvar-c): New test. (find-defs-defun-defvar-el): Update test. diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index e5ad36c30b..fef01eca9d 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -682,6 +682,7 @@ functions are annotated with \"\" via the ;;; Xref backend (declare-function xref-make "xref" (summary location)) +(declare-function xref-item-location "xref" (this)) (defun elisp--xref-backend () 'elisp) @@ -877,7 +878,6 @@ namespace but with lower confidence." ;; Use a property to transport the location of the identifier. (propertize ident 'pos (car bounds)))))) - (cl-defmethod xref-backend-definitions ((_backend (eql 'elisp)) identifier) (require 'find-func) (let ((sym (intern-soft identifier))) @@ -885,39 +885,53 @@ namespace but with lower confidence." (let* ((pos (get-text-property 0 'pos identifier)) (namespace (if pos (elisp--xref-infer-namespace pos) - 'any))) - (elisp--xref-find-definitions sym namespace))))) - -(defun elisp--xref-find-definitions (symbol &optional namespace) - "Return xrefs of definitions for SYMBOL in NAMESPACE. -NAMESPACE is one of: `function', `variable', `maybe-variable', `feature', -`face' or `any' (indicating any namespace). `maybe-variable' indicates a -variable namespace but will include definitions in other namespaces if -there are no matches for variables." - ;; FIXME: fix callers instead of having an optional argument - (unless namespace - (setq namespace 'any)) + 'any)) + (defs (elisp--xref-find-definitions sym))) + (if (eq namespace 'maybe-variable) + (or (elisp--xref-filter-definitions defs 'variable sym) + (elisp--xref-filter-definitions defs 'any sym)) + (elisp--xref-filter-definitions defs namespace sym)))))) + +(defun elisp--xref-filter-definitions (definitions namespace symbol) + (if (eq namespace 'any) + (if (memq symbol minor-mode-list) + ;; The symbol is a minor mode. These should be defined by + ;; "define-minor-mode", which means the variable and the + ;; function are declared in the same place. So we return only + ;; the function, arbitrarily. + ;; + ;; There is an exception, when the variable is defined in C + ;; code, as for abbrev-mode. + (cl-loop for d in definitions + for loc = (xref-item-location d) + for file = (xref-elisp-location-file loc) + when (or (not (eq (xref-elisp-location-type loc) 'defvar)) + (null file) + (string-prefix-p "src/" file)) + collect d) + definitions) + (let ((expected-types + (pcase-exhaustive namespace + ('function '( nil defalias define-type + cl-defgeneric cl-defmethod)) + ('variable '(defvar)) + ('face '(defface)) + ('feature '(feature))))) + (cl-loop for d in definitions + when (memq + (xref-elisp-location-type (xref-item-location d)) + expected-types) + collect d)))) + +(defun elisp--xref-find-definitions (symbol) ;; The file name is not known when `symbol' is defined via interactive eval. - (let ((maybe (eq namespace 'maybe-variable)) - (namespace (if (eq namespace 'maybe-variable) 'variable namespace)) - (xrefs nil) ; xrefs from NAMESPACE - (secondary-xrefs nil)) ; other xrefs - + (let (xrefs) (let ((temp elisp-xref-find-def-functions)) - ;; FIXME: The 'elisp-xref-find-def-functions' function interface does - ;; not allow for namespace filtering so we tacitly assume they all match. (while (and (null xrefs) temp) (setq xrefs (append xrefs (funcall (pop temp) symbol))))) (unless xrefs - (cl-flet ((add-xref (found-in-ns type symbol file &optional summary) - (let ((xref (elisp--xref-make-xref type symbol file summary))) - (push xref (if (or (eq namespace found-in-ns) - (eq namespace 'any)) - xrefs - secondary-xrefs))))) - ;; alphabetical by result type symbol ;; FIXME: advised function; list of advice functions @@ -926,161 +940,130 @@ there are no matches for variables." ;; Coding system symbols do not appear in ‘load-history’, ;; so we can’t get a location for them. - (when (and (symbolp symbol) - (symbol-function symbol) - (symbolp (symbol-function symbol))) - ;; aliased function - (let* ((alias-symbol symbol) - (alias-file (symbol-file alias-symbol)) - (real-symbol (symbol-function symbol)) - (real-file (find-lisp-object-file-name real-symbol 'defun))) - - (when real-file - (add-xref 'function nil real-symbol real-file)) - - (when alias-file - (add-xref 'function 'defalias alias-symbol alias-file)))) - - (when (facep symbol) - (let ((file (find-lisp-object-file-name symbol 'defface))) - (when file - (add-xref 'face 'defface symbol file)))) - - (when (fboundp symbol) - (let ((file (find-lisp-object-file-name symbol - (symbol-function symbol))) - generic doc) - (when file - (cond - ((eq file 'C-source) - ;; First call to find-lisp-object-file-name for an object - ;; defined in C; the doc strings from the C source have - ;; not been loaded yet. Second call will return "src/*.c" - ;; in file; handled by 't' case below. - (add-xref 'function nil symbol - (help-C-file-name (symbol-function symbol) 'subr))) - - ((and (setq doc (documentation symbol t)) - ;; This doc string is defined in cl-macs.el cl-defstruct - (string-match "Constructor for objects of type `\\(.*\\)'" - doc)) - ;; `symbol' is a name for the default constructor created by - ;; cl-defstruct, so return the location of the cl-defstruct. - (let* ((type-name (match-string 1 doc)) - (type-symbol (intern type-name)) - (file (find-lisp-object-file-name type-symbol - 'define-type)) - (summary (format elisp--xref-format-extra - 'cl-defstruct - (concat "(" type-name) - (concat "(:constructor " - (symbol-name symbol) - "))")))) - (add-xref 'function 'define-type type-symbol file summary))) - - ((setq generic (cl--generic symbol)) - ;; FIXME: move this to elisp-xref-find-def-functions, - ;; in cl-generic.el - - ;; A generic function. If there is a default method, it - ;; will appear in the method table, with no - ;; specializers. - ;; - ;; If the default method is declared by the cl-defgeneric - ;; declaration, it will have the same location as the - ;; cl-defgeneric, so we want to exclude it from the - ;; result. In this case, it will have a null doc - ;; string. User declarations of default methods may also - ;; have null doc strings, but we hope that is - ;; rare. Perhaps this heuristic will discourage that. - (dolist (method (cl--generic-method-table generic)) - (let* ((info (cl--generic-method-info method)) - ;; qual-string combined-args doconly - (specializers (cl--generic-method-specializers method)) - (non-default nil) - (met-name (cl--generic-load-hist-format - symbol - (cl--generic-method-qualifiers method) - specializers)) - (file (find-lisp-object-file-name met-name - 'cl-defmethod))) - (dolist (item specializers) - ;; default method has all 't' in specializers - (setq non-default (or non-default (not (equal t item))))) - - (when (and file - (or non-default - ;; assuming only co-located default has null - ;; doc string - (nth 2 info))) - (if specializers - (let ((summary (format elisp--xref-format-extra - 'cl-defmethod symbol - (nth 1 info)))) - (add-xref 'function - 'cl-defmethod met-name file summary)) - - (let ((summary (format elisp--xref-format-extra - 'cl-defmethod symbol "()"))) - (add-xref 'function - 'cl-defmethod met-name file summary)))))) - - (if (and (setq doc (documentation symbol t)) - ;; This doc string is created somewhere in - ;; cl--generic-make-function for an implicit - ;; defgeneric. - (string-match "\n\n(fn ARG &rest ARGS)" doc)) - ;; This symbol is an implicitly defined defgeneric, so - ;; don't return it. - nil - (add-xref 'function 'cl-defgeneric symbol file))) - - (t - (add-xref 'function nil symbol file)))))) - - (when (boundp symbol) - ;; A variable - (let ((file (find-lisp-object-file-name symbol 'defvar))) - (when file - (cond - ((eq file 'C-source) - ;; The doc strings from the C source have not been loaded - ;; yet; help-C-file-name does that. Second call will - ;; return "src/*.c" in file; handled below. - (add-xref 'variable - 'defvar symbol (help-C-file-name symbol 'var))) - - ((string= "src/" (substring file 0 4)) - ;; The variable is defined in a C source file; don't check - ;; for define-minor-mode. - (add-xref 'variable 'defvar symbol file)) - - ((memq symbol minor-mode-list) - ;; The symbol is a minor mode. These should be defined by - ;; "define-minor-mode", which means the variable and the - ;; function are declared in the same place. So we return only - ;; the function, arbitrarily, unless the search is in - ;; variable context, since it would be silly to have the - ;; user choose between both. - ;; - ;; There is an exception, when the variable is defined in C - ;; code, as for abbrev-mode. - (when (eq namespace 'variable) - (add-xref 'variable 'defvar symbol file))) - - (t - (add-xref 'variable 'defvar symbol file)))))) - - (when (featurep symbol) - (let ((file (ignore-errors - (find-library-name (symbol-name symbol))))) - (when file - (add-xref 'feature 'feature symbol file)))) - )) - - ;; If no xrefs consistent with the specified namespace were found - ;; and we weren't sure, use all other hits. - (or xrefs (and maybe secondary-xrefs)))) + (when (and (symbolp symbol) + (symbol-function symbol) + (symbolp (symbol-function symbol))) + ;; aliased function + (let* ((alias-symbol symbol) + (alias-file (symbol-file alias-symbol)) + (real-symbol (symbol-function symbol)) + (real-file (find-lisp-object-file-name real-symbol 'defun))) + + (when real-file + (push (elisp--xref-make-xref nil real-symbol real-file) xrefs)) + + (when alias-file + (push (elisp--xref-make-xref 'defalias alias-symbol alias-file) xrefs)))) + + (when (facep symbol) + (let ((file (find-lisp-object-file-name symbol 'defface))) + (when file + (push (elisp--xref-make-xref 'defface symbol file) xrefs)))) + + (when (fboundp symbol) + (let ((file (find-lisp-object-file-name symbol (symbol-function symbol))) + generic doc) + (when file + (cond + ((eq file 'C-source) + ;; First call to find-lisp-object-file-name for an object + ;; defined in C; the doc strings from the C source have + ;; not been loaded yet. Second call will return "src/*.c" + ;; in file; handled by 't' case below. + (push (elisp--xref-make-xref nil symbol (help-C-file-name (symbol-function symbol) 'subr)) xrefs)) + + ((and (setq doc (documentation symbol t)) + ;; This doc string is defined in cl-macs.el cl-defstruct + (string-match "Constructor for objects of type `\\(.*\\)'" doc)) + ;; `symbol' is a name for the default constructor created by + ;; cl-defstruct, so return the location of the cl-defstruct. + (let* ((type-name (match-string 1 doc)) + (type-symbol (intern type-name)) + (file (find-lisp-object-file-name type-symbol 'define-type)) + (summary (format elisp--xref-format-extra + 'cl-defstruct + (concat "(" type-name) + (concat "(:constructor " (symbol-name symbol) "))")))) + (push (elisp--xref-make-xref 'define-type type-symbol file summary) xrefs) + )) + + ((setq generic (cl--generic symbol)) + ;; FIXME: move this to elisp-xref-find-def-functions, in cl-generic.el + + ;; A generic function. If there is a default method, it + ;; will appear in the method table, with no + ;; specializers. + ;; + ;; If the default method is declared by the cl-defgeneric + ;; declaration, it will have the same location as the + ;; cl-defgeneric, so we want to exclude it from the + ;; result. In this case, it will have a null doc + ;; string. User declarations of default methods may also + ;; have null doc strings, but we hope that is + ;; rare. Perhaps this heuristic will discourage that. + (dolist (method (cl--generic-method-table generic)) + (let* ((info (cl--generic-method-info method));; qual-string combined-args doconly + (specializers (cl--generic-method-specializers method)) + (non-default nil) + (met-name (cl--generic-load-hist-format + symbol + (cl--generic-method-qualifiers method) + specializers)) + (file (find-lisp-object-file-name met-name 'cl-defmethod))) + (dolist (item specializers) + ;; default method has all 't' in specializers + (setq non-default (or non-default (not (equal t item))))) + + (when (and file + (or non-default + (nth 2 info))) ;; assuming only co-located default has null doc string + (if specializers + (let ((summary (format elisp--xref-format-extra 'cl-defmethod symbol (nth 1 info)))) + (push (elisp--xref-make-xref 'cl-defmethod met-name file summary) xrefs)) + + (let ((summary (format elisp--xref-format-extra 'cl-defmethod symbol "()"))) + (push (elisp--xref-make-xref 'cl-defmethod met-name file summary) xrefs)))) + )) + + (if (and (setq doc (documentation symbol t)) + ;; This doc string is created somewhere in + ;; cl--generic-make-function for an implicit + ;; defgeneric. + (string-match "\n\n(fn ARG &rest ARGS)" doc)) + ;; This symbol is an implicitly defined defgeneric, so + ;; don't return it. + nil + (push (elisp--xref-make-xref 'cl-defgeneric symbol file) xrefs)) + ) + + (t + (push (elisp--xref-make-xref nil symbol file) xrefs)) + )))) + + (when (boundp symbol) + ;; A variable + (let ((file (find-lisp-object-file-name symbol 'defvar))) + (when file + (cond + ((eq file 'C-source) + ;; The doc strings from the C source have not been loaded + ;; yet; help-C-file-name does that. Second call will + ;; return "src/*.c" in file; handled below. + (push (elisp--xref-make-xref 'defvar symbol (help-C-file-name symbol 'var)) xrefs)) + + (t + (push (elisp--xref-make-xref 'defvar symbol file) xrefs)) + + )))) + + (when (featurep symbol) + (let ((file (ignore-errors + (find-library-name (symbol-name symbol))))) + (when file + (push (elisp--xref-make-xref 'feature symbol file) xrefs)))) + );; 'unless xrefs' + + xrefs)) (declare-function xref-apropos-regexp "xref" (pattern)) @@ -1089,8 +1072,7 @@ there are no matches for variables." (let ((regexp (xref-apropos-regexp pattern)) lst) (dolist (sym (apropos-internal regexp)) - (push (elisp--xref-find-definitions sym 'any) - lst)) + (push (elisp--xref-find-definitions sym) lst)) (nreverse lst)))) (defvar elisp--xref-identifier-completion-table diff --git a/test/lisp/progmodes/elisp-mode-tests.el b/test/lisp/progmodes/elisp-mode-tests.el index f80aca0ad4..60946c2f44 100644 --- a/test/lisp/progmodes/elisp-mode-tests.el +++ b/test/lisp/progmodes/elisp-mode-tests.el @@ -752,15 +752,11 @@ to (xref-elisp-test-descr-to-target xref)." ;; Source for both variable and defun is "(define-minor-mode ;; compilation-minor-mode". There is no way to tell that directly from ;; the symbol, but we can use (memq sym minor-mode-list) to detect -;; that the symbol is a minor mode. See `elisp--xref-find-definitions' -;; for more comments. -;; -;; IMPROVEME: return defvar instead of defun if source near starting -;; point indicates the user is searching for a variable, not a -;; function. +;; that the symbol is a minor mode. In non-filtering mode we only +;; return the function. (require 'compile) ;; not loaded by default at test time (xref-elisp-deftest find-defs-defun-defvar-el - (elisp--xref-find-definitions 'compilation-minor-mode) + (xref-backend-definitions 'elisp "compilation-minor-mode") (list (cons (xref-make "(defun compilation-minor-mode)" @@ -770,6 +766,21 @@ to (xref-elisp-test-descr-to-target xref)." "(define-minor-mode compilation-minor-mode") )) +;; Returning only defvar because source near point indicates the user +;; is searching for a variable, not a function. +(xref-elisp-deftest find-defs-minor-defvar-c + (with-temp-buffer + (emacs-lisp-mode) + (insert "(foo overwrite-mode") + (xref-backend-definitions 'elisp + (xref-backend-identifier-at-point 'elisp))) + (list + (cons + (xref-make "(defvar overwrite-mode)" + (xref-make-elisp-location 'overwrite-mode 'defvar "src/buffer.c")) + "DEFVAR_PER_BUFFER (\"overwrite-mode\"") + )) + (xref-elisp-deftest find-defs-defvar-el (elisp--xref-find-definitions 'xref--marker-ring) (list commit e4fdf87e713d1161ebeca56c3c1d93bd971bd7f5 Author: Wilson Snyder Date: Tue Sep 14 21:21:03 2021 -0400 verilog-mode.el: Update verilog-mode from upstream. * lisp/progmodes/verilog-mode.el: (verilog-basic-complete-re) (verilog-behavioral-block-beg-re, verilog-defun-keywords) (verilog-defun-level-generate-only-re, verilog-defun-level-re) (verilog-endcomment-reason-re, verilog-indent-re) (verilog-keywords, verilog-no-indent-begin-re) (verilog-set-auto-endcomments): Support Verilog-A `analog` blocks (#1738). Reported by Dan McMahill. (verilog-read-defines): Fix verilog-read-defines to work with SystemVerilog types (#1734). Reported by Shareef Jalloq. (verilog-indent-declaration, verilog-pretty-declarations): Fix leaving extra spaces before tabs on lining up declarations. (#1723) Reported by TAKAI Kousuke. (verilog-auto-inst, verilog-auto-inst-port) (verilog-read-auto-template-middle, verilog-read-sub-decls-line): Support AUTONOHOOKUP to not AUTOWIRE hookup AUTO_TEMPLATE signals. (#1526) Reported by firefoxtc. diff --git a/lisp/progmodes/verilog-mode.el b/lisp/progmodes/verilog-mode.el index 7c8ccea065..5cc834f4a8 100644 --- a/lisp/progmodes/verilog-mode.el +++ b/lisp/progmodes/verilog-mode.el @@ -9,7 +9,7 @@ ;; Keywords: languages ;; The "Version" is the date followed by the decimal rendition of the Git ;; commit hex. -;; Version: 2021.04.12.188864585 +;; Version: 2021.09.01.191709444 ;; Yoni Rabkin contacted the maintainer of this ;; file on 19/3/2008, and the maintainer agreed that when a bug is @@ -124,7 +124,7 @@ ;; ;; This variable will always hold the version number of the mode -(defconst verilog-mode-version "2021-04-12-b41d849-vpo-GNU" +(defconst verilog-mode-version "2021-09-01-b6d4104-vpo-GNU" "Version of this Verilog mode.") (defconst verilog-mode-release-emacs t "If non-nil, this version of Verilog mode was released with Emacs itself.") @@ -829,7 +829,7 @@ The name of the function or case will be set between the braces." (defcustom verilog-auto-ignore-concat nil "Non-nil means ignore signals in {...} concatenations for AUTOWIRE etc. This will exclude signals referenced as pin connections in {...} -or (...) from AUTOWIRE, AUTOOUTPUT and friends." +or (...) from AUTOWIRE, AUTOOUTPUT and friends. See also AUTONOHOOKUP." :group 'verilog-mode-actions :type 'boolean) (put 'verilog-auto-ignore-concat 'safe-local-variable #'verilog-booleanp) @@ -2077,8 +2077,7 @@ find the errors." (if (boundp 'compilation-error-regexp-systems-alist) (if (and (not (equal compilation-error-regexp-systems-list 'all)) - ;; eval required due to bug1700, XEmacs otherwise errors on compile - (not (eval "(member compilation-error-regexp-systems-list 'verilog)"))) + (not (member 'verilog compilation-error-regexp-systems-list))) (push 'verilog compilation-error-regexp-systems-list))) (if (boundp 'compilation-error-regexp-alist-alist) (if (not (assoc 'verilog compilation-error-regexp-alist-alist)) @@ -2505,7 +2504,7 @@ find the errors." (defconst verilog-no-indent-begin-re (eval-when-compile (verilog-regexp-words - '("always" "always_comb" "always_ff" "always_latch" "initial" "final" ; procedural blocks + '("always" "always_comb" "always_ff" "always_latch" "analog" "initial" "final" ; procedural blocks "if" "else" ; conditional statements "while" "for" "foreach" "repeat" "do" "forever" )))) ; loop statements @@ -2651,6 +2650,7 @@ find the errors." "\\(\\\\s-+\\\\)\\|" ; 3 "\\(\\\\(?:[ \t]*@\\)\\)\\|" ; 4 (matches always or always_ff w/ @...) "\\(\\\\)\\|" ; 5 (matches always, always_comb, always_latch w/o @...) + "\\(\\\\)\\|" ; 6 "\\(\\\\)\\|" ; 7 "\\(\\\\)\\|" verilog-property-re "\\|" @@ -2853,7 +2853,7 @@ find the errors." (eval-when-compile (verilog-regexp-words '("Outputs" "Inouts" "Inputs" "Interfaces" "Interfaced")))) (defconst verilog-behavioral-block-beg-re - (eval-when-compile (verilog-regexp-words '("initial" "final" "always" "always_comb" "always_latch" "always_ff" + (eval-when-compile (verilog-regexp-words '("initial" "final" "always" "always_comb" "always_latch" "always_ff" "analog" "function" "task")))) (defconst verilog-coverpoint-re "\\w+\\s-*:\\s-*\\(coverpoint\\|cross\\|constraint\\)") (defconst verilog-in-constraint-re ; keywords legal in constraint blocks starting a statement/block @@ -2864,7 +2864,7 @@ find the errors." (verilog-regexp-words '( "{" - "always" "always_latch" "always_ff" "always_comb" + "always" "always_latch" "always_ff" "always_comb" "analog" "begin" "end" ;; "unique" "priority" "case" "casex" "casez" "randcase" "endcase" @@ -2956,13 +2956,13 @@ find the errors." '( "connectmodule" "module" "macromodule" "primitive" "class" "program" "interface" "package" "config") '( "initial" "final" "always" "always_comb" "always_ff" - "always_latch" "endtask" "endfunction" ))))) + "always_latch" "analog" "endtask" "endfunction" ))))) (defconst verilog-defun-level-generate-only-re (eval-when-compile (verilog-regexp-words '( "initial" "final" "always" "always_comb" "always_ff" - "always_latch" "endtask" "endfunction" )))) + "always_latch" "analog" "endtask" "endfunction" )))) (defconst verilog-cpp-level-re (eval-when-compile @@ -2989,7 +2989,7 @@ find the errors." (eval-when-compile (verilog-regexp-words '( - "always" "assign" "always_latch" "always_ff" "always_comb" "connectmodule" "constraint" + "always" "assign" "always_latch" "always_ff" "always_comb" "analog" "connectmodule" "constraint" "import" "initial" "final" "module" "macromodule" "repeat" "randcase" "while" "if" "for" "forever" "foreach" "else" "parameter" "do" "localparam" "assert" )))) @@ -3066,7 +3066,7 @@ find the errors." (defconst verilog-keywords (append verilog-compiler-directives '( - "after" "alias" "always" "always_comb" "always_ff" "always_latch" "and" + "after" "alias" "always" "always_comb" "always_ff" "always_latch" "analog" "and" "assert" "assign" "assume" "automatic" "before" "begin" "bind" "bins" "binsof" "bit" "break" "buf" "bufif0" "bufif1" "byte" "case" "casex" "casez" "cell" "chandle" "class" "clocking" "cmos" @@ -3283,7 +3283,7 @@ See also `verilog-font-lock-extra-types'.") "use" "wait" "while" ;; 1800-2005 "alias" "always_comb" "always_ff" "always_latch" "assert" - "assume" "before" "bind" "bins" "binsof" "break" "class" + "assume" "analog" "before" "bind" "bins" "binsof" "break" "class" "clocking" "constraint" "context" "continue" "cover" "covergroup" "coverpoint" "cross" "dist" "do" "endclass" "endclocking" "endgroup" "endinterface" "endpackage" @@ -5104,7 +5104,7 @@ primitive or interface named NAME." (throw 'skip 1))))))))) (; always, always_comb, always_latch w/o @... - (match-end 5) + (or (match-end 5) (match-end 6)) (goto-char (match-end 0)) (setq there (point)) (setq err nil) @@ -7157,11 +7157,11 @@ Be verbose about progress unless optional QUIET set." (forward-char -1) (just-one-space) (goto-char (marker-position m1)) - (just-one-space) - (indent-to ind)) + (delete-horizontal-space) + (indent-to ind 1)) (progn - (just-one-space) - (indent-to ind))))) + (delete-horizontal-space) + (indent-to ind 1))))) ((verilog-continued-line-1 (marker-position startpos)) (goto-char e) (indent-line-to ind)) @@ -7324,12 +7324,10 @@ BASEIND is the base indent to offset everything." (forward-char -1) (just-one-space) (goto-char (marker-position m1)) - (just-one-space) - (indent-to ind)) - (if (/= (current-column) ind) - (progn - (just-one-space) - (indent-to ind))))) + (delete-horizontal-space) + (indent-to ind 1)) + (delete-horizontal-space) + (indent-to ind 1))) (if (looking-at verilog-declaration-re-2-no-macro) (let ((p (match-end 0))) (set-marker m1 p) @@ -7338,12 +7336,10 @@ BASEIND is the base indent to offset everything." (forward-char -1) (just-one-space) (goto-char (marker-position m1)) - (just-one-space) - (indent-to ind)) - (if (/= (current-column) ind) - (progn - (just-one-space) - (indent-to ind)))))))))) + (delete-horizontal-space) + (indent-to ind 1)) + (delete-horizontal-space) + (indent-to ind 1)))))))) (goto-char pos))) (defun verilog-get-lineup-indent (b edpos) @@ -7457,7 +7453,7 @@ will be completed at runtime and should not be added to this list.") (defvar verilog-defun-keywords (append '( - "always" "always_comb" "always_ff" "always_latch" "assign" + "always" "always_comb" "always_ff" "always_latch" "analog" "assign" "begin" "end" "connectmodule" "endconnectmodule" "generate" "endgenerate" "module" "endmodule" "specify" "endspecify" "function" "endfunction" "initial" "final" "task" "endtask" "primitive" "endprimitive" @@ -9106,9 +9102,7 @@ Inserts the list of signals found, using submodi to look up each port." ;; We intentionally ignore (non-escaped) signals with .s in them ;; this prevents AUTOWIRE etc from noticing hierarchical sigs. (when port - (cond ((and verilog-auto-ignore-concat - (looking-at "[({]")) - nil) ; {...} or (...) historically ignored with auto-ignore-concat + (cond ((looking-at "[^\n]*AUTONOHOOKUP")) ((looking-at "\\([a-zA-Z_][a-zA-Z_0-9]*\\)\\s-*)") (verilog-read-sub-decls-sig submoddecls par-values comment port @@ -9555,7 +9549,10 @@ Returns REGEXP and list of ( (signal_name connection_name)... )." (cons (list (match-string-no-properties 1) (match-string-no-properties 2) - templateno lineno) + templateno lineno + (save-excursion + (goto-char (match-end 0)) + (looking-at "[^\n]*AUTONOHOOKUP"))) tpl-sig-list)) (goto-char (match-end 0))) ;; Regexp form?? @@ -9571,7 +9568,10 @@ Returns REGEXP and list of ( (signal_name connection_name)... )." (match-string 1)) "$") rep - templateno lineno) + templateno lineno + (save-excursion + (goto-char (match-end 0)) + (looking-at "[^\n]*AUTONOHOOKUP"))) tpl-wild-list))) ((looking-at "[ \t\f]+") (goto-char (match-end 0))) @@ -9745,6 +9745,9 @@ warning message, you need to add to your init file: (while (re-search-forward "^\\s-*\\(parameter\\|localparam\\)\\(\\s-*\\[[^]]*\\]\\)?\\s-*" nil t) (let (enumname) + ;; Advance over parameter's type if present + (if (looking-at "\\([a-zA-Z0-9_]+\\s-+\\)[a-zA-Z0-9_]+") + (goto-char (match-end 1))) ;; The primary way of getting defines is verilog-read-decls ;; However, that isn't called yet for included files, so we'll add another scheme (if (looking-at "[^\n]*\\(auto\\|synopsys\\)\\s +enum\\s +\\([a-zA-Z0-9_]+\\)") @@ -11040,7 +11043,7 @@ Intended for internal use inside a 'verilog-delete-auto-star-all) ;; Remove template comments ... anywhere in case was pasted after AUTOINST removed (goto-char (point-min)) - (while (re-search-forward "\\s-*// \\(Templated\\|Implicit \\.\\*\\)\\([ \tLT0-9]*\\| LHS: .*\\)$" nil t) + (while (re-search-forward "\\s-*// \\(Templated\\(\\s-*AUTONOHOOKUP\\)?\\|Implicit \\.\\*\\)\\([ \tLT0-9]*\\| LHS: .*\\)$" nil t) (replace-match "")) ;; Final customize @@ -11689,15 +11692,14 @@ If PAR-VALUES replace final strings with these parameter values." ;; verilog-insert requires the complete comment in one call - including the newline (cond ((equal verilog-auto-inst-template-numbers 'lhs) (verilog-insert " // Templated" - " LHS: " (nth 0 tpl-ass) - "\n")) + " LHS: " (nth 0 tpl-ass))) (verilog-auto-inst-template-numbers (verilog-insert " // Templated" " T" (int-to-string (nth 2 tpl-ass)) - " L" (int-to-string (nth 3 tpl-ass)) - "\n")) + " L" (int-to-string (nth 3 tpl-ass)))) (t - (verilog-insert " // Templated\n")))) + (verilog-insert " // Templated"))) + (verilog-insert (if (nth 4 tpl-ass) " AUTONOHOOKUP\n" "\n"))) (for-star (indent-to (+ (if (< verilog-auto-inst-column 48) 24 16) verilog-auto-inst-column)) @@ -12087,6 +12089,16 @@ Lisp Templates: After the evaluation is completed, @ substitution and [] substitution occur. + +Ignoring Hookup: + + AUTOWIRE and related AUTOs will read the signals created by a template. + To specify that a signal should not be parsed to participate in this + hookup, add a AUTONOHOOKUP comment to the template. For example: + + .pci_req_l (pci_req_not_to_wire), //AUTONOHOOKUP + + For more information see the \\[verilog-faq] and forums at URL `https://www.veripool.org'." (save-excursion commit 42b49e9f39a10f3b9a42a2a96750bbe10613e918 Author: Glenn Morris Date: Tue Sep 14 16:27:42 2021 -0700 * test/Makefile.in (XDG_CONFIG_HOME): Don't export (bug#50577). diff --git a/test/Makefile.in b/test/Makefile.in index 7047c24482..e0a2b7799a 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -75,7 +75,7 @@ EMACS_EXTRAOPT= EMACSOPT = --no-init-file --no-site-file --no-site-lisp -L "$(SEPCHAR)$(srcdir)" $(elpa_opts) $(EMACS_EXTRAOPT) # Prevent any settings in the user environment causing problems. -unexport EMACSDATA EMACSDOC EMACSPATH GREP_OPTIONS +unexport EMACSDATA EMACSDOC EMACSPATH GREP_OPTIONS XDG_CONFIG_HOME ## To run tests under a debugger, set this to eg: "gdb --args". GDB = commit 4c492c8c5f4722ba35d206071b1f6486c56f8213 Author: Glenn Morris Date: Tue Sep 14 14:27:41 2021 -0700 * doc/misc/flymake.texi: Avoid xrefs in @copying. This isn't really what @copying is for, and doesn't work with makeinfo 4.13. diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index e204e9a835..189b3d6528 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -13,22 +13,6 @@ @copying This manual is for GNU Flymake (version @value{VERSION}, @value{UPDATED}). -Flymake is a universal on-the-fly syntax checker for Emacs. When -enabled, Flymake contacts one or more source @dfn{backends} to -collect information about problems in the buffer, called -@dfn{diagnostics}, and visually annotates them with a special face. -The mode line displays overall status including totals for different -types of diagnostics. - -To learn about using Flymake, @pxref{Using Flymake}. - -Flymake is designed to be easily extended to support new backends via -an Elisp interface. @xref{Extending Flymake} - -Historically, Flymake used to accept diagnostics from a single -backend. Although obsolete, it is still functional. To learn how to -use and customize it, @pxref{The legacy Proc backend}. - Copyright @copyright{} 2004--2021 Free Software Foundation, Inc. @quotation @@ -64,6 +48,25 @@ modify this GNU manual.'' @ifnottex @node Top @top GNU Flymake +@end ifnottex + +Flymake is a universal on-the-fly syntax checker for Emacs. When +enabled, Flymake contacts one or more source @dfn{backends} to +collect information about problems in the buffer, called +@dfn{diagnostics}, and visually annotates them with a special face. +The mode line displays overall status including totals for different +types of diagnostics. + +To learn about using Flymake, @pxref{Using Flymake}. + +Flymake is designed to be easily extended to support new backends via +an Elisp interface. @xref{Extending Flymake}. + +Historically, Flymake used to accept diagnostics from a single +backend. Although obsolete, it is still functional. To learn how to +use and customize it, @pxref{The legacy Proc backend}. + +@ifnottex @insertcopying @end ifnottex @@ -611,7 +614,7 @@ of the problem detected in this region. Most commonly @var{locus} is the buffer object designating for the current buffer being syntax-checked. However, it may be a string nameing a file relative to the current working directory. @xref{Foreign and list-only -diagnostics} for when this may be useful. Depending on the type of +diagnostics}, for when this may be useful. Depending on the type of @var{locus}, @var{beg} and @var{end} are both either buffer positions or conses (@var{line} . @var{col}) which specify the line and column of the diagnostic's start and end positions, respectively. commit 55dda2571a829fcff7e04d83818150d7c0002f7a Author: João Távora Date: Tue Sep 14 19:22:28 2021 +0100 Unbreak make bootstrap (don't use cl-defun's &aux parameters) * lisp/progmodes/flymake.el (flymake--handle-report): Don't use &aux. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index e2981eb9bd..fb612eebc7 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -803,12 +803,7 @@ collections of diagnostics outside the buffer where this (cl-defun flymake--handle-report (backend token report-action &key explanation force region - &allow-other-keys - &aux - (state (or (gethash backend flymake--state) - (error "Can't find state for %s in `flymake--state'" - backend))) - expected-token) + &allow-other-keys) "Handle reports from BACKEND identified by TOKEN. BACKEND, REPORT-ACTION and EXPLANATION, and FORCE conform to the calling convention described in @@ -816,41 +811,45 @@ calling convention described in to handle a report even if TOKEN was not expected. REGION is a (BEG . END) pair of buffer positions indicating that this report applies to that region." - (cond - ((null state) - (flymake-error - "Unexpected report from unknown backend %s" backend)) - ((flymake--state-disabled state) - (flymake-error - "Unexpected report from disabled backend %s" backend)) - ((progn - (setq expected-token (flymake--state-running state)) - (null expected-token)) - ;; should never happen - (flymake-error "Unexpected report from stopped backend %s" backend)) - ((not (or (eq expected-token token) - force)) - (flymake-error "Obsolete report from backend %s with explanation %s" - backend explanation)) - ((eq :panic report-action) - (flymake--disable-backend backend explanation)) - ((not (listp report-action)) - (flymake--disable-backend backend - (format "Unknown action %S" report-action)) - (flymake-error "Expected report, but got unknown key %s" report-action)) - (t - (flymake--publish-diagnostics report-action - :backend backend - :state state - :region region) - (when flymake-check-start-time - (flymake-log :debug "backend %s reported %d diagnostics in %.2f second(s)" - backend - (length report-action) - (float-time - (time-since flymake-check-start-time)))))) - (setf (flymake--state-reported-p state) t) - (flymake--update-diagnostics-listings (current-buffer))) + (let ((state (or (gethash backend flymake--state) + (error "Can't find state for %s in `flymake--state'" + backend))) + expected-token) + (cond + ((null state) + (flymake-error + "Unexpected report from unknown backend %s" backend)) + ((flymake--state-disabled state) + (flymake-error + "Unexpected report from disabled backend %s" backend)) + ((progn + (setq expected-token (flymake--state-running state)) + (null expected-token)) + ;; should never happen + (flymake-error "Unexpected report from stopped backend %s" backend)) + ((not (or (eq expected-token token) + force)) + (flymake-error "Obsolete report from backend %s with explanation %s" + backend explanation)) + ((eq :panic report-action) + (flymake--disable-backend backend explanation)) + ((not (listp report-action)) + (flymake--disable-backend backend + (format "Unknown action %S" report-action)) + (flymake-error "Expected report, but got unknown key %s" report-action)) + (t + (flymake--publish-diagnostics report-action + :backend backend + :state state + :region region) + (when flymake-check-start-time + (flymake-log :debug "backend %s reported %d diagnostics in %.2f second(s)" + backend + (length report-action) + (float-time + (time-since flymake-check-start-time)))))) + (setf (flymake--state-reported-p state) t) + (flymake--update-diagnostics-listings (current-buffer)))) (defun flymake--clear-foreign-diags (state) (maphash (lambda (_buffer diags) commit 3d49ad73e5a93625629c96b6c0b921bb019ea9da Author: Harald Jörg Date: Tue Sep 14 17:53:52 2021 +0200 cperl-mode.el: Allow non-ASCII Perl identifiers Replace all "A-Z" regexp literals with unicode-aware rx constructs wherever Perl allows non-ASCII identifiers. * lisp/progmodes/cperl-mode.el (cperl-after-sub-regexp) (cperl-after-label. cperl-sniff-for-indent) (cperl-find-pods-heres, cperl-indent-exp) (cperl-fix-line-spacing, cperl-imenu--create-perl-index) (cperl-init-faces, cperl-find-tags): Replace ASCII regex literals by unicode-aware rx constructs. (cperl-init-faces): Eliminate unused lexical `font-lock-anchored'. (cperl-have-help-regexp, cperl-word-at-point-hard): Allow non-ASCII word characters. * test/lisp/progmodes/cperl-mode-tests.el (cperl-test-fontify-special-variables): New test for $^T and $^{VARNAME}. (cperl-test-ws-rx cperl-test-ws+-rx), (cperl-test-version-regexp, cperl-test-package-regexp): Skip for perl-mode. (cperl-test-identifier-rx, cperl--test-unicode-setup) (cperl-test-unicode-labels, cperl-test-unicode-sub) (cperl-test-unicode-varname) (cperl-test-unicode-varname-list, cperl-test-unicode-arrays) (cperl-test-unicode-hashes, cperl-test-unicode-hashref) (cperl-test-unicode-proto, cperl-test-unicode-fhs) (cperl-test-unicode-hashkeys, cperl-test-word-at-point): New tests for unicode identifiers. (cperl-test-imenu-index): Add a unicode identifier to the test. * test/lisp/progmodes/cperl-mode-resources/grammar.pl: Add a function with non-ASCII name for imenu tests. diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el index 76c82f8c73..1147889969 100644 --- a/lisp/progmodes/cperl-mode.el +++ b/lisp/progmodes/cperl-mode.el @@ -1407,7 +1407,7 @@ the last)." (concat ; Assume n groups before this... "\\(" ; n+1=name-group cperl-white-and-comment-rex ; n+2=pre-name - "\\(::[a-zA-Z_0-9:']+\\|[a-zA-Z_'][a-zA-Z_0-9:']*\\)" ; n+3=name + (rx-to-string `(group ,cperl--normal-identifier-rx)) "\\)" ; END n+1=name-group (if named "" "?") "\\(" ; n+4=proto-group @@ -2573,7 +2573,8 @@ Return the amount the indentation changed by." '(?w ?_)) (progn (backward-sexp) - (looking-at "[a-zA-Z_][a-zA-Z0-9_]*:[^:]")))) + (looking-at (rx (sequence (eval cperl--label-rx) + (not (in ":")))))))) (defun cperl-get-state (&optional parse-start start-state) "Return list (START STATE DEPTH PRESTART), @@ -2740,7 +2741,9 @@ Will not look before LIM." (progn (forward-sexp -1) (skip-chars-backward " \t") - (looking-at "[ \t]*[a-zA-Z_][a-zA-Z_0-9]*[ \t]*:"))) + (looking-at + (rx (sequence (0+ blank) + (eval cperl--label-rx)))))) (get-text-property (point) 'first-format-line))) ;; Look at previous line that's at column 0 @@ -3836,7 +3839,8 @@ recursive calls in starting lines of here-documents." "\\<" cperl-sub-regexp "\\>" ; sub with proto/attr "\\(" cperl-white-and-comment-rex - "\\(::[a-zA-Z_:'0-9]*\\|[a-zA-Z_'][a-zA-Z_:'0-9]*\\)\\)?" ; name + (rx (group (eval cperl--normal-identifier-rx))) + "\\)" "\\(" cperl-maybe-white-and-comment-rex "\\(([^()]*)\\|:[^:]\\)\\)" ; prototype or attribute start @@ -4111,10 +4115,12 @@ recursive calls in starting lines of here-documents." (t t)))) ;; or <$file> (and (eq c ?\<) - ;; Do not stringify , <$fh> : + ;; Stringify what looks like a glob, but + ;; do not stringify file handles , <$fh> : (save-match-data (looking-at - "\\$?\\([_a-zA-Z:][_a-zA-Z0-9:]*\\)?>")))) + (rx (sequence (opt "$") + (eval cperl--normal-identifier-rx))))))) tb (match-beginning 0)) (goto-char (match-beginning b1)) (cperl-backward-to-noncomment (point-min)) @@ -4184,7 +4190,16 @@ recursive calls in starting lines of here-documents." (error nil))) (if (or bb (looking-at ; $foo -> {s} - "[$@]\\$*\\([a-zA-Z0-9_:]+\\|[^{]\\)\\([ \t\n]*->\\)?[ \t\n]*{") + (rx + (sequence + (in "$@") (0+ "$") + (or + (eval cperl--normal-identifier-rx) + (not (in "{"))) + (opt (sequence (eval cperl--ws*-rx)) + "->") + (eval cperl--ws*-rx) + "{"))) (and ; $foo[12] -> {s} (memq (following-char) '(?\{ ?\[)) (progn @@ -4199,7 +4214,12 @@ recursive calls in starting lines of here-documents." (setq bb t)) ((and (eq (following-char) ?:) (eq b1 ?\{) ; Check for $ { s::bar } - (looking-at "::[a-zA-Z0-9_:]*[ \t\n\f]*}") + ;; (looking-at "::[a-zA-Z0-9_:]*[ \t\n\f]*}") + (looking-at + (rx (sequence "::" + (eval cperl--normal-identifier-rx) + (eval cperl--ws*-rx) + "}"))) (progn (goto-char (1- go)) (skip-chars-backward " \t\n\f") @@ -4364,7 +4384,7 @@ recursive calls in starting lines of here-documents." "\\(" ;; XXXX 1-char variables, exc. |()\s "[$@]" "\\(" - "[_a-zA-Z:][_a-zA-Z0-9:]*" + (rx (eval cperl--normal-identifier-rx)) "\\|" "{[^{}]*}" ; only one-level allowed "\\|" @@ -4820,6 +4840,7 @@ recursive calls in starting lines of here-documents." (progn (backward-sexp) ;; sub {BLK}, print {BLK} $data, but NOT `bless', `return', `tr', `constant' + ;; a-zA-Z is fine here, these are Perl keywords (or (and (looking-at "[a-zA-Z0-9_:]+[ \t\n\f]*[{#]") ; Method call syntax (not (looking-at "\\(bless\\|return\\|q[wqrx]?\\|tr\\|[smy]\\|constant\\)\\>"))) ;; sub bless::foo {} @@ -5028,7 +5049,11 @@ conditional/loop constructs." cperl-maybe-white-and-comment-rex "\\(state\\|my\\|local\\|our\\)\\)?" cperl-maybe-white-and-comment-rex - "\\$[_a-zA-Z0-9]+\\)?\\)\\>")) + (rx + (sequence + "$" + (eval cperl--basic-identifier-rx))) + "\\)?\\)\\>")) (progn (goto-char top) (forward-sexp 1) @@ -5122,7 +5147,14 @@ Returns some position at the last line." ;; Looking at: ;; foreach my $var ( (if (looking-at - "[ \t]*\\\\([ \t]*(\\|[ \t\n]*{\\)\\|[ \t]*{") + (rx (sequence + (0+ blank) + (opt (sequence "}" (0+ blank) )) + symbol-start + (or "else" "elsif" "continue" "if" "unless" "while" "until" + (sequence (or "for" "foreach") + (opt + (opt (sequence (1+ blank) + (or "state" "my" "local" "our"))) + (0+ blank) + "$" (eval cperl--basic-identifier-rx)))) + symbol-end + (group-n 1 + (or + (or (sequence (0+ blank) "(") + (sequence (eval cperl--ws*-rx) "{")) + (sequence (0+ blank) "{")))))) (progn - (setq ml (match-beginning 8)) ; "(" or "{" after control word + (setq ml (match-beginning 1)) ; "(" or "{" after control word (re-search-forward "[({]") (forward-char -1) (setq p (point)) @@ -5544,7 +5592,11 @@ comment, or POD." (setq lst index-sub-alist) (while lst (setq elt (car lst) lst (cdr lst)) - (cond ((string-match "\\(::\\|'\\)[_a-zA-Z0-9]+$" (car elt)) + (cond ((string-match + (rx (sequence (or "::" "'") + (eval cperl--basic-identifier-rx) + string-end)) + (car elt)) (setq pack (substring (car elt) 0 (match-beginning 0))) (if (setq group (assoc pack hier-list)) (if (listp (cdr group)) @@ -5646,8 +5698,7 @@ default function." (defun cperl-init-faces () (condition-case errs (progn - (let (t-font-lock-keywords t-font-lock-keywords-1 font-lock-anchored) - (setq font-lock-anchored t) + (let (t-font-lock-keywords t-font-lock-keywords-1) (setq t-font-lock-keywords (list @@ -5760,20 +5811,41 @@ default function." (if (eq (char-after (cperl-1- (match-end 0))) ?\{ ) 'font-lock-function-name-face 'font-lock-variable-name-face)))) - '("\\<\\(package\\|require\\|use\\|import\\|no\\|bootstrap\\)[ \t]+\\([a-zA-Z_][a-zA-Z_0-9:]*\\)[ \t;]" ; require A if B; - 2 font-lock-function-name-face) + `(,(rx (sequence symbol-start + (or "package" "require" "use" "import" + "no" "bootstrap") + (eval cperl--ws+-rx) + (group-n 1 (eval cperl--normal-identifier-rx)) + (any " \t;"))) ; require A if B; + 1 font-lock-function-name-face) '("^[ \t]*format[ \t]+\\([a-zA-Z_][a-zA-Z_0-9:]*\\)[ \t]*=[ \t]*$" 1 font-lock-function-name-face) - (cond (font-lock-anchored - '("\\([]}\\%@>*&]\\|\\$[a-zA-Z0-9_:]*\\)[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}" - (2 font-lock-string-face t) - ("\\=[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}" - nil nil - (1 font-lock-string-face t)))) - (t '("\\([]}\\%@>*&]\\|\\$[a-zA-Z0-9_:]*\\)[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}" - 2 font-lock-string-face t))) - '("[[ \t{,(]\\(-?[a-zA-Z0-9_:]+\\)[ \t]*=>" 1 - font-lock-string-face t) + ;; bareword hash key: $foo{bar} + `(,(rx (or (in "]}\\%@>*&") ; What Perl is this? + (sequence "$" (eval cperl--normal-identifier-rx))) + (0+ blank) "{" (0+ blank) + (group-n 1 (sequence (opt "-") + (eval cperl--basic-identifier-rx))) + (0+ blank) "}") +;; '("\\([]}\\%@>*&]\\|\\$[a-zA-Z0-9_:]*\\)[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}" + (1 font-lock-string-face t) + ;; anchored bareword hash key: $foo{bar}{baz} + (,(rx point + (0+ blank) "{" (0+ blank) + (group-n 1 (sequence (opt "-") + (eval cperl--basic-identifier-rx))) + (0+ blank) "}") + ;; ("\\=[ \t]*{[ \t]*\\(-?[a-zA-Z0-9_:]+\\)[ \t]*}" + nil nil + (1 font-lock-string-face t))) + ;; hash element assignments with bareword key => value + `(,(rx (in "[ \t{,()") + (group-n 1 (sequence (opt "-") + (eval cperl--basic-identifier-rx))) + (0+ blank) "=>") + 1 font-lock-string-face t) +;; '("[[ \t{,(]\\(-?[a-zA-Z0-9_:]+\\)[ \t]*=>" 1 +;; font-lock-string-face t) ;; labels `(,(rx (sequence @@ -5797,83 +5869,130 @@ default function." ;;; '("[$*]{?\\(\\sw+\\)" 1 font-lock-variable-name-face) ;;; '("\\([@%]\\|\\$#\\)\\(\\sw+\\)" ;;; (2 (cons font-lock-variable-name-face '(underline)))) - (cond (font-lock-anchored ;; 1=my_etc, 2=white? 3=(+white? 4=white? 5=var - `(,(concat "\\<\\(state\\|my\\|local\\|our\\)" - cperl-maybe-white-and-comment-rex - "\\((" - cperl-maybe-white-and-comment-rex - "\\)?\\([$@%*]\\([a-zA-Z0-9_:]+\\|[^a-zA-Z0-9_]\\)\\)") - (5 ,(if cperl-font-lock-multiline - 'font-lock-variable-name-face - '(progn (setq cperl-font-lock-multiline-start - (match-beginning 0)) - 'font-lock-variable-name-face))) - (,(concat "\\=" - cperl-maybe-white-and-comment-rex - "," - cperl-maybe-white-and-comment-rex - "\\([$@%*]\\([a-zA-Z0-9_:]+\\|[^a-zA-Z0-9_]\\)\\)") - ;; Bug in font-lock: limit is used not only to limit - ;; searches, but to set the "extend window for - ;; facification" property. Thus we need to minimize. - ,(if cperl-font-lock-multiline - '(if (match-beginning 3) - (save-excursion - (goto-char (match-beginning 3)) - (condition-case nil - (forward-sexp 1) - (error - (condition-case nil - (forward-char 200) - (error nil)))) ; typeahead - (1- (point))) ; report limit - (forward-char -2)) ; disable continued expr - '(if (match-beginning 3) - (point-max) ; No limit for continuation - (forward-char -2))) ; disable continued expr - ,(if cperl-font-lock-multiline - nil - '(progn ; Do at end - ;; "my" may be already fontified (POD), - ;; so cperl-font-lock-multiline-start is nil - (if (or (not cperl-font-lock-multiline-start) - (> 2 (count-lines - cperl-font-lock-multiline-start - (point)))) - nil - (put-text-property - (1+ cperl-font-lock-multiline-start) (point) - 'syntax-type 'multiline)) - (setq cperl-font-lock-multiline-start nil))) - (3 font-lock-variable-name-face)))) - (t '("^[ \t{}]*\\(state\\|my\\|local\\|our\\)[ \t]*\\(([ \t]*\\)?\\([$@%*][a-zA-Z0-9_:]+\\)" - 3 font-lock-variable-name-face))) - '("\\ 2 (count-lines + cperl-font-lock-multiline-start + (point)))) + nil + (put-text-property + (1+ cperl-font-lock-multiline-start) (point) + 'syntax-type 'multiline)) + (setq cperl-font-lock-multiline-start nil))) + (1 font-lock-variable-name-face))) + ;; foreach my $foo ( + `(,(rx symbol-start "for" (opt "each") + (opt (sequence (1+ blank) + (or "state" "my" "local" "our"))) + (0+ blank) + (group-n 1 (sequence "$" + (eval cperl--basic-identifier-rx))) + (0+ blank) "(") +;; '("\\") (setq search @@ -6472,6 +6593,9 @@ Will not move the position at the start to the left." "Run etags with appropriate options for Perl files. If optional argument ALL is `recursive', will process Perl files in subdirectories too." + ;; Apparently etags doesn't support UTF-8 encoded sources, and usage + ;; of etags has been commented out in the menu since ... well, + ;; forever. So, let's just stick to ASCII here. -- haj, 2021-09-14 (interactive) (let ((cmd "etags") (args `("-l" "none" "-r" @@ -6611,6 +6735,9 @@ Does not move point." ;; Search for the function (progn ;;save-match-data (while (re-search-forward + ;; FIXME: Should XS code be unicode aware? Recent C + ;; compilers (Gcc 10+) are, but I guess this isn't used + ;; much. -- haj, 2021-09-14 "^\\([ \t]*MODULE\\>[^\n]*\\\\|\\([a-zA-Z_][a-zA-Z_0-9]*\\)(\\|[ \t]*BOOT:\\)" nil t) (cond @@ -6673,7 +6800,7 @@ Does not move point." (setq lst (mapcar (lambda (elt) - (cond ((string-match "^[_a-zA-Z]" (car elt)) + (cond ((string-match (rx line-start (or alpha "_")) (car elt)) (goto-char (cdr elt)) (beginning-of-line) ; pos should be of the start of the line (list (car elt) @@ -6703,9 +6830,14 @@ Does not move point." "," (number-to-string (1- (elt elt 1))) ; Char pos 0-based "\n") - (if (and (string-match "^[_a-zA-Z]+::" (car elt)) - (string-match (concat "^" cperl-sub-regexp "[ \t]+\\([_a-zA-Z]+\\)[^:_a-zA-Z]") - (elt elt 3))) + (if (and (string-match (rx line-start + (eval cperl--basic-identifier-rx) "++") + (car elt)) + (string-match (rx-to-string `(sequence line-start + (regexp ,cperl-sub-regexp) + (1+ (in " \t")) + ,cperl--normal-identifier-rx)) + (elt elt 3))) ;; Need to insert the name without package as well (setq lst (cons (cons (substring (elt elt 3) (match-beginning 1) @@ -7155,14 +7287,14 @@ Currently it is tuned to C and Perl syntax." ;;(concat "\\(" (mapconcat #'identity - '("[$@%*&][0-9a-zA-Z_:]+\\([ \t]*[[{]\\)?" ; Usual variable + '("[$@%*&][[:alnum:]_:]+\\([ \t]*[[{]\\)?" ; Usual variable "[$@]\\^[a-zA-Z]" ; Special variable "[$@][^ \n\t]" ; Special variable "-[a-zA-Z]" ; File test "\\\\[a-zA-Z0]" ; Special chars "^=[a-z][a-zA-Z0-9_]*" ; POD sections "[-!&*+,./<=>?\\^|~]+" ; Operator - "[a-zA-Z_0-9:]+" ; symbol or number + "[[:alnum:]_:]+" ; symbol or number "x=" "#!") ;;"\\)\\|\\(" @@ -7178,7 +7310,7 @@ Currently it is tuned to C and Perl syntax." ;; Does not save-excursion ;; Get to the something meaningful (or (eobp) (eolp) (forward-char 1)) - (re-search-backward "[-a-zA-Z0-9_:!&*+,./<=>?\\^|~$%@]" + (re-search-backward "[-[:alnum:]_:!&*+,./<=>?\\^|~$%@]" (point-at-bol) 'to-beg) ;; (cond @@ -7187,8 +7319,8 @@ Currently it is tuned to C and Perl syntax." ;; (or (bobp) (backward-char 1)))) ;; Try to backtrace (cond - ((looking-at "[a-zA-Z0-9_:]") ; symbol - (skip-chars-backward "a-zA-Z0-9_:") + ((looking-at "[[:alnum:]_:]") ; symbol + (skip-chars-backward "[:alnum:]_:") (cond ((and (eq (preceding-char) ?^) ; $^I (eq (char-after (- (point) 2)) ?\$)) @@ -7199,7 +7331,7 @@ Currently it is tuned to C and Perl syntax." (eq (current-column) 1)) (forward-char -1))) ; =head1 (if (and (eq (preceding-char) ?\<) - (looking-at "\\$?[a-zA-Z0-9_:]+>")) ; + (looking-at "\\$?[[:alnum:]_:]+>")) ; (forward-char -1))) ((and (looking-at "=") (eq (preceding-char) ?x)) ; x= (forward-char -1)) @@ -7212,15 +7344,15 @@ Currently it is tuned to C and Perl syntax." (not (eq (char-after (- (point) 2)) ?\$))) ; $- (forward-char -1)) ((and (eq (following-char) ?\>) - (string-match "[a-zA-Z0-9_]" (char-to-string (preceding-char))) + (string-match "[[:alnum:]_]" (char-to-string (preceding-char))) (save-excursion (forward-sexp -1) (and (eq (preceding-char) ?\<) - (looking-at "\\$?[a-zA-Z0-9_:]+>")))) ; + (looking-at "\\$?[[:alnum:]_:]+>")))) ; (search-backward "<")))) ((and (eq (following-char) ?\$) (eq (preceding-char) ?\<) - (looking-at "\\$?[a-zA-Z0-9_:]+>")) ; <$fh> + (looking-at "\\$?[[:alnum:]_:]+>")) ; <$fh> (forward-char -1))) (if (looking-at cperl-have-help-regexp) (buffer-substring (match-beginning 0) (match-end 0)))) diff --git a/test/lisp/progmodes/cperl-mode-resources/grammar.pl b/test/lisp/progmodes/cperl-mode-resources/grammar.pl index c05fd7efc2..96a8699308 100644 --- a/test/lisp/progmodes/cperl-mode-resources/grammar.pl +++ b/test/lisp/progmodes/cperl-mode-resources/grammar.pl @@ -1,6 +1,7 @@ use 5.024; use strict; use warnings; +use utf8; sub outside { say "Line @{[__LINE__]}: package '@{[__PACKAGE__]}'"; @@ -155,4 +156,17 @@ package :: { Shoved::elsewhere(); +# Finally, try unicode identifiers. +package Erdős::Number; + +sub erdős_number { + my $name = shift; + if ($name eq "Erdős Pál") { + return 0; + } + else { + die "No access to the database. Sorry."; + } +} + 1; diff --git a/test/lisp/progmodes/cperl-mode-tests.el b/test/lisp/progmodes/cperl-mode-tests.el index 54012c3918..29b9e3f6fb 100644 --- a/test/lisp/progmodes/cperl-mode-tests.el +++ b/test/lisp/progmodes/cperl-mode-tests.el @@ -154,6 +154,22 @@ point in the distant past, and is still broken in perl-mode. " (should (equal (get-text-property (match-beginning 0) 'face) 'font-lock-keyword-face)))) +(ert-deftest cperl-test-fontify-special-variables () + "Test fontification of variables like $^T or ${^ENCODING}. +These can occur as \"local\" aliases." + (skip-unless (eq cperl-test-mode #'cperl-mode)) + (with-temp-buffer + (insert "local ($^I, ${^UNICODE});\n") + (goto-char (point-min)) + (funcall cperl-test-mode) + (font-lock-ensure) + (search-forward "$") + (should (equal (get-text-property (point) 'face) + 'font-lock-variable-name-face)) + (search-forward "$") + (should (equal (get-text-property (point) 'face) + 'font-lock-variable-name-face)))) + (ert-deftest cperl-test-identify-heredoc () "Test whether a construct containing \"<<\" followed by a bareword is properly identified for a here-document if @@ -297,6 +313,7 @@ the whole string." (ert-deftest cperl-test-ws-rx () "Tests capture of very simple regular expressions (yawn)." + (skip-unless (eq cperl-test-mode #'cperl-mode)) (let ((valid '(" " "\t" "\n")) (invalid @@ -306,6 +323,7 @@ the whole string." (ert-deftest cperl-test-ws+-rx () "Tests sequences of whitespace and comment lines." + (skip-unless (eq cperl-test-mode #'cperl-mode)) (let ((valid `(" " "\t#\n" "\n# \n" ,(concat "# comment\n" "# comment\n" "\n" "#comment\n"))) @@ -316,6 +334,7 @@ the whole string." (ert-deftest cperl-test-version-regexp () "Tests the regexp for recommended syntax of versions in Perl." + (skip-unless (eq cperl-test-mode #'cperl-mode)) (let ((valid '("1" "1.1" "1.1_1" "5.032001" "v120.100.103")) @@ -331,6 +350,7 @@ the whole string." (ert-deftest cperl-test-package-regexp () "Tests the regular expression of Perl package names with versions. Also includes valid cases with whitespace in strange places." + (skip-unless (eq cperl-test-mode #'cperl-mode)) (let ((valid '("package Foo" "package Foo::Bar" @@ -346,6 +366,284 @@ Also includes valid cases with whitespace in strange places." (cperl-test--validate-regexp (rx (eval cperl--package-rx)) valid invalid))) +(ert-deftest cperl-test-identifier-rx () + "Test valid and invalid identifiers (no sigils)." + (skip-unless (eq cperl-test-mode #'cperl-mode)) + (let ((valid + '("foo" "FOO" "f_oo" "a123" + "manĝis")) ; Unicode is allowed! + (invalid + '("$foo" ; no sigils allowed (yet) + "Foo::bar" ; no package qualifiers allowed + "lots_of_€"))) ; € is not alphabetic + (cperl-test--validate-regexp (rx (eval cperl--basic-identifier-rx)) + valid invalid))) + +;;; Test unicode identifier in various places + +(defun cperl--test-unicode-setup (code string) + "Insert CODE, prepare it for tests, and find STRING. +Invoke the appropriate major mode, ensure fontification, and set +point after the first occurrence of STRING (no regexp!)." + (insert code) + (funcall cperl-test-mode) + (font-lock-ensure) + (goto-char (point-min)) + (search-forward string)) + +(ert-deftest cperl-test-unicode-labels () + "Verify that non-ASCII labels are processed correctly." + (with-temp-buffer + (cperl--test-unicode-setup "LABEł: for ($manĝi) { say; }" "LAB") + (should (equal (get-text-property (point) 'face) + 'font-lock-constant-face)))) + +(ert-deftest cperl-test-unicode-sub () + (with-temp-buffer + (cperl--test-unicode-setup + (concat "use strict;\n" ; distinguish bob from b-o-f + "sub ℏ {\n" + " 6.62607015e-34\n" + "};") + "sub ") ; point is before "ℏ" + + ;; Testing fontification + ;; FIXME 2021-09-10: This tests succeeds because cperl-mode + ;; accepts almost anything as a sub name for fontification. For + ;; example, it fontifies "sub @ {...;}" which is a syntax error in + ;; Perl. I let this pass for the moment. + (should (equal (get-text-property (point) 'face) + 'font-lock-function-name-face)) + + ;; Testing `beginning-of-defun'. Not available in perl-mode, + ;; where it jumps to the beginning of the buffer. + (when (eq cperl-test-mode #'cperl-mode) + (goto-char (point-min)) + (search-forward "-34") + (beginning-of-defun) + (should (looking-at "sub"))))) + +(ert-deftest cperl-test-unicode-varname () + (with-temp-buffer + (cperl--test-unicode-setup + (concat "use strict;\n" + "my $π = 3.1415926535897932384626433832795028841971;\n" + "\n" + "my $manĝi = $π;\n" + "__END__\n") + "my $") ; perl-mode doesn't fontify the sigil, so include it here + + ;; Testing fontification + ;; FIXME 2021-09-10: This test succeeds in cperl-mode because the + ;; π character is "not ASCII alphabetic", so it treats $π as a + ;; punctuation variable. The following two `should' forms with a + ;; longer variable name were added for stronger verification. + (should (equal (get-text-property (point) 'face) + 'font-lock-variable-name-face)) + ;; Test both ends of a longer variable name + (search-forward "my $") ; again skip the sigil + (should (equal (get-text-property (point) 'face) + 'font-lock-variable-name-face)) + (search-forward "manĝi") + (should (equal (get-text-property (1- (match-end 0)) 'face) + 'font-lock-variable-name-face)))) + +(ert-deftest cperl-test-unicode-varname-list () + "Verify that all elements of a variable list are fontified." + + (let ((hash-face (if (eq cperl-test-mode #'perl-mode) + 'perl-non-scalar-variable + 'cperl-hash-face)) + (array-face (if (eq cperl-test-mode #'perl-mode) + 'perl-non-scalar-variable + 'cperl-array-face))) + (with-temp-buffer + (cperl--test-unicode-setup + "my (%äsh,@ärräy,$scâlâr);" "%") + (should (equal (get-text-property (point) 'face) + hash-face)) + (search-forward "@") + (should (equal (get-text-property (point) 'face) + array-face)) + (search-forward "scâlâr") + (should (equal (get-text-property (match-beginning 0) 'face) + 'font-lock-variable-name-face)) + (should (equal (get-text-property (1- (match-end 0)) 'face) + 'font-lock-variable-name-face))) + + ;; Now with package-qualified variables + (with-temp-buffer + (cperl--test-unicode-setup + "local (%Søme::äsh,@Søme::ärräy,$Søme::scâlâr);" "%") + (should (equal (get-text-property (point) 'face) + hash-face)) + (search-forward "Søme::") ; test basic identifier + (should (equal (get-text-property (point) 'face) + hash-face)) + (search-forward "@") ; test package name + (should (equal (get-text-property (point) 'face) + array-face)) + (search-forward "Søme::") ; test basic identifier + (should (equal (get-text-property (point) 'face) + array-face)) + (search-forward "Søme") ; test package name + (should (equal (get-text-property (match-beginning 0) 'face) + 'font-lock-variable-name-face)) + (should (equal (get-text-property (1- (match-end 0)) 'face) + 'font-lock-variable-name-face)) + (search-forward "scâlâr") ; test basic identifier + (should (equal (get-text-property (match-beginning 0) 'face) + 'font-lock-variable-name-face)) + (should (equal (get-text-property (1- (match-end 0)) 'face) + 'font-lock-variable-name-face))))) + +(ert-deftest cperl-test-unicode-arrays () + "Test fontification of array access." + ;; Perl mode just looks at the sigil, for element access + (skip-unless (eq cperl-test-mode #'cperl-mode)) + ;; simple array element + (with-temp-buffer + (cperl--test-unicode-setup + "$ärräy[1] = 7;" "$") + (should (equal (get-text-property (point) 'face) + 'cperl-array-face))) + ;; array slice + (with-temp-buffer + (cperl--test-unicode-setup + "@ärräy[(1..3)] = (4..6);" "@") + (should (equal (get-text-property (point) 'face) + 'cperl-array-face))) + ;; array max index + (with-temp-buffer + (cperl--test-unicode-setup + "$#ärräy = 1;" "$") + (should (equal (get-text-property (point) 'face) + 'cperl-array-face))) + ;; array dereference + (with-temp-buffer + (cperl--test-unicode-setup + "@$ärräy = (1,2,3)" "@") + (should (equal (get-text-property (1- (point)) 'face) + 'cperl-array-face)) + (should (equal (get-text-property (1+ (point)) 'face) + 'font-lock-variable-name-face)))) + +(ert-deftest cperl-test-unicode-hashes () + "Test fontification of hash access." + ;; Perl mode just looks at the sigil, for element access + (skip-unless (eq cperl-test-mode #'cperl-mode)) + ;; simple hash element + (with-temp-buffer + (cperl--test-unicode-setup + "$häsh{'a'} = 7;" "$") + (should (equal (get-text-property (point) 'face) + 'cperl-hash-face))) + ;; hash array slice + (with-temp-buffer + (cperl--test-unicode-setup + "@häsh{(1..3)} = (4..6);" "@") + (should (equal (get-text-property (point) 'face) + 'cperl-hash-face))) + ;; hash subset + (with-temp-buffer + (cperl--test-unicode-setup + "my %hash = %häsh{'a',2,3};" "= %") + (should (equal (get-text-property (point) 'face) + 'cperl-hash-face))) + ;; hash dereference + (with-temp-buffer + (cperl--test-unicode-setup + "%$äsh = (key => 'value');" "%") + (should (equal (get-text-property (1- (point)) 'face) + 'cperl-hash-face)) + (should (equal (get-text-property (1+ (point)) 'face) + 'font-lock-variable-name-face)))) + +(ert-deftest cperl-test-unicode-hashref () + "Verify that a hashref access disambiguates {s}. +CPerl mode takes the token \"s\" as a substitution unless +detected otherwise. Not for perl-mode: it doesn't stringify +bareword hash keys and doesn't recognize a substitution +\"s}foo}bar}\"" + (skip-unless (eq cperl-test-mode #'cperl-mode)) + (with-temp-buffer + (cperl--test-unicode-setup "$häshref->{s} # }}" "{") + (should (equal (get-text-property (point) 'face) + 'font-lock-string-face)) + (should (equal (get-text-property (1+ (point)) 'face) + nil)))) + +(ert-deftest cperl-test-unicode-proto () + ;; perl-mode doesn't fontify prototypes at all + (skip-unless (eq cperl-test-mode #'cperl-mode)) + (with-temp-buffer + (cperl--test-unicode-setup + (concat "sub prötötyped ($) {\n" + " ...;" + "}\n") + "prötötyped (") + + (should (equal (get-text-property (point) 'face) + 'font-lock-string-face)))) + +(ert-deftest cperl-test-unicode-fhs () + (with-temp-buffer + (cperl--test-unicode-setup + (concat "while () {\n" + " ...;)\n" + "}\n") + "while (<") ; point is before the first char of the handle + ;; Testing fontification + ;; FIXME 2021-09-10: perl-mode.el and cperl-mode.el handle these + ;; completely differently. perl-mode interprets barewords as + ;; constants, cperl-mode does not fontify them. Both treat + ;; non-barewords as globs, which are not fontified by perl-mode, + ;; but fontified as strings in cperl-mode. We keep (and test) + ;; that behavior "as is" because both bareword filehandles and + ;; syntax are no longer recommended. + (let ((bareword-face + (if (equal cperl-test-mode 'perl-mode) 'font-lock-constant-face + nil))) + (should (equal (get-text-property (point) 'face) + bareword-face))))) + +(ert-deftest cperl-test-unicode-hashkeys () + "Test stringification of bareword hash keys. Not in perl-mode. +perl-mode generally does not stringify bareword hash keys." + (skip-unless (eq cperl-test-mode #'cperl-mode)) + ;; Plain hash key + (with-temp-buffer + (cperl--test-unicode-setup + "$häsh { kéy }" "{ ") + (should (equal (get-text-property (point) 'face) + 'font-lock-string-face))) + ;; Nested hash key + (with-temp-buffer + (cperl--test-unicode-setup + "$häsh { kéy } { kèy }" "} { ") + (should (equal (get-text-property (point) 'face) + 'font-lock-string-face))) + ;; Key => value + (with-temp-buffer + (cperl--test-unicode-setup + "( kéy => 'value'," "( ") + (should (equal (get-text-property (point) 'face) + 'font-lock-string-face)))) + +(ert-deftest cperl-test-word-at-point () + "Test whether the function captures non-ASCII words." + (skip-unless (eq cperl-test-mode #'cperl-mode)) + (let ((words '("rôle" "café" "ångström" + "Data::Dump::dump" + "_underscore"))) + (dolist (word words) + (with-temp-buffer + (insert " + ") ; this will be the suffix + (beginning-of-line) + (insert ")") ; A non-word char + (insert word) + (should (string= word (cperl-word-at-point-hard))))))) + ;;; Function test: Building an index for imenu (ert-deftest cperl-test-imenu-index () @@ -369,7 +667,8 @@ created by CPerl mode, so skip it for Perl mode." "Versioned::Package::outer" "lexical" "Versioned::Block::signatured" - "Package::in_package_again"))) + "Package::in_package_again" + "Erdős::Number::erdős_number"))) (dolist (sub expected) (should (assoc-string sub index))))))) commit 89068554d7d0e9970a7269a0963e7a2bd0b1cc99 Author: Glenn Morris Date: Tue Sep 14 08:05:29 2021 -0700 * lisp/emacs-lisp/checkdoc.el (checkdoc-symbol-words): Fix type. diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el index c91645568c..e10ea736cd 100644 --- a/lisp/emacs-lisp/checkdoc.el +++ b/lisp/emacs-lisp/checkdoc.el @@ -308,7 +308,7 @@ with a universal argument.") "A list of symbol names (strings) which also happen to make good words. These words are ignored when unquoted symbols are searched for. This should be set in an Emacs Lisp file's local variables." - :type '(repeat (symbol :tag "Word")) + :type '(repeat (string :tag "Word")) :version "28.1") ;;;###autoload(put 'checkdoc-symbol-words 'safe-local-variable #'checkdoc-list-of-strings-p) commit 483df14d5c4e04ff2a9fec18b68399c0ab2b56b4 Author: Alan Third Date: Tue Sep 14 13:03:37 2021 +0100 A further fix for toolbar visibility problems on macOS (bug#50534) * src/nsterm.m (ns_update_begin): Ensure the toolbar's visibility is set correctly. diff --git a/src/nsterm.m b/src/nsterm.m index 7c90bbd578..4ef20e4c2b 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1021,16 +1021,14 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) ns_update_auto_hide_menu_bar (); -#ifdef NS_IMPL_COCOA - if ([view isFullscreen] && [view fsIsNative]) + NSToolbar *toolbar = [[FRAME_NS_VIEW (f) window] toolbar]; + if (toolbar) { - // Fix reappearing tool bar in fullscreen for Mac OS X 10.7 + /* Ensure the toolbars visibility is set correctly. */ BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO; - NSToolbar *toolbar = [[FRAME_NS_VIEW (f) window] toolbar]; if (! tbar_visible != ! [toolbar isVisible]) [toolbar setVisible: tbar_visible]; } -#endif ns_updating_frame = f; [view lockFocus]; commit 0934363c96ff774ce242b51529688bbfbb48ba02 Author: Eli Zaretskii Date: Tue Sep 14 16:47:40 2021 +0300 Fix recent changes in Flymake manual * doc/misc/flymake.texi: Fix typos. Downcase the first word of each index entry, for more reliable sorting. (Starting Flymake, Finding diagnostics, Troubleshooting): Fix typos. diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index 3dadd43582..e204e9a835 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -15,19 +15,19 @@ This manual is for GNU Flymake (version @value{VERSION}, @value{UPDATED}). Flymake is a universal on-the-fly syntax checker for Emacs. When enabled, Flymake contacts one or more source @dfn{backends} to -collects information about problems in the buffer, called +collect information about problems in the buffer, called @dfn{diagnostics}, and visually annotates them with a special face. -The mode line display overall status including totals for different +The mode line displays overall status including totals for different types of diagnostics. -To learn about using Flymake, @xref{Using Flymake}. +To learn about using Flymake, @pxref{Using Flymake}. Flymake is designed to be easily extended to support new backends via an Elisp interface. @xref{Extending Flymake} Historically, Flymake used to accept diagnostics from a single -backend. Although obsolete, it is still functional. To learn how to -use and customize it, @xref{The legacy Proc backend}. +backend. Although obsolete, it is still functional. To learn how to +use and customize it, @pxref{The legacy Proc backend}. Copyright @copyright{} 2004--2021 Free Software Foundation, Inc. @@ -100,10 +100,10 @@ write your own Flymake backend functions. @xref{Backend functions}. @node Starting Flymake @section Starting Flymake -@cindex Starting Flymake +@cindex starting Flymake -To use Flymake, the minor-mode @code{flymake-mode} must be activated. -If it's not, use the command @kbd{flymake-mode} to toggle it on. The +To use Flymake, activate the minor-mode @code{flymake-mode}. +Use the command @kbd{flymake-mode} to toggle it on and off. The mode line should indicate its presence via an indicator (@pxref{Mode line status}). @@ -127,22 +127,22 @@ When the user invokes the command @code{flymake-start}. @end itemize If the check detected errors or warnings, the respective buffer -regions are highlighted. @xref{Finding diagnostics} for how to +regions are highlighted. @xref{Finding diagnostics}, for how to learn what the problems are. @node Finding diagnostics @section Finding diagnostics -@cindex Read diagnostic message +@cindex read diagnostic message If Flymake has highlighted the buffer, you may hover the mouse on the highlighted regions to learn what the specific problem -is. Alternatively, place point on the highlighted regions and use the +is. Alternatively, place point on the highlighted regions and use the commands @code{eldoc} or @code{display-local-help}. -@cindex Next and previous diagnostic +@cindex next and previous diagnostic If the diagnostics are outside the visible region of the buffer, @code{flymake-goto-next-error} and @code{flymake-goto-prev-error} are -let you navigate to the next/previous errorenous regions, +let you navigate to the next/previous erroneous regions, respectively. It might be a good idea to map them to @kbd{M-n} and @kbd{M-p} in @code{flymake-mode}, by adding to your init file: @@ -151,9 +151,9 @@ respectively. It might be a good idea to map them to @kbd{M-n} and (define-key flymake-mode-map (kbd "M-p") 'flymake-goto-prev-error) @end lisp -@cindex Listing diagnostics +@cindex listing diagnostics Sometimes it is useful to have a detailed overview of the diagnostics -in your files without having to jump to each one to one. The commands +in your files without having to jump to each one. The commands @code{flymake-show-buffer-diagnostics} and @code{flymake-show-project-diagnostics} are designed to handle this situation. When invoked, they bring up a separate buffer containing a @@ -164,13 +164,13 @@ emacs, The Emacs Editor}). The listings is continuously updated as you edit source code, adding or removing lines as you make or correct mistakes. Each line of this listing includes the type of the diagnostic, its line and column in -the file as well as the diagnostic message. You may sort the listing +the file, as well as the diagnostic message. You may sort the listing by each of these columns. @node Mode line status @section Mode line status -@cindex Flymake mode line -@cindex Syntax check status +@cindex flymake mode line +@cindex syntax check status When enabled, Flymake displays its status in the mode line, which provides a visual summary of diagnostic collection. It may also hint @@ -210,13 +210,13 @@ can use the variables @code{flymake-mode-line-format} and @node Troubleshooting @section Troubleshooting -@cindex Troubleshooting -@cindex Backend exceptions +@cindex troubleshooting +@cindex backend exceptions @cindex disabled backends @cindex backends, disabled As Flymake supports multiple simutaneously active external backends, -is becomes useful monitor their status. For example, some backends +is becomes useful to monitor their status. For example, some backends may take longer than others to respond or complete, and some may decide to @emph{disable} themselves if they are not suitable for the current buffer or encounter some unavoidable problem. A disabled @@ -235,7 +235,7 @@ some external roadblock has been removed (for example after the user installed a needed syntax-check program). Invoking @code{flymake-start} with a prefix argument is a way to reset the disabled backend list, so that they will be tried again in the next -check. Manually toggle @code{flymake-mode} off and on again also +check. Manually toggling @code{flymake-mode} off and on again also works. @cindex logging @@ -1174,7 +1174,7 @@ of every syntax check attempt. @section Locating the buildfile @cindex locating the buildfile @cindex buildfile, locating -@cindex Makefile, locating +@cindex makefile, locating The Proc backend can be configured to use different tools for performing syntax checks. For example, it can use direct compiler commit 159dbd5eb211e36d98e200781f2eae93f0973aeb Author: Lars Ingebrigtsen Date: Tue Sep 14 13:44:15 2021 +0200 Make `find-function-source-path' into obsolete alias * lisp/finder.el (finder-commentary): Adjust usage. * lisp/emacs-lisp/find-func.el (find-function-source-path): Made into obsolete alias (bug#50508). (find-library-source-path): New name. (find-library-name, find-library, find-function-noselect) (find-variable-noselect, find-definition-noselect): Adjust usage and update doc strings. diff --git a/etc/NEWS b/etc/NEWS index 279bb17bbb..5809716868 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3384,6 +3384,19 @@ In Emacs 24.3, the variable 'dbus-event-error-hooks' was renamed to The old names, which were kept as obsolete aliases of the new names, have now been removed. +--- +** 'find-function-source-path' renamed and re-documented. +The 'find-function' command (and various related commands) were +documented to respect 'find-function-source-path', and to search for +objects in files specified by that variable. It's unclear when this +actually changed, but at some point (perhaps decades ago) these +commands started using 'load-history' to determine where symbols had +been defined (which is much faster). The doc strings of all the +affected function has been updated. 'find-function-source-path' was +still being used by 'find-library' and related commands, so the +variable has been renamed to 'find-library-source-path', and +'find-function-source-path' is now an obsolete variable alias. + * Lisp Changes in Emacs 28.1 diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el index 4bbcf45356..303039d653 100644 --- a/lisp/emacs-lisp/find-func.el +++ b/lisp/emacs-lisp/find-func.el @@ -145,13 +145,16 @@ in which case the function is called with one argument (the object we're looking for) and it should search for it.") (put 'find-function-regexp-alist 'risky-local-variable t) -(defcustom find-function-source-path nil - "The default list of directories where `find-function' searches. +(define-obsolete-variable-alias 'find-function-source-path + 'find-library-source-path "28.1") +(defcustom find-library-source-path nil + "The default list of directories where `find-library' searches. -If this variable is nil then `find-function' searches `load-path' by +If this variable is nil then `find-library' searches `load-path' by default." :type '(repeat directory) - :group 'find-function) + :group 'find-function + :version "28.1") (defcustom find-function-recenter-line 1 "The window line-number from which to start displaying a symbol definition. @@ -200,20 +203,20 @@ LIBRARY should be a string (the name of the library)." (setq library (gethash (file-name-nondirectory library) comp-eln-to-el-h)))) (or (locate-file library - (or find-function-source-path load-path) + (or find-library-source-path load-path) (find-library-suffixes)) (locate-file library - (or find-function-source-path load-path) + (or find-library-source-path load-path) load-file-rep-suffixes) (when (file-name-absolute-p library) (let ((rel (find-library--load-name library))) (when rel (or (locate-file rel - (or find-function-source-path load-path) + (or find-library-source-path load-path) (find-library-suffixes)) (locate-file rel - (or find-function-source-path load-path) + (or find-library-source-path load-path) load-file-rep-suffixes))))) (find-library--from-load-history library) (signal 'file-error (list "Can't find library" library)))) @@ -286,7 +289,10 @@ TYPE should be nil to find a function, or `defvar' to find a variable." (defun find-library (library) "Find the Emacs Lisp source of LIBRARY. -Interactively, prompt for LIBRARY using the one at or near point." +Interactively, prompt for LIBRARY using the one at or near point. + +This function searches `find-library-source-path' if non-nil, and +`load-path' otherwise." (interactive (list (read-library-name))) (prog1 (switch-to-buffer (find-file-noselect (find-library-name library))) @@ -297,9 +303,9 @@ Interactively, prompt for LIBRARY using the one at or near point." "Read and return a library name, defaulting to the one near point. A library name is the filename of an Emacs Lisp library located -in a directory under `load-path' (or `find-function-source-path', +in a directory under `load-path' (or `find-library-source-path', if non-nil)." - (let* ((dirs (or find-function-source-path load-path)) + (let* ((dirs (or find-library-source-path load-path)) (suffixes (find-library-suffixes)) (table (apply-partially 'locate-file-completion-table dirs suffixes)) @@ -521,11 +527,7 @@ the buffer, returns (BUFFER). If FUNCTION is a built-in function, this function normally attempts to find it in the Emacs C sources; however, if LISP-ONLY -is non-nil, signal an error instead. - -If the file where FUNCTION is defined is not known, then it is -searched for in `find-function-source-path' if non-nil, otherwise -in `load-path'." +is non-nil, signal an error instead." (if (not function) (error "You didn't specify a function")) (let ((func-lib (find-function-library function lisp-only t))) @@ -589,8 +591,6 @@ near point (selected by `function-called-at-point') in a buffer and places point before the definition. Set mark before moving, if the buffer already existed. -The library where FUNCTION is defined is searched for in -`find-function-source-path', if non-nil, otherwise in `load-path'. See also `find-function-recenter-line' and `find-function-after-hook'." (interactive (find-function-read)) (find-function-do-it function nil 'switch-to-buffer)) @@ -617,10 +617,7 @@ See `find-function' for more details." Finds the library containing the definition of VARIABLE in a buffer and the point of the definition. The buffer is not selected. -If the variable's definition can't be found in the buffer, return (BUFFER). - -The library where VARIABLE is defined is searched for in FILE or -`find-function-source-path', if non-nil, otherwise in `load-path'." +If the variable's definition can't be found in the buffer, return (BUFFER)." (if (not variable) (error "You didn't specify a variable") (let ((library (or file @@ -638,8 +635,6 @@ places point before the definition. Set mark before moving, if the buffer already existed. -The library where VARIABLE is defined is searched for in -`find-function-source-path', if non-nil, otherwise in `load-path'. See also `find-function-recenter-line' and `find-function-after-hook'." (interactive (find-function-read 'defvar)) (find-function-do-it variable 'defvar 'switch-to-buffer)) @@ -666,10 +661,7 @@ See `find-variable' for more details." If the definition can't be found in the buffer, return (BUFFER). TYPE says what type of definition: nil for a function, `defvar' for a variable, `defface' for a face. This function does not switch to the -buffer nor display it. - -The library where SYMBOL is defined is searched for in FILE or -`find-function-source-path', if non-nil, otherwise in `load-path'." +buffer nor display it." (cond ((not symbol) (error "You didn't specify a symbol")) @@ -693,8 +685,6 @@ places point before the definition. Set mark before moving, if the buffer already existed. -The library where FACE is defined is searched for in -`find-function-source-path', if non-nil, otherwise in `load-path'. See also `find-function-recenter-line' and `find-function-after-hook'." (interactive (find-function-read 'defface)) (find-function-do-it face 'defface 'switch-to-buffer)) diff --git a/lisp/finder.el b/lisp/finder.el index 555506db22..c2b9a6d0ef 100644 --- a/lisp/finder.el +++ b/lisp/finder.el @@ -379,7 +379,7 @@ FILE should be in a form suitable for passing to `locate-library'." (list (completing-read "Library name: " (apply-partially 'locate-file-completion-table - (or find-function-source-path load-path) + (or find-library-source-path load-path) (find-library-suffixes))))) (let ((str (lm-commentary (find-library-name file)))) (or str (error "Can't find any Commentary section")) commit 56254fb98c3369392dc2bd4383b7ff2ea24f2dc4 Author: João Távora Date: Sun Sep 12 22:16:35 2021 +0100 Re-organize and rewrite parts of the Flymake manual bug#50244 * doc/misc/flymake.texi (Starting Flymake): New section. (Finding diagnostics): New section, now contains info previously in "Listing diagnostics" (Mode line status): Renamed from "Mode-line syntax check status" (Troubleshooting): Renamed from "Backend exceptions". (Flymake error types): Tweak phrasing. diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index 710c9c5d87..3dadd43582 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -1,18 +1,33 @@ \input texinfo @c -*-texinfo; coding: utf-8 -*- @comment %**start of header @setfilename ../../info/flymake.info -@set VERSION 1.0 -@set UPDATED June 2018 +@set VERSION 1.2 +@set UPDATED September 2021 @settitle GNU Flymake @value{VERSION} -@include docstyle.texi +@include ../emacs/docstyle.texi @syncodeindex pg cp @syncodeindex vr cp @syncodeindex fn cp @comment %**end of header @copying -This manual is for GNU Flymake (version @value{VERSION}, @value{UPDATED}), -which is a universal on-the-fly syntax checker for GNU Emacs. +This manual is for GNU Flymake (version @value{VERSION}, @value{UPDATED}). + +Flymake is a universal on-the-fly syntax checker for Emacs. When +enabled, Flymake contacts one or more source @dfn{backends} to +collects information about problems in the buffer, called +@dfn{diagnostics}, and visually annotates them with a special face. +The mode line display overall status including totals for different +types of diagnostics. + +To learn about using Flymake, @xref{Using Flymake}. + +Flymake is designed to be easily extended to support new backends via +an Elisp interface. @xref{Extending Flymake} + +Historically, Flymake used to accept diagnostics from a single +backend. Although obsolete, it is still functional. To learn how to +use and customize it, @xref{The legacy Proc backend}. Copyright @copyright{} 2004--2021 Free Software Foundation, Inc. @@ -41,6 +56,7 @@ modify this GNU manual.'' @page @vskip 0pt plus 1filll @insertcopying + @end titlepage @contents @@ -64,19 +80,34 @@ modify this GNU manual.'' @cindex overview of flymake @cindex using flymake -Flymake is a universal on-the-fly buffer checker implemented as an -Emacs minor mode. To use Flymake, you must first activate -@code{flymake-mode} by using the command @kbd{flymake-mode}. +Flymake is only useful if at least one @dfn{backend} is configured to +provide the buffer-checking service. This is done via the hook +@code{flymake-diagnostic-functions}. @xref{Hooks,Hooks,, emacs, The +Emacs Editor}. + +It's possible that some major modes or a third-party package has +already setup this hook for you, by adding @dfn{backend functions} to +@code{flymake-diagnostic-functions}. If you know Elisp you may also +write your own Flymake backend functions. @xref{Backend functions}. + +@menu +* Starting Flymake:: +* Finding diagnostics:: +* Mode line status:: +* Troubleshooting:: +* Customizable variables:: +@end menu -When enabled, Flymake collects information about problems in the -buffer, called @dfn{diagnostics}, from one or more different sources, -or @dfn{backends}, and then visually annotates the buffer by -highlighting problematic buffer regions with a special face. +@node Starting Flymake +@section Starting Flymake +@cindex Starting Flymake -It also displays an overall buffer status in the mode line containing -totals for different types of diagnostics. +To use Flymake, the minor-mode @code{flymake-mode} must be activated. +If it's not, use the command @kbd{flymake-mode} to toggle it on. The +mode line should indicate its presence via an indicator (@pxref{Mode +line status}). -Syntax check is done ``on-the-fly''. It is started whenever +Syntax checks happen ``on-the-fly''. Each check is started whenever: @itemize @bullet @item @@ -90,68 +121,56 @@ nil; @item some changes were made to the buffer more than @code{0.5} seconds ago (the delay is configurable in @code{flymake-no-changes-timeout}). -@end itemize -Syntax check can also be started manually by typing the @kbd{M-x -flymake-start @key{RET}} command. +@item +When the user invokes the command @code{flymake-start}. +@end itemize If the check detected errors or warnings, the respective buffer -regions are highlighted. You can place point on those regions and use -@kbd{C-h .} (@code{display-local-help}) to see what the specific -problem was. Alternatively, hovering the mouse on those regions -should also display a tool-tip with the same information. +regions are highlighted. @xref{Finding diagnostics} for how to +learn what the problems are. + +@node Finding diagnostics +@section Finding diagnostics + +@cindex Read diagnostic message +If Flymake has highlighted the buffer, you may hover the mouse on the +highlighted regions to learn what the specific problem +is. Alternatively, place point on the highlighted regions and use the +commands @code{eldoc} or @code{display-local-help}. +@cindex Next and previous diagnostic +If the diagnostics are outside the visible region of the buffer, @code{flymake-goto-next-error} and @code{flymake-goto-prev-error} are -commands that allow easy navigation to the next/previous erroneous -regions, respectively. It might be a good idea to map them to @kbd{M-n} -and @kbd{M-p} in @code{flymake-mode}, by adding to your init file: +let you navigate to the next/previous errorenous regions, +respectively. It might be a good idea to map them to @kbd{M-n} and +@kbd{M-p} in @code{flymake-mode}, by adding to your init file: @lisp (define-key flymake-mode-map (kbd "M-n") 'flymake-goto-next-error) (define-key flymake-mode-map (kbd "M-p") 'flymake-goto-prev-error) @end lisp -Flymake is a universal syntax checker in the sense that it's easily -extended to support new backends (@pxref{Extending Flymake}). - -Historically, Flymake used to accept diagnostics from a single -backend, albeit a reasonably flexible one. - -This backend isn't (yet) obsolete, and so is still available as a -fallback and active by default (@pxref{The legacy Proc backend}). It works by -selecting a syntax check tool from a preconfigured list (compiler for -C@t{++} files, @command{perl} for Perl files, etc.), and executing it in the -background, passing it a temporary file which is a copy of the current -buffer, and parsing the output for known error/warning message -patterns. - -@menu -* Syntax check statuses:: -* Backend exceptions:: -* Customizable variables:: -@end menu - -@node Listing diagnostics -@section Listing diagnostics @cindex Listing diagnostics - Sometimes it is useful to have a detailed overview of the diagnostics -in your files. The command @code{flymake-show-diagnostics-buffer} -displays a structured listing of diagnostics in the current buffer. -The listing is displayed in a separate buffer and is continuously -updated as you edit source code, adding or removing lines as you make -or correct mistakes. - -Each line of this listing includes the type of the diagnostic, its -line and column in the file as well as the diagnostic message. You -may sort the listing by each of these columns. - -@code{flymake-show-project-diagnostics} does something similar but for -the whole project (@pxref{Projects,,, emacs, The Emacs Editor}). - -@node Mode-line synatx-check status -@section Mode-line synatx-check status -@cindex Mode-line synatx-check status +in your files without having to jump to each one to one. The commands +@code{flymake-show-buffer-diagnostics} and +@code{flymake-show-project-diagnostics} are designed to handle this +situation. When invoked, they bring up a separate buffer containing a +detailed structured listing of multiple diagnostics in the current +buffer or for the current project, respectively (@pxref{Projects,,, +emacs, The Emacs Editor}). + +The listings is continuously updated as you edit source code, adding or +removing lines as you make or correct mistakes. Each line of this +listing includes the type of the diagnostic, its line and column in +the file as well as the diagnostic message. You may sort the listing +by each of these columns. + +@node Mode line status +@section Mode line status +@cindex Flymake mode line +@cindex Syntax check status When enabled, Flymake displays its status in the mode line, which provides a visual summary of diagnostic collection. It may also hint @@ -175,7 +194,7 @@ delay and Flymake will resume normal operation soon. @item @code{!} @tab All the configured Flymake backends have disabled themselves: Flymake cannot annotate the buffer and action from the user is needed to -investigate and remedy the situation (@pxref{Backend exceptions}). +investigate and remedy the situation (@pxref{Troubleshooting}). @item @code{?} @tab There are no applicable Flymake backends for this buffer, thus Flymake @@ -184,17 +203,24 @@ and add a new backend (@pxref{Extending Flymake}). @end multitable -@node Backend exceptions -@section Backend exceptions -@cindex backend exceptions +If you would like to customize the appearance of the mode-line, you +can use the variables @code{flymake-mode-line-format} and +@code{flymake-mode-line-counter-format} for that purpose. +@xref{Customizable variables}. + +@node Troubleshooting +@section Troubleshooting +@cindex Troubleshooting +@cindex Backend exceptions @cindex disabled backends @cindex backends, disabled -Some backends may take longer than others to respond or complete, and -some may decide to @emph{disable} themselves if they are not suitable -for the current buffer or encounter some unavoidable problem. A -disabled backend is not tried again for future checks of the current -buffer. +As Flymake supports multiple simutaneously active external backends, +is becomes useful monitor their status. For example, some backends +may take longer than others to respond or complete, and some may +decide to @emph{disable} themselves if they are not suitable for the +current buffer or encounter some unavoidable problem. A disabled +backend is not tried again for future checks of the current buffer. @findex flymake-reporting-backends @findex flymake-running-backends @@ -204,18 +230,23 @@ The commands @code{flymake-reporting-backends}, show the backends currently used and those which are disabled. @cindex reset disabled backends -Toggling @code{flymake-mode} off and on again, or invoking -@code{flymake-start} with a prefix argument is one way to reset the -disabled backend list, so that they will be tried again in the next check. +Sometimes, re-starting a backend that disabled itself is useful after +some external roadblock has been removed (for example after the user +installed a needed syntax-check program). Invoking +@code{flymake-start} with a prefix argument is a way to reset the +disabled backend list, so that they will be tried again in the next +check. Manually toggle @code{flymake-mode} off and on again also +works. @cindex logging @cindex flymake logging -Flymake also uses a simple logging facility for indicating important -points in the control flow. The logging facility sends logging -messages to the @file{*Flymake log*} buffer. The information logged -can be used for resolving various problems related to Flymake. For -convenience, a shortcut to this buffer can be found in Flymake's menu, -accessible from the top menu bar or just left of the status indicator. +Flymake uses a simple logging facility for indicating important points +in the control flow. The logging facility sends logging messages to +the @file{*Flymake log*} buffer. The logged information can be used +for resolving various problems related to Flymake. For convenience, a +shortcut to this buffer can be found in Flymake's menu, accessible +from the top menu bar or just left of the status indicator. The +command @code{flymake-switch-to-log-buffer} is another alternative. @vindex warning-minimum-log-level @vindex warning-minimum-level @@ -235,7 +266,7 @@ configuration of the Flymake user interface. Format to use for the Flymake mode line indicator. @item flymake-mode-line-counter-format -Mode-line construct for formatting Flymake diagnostic counters inside +mode line construct for formatting Flymake diagnostic counters inside the Flymake mode line indicator. @item flymake-no-changes-timeout @@ -288,10 +319,10 @@ Flymake can primarily be extended in one of two ways: @enumerate @item By changing the look and feel of the annotations produced by the -different backends. +different backends. @xref{Flymake error types}. @item -By adding a new buffer-checking backend. +By adding a new buffer-checking backend. @xref{Backend functions}. @end enumerate The following sections discuss each approach in detail. @@ -306,10 +337,12 @@ The following sections discuss each approach in detail. @cindex customizing error types @cindex error types, customization -To customize the appearance of error types, set properties on the -symbols associated with each diagnostic type. The standard diagnostic -symbols are @code{:error}, @code{:warning} and @code{:note} (though -the backend may define more, @pxref{Backend functions}). +To customize the appearance of error types, the user must set +properties on the symbols associated with each diagnostic type. + +The three standard diagnostic keyowrd symbols -- @code{:error}, +@code{:warning} and @code{:note} -- have pre-configured appearances. +However a backend may define more (@pxref{Backend functions}). The following properties can be set: @@ -507,7 +540,7 @@ manual}) or other asynchronous mechanisms. In any case, backend functions are expected to return quickly or signal an error, in which case the backend is disabled -(@pxref{Backend exceptions}). +(@pxref{Troubleshooting}). If the function returns, Flymake considers the backend to be @dfn{running}. If it has not done so already, the backend is expected @@ -620,7 +653,7 @@ elisp, The Emacs Lisp Reference Manual}). @cindex add a log message For troubleshooting purposes, backends may record arbitrary exceptional or erroneous situations into the Flymake log -buffer (@pxref{Backend exceptions}): +buffer (@pxref{Troubleshooting}): @deffn Macro flymake-log level msg &optional args Log, at level @var{level}, the message @var{msg} formatted with @@ -1115,7 +1148,7 @@ correct @file{file.h}. First matching master file found stops the search. The master file is then patched and saved to disk. In case no master file is found, syntax check is aborted, and corresponding status (@samp{!}) is reported in the mode line. -@xref{Syntax check statuses}. +@xref{Mode line status}. @node Getting the include directories @section Getting the include directories commit 4633e02726fbd683332eb64dee97b109aef4361f Author: João Távora Date: Wed Sep 1 23:45:53 2021 +0100 Add support for project-wide diagnostics in Flymake (bug#50244) This is done with two new concepts: "foreign diagnostics" and "list-only diagnostics". The manual has been updated with a description of these new concepts. * doc/misc/flymake.texi (Flymake utility functions): Explain creation of foreign diagnostics. (Foreign and list-only diagnostics): New subsection. (Listing diagnostics): New subsection. * lisp/progmodes/flymake.el (Version): Bump to 1.2.1 (project): Require project. (flymake--diag): Add new slots 'orig-beg' and 'orig-end'. Rename 'buffer' slot to 'locus'. (flymake-make-diagnostic): Rework docstring. Accept stringp LOCUS arg. (flymake-diagnostic-beg, flymake-diagnostic-end) (flymake-diagnostic-buffer): Simplify definition. (flymake--equal-diagnostic-p): New helper (flymake--highlight-line): Rework. Accept FOREIGN arg. (flymake--state): Work docstring. Add new slot 'foreign-diags' (flymake--handle-report): Call flymake--update-diagnostics-listings. (flymake--handle-report): New helper. (flymake--mode): Forward declare. (flymake--handle-report): Rework for foreign diagnostics. (flymake-mode): When turning on, notice any Flymake diagnostics for current buffer. When turning off update diagnostics listings. (flymake-kill-buffer-hook): Turn off flymake explicitly before killing. (flymake--mode-line-counter): Use flymake-diagnostics to collect diagnostics. (flymake-show-diagnostic): Visit buffer of file-specific diagnostic. (flymake--tabulated-entries-1): New helper extracted from flymake--diagnostic-buffer-entries. (flymake--diagnostics-buffer-entries): Rework. (flymake--diagnostics-base-tabulated-list-format): New helper. (flymake--diagnostics-buffer-name): Adjust. (flymake-list-only-diagnostics): New variable. (flymake--project-diagnostic-list-project): New variable. (flymake--clear-list-only-diagnostics): New helper. (flymake-project-diagnostics-mode): New major mode. (flymake--project-diagnostics) (flymake--project-diagnostics-entries) (flymake--project-diagnostics-buffer): New helpers. (flymake-show-project-diagnostics): New command. (flymake--update-diagnostics-listings): New helper. (flymake-show-buffer-diagnostics): Renamed from flymake-diagnostics-buffer. * etc/NEWS: Mention change. diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index 9c838a8341..710c9c5d87 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -131,9 +131,27 @@ patterns. * Customizable variables:: @end menu -@node Syntax check statuses -@section Syntax check statuses -@cindex Syntax check statuses +@node Listing diagnostics +@section Listing diagnostics +@cindex Listing diagnostics + +Sometimes it is useful to have a detailed overview of the diagnostics +in your files. The command @code{flymake-show-diagnostics-buffer} +displays a structured listing of diagnostics in the current buffer. +The listing is displayed in a separate buffer and is continuously +updated as you edit source code, adding or removing lines as you make +or correct mistakes. + +Each line of this listing includes the type of the diagnostic, its +line and column in the file as well as the diagnostic message. You +may sort the listing by each of these columns. + +@code{flymake-show-project-diagnostics} does something similar but for +the whole project (@pxref{Projects,,, emacs, The Emacs Editor}). + +@node Mode-line synatx-check status +@section Mode-line synatx-check status +@cindex Mode-line synatx-check status When enabled, Flymake displays its status in the mode line, which provides a visual summary of diagnostic collection. It may also hint @@ -540,6 +558,7 @@ reports targeting other parts of the buffer remain valid. @menu * Flymake utility functions:: +* Foreign and list-only diagnostics:: * An annotated example backend:: @end menu @@ -551,20 +570,26 @@ reports targeting other parts of the buffer remain valid. Before delivering them to Flymake, backends create diagnostic objects by calling the function @code{flymake-make-diagnostic}. -@deffn Function flymake-make-diagnostic buffer beg end type text -Make a Flymake diagnostic for @var{buffer}'s region from @var{beg} to -@var{end}. @var{type} is a diagnostic symbol (@pxref{Flymake error -types}), and @var{text} is a description of the problem detected in -this region. Currently, it is unspecified behavior to make -diagnostics for buffers other than the buffer that the Flymake backend -is responsible for. +@deffn Function flymake-make-diagnostic locus beg end type text &optional data +Make a Flymake diagnostic for the region of text in @var{locus}'s +delimited by @var{beg} and @var{end}. @var{type} is a diagnostic +symbol (@pxref{Flymake error types}), and @var{text} is a description +of the problem detected in this region. Most commonly @var{locus} is +the buffer object designating for the current buffer being +syntax-checked. However, it may be a string nameing a file relative +to the current working directory. @xref{Foreign and list-only +diagnostics} for when this may be useful. Depending on the type of +@var{locus}, @var{beg} and @var{end} are both either buffer positions +or conses (@var{line} . @var{col}) which specify the line and column +of the diagnostic's start and end positions, respectively. @end deffn @cindex access diagnostic object These objects' properties can be accessed with the functions @code{flymake-diagnostic-backend}, @code{flymake-diagnostic-buffer}, @code{flymake-diagnostic-text}, @code{flymake-diagnostic-beg}, -@code{flymake-diagnostic-end} and @code{flymake-diagnostic-type}. +@code{flymake-diagnostic-end}, @code{flymake-diagnostic-type} and +@code{flymake-diagnostic-data}. Additionally, the function @code{flymake-diagnostics} will collect such objects in the region you specify. @@ -604,6 +629,58 @@ Log, at level @var{level}, the message @var{msg} formatted with used to display the warning in Flymake's log buffer. @end deffn +@node Foreign and list-only diagnostics +@subsection Foreign and list-only diagnostics +@cindex create diagnostic object for other buffer + +It is possible for a given backend's implementation to use +@code{flymake-make-diagnostic} to create diagnostics for buffers or +files other than the ``source'' buffer where Flymake was enabled. For +instance, this is useful when a given backend has access to +information about the health of neighboring files that are not yet +visited or whose diagnostics depend on the current buffer's state. +There are two alternative ways to go about doing this: + +@enumerate + +@item +@cindex foreign diagnostics +@cindex domestic diagnostics +If the information about neighboring diagnostics is obtained +regularly, like when each syntax-checking iteration of a @code{.c} +file also reports a number of associated problems in an neighboring +@code{.h} file, it is better to create so-called ``foreign'' +diagnostics. + +This is done by passing a file name to @code{flymake-make-diagnostic} +(@pxref{Flymake utility functions}). Then, the resulting object is +simply reported along with the other ``domestic'' diagnostics for the +source buffer (@pxref{Backend functions}). When the neighboring file +is visited as a buffer and Flymake is active there, a number of +supplemental annotations will appear and automatically update whenever +as the ``source'' buffer is syntax-checked. + +@item +@cindex list-only diagnostics +@vindex flymake-list-only-diagnostics +If information about neighboring diagnostics is obtained infrequently, +like when running a time-consuming and sporadic check of a large +project, it is easier for the backend to modify the global variable +@code{flymake-list-only-diagnostics}. + +Flymake will look up this variable when asked to compile project-wide +lists of diagnostics. The backend should add one or more alist +entries that look like (@var{file-name} . @var{diags}). +@var{file-name} is the absolute name of the neighboring file presumed +not to be visited in Emacs already, as that would mean that that +buffer contains more up-to-date information on its diagnostics. +@var{diags} is a list of diagnostic objects. When the neighboring +file @var{file-name} is visited as a buffer and Flymake is activated +there, the ``list-only'' diagnostics will @emph{not} produce +annotations for @var{diags}, as Flymake assumes that the Flymake +activation in the new buffer will take care of that. +@end enumerate + @node An annotated example backend @subsection An annotated example backend @cindex example of backend diff --git a/etc/NEWS b/etc/NEWS index bba86984c8..279bb17bbb 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2524,6 +2524,15 @@ images are marked. ** Flymake mode ++++ +*** New command 'flymake-show-project-diagnostics' +This lists all diagnostics for buffers in the currently active +project. The listing is similar to the one obtained by +'flymake-show-buffer-diagnostics', but adds a column for the +project-relative file name. For backends which support it, +'flymake-show-project-diagnostics' also lists diagnostics for files +that have not yet been visited. + +++ *** New user options to customize Flymake's mode-line. The new user option 'flymake-mode-line-format' is a mix of strings and diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 71dd4b6b1a..e2981eb9bd 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -4,9 +4,9 @@ ;; Author: Pavel Kobyakov ;; Maintainer: João Távora -;; Version: 1.1.1 +;; Version: 1.2.1 ;; Keywords: c languages tools -;; Package-Requires: ((emacs "26.1") (eldoc "1.1.0")) +;; Package-Requires: ((emacs "26.1") (eldoc "1.1.0") (project "0.7.1")) ;; This is a GNU ELPA :core package. Avoid functionality that is not ;; compatible with the version of Emacs recorded above. @@ -121,6 +121,7 @@ (require 'mwheel) ;; when-let*, if-let*, hash-table-keys, hash-table-values: (eval-when-compile (require 'subr-x)) +(require 'project) (defgroup flymake nil "Universal on-the-fly syntax checker." @@ -305,41 +306,51 @@ generated it." (cl-defstruct (flymake--diag (:constructor flymake--diag-make)) - buffer beg end type text backend data overlay-properties overlay) + locus beg end type text backend data overlay-properties overlay + ;; FIXME: See usage of these two in `flymake--highlight-line'. + ;; Ideally they wouldn't be needed. + orig-beg orig-end) ;;;###autoload -(defun flymake-make-diagnostic (buffer +(defun flymake-make-diagnostic (locus beg end type text &optional data overlay-properties) - "Make a Flymake diagnostic for BUFFER's region from BEG to END. + "Make a Flymake diagnostic for LOCUS's region from BEG to END. +LOCUS is a buffer object or a string designating a file name. + TYPE is a diagnostic symbol and TEXT is string describing the problem detected in this region. DATA is any object that the caller wishes to attach to the created diagnostic for later retrieval with `flymake-diagnostic-data'. -BEG and END may each be either buffer positions (number or -markers) or a cons (LINE . COL). When using the second form the -numbers will be converted to buffer positions (using -`flymake-diag-region') as soon as the diagnostic is appended to -an actual buffer. +If LOCUS is a buffer BEG and END should be buffer positions +inside it. If LOCUS designates a file, BEG and END should be a +cons (LINE . COL) indicating a file position. In this second +case, END may be ommited in which case the region is computed +using `flymake-diag-region' if the diagnostic is appended to an +actual buffer. OVERLAY-PROPERTIES is an alist of properties attached to the created diagnostic, overriding the default properties and any -properties of `flymake-overlay-control' of the diagnostic's -type." - (flymake--diag-make :buffer buffer :beg beg :end end +properties listed in the `flymake-overlay-control' property of +the diagnostic's type symbol." + (when (stringp locus) + (setq locus (expand-file-name locus))) + (flymake--diag-make :locus locus :beg beg :end end :type type :text text :data data - :overlay-properties overlay-properties)) + :overlay-properties overlay-properties + :orig-beg beg + :orig-end end)) ;;;###autoload (defun flymake-diagnostics (&optional beg end) "Get Flymake diagnostics in region determined by BEG and END. -If neither BEG or END is supplied, use the whole buffer, +If neither BEG or END is supplied, use whole accessible buffer, otherwise if BEG is non-nil and END is nil, consider only diagnostics at BEG." (mapcar (lambda (ov) (overlay-get ov 'flymake-diagnostic)) @@ -354,27 +365,10 @@ diagnostics at BEG." (flymake--diag-accessor flymake-diagnostic-text flymake--diag-text text) (flymake--diag-accessor flymake-diagnostic-type flymake--diag-type type) (flymake--diag-accessor flymake-diagnostic-backend flymake--diag-backend backend) -(flymake--diag-accessor flymake-diagnostic-data flymake--diag-data backend) - -(defun flymake-diagnostic-buffer (diag) - "Get Flymake diagnostic DIAG's buffer." - (flymake--diag-buffer diag)) - -(defun flymake-diagnostic-beg (diag) - "Get Flymake diagnostic DIAG's start position. -May only be queried after DIAG has been reported to Flymake." - (let ((overlay (flymake--diag-overlay diag))) - (unless overlay - (error "DIAG %s not reported to Flymake yet" diag)) - (overlay-start overlay))) - -(defun flymake-diagnostic-end (diag) - "Get Flymake diagnostic DIAG's end position. -May only be queried after DIAG has been reported to Flymake." - (let ((overlay (flymake--diag-overlay diag))) - (unless overlay - (error "DIAG %s not reported to Flymake yet" diag)) - (overlay-end overlay))) +(flymake--diag-accessor flymake-diagnostic-data flymake--diag-data data) +(flymake--diag-accessor flymake-diagnostic-beg flymake--diag-beg beg) +(flymake--diag-accessor flymake-diagnostic-end flymake--diag-end end) +(flymake--diag-accessor flymake-diagnostic-buffer flymake--diag-locus locus) (cl-defun flymake--overlays (&key beg end filter compare key) "Get flymake-related overlays. @@ -634,13 +628,74 @@ associated `flymake-category' return DEFAULT." bitmap (list bitmap))))))) -(defun flymake--highlight-line (diagnostic) - "Highlight buffer with info in DIAGNOSTIC." - (let ((type (or (flymake-diagnostic-type diagnostic) - :error)) - (ov (make-overlay - (flymake--diag-beg diagnostic) - (flymake--diag-end diagnostic)))) +(defun flymake--equal-diagnostic-p (a b) + "Tell if A and B are equivalent `flymake--diag' objects." + (or (eq a b) + (cl-loop for comp in '(flymake--diag-end + flymake--diag-beg + flymake-diagnostic-type + flymake-diagnostic-backend + flymake-diagnostic-text) + always (equal (funcall comp a) (funcall comp b))))) + +(cl-defun flymake--highlight-line (diagnostic &optional foreign) + "Attempt to overlay DIAGNOSTIC in current buffer. + +FOREIGN says if DIAGNOSTIC is \"foreign\" to the current buffer, +i.e. managed by another buffer where `flymake-mode' is also +active. + +This function mayskip overlay creation if a diagnostic which is +the same as DIAGNOSTIC is already highlighted +(in the sense of `flymake--equal-diagnostic-p'). In that case +the action to take depends on FOREIGN. If nil the existing +overlay is deleted, else no overlay is created. + +Return nil or the overlay created." + (let* ((type (or (flymake-diagnostic-type diagnostic) + :error)) + (beg (flymake--diag-beg diagnostic)) + (end (flymake--diag-end diagnostic)) + (convert (lambda (cell) + (flymake-diag-region (current-buffer) + (car cell) + (cdr cell)))) + ov) + ;; Convert (LINE . COL) forms of `flymake--diag-beg' and + ;; `flymake--diag-end'. Record the converted positions. + ;; + (cond ((and (consp beg) (not (null end))) + (setq beg (car (funcall convert beg))) + (when (consp end) + (setq end (car (funcall convert end))))) + ((consp beg) + (cl-destructuring-bind (a . b) (funcall convert beg) + (setq beg a end b)))) + (setf (flymake--diag-beg diagnostic) beg + (flymake--diag-end diagnostic) end) + ;; Try to fix the remedy the situation if there is the same + ;; diagnostic is already registered in the same place, which only + ;; happens for clashes between domestic and foreign diagnostics + (cl-loop for e in (flymake-diagnostics beg end) + when (flymake--equal-diagnostic-p e diagnostic) + ;; FIXME. This is an imperfect heuristic. Ideally, we'd + ;; want to delete no overlays and keep annotating the + ;; superseded foreign in an overlay but hide it from most + ;; `flymake-diagnostics' calls. If the target buffer is + ;; killed we can keep the "latent" state of the foreign + ;; diagnostic (with filename and updated line/col info). + ;; If it is revisited the foreign diagnostic can be + ;; revived again. + do (if foreign + (cl-return-from flymake--highlight-line nil) + (setf (flymake--diag-beg e) + (flymake--diag-orig-beg e) + (flymake--diag-end e) + (flymake--diag-orig-end e)) + (delete-overlay (flymake--diag-overlay e)))) + (setq ov (make-overlay end beg)) + (setf (flymake--diag-beg diagnostic) (overlay-start ov) + (flymake--diag-end diagnostic) (overlay-end ov)) ;; First set `category' in the overlay ;; (overlay-put ov 'category @@ -685,6 +740,7 @@ associated `flymake-category' return DEFAULT." ;; (overlay-put ov 'evaporate t) (overlay-put ov 'flymake-diagnostic diagnostic) + (setf (flymake--diag-overlay diagnostic) ov) ov)) ;; Nothing in Flymake uses this at all any more, so this is just for @@ -710,11 +766,16 @@ since it last was contacted. `disabled', a string with the explanation for a previous exceptional situation reported by the backend, nil if the -backend is operating normally.") +backend is operating normally. + +`foreign-diags', a hash table of buffers/files to +collections of diagnostics outside the buffer where this +`flymake--state' pertains.") (cl-defstruct (flymake--state (:constructor flymake--make-backend-state)) - running reported-p disabled diags) + running reported-p disabled diags (foreign-diags + (make-hash-table))) (defmacro flymake--with-backend-state (backend state-var &rest body) "Bind BACKEND's STATE-VAR to its state, run BODY." @@ -781,25 +842,41 @@ report applies to that region." (flymake--publish-diagnostics report-action :backend backend :state state - :region region))) - (setf (flymake--state-reported-p state) t)) + :region region) + (when flymake-check-start-time + (flymake-log :debug "backend %s reported %d diagnostics in %.2f second(s)" + backend + (length report-action) + (float-time + (time-since flymake-check-start-time)))))) + (setf (flymake--state-reported-p state) t) + (flymake--update-diagnostics-listings (current-buffer))) + +(defun flymake--clear-foreign-diags (state) + (maphash (lambda (_buffer diags) + (cl-loop for d in diags + when (flymake--diag-overlay d) + do (delete-overlay it))) + (flymake--state-foreign-diags state)) + (clrhash (flymake--state-foreign-diags state))) -(cl-defun flymake--publish-diagnostics (diags &key backend state region - &aux new-diags other-diags) +(defvar-local flymake-mode nil) + +(cl-defun flymake--publish-diagnostics (diags &key backend state region) "Helper for `flymake--handle-report'. -Publish DIAGS " - (cl-loop for d in diags - if (eq (flymake--diag-buffer d) (current-buffer)) - do (push d new-diags) - else - do (push d other-diags)) +Publish DIAGS, which contain diagnostics for the current buffer +and other buffers." + (dolist (d diags) (setf (flymake--diag-backend d) backend)) (save-restriction (widen) - ;; Before adding to backend's diagnostic list, decide if - ;; some or all must be deleted. When deleting, also delete - ;; the associated overlay. + ;; First, clean up. Remove diagnostics from bookeeping lists and + ;; their overlays from buffers. + ;; (cond - (region + (;; If there is a `region' arg, only affect the diagnostics whose + ;; overlays are in a certain region. Discard "foreign" + ;; diagnostics. + region (cl-loop for diag in (flymake--state-diags state) for ov = (flymake--diag-overlay diag) if (or (not (overlay-buffer ov)) @@ -810,29 +887,42 @@ Publish DIAGS " else collect diag into surviving finally (setf (flymake--state-diags state) surviving))) - ((not (flymake--state-reported-p state)) - (dolist (diag (flymake--state-diags state)) - (delete-overlay (flymake--diag-overlay diag))) - (setf (flymake--state-diags state) nil))) - ;; Now make new overlays - (mapc (lambda (diag) - (let ((overlay (flymake--highlight-line diag))) - (setf (flymake--diag-backend diag) backend - (flymake--diag-overlay diag) overlay))) - new-diags) - (setf (flymake--state-diags state) - (append new-diags (flymake--state-diags state))) - (when flymake-check-start-time - (flymake-log :debug "backend %s reported %d diagnostics in %.2f second(s)" - backend - (length new-diags) - (float-time - (time-since flymake-check-start-time)))) - (when (and (get-buffer (flymake--diagnostics-buffer-name)) - (get-buffer-window (flymake--diagnostics-buffer-name)) - (null (cl-set-difference (flymake-running-backends) - (flymake-reporting-backends)))) - (flymake-show-diagnostics-buffer)))) + (;; Else, if this is the first report, zero all lists and delete + ;; all associated overlays. + (not (flymake--state-reported-p state)) + (cl-loop for diag in (flymake--state-diags state) + for ov = (flymake--diag-overlay diag) + when ov do (delete-overlay ov)) + (setf (flymake--state-diags state) nil) + ;; Also clear all overlays for `foreign-diags' in all other + ;; buffers. + (flymake--clear-foreign-diags state)) + (;; If this is not the first report, do no cleanup. + t)) + + ;; Now place new overlays for all diagnostics: "domestic" + ;; diagnostics are for the current buffer; "foreign" may be for a + ;; some other live buffer or for a file name that hasn't a buffer + ;; yet. If a foreign diagnostic is for a buffer, convert to a + ;; file name, protecting it against that buffer's killing. + ;; + (cl-loop + for d in diags + for locus = (flymake--diag-locus d) + do (cond ((eq locus (current-buffer)) + (push d (flymake--state-diags state)) + (flymake--highlight-line d)) + (t + (when (or (buffer-live-p locus) + (setq locus (find-buffer-visiting locus))) + (with-current-buffer locus + (when flymake-mode (flymake--highlight-line d 'foreign)) + ;; Ensure locus of a foreign diag is always a file-name + ;; string, even if created from a buffer. + (setf (flymake--diag-locus d) (buffer-file-name)))) + (cl-assert (stringp (flymake--diag-locus d))) + (push d (gethash (flymake--diag-locus d) + (flymake--state-foreign-diags state)))))))) (defun flymake-make-report-fn (backend &optional token) "Make a suitable anonymous report function for BACKEND. @@ -1043,7 +1133,37 @@ special *Flymake log* buffer." :group 'flymake :lighter (setq flymake--state (make-hash-table)) (setq flymake--recent-changes nil) - (when flymake-start-on-flymake-mode (flymake-start t))) + (when flymake-start-on-flymake-mode (flymake-start t)) + + ;; Other diagnostic sources may already target this buffer's file + ;; before we turned on: these sources may be of two types... + (let ((source (current-buffer)) + (bfn buffer-file-name)) + ;; 1. For `flymake-list-only-diagnostics': here, we do nothing. + ;; FIXME: We could remove the corresponding entry from that + ;; variable, as we assume that new diagnostics will come in soon + ;; via the brand new `flymake-mode' setup. For simplicity's + ;; sake, we have opted to leave the backend for now. + nil + ;; 2. other buffers where a backend has created "foreign" + ;; diagnostics and pointed them here. We must highlight them in + ;; this buffer, i.e. create overlays for them. Those other + ;; buffers and backends are still responsible for them, i.e. the + ;; current buffer does not "own" these foreign diags. + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (and flymake-mode flymake--state) + (maphash (lambda (_backend state) + (maphash (lambda (file diags) + (when (or (eq file source) + (string= bfn (expand-file-name file))) + (with-current-buffer source + (mapc (lambda (diag) + (flymake--highlight-line diag + 'foreign)) + diags)))) + (flymake--state-foreign-diags state))) + flymake--state)))))) ;; Turning the mode OFF. (t @@ -1053,11 +1173,16 @@ special *Flymake log* buffer." :group 'flymake :lighter ;;+(remove-hook 'find-file-hook (function flymake-find-file-hook) t) (remove-hook 'eldoc-documentation-functions 'flymake-eldoc-function t) - (mapc #'delete-overlay (flymake--overlays)) - (when flymake-timer (cancel-timer flymake-timer) - (setq flymake-timer nil))))) + (setq flymake-timer nil)) + (mapc #'delete-overlay (flymake--overlays)) + (when flymake--state + (maphash (lambda (_backend state) + (flymake--clear-foreign-diags state)) + flymake--state))) + ;; turning Flymake on or off has consequences for listings + (flymake--update-diagnostics-listings (current-buffer)))) (defun flymake--schedule-timer-maybe () "(Re)schedule an idle timer for checking the buffer. @@ -1109,9 +1234,9 @@ START and STOP and LEN are as in `after-change-functions'." (flymake-start t))) (defun flymake-kill-buffer-hook () - (when flymake-timer - (cancel-timer flymake-timer) - (setq flymake-timer nil))) + ;; Explicitly set flymake off, because that does a lot of useful + ;; cleanup. + (flymake-mode -1)) (defun flymake-find-file-hook () (unless (or flymake-mode @@ -1318,13 +1443,10 @@ TYPE is usually keyword `:error', `:warning' or `:note'." (face (flymake--lookup-type-property type 'mode-line-face 'compilation-error))) - (maphash (lambda - (_b state) - (dolist (d (flymake--state-diags state)) - (when (= (flymake--severity type) - (flymake--severity (flymake-diagnostic-type d))) - (cl-incf count)))) - flymake--state) + (dolist (d (flymake-diagnostics)) + (when (= (flymake--severity type) + (flymake--severity (flymake-diagnostic-type d))) + (cl-incf count))) (when (or (cl-plusp count) (cond ((eq flymake-suppress-zero-counters t) nil) @@ -1354,7 +1476,7 @@ TYPE is usually keyword `:error', `:warning' or `:note'." (flymake-goto-next-error 1 (list type) t)))) map)))))) -;;; Diagnostics buffer +;;; Per-buffer diagnostic listing (defvar-local flymake--diagnostics-buffer-source nil) @@ -1369,14 +1491,30 @@ TYPE is usually keyword `:error', `:warning' or `:note'." (interactive (list (point) t)) (let* ((id (or (tabulated-list-get-id pos) (user-error "Nothing at point"))) - (diag (plist-get id :diagnostic))) - (with-current-buffer (flymake-diagnostic-buffer diag) + (diag (plist-get id :diagnostic)) + (locus (flymake--diag-locus diag)) + (beg (flymake--diag-beg diag)) + (end (flymake--diag-end diag)) + (visit (lambda (b e) + (goto-char b) + (pulse-momentary-highlight-region (point) + (or e (line-end-position)) + 'highlight)))) + (with-current-buffer (cond ((bufferp locus) locus) + (t (find-file-noselect locus))) (with-selected-window (display-buffer (current-buffer) other-window) - (goto-char (flymake-diagnostic-beg diag)) - (pulse-momentary-highlight-region (point) - (flymake-diagnostic-end diag) - 'highlight)) + (cond (;; an annotated diagnostic (most common case), or a + ;; non-annotated buffer diag + (number-or-marker-p beg) + (funcall visit beg end)) + (;; a non-annotated file diag (TODO: could use `end' + ;; here, too) + (pcase-let ((`(,bbeg . ,bend) + (flymake-diag-region (current-buffer) + (car beg) + (cdr beg)))) + (funcall visit bbeg bend))))) (current-buffer)))) (defun flymake-goto-diagnostic (pos) @@ -1386,75 +1524,116 @@ POS can be a buffer position or a button" (pop-to-buffer (flymake-show-diagnostic (if (button-type pos) (button-start pos) pos)))) +(defun flymake--tabulated-entries-1 (diags project-root) + "Helper for `flymake--diagnostic-buffer-entries'. +PROJECT-ROOT indicates that each entry should be preceded by the +filename of the diagnostic relative to that directory." + (cl-loop + for diag in diags + for locus = (flymake-diagnostic-buffer diag) + for file = (if (bufferp locus) + (buffer-file-name locus) + locus) + for overlay = (flymake--diag-overlay diag) + for (line . col) = + (cond (;; has live overlay, use overlay for position + (and overlay (overlay-buffer overlay)) + (with-current-buffer (overlay-buffer overlay) + (save-excursion + (goto-char (overlay-start overlay)) + (cons (line-number-at-pos) + (- (point) + (line-beginning-position)))))) + (;; diagnostic not annotated, maybe foreign, check for cons + (consp (flymake--diag-beg diag)) + (flymake--diag-beg diag)) + (;; may still be a valid foreign diagnostic + (consp (flymake--diag-orig-beg diag)) + (flymake--diag-orig-beg diag)) + (;; somehow dead annotated diagnostic, ignore/give up + t nil)) + for type = (flymake-diagnostic-type diag) + for backend = (flymake-diagnostic-backend diag) + for bname = (or (ignore-errors (symbol-name backend)) + "(anonymous function)") + for data-vec = `[,(format "%s" line) + ,(format "%s" col) + ,(propertize (format "%s" + (flymake--lookup-type-property + type 'flymake-type-name type)) + 'face (flymake--lookup-type-property + type 'mode-line-face 'flymake-error)) + ,(propertize + (if bname + (replace-regexp-in-string "\\(.\\)[^-]+\\(-\\|$\\)" + "\\1\\2" bname) + "(anon)") + 'help-echo (format "From `%s' backend" backend)) + (,(replace-regexp-in-string "\n.*" "" + (flymake-diagnostic-text diag)) + mouse-face highlight + help-echo "mouse-2: visit this diagnostic" + face nil + action flymake-goto-diagnostic + mouse-action flymake-goto-diagnostic)] + when (and line col) collect + (list (list :diagnostic diag + :line line + :severity (flymake--lookup-type-property + type + 'severity (warning-numeric-level :error))) + (if project-root + (vconcat `[(,(file-name-nondirectory file) + help-echo ,(file-relative-name file project-root) + face nil + mouse-face highlight + action flymake-goto-diagnostic + mouse-action flymake-goto-diagnostic )] + data-vec) + data-vec)))) + (defun flymake--diagnostics-buffer-entries () + "Get tabulated list entries for current tabulated list buffer. +Expects `flymake--diagnostics-buffer-entries' to be bound to a +buffer." ;; Do nothing if 'flymake--diagnostics-buffer-source' has not yet ;; been set to a valid buffer. This could happen when this function ;; is called too early. For example 'global-display-line-numbers-mode' ;; calls us from its mode hook, when the diagnostic buffer has just ;; been created by 'flymake-show-diagnostics-buffer', but is not yet - ;; set up properly. + ;; set up properly (Bug#40529). (when (bufferp flymake--diagnostics-buffer-source) (with-current-buffer flymake--diagnostics-buffer-source - (cl-loop for diag in - (cl-sort (flymake-diagnostics) #'< :key #'flymake-diagnostic-beg) - for (line . col) = - (save-excursion - (goto-char (flymake-diagnostic-beg diag)) - (cons (line-number-at-pos) - (- (point) - (line-beginning-position)))) - for type = (flymake-diagnostic-type diag) - for backend = (flymake-diagnostic-backend diag) - for bname = (or (ignore-errors (symbol-name backend)) - "(anonymous function)") - collect - (list (list :diagnostic diag - :line line - :severity (flymake--lookup-type-property - type - 'severity (warning-numeric-level :error))) - `[,(format "%s" line) - ,(format "%s" col) - ,(propertize (format "%s" - (flymake--lookup-type-property - type 'flymake-type-name type)) - 'face (flymake--lookup-type-property - type 'mode-line-face 'flymake-error)) - ,(propertize - (if bname - (replace-regexp-in-string "\\(.\\)[^-]+\\(-\\|$\\)" - "\\1\\2" bname) - "(anon)") - 'help-echo (format "From `%s' backend" backend)) - (,(format "%s" (flymake-diagnostic-text diag)) - mouse-face highlight - help-echo "mouse-2: visit this diagnostic" - face nil - action flymake-goto-diagnostic - mouse-action flymake-goto-diagnostic)]))))) + (when flymake-mode + (flymake--tabulated-entries-1 (flymake-diagnostics) nil))))) + +(defvar flymake--diagnostics-base-tabulated-list-format + `[("Line" 5 ,(lambda (l1 l2) + (< (plist-get (car l1) :line) + (plist-get (car l2) :line))) + :right-align t) + ("Col" 3 nil :right-align t) + ("Type" 8 ,(lambda (l1 l2) + (< (plist-get (car l1) :severity) + (plist-get (car l2) :severity)))) + ("Backend" 8 t) + ("Message" 0 t)]) (define-derived-mode flymake-diagnostics-buffer-mode tabulated-list-mode "Flymake diagnostics" "A mode for listing Flymake diagnostics." - (setq tabulated-list-format - `[("Line" 5 ,(lambda (l1 l2) - (< (plist-get (car l1) :line) - (plist-get (car l2) :line))) - :right-align t) - ("Col" 3 nil :right-align t) - ("Type" 8 ,(lambda (l1 l2) - (< (plist-get (car l1) :severity) - (plist-get (car l2) :severity)))) - ("Backend" 8 t) - ("Message" 0 t)]) + (setq tabulated-list-format flymake--diagnostics-base-tabulated-list-format) (setq tabulated-list-entries 'flymake--diagnostics-buffer-entries) (tabulated-list-init-header)) (defun flymake--diagnostics-buffer-name () - (format "*Flymake diagnostics for %s*" (current-buffer))) + (format "*Flymake diagnostics for `%s'*" (current-buffer))) + +(define-obsolete-function-alias 'flymake-show-diagnostics-buffer + 'flymake-show-buffer-diagnostics "1.2.1") -(defun flymake-show-diagnostics-buffer () +(defun flymake-show-buffer-diagnostics () "Show a list of Flymake diagnostics for current buffer." (interactive) (let* ((name (flymake--diagnostics-buffer-name)) @@ -1465,8 +1644,121 @@ POS can be a buffer position or a button" (current-buffer))))) (with-current-buffer target (setq flymake--diagnostics-buffer-source source) - (revert-buffer) - (display-buffer (current-buffer))))) + (display-buffer (current-buffer)) + (revert-buffer)))) + + +;;; Per-project diagnostic listing +;;; + +(defvar flymake-list-only-diagnostics nil + "Diagnostics list meant for listing, not highlighting. +This variable holds an alist ((FILE-NAME . DIAGS) ...) where +FILE-NAME is a string holding an absolute file name and DIAGS is +a list of diagnostic objects created with with +`flymake-make-diagnostic'. These diagnostics are never annotated +as overlays in actual buffers: they merely serve as temporary +stand-ins for more accurate diagnostics that are produced once +the file they refer to is visited and `flymake-mode' is turned on +in the resulting buffer. + +Flymake backends that somehow gain sporadic information about +diagnostics in neighbouring files may freely modify this variable +by adding or removing entries to for those files. If the +information about those neighbouring files is acquired repeatedly +and reliably, it may be more sensible to report them as +\"foreign\" diagnostics instead. + +Commands such as `flymake-show-project-diagnostics' will include +some of this variable's contents the diagnostic listings.") + +(defvar-local flymake--project-diagnostic-list-project nil) + +(define-derived-mode flymake-project-diagnostics-mode tabulated-list-mode + "Flymake diagnostics" + "A mode for listing Flymake diagnostics." + (setq tabulated-list-format + (vconcat [("File" 25 t)] + flymake--diagnostics-base-tabulated-list-format)) + (setq tabulated-list-entries + 'flymake--project-diagnostics-entries) + (tabulated-list-init-header)) + +(cl-defun flymake--project-diagnostics (&optional (project (project-current))) + "Get all known relevant diagnostics for PROJECT." + (let* ((root (project-root project)) + (visited-buffers (cl-remove-if-not #'buffer-file-name (project-buffers project))) + buffer-annotated-diags + relevant-foreign-diags + list-only-diags + annotated-diag-files) + (setq buffer-annotated-diags + (cl-loop for buf in visited-buffers + for diags = (with-current-buffer buf + (flymake-diagnostics)) + when diags do + (push (buffer-file-name buf) annotated-diag-files) + append (cl-sort diags #'< :key #'flymake-diagnostic-beg))) + (cl-loop + for buf in visited-buffers + do (with-current-buffer buf + (when (and flymake-mode flymake--state) + (maphash + (lambda (_backend state) + (maphash + (lambda (foreign-file diags) + (setq foreign-file (expand-file-name foreign-file)) + ;; FIXME: This is not right if more than one visited + ;; source targets the same foreign file. Don't + ;; think we can get away without some kind of + ;; `cl-remove-duplicates' here that utilizes + ;; `flymake--equal-diagnostic-p'. + (unless (member foreign-file annotated-diag-files) + (push foreign-file annotated-diag-files) + (setq relevant-foreign-diags + (append relevant-foreign-diags + diags)))) + (flymake--state-foreign-diags state))) + flymake--state)))) + (setq list-only-diags + (cl-loop for (file-name . diags) in flymake-list-only-diagnostics + if (and (string-prefix-p (expand-file-name root) file-name) + (not (member file-name annotated-diag-files))) + append diags)) + (append buffer-annotated-diags relevant-foreign-diags list-only-diags))) + +(defun flymake--project-diagnostics-entries () + (let ((p (project-current))) + (flymake--tabulated-entries-1 (flymake--project-diagnostics p) + (project-root p)))) + +(defun flymake--project-diagnostics-buffer (root) + (get-buffer-create (format "*Flymake diagnostics for `%s'*" root))) + +(defun flymake-show-project-diagnostics () + "Show a list of Flymake diagnostics for the current project." + (interactive) + (let* ((prj (project-current)) + (root (project-root prj)) + (buffer (flymake--project-diagnostics-buffer root))) + (with-current-buffer buffer + (flymake-project-diagnostics-mode) + (setq-local flymake--project-diagnostic-list-project prj) + (display-buffer (current-buffer)) + (revert-buffer)))) + +(defun flymake--update-diagnostics-listings (buffer) + "Update diagnostics listings somehow relevant to BUFFER" + (dolist (probe (buffer-list)) + (with-current-buffer probe + (when (or (and (eq major-mode 'flymake-project-diagnostics-mode) + flymake--project-diagnostic-list-project + (buffer-file-name buffer) + (memq buffer + (project-buffers flymake--project-diagnostic-list-project))) + (and (eq major-mode 'flymake-diagnostics-buffer-mode) + (eq flymake--diagnostics-buffer-source buffer))) + (revert-buffer))))) (provide 'flymake) commit 79eb840753edb2dd9e0bf1d4befb8e3e1275e009 Author: João Távora Date: Mon Sep 13 17:43:43 2021 +0100 Bump lisp/progmodes/project.el version to 0.7.1 Amont other things exposes the new project-buffers generic function to ELPA users. * lisp/progmodes/project.el (Version): Bump to 0.7.1 diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index b2b1a7870a..67c34b4e5c 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -1,7 +1,7 @@ ;;; project.el --- Operations on the current project -*- lexical-binding: t; -*- ;; Copyright (C) 2015-2021 Free Software Foundation, Inc. -;; Version: 0.6.1 +;; Version: 0.7.1 ;; Package-Requires: ((emacs "26.1") (xref "1.0.2")) ;; This is a GNU ELPA :core package. Avoid using functionality that commit aec08e609e6c672c6e85d91b84f45e38019ccc9e Author: João Távora Date: Mon Aug 30 16:24:25 2021 +0100 Keep and report "foreign" diangnostics in flymake-cc Flymake backend This includes diagnostics for .h files that sprang up when checking a c file. Those diagnostics are reported to the Flymake infrastructure which does not (yet) do anything with them. This includes a change to the test fixtures, too. * lisp/progmodes/flymake-cc.el (flymake-cc--make-diagnostics): Rework * test/lisp/progmodes/flymake-resources/another-problematic-file.c: New file. * test/lisp/progmodes/flymake-resources/some-problems.h: Add a function declaration.. diff --git a/lisp/progmodes/flymake-cc.el b/lisp/progmodes/flymake-cc.el index bd403faf7c..907300eb27 100644 --- a/lisp/progmodes/flymake-cc.el +++ b/lisp/progmodes/flymake-cc.el @@ -61,23 +61,34 @@ SOURCE." (cl-loop while (search-forward-regexp - "^\\(In file included from \\)?:\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?:\n?\\(.*\\): \\(.*\\)$" + "^\\(In file included from \\)?\\([^ :]+\\):\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?:\n?\\(.*\\): \\(.*\\)$" nil t) - for msg = (match-string 5) - for (beg . end) = (flymake-diag-region - source - (string-to-number (match-string 2)) - (and (match-string 3) (string-to-number (match-string 3)))) + for msg = (match-string 6) + for locus = (match-string 2) + for line = (string-to-number (match-string 3)) + for col = (ignore-errors (string-to-number (match-string 4))) + for source-buffer = (and (string= locus "") source) for type = (if (match-string 1) :error - (assoc-default - (match-string 4) - '(("error" . :error) - ("note" . :note) - ("warning" . :warning)) - #'string-match - :error)) - collect (flymake-make-diagnostic source beg end type msg))) + (save-match-data + (assoc-default + (match-string 5) + '(("error" . :error) + ("note" . :note) + ("warning" . :warning)) + #'string-match + :error))) + for diag = + (cond (source-buffer + (pcase-let ((`(,beg . ,end) + (flymake-diag-region source-buffer line col))) + (flymake-make-diagnostic source-buffer beg end type msg))) + (t (flymake-make-diagnostic locus (cons line col) nil type msg))) + collect diag + ;; If "In file included from..." matched, then move to end of that + ;; line. This helps us collect the diagnostic at its .h locus, + ;; too. + when (match-end 1) do (goto-char (match-end 2)))) (defun flymake-cc-use-special-make-target () "Command for checking a file via a CHK_SOURCES Make target." diff --git a/test/lisp/progmodes/flymake-resources/another-problematic-file.c b/test/lisp/progmodes/flymake-resources/another-problematic-file.c new file mode 100644 index 0000000000..03eacdd801 --- /dev/null +++ b/test/lisp/progmodes/flymake-resources/another-problematic-file.c @@ -0,0 +1,5 @@ +#include "some-problems.h" + +int frob(char* freb) { + return 42; +} diff --git a/test/lisp/progmodes/flymake-resources/some-problems.h b/test/lisp/progmodes/flymake-resources/some-problems.h index 165d8dd525..86ea2de3b0 100644 --- a/test/lisp/progmodes/flymake-resources/some-problems.h +++ b/test/lisp/progmodes/flymake-resources/some-problems.h @@ -2,4 +2,6 @@ strange; +int frob(char); + sint main(); commit 7d9d3951c6736d5606828e2727a7319856511ddb Author: João Távora Date: Fri Sep 3 19:03:51 2021 +0100 Abbreviate Flymake backend name in flymake-show-diagnostics-buffer * lisp/progmodes/flymake.el (flymake--diagnostics-buffer-entries): (flymake-diagnostics-buffer-mode): Report abbreviated backend, too. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index b1dbde9bcd..71dd4b6b1a 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -1404,6 +1404,9 @@ POS can be a buffer position or a button" (- (point) (line-beginning-position)))) for type = (flymake-diagnostic-type diag) + for backend = (flymake-diagnostic-backend diag) + for bname = (or (ignore-errors (symbol-name backend)) + "(anonymous function)") collect (list (list :diagnostic diag :line line @@ -1417,6 +1420,12 @@ POS can be a buffer position or a button" type 'flymake-type-name type)) 'face (flymake--lookup-type-property type 'mode-line-face 'flymake-error)) + ,(propertize + (if bname + (replace-regexp-in-string "\\(.\\)[^-]+\\(-\\|$\\)" + "\\1\\2" bname) + "(anon)") + 'help-echo (format "From `%s' backend" backend)) (,(format "%s" (flymake-diagnostic-text diag)) mouse-face highlight help-echo "mouse-2: visit this diagnostic" @@ -1436,6 +1445,7 @@ POS can be a buffer position or a button" ("Type" 8 ,(lambda (l1 l2) (< (plist-get (car l1) :severity) (plist-get (car l2) :severity)))) + ("Backend" 8 t) ("Message" 0 t)]) (setq tabulated-list-entries 'flymake--diagnostics-buffer-entries) commit e793a73fbea3167368bd173211c1995e07dcf913 Author: João Távora Date: Tue Aug 31 18:32:58 2021 +0100 Unbreak M-x compile-defun of functions using flymake-log * lisp/progmodes/flymake.el (flymake-log): Check if compilation unit is indeed a string before treating it as a file name. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 6386718a46..b1dbde9bcd 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -291,7 +291,7 @@ generated it." (macroexp-file-name) (and (not load-file-name) (bound-and-true-p byte-compile-current-file)))) - (sublog (if file + (sublog (if (stringp file) (intern (file-name-nondirectory (file-name-sans-extension file)))))) commit 404023299429a2619c6e4bc5da80eb248d060ef9 Author: João Távora Date: Tue Aug 31 18:43:12 2021 +0100 Refactor some Flymake functions * lisp/progmodes/flymake.el (flymake-diagnostic-buffer): New helper. (flymake-diagnostic-beg, flymake-diagnostic-end): Tweak docstring. (flymake--handle-report): Simplify. (flymake--publish-diagnostics): Helper for flymake--handle-report. (flymake--mode-line-counter, flymake-show-diagnostic) (flymake--diagnostics-buffer-entries): Use flymake-diagnostic-buffer, flymake-diagonstic-type, flymake-diagnostic-beg. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index a3918dcf2d..6386718a46 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -319,7 +319,13 @@ generated it." TYPE is a diagnostic symbol and TEXT is string describing the problem detected in this region. DATA is any object that the caller wishes to attach to the created diagnostic for later -retrieval. +retrieval with `flymake-diagnostic-data'. + +BEG and END may each be either buffer positions (number or +markers) or a cons (LINE . COL). When using the second form the +numbers will be converted to buffer positions (using +`flymake-diag-region') as soon as the diagnostic is appended to +an actual buffer. OVERLAY-PROPERTIES is an alist of properties attached to the created diagnostic, overriding the default properties and any @@ -345,15 +351,18 @@ diagnostics at BEG." ,(format "Get Flymake diagnostic DIAG's %s." (symbol-name thing)) (,internal diag))) -(flymake--diag-accessor flymake-diagnostic-buffer flymake--diag-buffer buffer) (flymake--diag-accessor flymake-diagnostic-text flymake--diag-text text) (flymake--diag-accessor flymake-diagnostic-type flymake--diag-type type) (flymake--diag-accessor flymake-diagnostic-backend flymake--diag-backend backend) (flymake--diag-accessor flymake-diagnostic-data flymake--diag-data backend) +(defun flymake-diagnostic-buffer (diag) + "Get Flymake diagnostic DIAG's buffer." + (flymake--diag-buffer diag)) + (defun flymake-diagnostic-beg (diag) "Get Flymake diagnostic DIAG's start position. -This position only be queried after DIAG has been reported to Flymake." +May only be queried after DIAG has been reported to Flymake." (let ((overlay (flymake--diag-overlay diag))) (unless overlay (error "DIAG %s not reported to Flymake yet" diag)) @@ -361,7 +370,7 @@ This position only be queried after DIAG has been reported to Flymake." (defun flymake-diagnostic-end (diag) "Get Flymake diagnostic DIAG's end position. -This position only be queried after DIAG has been reported to Flymake." +May only be queried after DIAG has been reported to Flymake." (let ((overlay (flymake--diag-overlay diag))) (unless overlay (error "DIAG %s not reported to Flymake yet" diag)) @@ -627,7 +636,7 @@ associated `flymake-category' return DEFAULT." (defun flymake--highlight-line (diagnostic) "Highlight buffer with info in DIAGNOSTIC." - (let ((type (or (flymake--diag-type diagnostic) + (let ((type (or (flymake-diagnostic-type diagnostic) :error)) (ov (make-overlay (flymake--diag-beg diagnostic) @@ -665,7 +674,7 @@ associated `flymake-category' return DEFAULT." (lambda (window _ov pos) (with-selected-window window (mapconcat - #'flymake--diag-text + #'flymake-diagnostic-text (flymake-diagnostics pos) "\n")))) (default-maybe 'severity (warning-numeric-level :error)) @@ -730,9 +739,15 @@ backend is operating normally.") (and (>= start1 start0) (< start1 end0)) (and (> end1 start0) (<= end1 end0)))) -(cl-defun flymake--handle-report (backend token report-action - &key explanation force region - &allow-other-keys) +(cl-defun flymake--handle-report + (backend token report-action + &key explanation force region + &allow-other-keys + &aux + (state (or (gethash backend flymake--state) + (error "Can't find state for %s in `flymake--state'" + backend))) + expected-token) "Handle reports from BACKEND identified by TOKEN. BACKEND, REPORT-ACTION and EXPLANATION, and FORCE conform to the calling convention described in @@ -740,81 +755,84 @@ calling convention described in to handle a report even if TOKEN was not expected. REGION is a (BEG . END) pair of buffer positions indicating that this report applies to that region." - (let* ((state (gethash backend flymake--state)) - first-report) - (unless state - (error "Can't find state for %s in `flymake--state'" backend)) - (setf first-report (not (flymake--state-reported-p state))) - (setf (flymake--state-reported-p state) t) - (let (expected-token + (cond + ((null state) + (flymake-error + "Unexpected report from unknown backend %s" backend)) + ((flymake--state-disabled state) + (flymake-error + "Unexpected report from disabled backend %s" backend)) + ((progn + (setq expected-token (flymake--state-running state)) + (null expected-token)) + ;; should never happen + (flymake-error "Unexpected report from stopped backend %s" backend)) + ((not (or (eq expected-token token) + force)) + (flymake-error "Obsolete report from backend %s with explanation %s" + backend explanation)) + ((eq :panic report-action) + (flymake--disable-backend backend explanation)) + ((not (listp report-action)) + (flymake--disable-backend backend + (format "Unknown action %S" report-action)) + (flymake-error "Expected report, but got unknown key %s" report-action)) + (t + (flymake--publish-diagnostics report-action + :backend backend + :state state + :region region))) + (setf (flymake--state-reported-p state) t)) + +(cl-defun flymake--publish-diagnostics (diags &key backend state region + &aux new-diags other-diags) + "Helper for `flymake--handle-report'. +Publish DIAGS " + (cl-loop for d in diags + if (eq (flymake--diag-buffer d) (current-buffer)) + do (push d new-diags) + else + do (push d other-diags)) + (save-restriction + (widen) + ;; Before adding to backend's diagnostic list, decide if + ;; some or all must be deleted. When deleting, also delete + ;; the associated overlay. + (cond + (region + (cl-loop for diag in (flymake--state-diags state) + for ov = (flymake--diag-overlay diag) + if (or (not (overlay-buffer ov)) + (flymake--intersects-p + (overlay-start ov) (overlay-end ov) + (car region) (cdr region))) + do (delete-overlay ov) + else collect diag into surviving + finally (setf (flymake--state-diags state) + surviving))) + ((not (flymake--state-reported-p state)) + (dolist (diag (flymake--state-diags state)) + (delete-overlay (flymake--diag-overlay diag))) + (setf (flymake--state-diags state) nil))) + ;; Now make new overlays + (mapc (lambda (diag) + (let ((overlay (flymake--highlight-line diag))) + (setf (flymake--diag-backend diag) backend + (flymake--diag-overlay diag) overlay))) new-diags) - (cond - ((null state) - (flymake-error - "Unexpected report from unknown backend %s" backend)) - ((flymake--state-disabled state) - (flymake-error - "Unexpected report from disabled backend %s" backend)) - ((progn - (setq expected-token (flymake--state-running state)) - (null expected-token)) - ;; should never happen - (flymake-error "Unexpected report from stopped backend %s" backend)) - ((not (or (eq expected-token token) - force)) - (flymake-error "Obsolete report from backend %s with explanation %s" - backend explanation)) - ((eq :panic report-action) - (flymake--disable-backend backend explanation)) - ((not (listp report-action)) - (flymake--disable-backend backend - (format "Unknown action %S" report-action)) - (flymake-error "Expected report, but got unknown key %s" report-action)) - (t - (setq new-diags - (cl-remove-if-not - (lambda (diag) (eq (flymake--diag-buffer diag) (current-buffer))) - report-action)) - (save-restriction - (widen) - ;; Before adding to backend's diagnostic list, decide if - ;; some or all must be deleted. When deleting, also delete - ;; the associated overlay. - (cond - (region - (cl-loop for diag in (flymake--state-diags state) - for ov = (flymake--diag-overlay diag) - if (or (not (overlay-buffer ov)) - (flymake--intersects-p - (overlay-start ov) (overlay-end ov) - (car region) (cdr region))) - do (delete-overlay ov) - else collect diag into surviving - finally (setf (flymake--state-diags state) - surviving))) - (first-report - (dolist (diag (flymake--state-diags state)) - (delete-overlay (flymake--diag-overlay diag))) - (setf (flymake--state-diags state) nil))) - ;; Now make new ones - (mapc (lambda (diag) - (let ((overlay (flymake--highlight-line diag))) - (setf (flymake--diag-backend diag) backend - (flymake--diag-overlay diag) overlay))) - new-diags) - (setf (flymake--state-diags state) - (append new-diags (flymake--state-diags state))) - (when flymake-check-start-time - (flymake-log :debug "backend %s reported %d diagnostics in %.2f second(s)" - backend - (length new-diags) - (float-time - (time-since flymake-check-start-time)))) - (when (and (get-buffer (flymake--diagnostics-buffer-name)) - (get-buffer-window (flymake--diagnostics-buffer-name)) - (null (cl-set-difference (flymake-running-backends) - (flymake-reporting-backends)))) - (flymake-show-diagnostics-buffer)))))))) + (setf (flymake--state-diags state) + (append new-diags (flymake--state-diags state))) + (when flymake-check-start-time + (flymake-log :debug "backend %s reported %d diagnostics in %.2f second(s)" + backend + (length new-diags) + (float-time + (time-since flymake-check-start-time)))) + (when (and (get-buffer (flymake--diagnostics-buffer-name)) + (get-buffer-window (flymake--diagnostics-buffer-name)) + (null (cl-set-difference (flymake-running-backends) + (flymake-reporting-backends)))) + (flymake-show-diagnostics-buffer)))) (defun flymake-make-report-fn (backend &optional token) "Make a suitable anonymous report function for BACKEND. @@ -1137,7 +1155,7 @@ default) no filter is applied." (not filter) (cl-find (flymake--severity - (flymake--diag-type diag)) + (flymake-diagnostic-type diag)) filter :key #'flymake--severity))))) :compare (if (cl-plusp n) #'< #'>) :key #'overlay-start)) @@ -1304,7 +1322,7 @@ TYPE is usually keyword `:error', `:warning' or `:note'." (_b state) (dolist (d (flymake--state-diags state)) (when (= (flymake--severity type) - (flymake--severity (flymake--diag-type d))) + (flymake--severity (flymake-diagnostic-type d))) (cl-incf count)))) flymake--state) (when (or (cl-plusp count) @@ -1352,11 +1370,11 @@ TYPE is usually keyword `:error', `:warning' or `:note'." (let* ((id (or (tabulated-list-get-id pos) (user-error "Nothing at point"))) (diag (plist-get id :diagnostic))) - (with-current-buffer (flymake--diag-buffer diag) + (with-current-buffer (flymake-diagnostic-buffer diag) (with-selected-window (display-buffer (current-buffer) other-window) - (goto-char (flymake--diag-beg diag)) - (pulse-momentary-highlight-region (flymake-diagnostic-beg diag) + (goto-char (flymake-diagnostic-beg diag)) + (pulse-momentary-highlight-region (point) (flymake-diagnostic-end diag) 'highlight)) (current-buffer)))) @@ -1381,11 +1399,11 @@ POS can be a buffer position or a button" (cl-sort (flymake-diagnostics) #'< :key #'flymake-diagnostic-beg) for (line . col) = (save-excursion - (goto-char (flymake--diag-beg diag)) + (goto-char (flymake-diagnostic-beg diag)) (cons (line-number-at-pos) (- (point) (line-beginning-position)))) - for type = (flymake--diag-type diag) + for type = (flymake-diagnostic-type diag) collect (list (list :diagnostic diag :line line @@ -1399,7 +1417,7 @@ POS can be a buffer position or a button" type 'flymake-type-name type)) 'face (flymake--lookup-type-property type 'mode-line-face 'flymake-error)) - (,(format "%s" (flymake--diag-text diag)) + (,(format "%s" (flymake-diagnostic-text diag)) mouse-face highlight help-echo "mouse-2: visit this diagnostic" face nil commit 6e100869012da9244679696634cab6b9cac96303 Author: João Távora Date: Sun Aug 29 22:35:46 2021 +0100 Rename flymake--backend-state to flymake--state The previous name was confusing and akward and dreadful to type and read. * lisp/progmodes/flymake.el (flymake--state): Rename from flymake--backend-state. (flymake--with-backend-state): Use flymake--state. (flymake--handle-report): Use flymake--state. (flymake--collect): Use flymake--state. (flymake-running-backends): Use flymake--state. (flymake--disable-backend): Use flymake--state. (flymake--run-backend): Use flymake--state. (flymake-start): Use flymake--state. (flymake-mode): Use flymake--state. (flymake--mode-line-title): Use flymake--state. (flymake--mode-line-exception): Use flymake--state. (flymake--mode-line-counter): Use flymake--state. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 5879bce2a7..a3918dcf2d 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -682,11 +682,11 @@ associated `flymake-category' return DEFAULT." ;; third-party compatibility. (define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") -(defvar-local flymake--backend-state nil - "Buffer-local hash table of a Flymake backend's state. +(defvar-local flymake--state nil + "State of a buffer's multiple Flymake backends. The keys to this hash table are functions as found in `flymake-diagnostic-functions'. The values are structures -of the type `flymake--backend-state', with these slots: +of the type `flymake--state', with these slots: `running', a symbol to keep track of a backend's replies via its REPORT-FN argument. A backend is running if this key is @@ -703,7 +703,7 @@ since it last was contacted. exceptional situation reported by the backend, nil if the backend is operating normally.") -(cl-defstruct (flymake--backend-state +(cl-defstruct (flymake--state (:constructor flymake--make-backend-state)) running reported-p disabled diags) @@ -713,9 +713,9 @@ backend is operating normally.") (let ((b (make-symbol "b"))) `(let* ((,b ,backend) (,state-var - (or (gethash ,b flymake--backend-state) + (or (gethash ,b flymake--state) (puthash ,b (flymake--make-backend-state) - flymake--backend-state)))) + flymake--state)))) ,@body))) (defun flymake-is-running () @@ -740,23 +740,23 @@ calling convention described in to handle a report even if TOKEN was not expected. REGION is a (BEG . END) pair of buffer positions indicating that this report applies to that region." - (let* ((state (gethash backend flymake--backend-state)) + (let* ((state (gethash backend flymake--state)) first-report) (unless state - (error "Can't find state for %s in `flymake--backend-state'" backend)) - (setf first-report (not (flymake--backend-state-reported-p state))) - (setf (flymake--backend-state-reported-p state) t) + (error "Can't find state for %s in `flymake--state'" backend)) + (setf first-report (not (flymake--state-reported-p state))) + (setf (flymake--state-reported-p state) t) (let (expected-token new-diags) (cond ((null state) (flymake-error "Unexpected report from unknown backend %s" backend)) - ((flymake--backend-state-disabled state) + ((flymake--state-disabled state) (flymake-error "Unexpected report from disabled backend %s" backend)) ((progn - (setq expected-token (flymake--backend-state-running state)) + (setq expected-token (flymake--state-running state)) (null expected-token)) ;; should never happen (flymake-error "Unexpected report from stopped backend %s" backend)) @@ -782,7 +782,7 @@ report applies to that region." ;; the associated overlay. (cond (region - (cl-loop for diag in (flymake--backend-state-diags state) + (cl-loop for diag in (flymake--state-diags state) for ov = (flymake--diag-overlay diag) if (or (not (overlay-buffer ov)) (flymake--intersects-p @@ -790,20 +790,20 @@ report applies to that region." (car region) (cdr region))) do (delete-overlay ov) else collect diag into surviving - finally (setf (flymake--backend-state-diags state) + finally (setf (flymake--state-diags state) surviving))) (first-report - (dolist (diag (flymake--backend-state-diags state)) + (dolist (diag (flymake--state-diags state)) (delete-overlay (flymake--diag-overlay diag))) - (setf (flymake--backend-state-diags state) nil))) + (setf (flymake--state-diags state) nil))) ;; Now make new ones (mapc (lambda (diag) (let ((overlay (flymake--highlight-line diag))) (setf (flymake--diag-backend diag) backend (flymake--diag-overlay diag) overlay))) new-diags) - (setf (flymake--backend-state-diags state) - (append new-diags (flymake--backend-state-diags state))) + (setf (flymake--state-diags state) + (append new-diags (flymake--state-diags state))) (when flymake-check-start-time (flymake-log :debug "backend %s reported %d diagnostics in %.2f second(s)" backend @@ -830,12 +830,12 @@ different runs of the same backend." (defun flymake--collect (fn &optional message-prefix) "Collect Flymake backends matching FN. If MESSAGE-PREFIX, echo a message using that prefix." - (unless flymake--backend-state + (unless flymake--state (user-error "Flymake is not initialized")) (let (retval) (maphash (lambda (backend state) (when (funcall fn state) (push backend retval))) - flymake--backend-state) + flymake--state) (when message-prefix (message "%s%s" message-prefix @@ -846,21 +846,21 @@ If MESSAGE-PREFIX, echo a message using that prefix." (defun flymake-running-backends () "Compute running Flymake backends in current buffer." (interactive) - (flymake--collect #'flymake--backend-state-running + (flymake--collect #'flymake--state-running (and (called-interactively-p 'interactive) "Running backends: "))) (defun flymake-disabled-backends () "Compute disabled Flymake backends in current buffer." (interactive) - (flymake--collect #'flymake--backend-state-disabled + (flymake--collect #'flymake--state-disabled (and (called-interactively-p 'interactive) "Disabled backends: "))) (defun flymake-reporting-backends () "Compute reporting Flymake backends in current buffer." (interactive) - (flymake--collect #'flymake--backend-state-reported-p + (flymake--collect #'flymake--state-reported-p (and (called-interactively-p 'interactive) "Reporting backends: "))) @@ -869,9 +869,9 @@ If MESSAGE-PREFIX, echo a message using that prefix." If it is running also stop it." (flymake-log :warning "Disabling backend %s because %s" backend explanation) (flymake--with-backend-state backend state - (setf (flymake--backend-state-running state) nil - (flymake--backend-state-disabled state) explanation - (flymake--backend-state-reported-p state) t))) + (setf (flymake--state-running state) nil + (flymake--state-disabled state) explanation + (flymake--state-reported-p state) t))) (defun flymake--run-backend (backend &optional args) "Run the backend BACKEND, re-enabling if necessary. @@ -880,9 +880,9 @@ with a report function." (flymake-log :debug "Running backend %s" backend) (let ((run-token (cl-gensym "backend-token"))) (flymake--with-backend-state backend state - (setf (flymake--backend-state-running state) run-token - (flymake--backend-state-disabled state) nil - (flymake--backend-state-reported-p state) nil)) + (setf (flymake--state-running state) run-token + (flymake--state-disabled state) nil + (flymake--state-reported-p state) nil)) ;; FIXME: Should use `condition-case-unless-debug' here, but don't ;; for two reasons: (1) that won't let me catch errors from inside ;; `ert-deftest' where `debug-on-error' appears to be always @@ -964,7 +964,7 @@ Interactively, with a prefix arg, FORCE is t." (cond ((and (not force) (flymake--with-backend-state backend state - (flymake--backend-state-disabled state))) + (flymake--state-disabled state))) (flymake-log :debug "Backend %s is disabled, not starting" backend)) (t @@ -1019,10 +1019,10 @@ special *Flymake log* buffer." :group 'flymake :lighter ;; If Flymake happened to be already already ON, we must cleanup ;; existing diagnostic overlays, lest we forget them by blindly - ;; reinitializing `flymake--backend-state' in the next line. + ;; reinitializing `flymake--state' in the next line. ;; See https://github.com/joaotavora/eglot/issues/223. (mapc #'delete-overlay (flymake--overlays)) - (setq flymake--backend-state (make-hash-table)) + (setq flymake--state (make-hash-table)) (setq flymake--recent-changes nil) (when flymake-start-on-flymake-mode (flymake-start t))) @@ -1247,7 +1247,7 @@ correctly.") help-echo ,(lambda (&rest _) (concat - (format "%s known backends\n" (hash-table-count flymake--backend-state)) + (format "%s known backends\n" (hash-table-count flymake--state)) (format "%s running\n" (length (flymake-running-backends))) (format "%s disabled\n" (length (flymake-disabled-backends))) "mouse-1: Display minor mode menu\n" @@ -1268,7 +1268,7 @@ correctly.") "Helper for `flymake-mode-line-exception'." (pcase-let* ((running) (reported) (`(,ind ,face ,explain) - (cond ((zerop (hash-table-count flymake--backend-state)) + (cond ((zerop (hash-table-count flymake--state)) '("?" nil "No known backends")) ((cl-set-difference (setq running (flymake-running-backends)) @@ -1302,11 +1302,11 @@ TYPE is usually keyword `:error', `:warning' or `:note'." 'compilation-error))) (maphash (lambda (_b state) - (dolist (d (flymake--backend-state-diags state)) + (dolist (d (flymake--state-diags state)) (when (= (flymake--severity type) (flymake--severity (flymake--diag-type d))) (cl-incf count)))) - flymake--backend-state) + flymake--state) (when (or (cl-plusp count) (cond ((eq flymake-suppress-zero-counters t) nil) commit eeb6d9d221d99f65c7995d065f9e11b2ef102c6b Author: Lars Ingebrigtsen Date: Tue Sep 14 13:21:47 2021 +0200 Project File Commands manual clarification * doc/emacs/maintaining.texi (Project File Commands): Clarify what happens with the file name under point. diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi index 4ec2b2d72a..4ee0f32bd5 100644 --- a/doc/emacs/maintaining.texi +++ b/doc/emacs/maintaining.texi @@ -1735,7 +1735,8 @@ the full file name of the file to visit, you can type only the file's base name (i.e., omit the leading directories). In addition, the completion candidates considered by the command include only the files belonging to the current project, and nothing else. If there's a file -name at point, this command offers that file as the default to visit. +name at point, this command offers that file as the first element of +the ``future history''. @findex project-find-regexp The command @kbd{C-x p g} (@code{project-find-regexp}) is similar to diff --git a/etc/NEWS b/etc/NEWS index d4f4c81f89..bba86984c8 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2342,10 +2342,12 @@ This specifies the file in which to save the list of known projects. This command lets you interactively remove an entry from the list of projects in 'project-list-file'. +--- *** 'project-find-file' now accepts non-existent file names. This is to allow easy creation of files inside some nested sub-directory. ++++ *** 'project-find-file' doesn't use the string at point as default input. Now it's only suggested as part of the "future history". commit 1e83d04214f27a79a4d4841772da946e24cbf21d Author: Alan Third Date: Mon Sep 13 20:09:22 2021 +0100 Fix incorrectly appearing toolbar on NS (bug#50534) * src/nsmenu.m (update_frame_tool_bar): Ensure both sides of the test are booleans. * src/nsterm.m ([EmacsWindow createToolbar:]): Make the toolbar non-visible initially, in case things get out of sync. Remove call to update_frame_tool_bar: the window isn't yet associated with the view, so it will return immediately. diff --git a/src/nsmenu.m b/src/nsmenu.m index 3493e4e131..f0c5bb24e6 100644 --- a/src/nsmenu.m +++ b/src/nsmenu.m @@ -1094,7 +1094,7 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item #undef TOOLPROP } - if ([toolbar isVisible] != FRAME_EXTERNAL_TOOL_BAR (f)) + if (![toolbar isVisible] != !FRAME_EXTERNAL_TOOL_BAR (f)) { f->output_data.ns->in_animation = 1; [toolbar setVisible: FRAME_EXTERNAL_TOOL_BAR (f)]; diff --git a/src/nsterm.m b/src/nsterm.m index 8d88f7bd3d..7c90bbd578 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -8323,10 +8323,9 @@ - (void)createToolbar: (struct frame *)f EmacsToolbar *toolbar = [[EmacsToolbar alloc] initForView:view withIdentifier:[NSString stringWithLispString:f->name]]; + [toolbar setVisible:NO]; [self setToolbar:toolbar]; - update_frame_tool_bar (f); - #ifdef NS_IMPL_COCOA { NSButton *toggleButton; commit 443d37ff213ec31c8450a9cce36a7c44ac63945b Author: Alan Third Date: Thu Sep 9 22:33:01 2021 +0100 Fix libgccjit detection on macOS * configure.ac: Combine the Homebrew and MacPorts detection so they will not create nonsense flags if both are installed. diff --git a/configure.ac b/configure.ac index 418a62fd5e..1146b581cd 100644 --- a/configure.ac +++ b/configure.ac @@ -3822,40 +3822,44 @@ if test "${with_native_compilation}" != "no"; then AC_MSG_ERROR(['--with-native-compilation' requires zlib]) fi - # Ensure libgccjit installed by Homebrew can be found. - if test -n "$BREW"; then - if test -n "`$BREW --prefix --installed libgccjit 2>/dev/null`"; then - BREW_LIBGCCJIT_INCLUDE=$(dirname $($BREW ls -v libgccjit | \ - grep libgccjit.h)) - BREW_LIBGCCJIT_LIB=$(dirname $($BREW ls -v libgccjit| \ - grep libgccjit.so\$)) - CFLAGS="$CFLAGS -I${BREW_LIBGCCJIT_INCLUDE}" - LDFLAGS="$LDFLAGS -L${BREW_LIBGCCJIT_LIB}" + SAVE_CFLAGS=$CFLAGS + SAVE_LIBS=$LIBS + + if test "${opsys}" = "darwin"; then + # Ensure libgccjit installed by Homebrew or macports can be found. + if test -n "$BREW"; then + if test -n "`$BREW --prefix --installed libgccjit 2>/dev/null`"; then + MAC_CFLAGS="-I$(dirname $($BREW ls -v libgccjit | \ + grep libgccjit.h))" + MAC_LIBS="-L$(dirname $($BREW ls -v libgccjit| \ + grep libgccjit.so\$))" + fi fi - fi - # Ensure libgccjit installed by MacPorts can be found. - if test -n "$HAVE_MACPORTS"; then - # Determine which gcc version has been installed (gcc11, for - # instance). - PORT_PACKAGE=$(port installed active | grep '^ *gcc@<:@0-9@:>@* ' | \ - awk '{ print $1; }') - MACPORTS_LIBGCCJIT_INCLUDE=$(dirname $(port contents $PORT_PACKAGE | \ - grep libgccjit.h)) - MACPORTS_LIBGCCJIT_LIB=$(dirname $(port contents $PORT_PACKAGE | \ - grep libgccjit.dylib)) - CFLAGS="$CFLAGS -I${MACPORTS_LIBGCCJIT_INCLUDE}" - LDFLAGS="$LDFLAGS -L${MACPORTS_LIBGCCJIT_LIB}" + if test -n "$HAVE_MACPORTS"; then + # Determine which gcc version has been installed (gcc11, for + # instance). + PORT_PACKAGE=$(port installed active | grep '^ *gcc@<:@0-9@:>@* ' | \ + awk '{ print $1; }') + if test -n "$PORT_PACKAGE"; then + MAC_CFLAGS="-I$(dirname $(port contents $PORT_PACKAGE | \ + grep libgccjit.h))" + MAC_LIBS="-L$(dirname $(port contents $PORT_PACKAGE | \ + grep libgccjit.dylib))" + fi + fi + + if test -n "$MAC_CFLAGS" && test -n "$MAC_LIBS"; then + CFLAGS="$CFLAGS ${MAC_CFLAGS}" + LIBS="$LIBS ${MAC_LIBS}" + fi fi # Check if libgccjit is available. AC_CHECK_LIB(gccjit, gcc_jit_context_acquire, [], [libgccjit_not_found]) AC_CHECK_HEADERS(libgccjit.h, [], [libgccjit_dev_not_found]) - emacs_save_LIBS=$LIBS - LIBS="-lgccjit" # Check if libgccjit really works. AC_RUN_IFELSE([libgccjit_smoke_test], [], [libgccjit_broken]) - LIBS=$emacs_save_LIBS HAVE_NATIVE_COMP=yes case "${opsys}" in # mingw32 loads the library dynamically. @@ -3863,17 +3867,17 @@ if test "${with_native_compilation}" != "no"; then # OpenBSD doesn't have libdl, all the functions are in libc netbsd|openbsd) LIBGCCJIT_LIBS="-lgccjit" ;; + darwin) + LIBGCCJIT_CFLAGS="${MAC_CFLAGS}" + LIBGCCJIT_LIBS="${MAC_LIBS} -lgccjit -ldl";; *) LIBGCCJIT_LIBS="-lgccjit -ldl" ;; esac NEED_DYNLIB=yes AC_DEFINE(HAVE_NATIVE_COMP, 1, [Define to 1 if native compiler is available.]) - # Ensure libgccjit installed by MacPorts can be found. - if test -n "$HAVE_MACPORTS"; then - LIBGCCJIT_CFLAGS="$LIBGCCJIT_CFLAGS -I${MACPORTS_LIBGCCJIT_INCLUDE}" - LIBGCCJIT_LIBS="-L${MACPORTS_LIBGCCJIT_LIB} $LIBGCCJIT_LIBS" - fi + CFLAGS=$SAVE_CFLAGS + LIBS=$SAVE_LIBS fi AC_DEFINE_UNQUOTED(NATIVE_ELISP_SUFFIX, ".eln", [System extension for native compiled elisp])