diff options
Diffstat (limited to 'lisp/progmodes')
61 files changed, 3959 insertions, 3818 deletions
diff --git a/lisp/progmodes/antlr-mode.el b/lisp/progmodes/antlr-mode.el index 4bc6de0c759..d6e2ab8a87a 100644 --- a/lisp/progmodes/antlr-mode.el +++ b/lisp/progmodes/antlr-mode.el @@ -2437,7 +2437,6 @@ the default language." #'antlr-imenu-create-index-function) (set (make-local-variable 'imenu-generic-expression) t) ; fool stupid test (and antlr-imenu-name ; there should be a global variable... - (fboundp 'imenu-add-to-menubar) (imenu-add-to-menubar (if (stringp antlr-imenu-name) antlr-imenu-name "Index"))) (antlr-set-tabs)) diff --git a/lisp/progmodes/asm-mode.el b/lisp/progmodes/asm-mode.el index 370fb1b80b4..aaf063b5174 100644 --- a/lisp/progmodes/asm-mode.el +++ b/lisp/progmodes/asm-mode.el @@ -24,16 +24,16 @@ ;;; Commentary: ;; This mode was written by Eric S. Raymond <esr@snark.thyrsus.com>, -;; inspired by an earlier asm-mode by Martin Neitzel. +;; inspired by an earlier `asm-mode' by Martin Neitzel. -;; This major mode is based on prog mode. It defines a private abbrev table -;; that can be used to save abbrevs for assembler mnemonics. It binds just -;; five keys: +;; This major mode is based on `prog-mode'. It defines a private +;; abbrev table that can be used to save abbrevs for assembler +;; mnemonics. It binds just five keys: ;; ;; TAB tab to next tab stop ;; : outdent preceding label, tab to tab stop ;; comment char place or move comment -;; asm-comment-char specifies which character this is; +;; `asm-comment-char' specifies which character this is; ;; you can use a different character in different ;; Asm mode buffers. ;; C-j, C-m newline and tab to tab stop @@ -41,9 +41,9 @@ ;; Code is indented to the first tab stop level. ;; This mode runs two hooks: -;; 1) An asm-mode-set-comment-hook before the part of the initialization -;; depending on asm-comment-char, and -;; 2) an asm-mode-hook at the end of initialization. +;; 1) `asm-mode-set-comment-hook' before the part of the initialization +;; depending on `asm-comment-char', and +;; 2) `asm-mode-hook' at the end of initialization. ;;; Code: @@ -68,13 +68,11 @@ "Abbrev table used while in Asm mode.") (define-abbrev-table 'asm-mode-abbrev-table ()) -(defvar asm-mode-map - (let ((map (make-sparse-keymap))) - ;; Note that the comment character isn't set up until asm-mode is called. - (define-key map ":" 'asm-colon) - (define-key map "\C-c;" 'comment-region) - map) - "Keymap for Asm mode.") +(defvar-keymap asm-mode-map + :doc "Keymap for Asm mode." + ;; Note that the comment character isn't set up until asm-mode is called. + ":" #'asm-colon + "C-c ;" #'comment-region) (easy-menu-define asm-mode-menu asm-mode-map "Menu for Asm mode." @@ -130,7 +128,7 @@ Special commands: (setq-local tab-always-indent nil) (run-hooks 'asm-mode-set-comment-hook) - ;; Make our own local child of asm-mode-map + ;; Make our own local child of `asm-mode-map' ;; so we can define our own comment character. (use-local-map (nconc (make-sparse-keymap) asm-mode-map)) (local-set-key (vector asm-comment-char) #'asm-comment) diff --git a/lisp/progmodes/bat-mode.el b/lisp/progmodes/bat-mode.el index 7ef2500e46b..6bac297a298 100644 --- a/lisp/progmodes/bat-mode.el +++ b/lisp/progmodes/bat-mode.el @@ -71,8 +71,8 @@ "doskey" "echo" "endlocal" "erase" "fc" "find" "findstr" "format" "ftype" "label" "md" "mkdir" "more" "move" "net" "path" "pause" "popd" "prompt" "pushd" "rd" "ren" "rename" "replace" "rmdir" "set" - "setlocal" "shift" "sort" "subst" "time" "title" "tree" "type" - "ver" "vol" "xcopy")) + "setlocal" "setx" "shift" "sort" "subst" "time" "title" "tree" + "type" "ver" "vol" "xcopy")) (CONTROLFLOW '("call" "cmd" "defined" "do" "else" "equ" "exist" "exit" "for" "geq" "goto" "gtr" "if" "in" "leq" "lss" "neq" "not" "start")) @@ -82,7 +82,7 @@ (2 font-lock-constant-face t)) ("^:[^:].*" . 'bat-label-face) - ("\\_<\\(defined\\|set\\)\\_>[ \t]*\\(\\(\\sw\\|\\s_\\)+\\)" + ("\\_<\\(defined\\|set\\|setx\\)\\_>[ \t]*\\(\\(\\sw\\|\\s_\\)+\\)" (2 font-lock-variable-name-face)) ("%~\\([0-9]\\)" (1 font-lock-variable-name-face)) diff --git a/lisp/progmodes/bug-reference.el b/lisp/progmodes/bug-reference.el index 0a2d5ed796b..d3626dbaf01 100644 --- a/lisp/progmodes/bug-reference.el +++ b/lisp/progmodes/bug-reference.el @@ -40,12 +40,10 @@ ;; Somewhat arbitrary, by analogy with eg goto-address. :group 'comm) -(defvar bug-reference-map - (let ((map (make-sparse-keymap))) - (define-key map [mouse-2] 'bug-reference-push-button) - (define-key map (kbd "C-c RET") 'bug-reference-push-button) - map) - "Keymap used by bug reference buttons.") +(defvar-keymap bug-reference-map + :doc "Keymap used by bug reference buttons." + "<mouse-2>" #'bug-reference-push-button + "C-c RET" #'bug-reference-push-button) ;; E.g., "https://gcc.gnu.org/PR%s" (defvar bug-reference-url-format nil @@ -269,9 +267,9 @@ via the internet it might also be http.") ;; pull/17 page if 17 is a PR. Explicit user/project#17 links to ;; possibly different projects are also supported. (cl-defmethod bug-reference--build-forge-setup-entry - (host-domain (_forge-type (eql github)) protocol) + (host-domain (_forge-type (eql 'github)) protocol) `(,(concat "[/@]" (regexp-quote host-domain) - "[/:]\\([.A-Za-z0-9_/-]+\\)\\.git") + "[/:]\\([.A-Za-z0-9_/-]+?\\)\\(?:\\.git\\)?/?\\'") "\\(\\([.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\)\\>" ,(lambda (groups) (let ((ns-project (nth 1 groups))) @@ -285,9 +283,9 @@ via the internet it might also be http.") ;; namespace/project#18 or namespace/project!17 references to possibly ;; different projects are also supported. (cl-defmethod bug-reference--build-forge-setup-entry - (host-domain (_forge-type (eql gitlab)) protocol) + (host-domain (_forge-type (eql 'gitlab)) protocol) `(,(concat "[/@]" (regexp-quote host-domain) - "[/:]\\([.A-Za-z0-9_/-]+\\)\\.git") + "[/:]\\([.A-Za-z0-9_/-]+?\\)\\(?:\\.git\\)?/?\\'") "\\(\\([.A-Za-z0-9_/-]+\\)?\\([#!]\\)\\([0-9]+\\)\\)\\>" ,(lambda (groups) (let ((ns-project (nth 1 groups))) @@ -302,9 +300,9 @@ via the internet it might also be http.") ;; Gitea: The systematics is exactly as for Github projects. (cl-defmethod bug-reference--build-forge-setup-entry - (host-domain (_forge-type (eql gitea)) protocol) + (host-domain (_forge-type (eql 'gitea)) protocol) `(,(concat "[/@]" (regexp-quote host-domain) - "[/:]\\([.A-Za-z0-9_/-]+\\)\\.git") + "[/:]\\([.A-Za-z0-9_/-]+?\\)\\(?:\\.git\\)?/?\\'") "\\(\\([.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\)\\>" ,(lambda (groups) (let ((ns-project (nth 1 groups))) @@ -323,7 +321,7 @@ via the internet it might also be http.") ;; repo without tracker, or a repo with a tracker using a different ;; name, etc. So we can only try to make a good guess. (cl-defmethod bug-reference--build-forge-setup-entry - (host-domain (_forge-type (eql sourcehut)) protocol) + (host-domain (_forge-type (eql 'sourcehut)) protocol) `(,(concat "[/@]\\(?:git\\|hg\\)." (regexp-quote host-domain) "[/:]\\(~[.A-Za-z0-9_/-]+\\)") "\\(\\(~[.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\)\\>" diff --git a/lisp/progmodes/cc-align.el b/lisp/progmodes/cc-align.el index 8298d5fef04..e14f5b9058f 100644 --- a/lisp/progmodes/cc-align.el +++ b/lisp/progmodes/cc-align.el @@ -202,6 +202,58 @@ Works with: arglist-cont-nonempty, arglist-close." (skip-chars-forward " \t")) (vector (current-column))))))) +(defun c-lineup-argcont-1 (elem) + ;; Move to the start of the current arg and return non-nil, otherwise + ;; return nil. + (beginning-of-line) + + (when (eq (car elem) 'arglist-cont-nonempty) + ;; Our argument list might not be the innermost one. If it + ;; isn't, go back to the first position in it. We do this by + ;; stepping back over open parens until we get to the open paren + ;; of our argument list. + (let ((open-paren (c-langelem-2nd-pos c-syntactic-element)) + (paren-state (c-parse-state))) + (while (not (eq (car paren-state) open-paren)) + (unless (consp (car paren-state)) ;; ignore matched braces + (goto-char (car paren-state))) + (setq paren-state (cdr paren-state))))) + + (let ((start (point)) c) + + (when (bolp) + ;; Previous line ending in a comma means we're the start of an + ;; argument. This should quickly catch most cases not for us. + ;; This case is only applicable if we're the innermost arglist. + (c-backward-syntactic-ws) + (setq c (char-before))) + + (unless (eq c ?,) + ;; In a gcc asm, ":" on the previous line means the start of an + ;; argument. And lines starting with ":" are not for us, don't + ;; want them to indent to the preceding operand. + (let ((gcc-asm (save-excursion + (goto-char start) + (c-in-gcc-asm-p)))) + (unless (and gcc-asm + (or (eq c ?:) + (save-excursion + (goto-char start) + (looking-at "[ \t]*:")))) + + (c-lineup-argcont-scan (if gcc-asm ?:)) + t))))) + +(defun c-lineup-argcont-scan (&optional other-match) + ;; Find the start of an argument, for `c-lineup-argcont'. + (when (zerop (c-backward-token-2 1 t)) + (let ((c (char-after))) + (if (or (eq c ?,) (eq c other-match)) + (progn + (forward-char) + (c-forward-syntactic-ws)) + (c-lineup-argcont-scan other-match))))) + ;; Contributed by Kevin Ryde <user42@zip.com.au>. (defun c-lineup-argcont (elem) "Line up a continued argument. @@ -217,56 +269,30 @@ but of course only between operand specifications, not in the expressions for the operands. Works with: arglist-cont, arglist-cont-nonempty." - (save-excursion - (beginning-of-line) + (when (c-lineup-argcont-1 elem) + (vector (current-column))))) - (when (eq (car elem) 'arglist-cont-nonempty) - ;; Our argument list might not be the innermost one. If it - ;; isn't, go back to the last position in it. We do this by - ;; stepping back over open parens until we get to the open paren - ;; of our argument list. - (let ((open-paren (c-langelem-2nd-pos c-syntactic-element)) - (paren-state (c-parse-state))) - (while (not (eq (car paren-state) open-paren)) - (unless (consp (car paren-state)) ;; ignore matched braces - (goto-char (car paren-state))) - (setq paren-state (cdr paren-state))))) - - (let ((start (point)) c) - - (when (bolp) - ;; Previous line ending in a comma means we're the start of an - ;; argument. This should quickly catch most cases not for us. - ;; This case is only applicable if we're the innermost arglist. - (c-backward-syntactic-ws) - (setq c (char-before))) - - (unless (eq c ?,) - ;; In a gcc asm, ":" on the previous line means the start of an - ;; argument. And lines starting with ":" are not for us, don't - ;; want them to indent to the preceding operand. - (let ((gcc-asm (save-excursion - (goto-char start) - (c-in-gcc-asm-p)))) - (unless (and gcc-asm - (or (eq c ?:) - (save-excursion - (goto-char start) - (looking-at "[ \t]*:")))) - - (c-lineup-argcont-scan (if gcc-asm ?:)) - (vector (current-column)))))))) +(defun c-lineup-argcont-+ (langelem) + "Indent an argument continuation `c-basic-offset' in from the first argument. -(defun c-lineup-argcont-scan (&optional other-match) - ;; Find the start of an argument, for `c-lineup-argcont'. - (when (zerop (c-backward-token-2 1 t)) - (let ((c (char-after))) - (if (or (eq c ?,) (eq c other-match)) - (progn - (forward-char) - (c-forward-syntactic-ws)) - (c-lineup-argcont-scan other-match))))) +This first argument is that on a previous line at the same level of nesting. + +foo (xyz, uvw, aaa + bbb + ccc + + ddd + eee + fff); <- c-lineup-argcont-+ + <--> c-basic-offset + +Only continuation lines like this are touched, nil being returned +on lines which are the start of an argument. + +Works with: arglist-cont, arglist-cont-nonempty." + (save-excursion + (when (c-lineup-argcont-1 langelem) ; Check we've got a continued argument... + ;; ... but ignore the position found. + (goto-char (c-langelem-2nd-pos c-syntactic-element)) + (forward-char) + (c-forward-syntactic-ws) + (vector (+ (current-column) c-basic-offset))))) (defun c-lineup-arglist-intro-after-paren (_langelem) "Line up a line to just after the open paren of the surrounding paren diff --git a/lisp/progmodes/cc-awk.el b/lisp/progmodes/cc-awk.el index 188d5a8a837..9ea1557391b 100644 --- a/lisp/progmodes/cc-awk.el +++ b/lisp/progmodes/cc-awk.el @@ -56,6 +56,8 @@ ;; Silence the byte compiler. (cc-bytecomp-defvar c-new-BEG) (cc-bytecomp-defvar c-new-END) +(cc-bytecomp-defun c-restore-string-fences) +(cc-bytecomp-defun c-clear-string-fences) ;; Some functions in cc-engine that are used below. There's a cyclic ;; dependency so it can't be required here. (Perhaps some functions @@ -934,7 +936,7 @@ ;; It prepares the buffer for font ;; locking, hence must get called before `font-lock-after-change-function'. ;; - ;; This function is the AWK value of `c-before-font-lock-function'. + ;; This function is the AWK value of `c-before-font-lock-functions'. ;; It does hidden buffer changes. (c-save-buffer-state () (setq c-new-END (c-awk-end-of-change-region beg end old-len)) @@ -1109,29 +1111,30 @@ nor helpful. Note that this function might do hidden buffer changes. See the comment at the start of cc-engine.el for more info." (interactive "p") - (or arg (setq arg 1)) - (save-match-data - (c-save-buffer-state ; ensures the buffer is writable. - nil - (let ((found t)) ; Has the most recent regexp search found b-of-defun? - (if (>= arg 0) - ;; Go back one defun each time round the following loop. (For +ve arg) - (while (and found (> arg 0) (not (eq (point) (point-min)))) - ;; Go back one "candidate" each time round the next loop until one - ;; is genuinely a beginning-of-defun. - (while (and (setq found (search-backward-regexp - "^[^#} \t\n\r]" (point-min) 'stop-at-limit)) - (not (memq (c-awk-get-NL-prop-prev-line) '(?\$ ?\} ?\#))))) - (setq arg (1- arg))) - ;; The same for a -ve arg. - (if (not (eq (point) (point-max))) (forward-char 1)) - (while (and found (< arg 0) (not (eq (point) (point-max)))) ; The same for -ve arg. - (while (and (setq found (search-forward-regexp - "^[^#} \t\n\r]" (point-max) 'stop-at-limit)) - (not (memq (c-awk-get-NL-prop-prev-line) '(?\$ ?\} ?\#))))) - (setq arg (1+ arg))) - (if found (goto-char (match-beginning 0)))) - (eq arg 0))))) + (c-with-string-fences + (or arg (setq arg 1)) + (save-match-data + (c-save-buffer-state ; ensures the buffer is writable. + nil + (let ((found t)) ; Has the most recent regexp search found b-of-defun? + (if (>= arg 0) + ;; Go back one defun each time round the following loop. (For +ve arg) + (while (and found (> arg 0) (not (eq (point) (point-min)))) + ;; Go back one "candidate" each time round the next loop until one + ;; is genuinely a beginning-of-defun. + (while (and (setq found (search-backward-regexp + "^[^#} \t\n\r]" (point-min) 'stop-at-limit)) + (not (memq (c-awk-get-NL-prop-prev-line) '(?\$ ?\} ?\#))))) + (setq arg (1- arg))) + ;; The same for a -ve arg. + (if (not (eq (point) (point-max))) (forward-char 1)) + (while (and found (< arg 0) (not (eq (point) (point-max)))) ; The same for -ve arg. + (while (and (setq found (search-forward-regexp + "^[^#} \t\n\r]" (point-max) 'stop-at-limit)) + (not (memq (c-awk-get-NL-prop-prev-line) '(?\$ ?\} ?\#))))) + (setq arg (1+ arg))) + (if found (goto-char (match-beginning 0)))) + (eq arg 0)))))) (defun c-awk-forward-awk-pattern () ;; Point is at the start of an AWK pattern (which may be null) or function @@ -1187,39 +1190,40 @@ no explicit action; see function `c-awk-beginning-of-defun'. Note that this function might do hidden buffer changes. See the comment at the start of cc-engine.el for more info." (interactive "p") - (or arg (setq arg 1)) - (save-match-data - (c-save-buffer-state - nil - (let ((start-point (point)) end-point) - ;; Strategy: (For +ve ARG): If we're not already at a beginning-of-defun, - ;; move backwards to one. - ;; Repeat [(i) move forward to end-of-current-defun (see below); - ;; (ii) If this isn't it, move forward to beginning-of-defun]. - ;; We start counting ARG only when step (i) has passed the original point. - (when (> arg 0) - ;; Try to move back to a beginning-of-defun, if not already at one. - (if (not (c-awk-beginning-of-defun-p)) - (when (not (c-awk-beginning-of-defun 1)) ; No bo-defun before point. - (goto-char start-point) - (c-awk-beginning-of-defun -1))) ; if this fails, we're at EOB, tough! - ;; Now count forward, one defun at a time - (while (and (not (eobp)) - (c-awk-end-of-defun1) - (if (> (point) start-point) (setq arg (1- arg)) t) - (> arg 0) - (c-awk-beginning-of-defun -1)))) - - (when (< arg 0) - (setq end-point start-point) - (while (and (not (bobp)) - (c-awk-beginning-of-defun 1) - (if (< (setq end-point (if (bobp) (point) - (save-excursion (c-awk-end-of-defun1)))) - start-point) - (setq arg (1+ arg)) t) - (< arg 0))) - (goto-char (min start-point end-point))))))) + (c-with-string-fences + (or arg (setq arg 1)) + (save-match-data + (c-save-buffer-state + nil + (let ((start-point (point)) end-point) + ;; Strategy: (For +ve ARG): If we're not already at a beginning-of-defun, + ;; move backwards to one. + ;; Repeat [(i) move forward to end-of-current-defun (see below); + ;; (ii) If this isn't it, move forward to beginning-of-defun]. + ;; We start counting ARG only when step (i) has passed the original point. + (when (> arg 0) + ;; Try to move back to a beginning-of-defun, if not already at one. + (if (not (c-awk-beginning-of-defun-p)) + (when (not (c-awk-beginning-of-defun 1)) ; No bo-defun before point. + (goto-char start-point) + (c-awk-beginning-of-defun -1))) ; if this fails, we're at EOB, tough! + ;; Now count forward, one defun at a time + (while (and (not (eobp)) + (c-awk-end-of-defun1) + (if (> (point) start-point) (setq arg (1- arg)) t) + (> arg 0) + (c-awk-beginning-of-defun -1)))) + + (when (< arg 0) + (setq end-point start-point) + (while (and (not (bobp)) + (c-awk-beginning-of-defun 1) + (if (< (setq end-point (if (bobp) (point) + (save-excursion (c-awk-end-of-defun1)))) + start-point) + (setq arg (1+ arg)) t) + (< arg 0))) + (goto-char (min start-point end-point)))))))) (cc-provide 'cc-awk) ; Changed from 'awk-mode, ACM 2002/5/21 diff --git a/lisp/progmodes/cc-cmds.el b/lisp/progmodes/cc-cmds.el index f42f82e53bb..82268f49433 100644 --- a/lisp/progmodes/cc-cmds.el +++ b/lisp/progmodes/cc-cmds.el @@ -49,6 +49,8 @@ ; which looks at this. (cc-bytecomp-defun electric-pair-post-self-insert-function) (cc-bytecomp-defvar c-indent-to-body-directives) +(cc-bytecomp-defun c-restore-string-fences) +(cc-bytecomp-defun c-clear-string-fences) (defvar c-syntactic-context) ;; Indentation / Display syntax functions @@ -210,35 +212,36 @@ and takes care to set the indentation before calling "Show syntactic information for current line. With universal argument, inserts the analysis as a comment on that line." (interactive "P") - (let* ((c-parsing-error nil) - (syntax (if (boundp 'c-syntactic-context) - ;; Use `c-syntactic-context' in the same way as - ;; `c-indent-line', to be consistent. - c-syntactic-context - (c-save-buffer-state nil - (c-guess-basic-syntax))))) - (if (not (consp arg)) - (let (elem pos ols) - (message "Syntactic analysis: %s" syntax) - (unwind-protect - (progn - (while syntax - (setq elem (pop syntax)) - (when (setq pos (c-langelem-pos elem)) - (push (c-put-overlay pos (1+ pos) - 'face 'highlight) - ols)) - (when (setq pos (c-langelem-2nd-pos elem)) - (push (c-put-overlay pos (1+ pos) - 'face 'secondary-selection) - ols))) - (sit-for 10)) - (while ols - (c-delete-overlay (pop ols))))) - (indent-for-comment) - (insert-and-inherit (format "%s" syntax)) - )) - (c-keep-region-active)) + (c-with-string-fences + (let* ((c-parsing-error nil) + (syntax (if (boundp 'c-syntactic-context) + ;; Use `c-syntactic-context' in the same way as + ;; `c-indent-line', to be consistent. + c-syntactic-context + (c-save-buffer-state nil + (c-guess-basic-syntax))))) + (if (not (consp arg)) + (let (elem pos ols) + (message "Syntactic analysis: %s" syntax) + (unwind-protect + (progn + (while syntax + (setq elem (pop syntax)) + (when (setq pos (c-langelem-pos elem)) + (push (c-put-overlay pos (1+ pos) + 'face 'highlight) + ols)) + (when (setq pos (c-langelem-2nd-pos elem)) + (push (c-put-overlay pos (1+ pos) + 'face 'secondary-selection) + ols))) + (sit-for 10)) + (while ols + (c-delete-overlay (pop ols))))) + (indent-for-comment) + (insert-and-inherit (format "%s" syntax)) + )) + (c-keep-region-active))) (defun c-syntactic-information-on-region (from to) "Insert a comment with the syntactic analysis on every line in the region." @@ -414,23 +417,25 @@ argument is supplied, or `c-hungry-delete-key' is nil, or point is inside a literal then the function in the variable `c-backspace-function' is called." (interactive "*P") - (if (c-save-buffer-state () - (or (not c-hungry-delete-key) - arg - (c-in-literal))) - (funcall c-backspace-function (prefix-numeric-value arg)) - (c-hungry-delete-backwards))) + (c-with-string-fences + (if (c-save-buffer-state () + (or (not c-hungry-delete-key) + arg + (c-in-literal))) + (funcall c-backspace-function (prefix-numeric-value arg)) + (c-hungry-delete-backwards)))) (defun c-hungry-delete-backwards () "Delete the preceding character or all preceding whitespace back to the previous non-whitespace character. See also \\[c-hungry-delete-forward]." (interactive) - (let ((here (point))) - (c-skip-ws-backward) - (if (/= (point) here) - (delete-region (point) here) - (funcall c-backspace-function 1)))) + (c-with-string-fences + (let ((here (point))) + (c-skip-ws-backward) + (if (/= (point) here) + (delete-region (point) here) + (funcall c-backspace-function 1))))) (defalias 'c-hungry-backspace 'c-hungry-delete-backwards) @@ -442,23 +447,26 @@ argument is supplied, or `c-hungry-delete-key' is nil, or point is inside a literal then the function in the variable `c-delete-function' is called." (interactive "*P") - (if (c-save-buffer-state () - (or (not c-hungry-delete-key) - arg - (c-in-literal))) - (funcall c-delete-function (prefix-numeric-value arg)) - (c-hungry-delete-forward))) + (c-with-string-fences + (if + (c-save-buffer-state () + (or (not c-hungry-delete-key) + arg + (c-in-literal))) + (funcall c-delete-function (prefix-numeric-value arg)) + (c-hungry-delete-forward)))) (defun c-hungry-delete-forward () "Delete the following character or all following whitespace up to the next non-whitespace character. See also \\[c-hungry-delete-backwards]." (interactive) - (let ((here (point))) - (c-skip-ws-forward) - (if (/= (point) here) - (delete-region (point) here) - (funcall c-delete-function 1)))) + (c-with-string-fences + (let ((here (point))) + (c-skip-ws-forward) + (if (/= (point) here) + (delete-region (point) here) + (funcall c-delete-function 1))))) ;; This function is only used in XEmacs. (defun c-electric-delete (arg) @@ -519,7 +527,8 @@ function to control that." (defmacro c--call-post-self-insert-hook-more-safely () ;; Call post-self-insert-hook, if such exists. See comment for - ;; `c--call-post-self-insert-hook-more-safely-1'. + ;; `c--call-post-self-insert-hook-more-safely-1'. This macro should be + ;; invoked OUTSIDE of `c-with-string-fences'. (if (boundp 'post-self-insert-hook) '(c--call-post-self-insert-hook-more-safely-1) '(progn))) @@ -530,30 +539,30 @@ If `c-electric-flag' is set, handle it specially according to the variable `c-electric-pound-behavior'. If a numeric ARG is supplied, or if point is inside a literal or a macro, nothing special happens." (interactive "*P") - (if (c-save-buffer-state () - (or arg - (not c-electric-flag) - (not (memq 'alignleft c-electric-pound-behavior)) - (save-excursion - (skip-chars-backward " \t") - (not (bolp))) - (save-excursion - (and (= (forward-line -1) 0) - (progn (end-of-line) - (eq (char-before) ?\\)))) - (c-in-literal))) - ;; do nothing special - (let (post-self-insert-hook) ; Disable random functionality. - (self-insert-command (prefix-numeric-value arg))) - ;; place the pound character at the left edge - (let ((pos (- (point-max) (point))) - (bolp (bolp))) - (beginning-of-line) - (delete-horizontal-space) - (insert (c-last-command-char)) - (and (not bolp) - (goto-char (- (point-max) pos))) - )) + (c-with-string-fences + (if (c-save-buffer-state () + (or arg + (not c-electric-flag) + (not (memq 'alignleft c-electric-pound-behavior)) + (save-excursion + (skip-chars-backward " \t") + (not (bolp))) + (save-excursion + (and (= (forward-line -1) 0) + (progn (end-of-line) + (eq (char-before) ?\\)))) + (c-in-literal))) + ;; do nothing special + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) + ;; place the pound character at the left edge + (let ((pos (- (point-max) (point))) + (bolp (bolp))) + (beginning-of-line) + (delete-horizontal-space) + (insert (c-last-command-char)) + (and (not bolp) + (goto-char (- (point-max) pos)))))) (c--call-post-self-insert-hook-more-safely)) (defun c-point-syntax () @@ -883,25 +892,26 @@ settings of `c-cleanup-list' are done." (interactive "*P") (let (safepos literal - ;; We want to inhibit blinking the paren since this would be - ;; most disruptive. We'll blink it ourselves later on. - (old-blink-paren blink-paren-function) - blink-paren-function case-fold-search - (at-eol (looking-at "[ \t]*\\\\?$")) - (active-region (and (fboundp 'use-region-p) (use-region-p))) - got-pair-} electric-pair-deletion) - - (c-save-buffer-state () - (setq safepos (c-safe-position (point) (c-parse-state)) - literal (c-in-literal safepos))) - - ;; Insert the brace. Note that expand-abbrev might reindent - ;; the line here if there's a preceding "else" or something. - (let (post-self-insert-hook) ; the only way to get defined functionality - ; from `self-insert-command'. - (self-insert-command (prefix-numeric-value arg))) - - ;; Emulate `electric-pair-mode'. + ;; We want to inhibit blinking the paren since this would be + ;; most disruptive. We'll blink it ourselves later on. + (old-blink-paren blink-paren-function) + blink-paren-function case-fold-search + (at-eol (looking-at "[ \t]*\\\\?$")) + (active-region (and (fboundp 'use-region-p) (use-region-p))) + got-pair-} electric-pair-deletion) + + (c-with-string-fences + (c-save-buffer-state () + (setq safepos (c-safe-position (point) (c-parse-state)) + literal (c-in-literal safepos))) + + ;; Insert the brace. Note that expand-abbrev might reindent + ;; the line here if there's a preceding "else" or something. + (let (post-self-insert-hook) ; the only way to get defined functionality + ; from `self-insert-command'. + (self-insert-command (prefix-numeric-value arg)))) + + ;; Emulate `electric-pair-mode', outside of `c-with-string-fences'. (when (and (boundp 'electric-pair-mode) electric-pair-mode) (let ((size (buffer-size)) @@ -912,30 +922,31 @@ settings of `c-cleanup-list' are done." (eq (char-after) ?})) electric-pair-deletion (< (buffer-size) size)))) - ;; Perform any required CC Mode electric actions. - (cond - ((or literal arg (not c-electric-flag) active-region)) - ((not at-eol) - (c-indent-line)) - (electric-pair-deletion - (c-indent-line) - (c-do-brace-electrics 'ignore nil)) - (t (c-do-brace-electrics nil nil) - (when got-pair-} + (c-with-string-fences + ;; Perform any required CC Mode electric actions. + (cond + ((or literal arg (not c-electric-flag) active-region)) + ((not at-eol) + (c-indent-line)) + (electric-pair-deletion + (c-indent-line) + (c-do-brace-electrics 'ignore nil)) + (t (c-do-brace-electrics nil nil) + (when got-pair-} + (save-excursion + (forward-char) + (c-do-brace-electrics 'assume 'ignore)) + (c-indent-line)))) + + ;; blink the paren + (and (eq (c-last-command-char) ?\}) + (not executing-kbd-macro) + old-blink-paren (save-excursion - (forward-char) - (c-do-brace-electrics 'assume 'ignore)) - (c-indent-line)))) - - ;; blink the paren - (and (eq (c-last-command-char) ?\}) - (not executing-kbd-macro) - old-blink-paren - (save-excursion - (c-save-buffer-state nil - (c-backward-syntactic-ws safepos)) - (funcall old-blink-paren))) - (c--call-post-self-insert-hook-more-safely))) + (c-save-buffer-state nil + (c-backward-syntactic-ws safepos)) + (funcall old-blink-paren))))) + (c--call-post-self-insert-hook-more-safely)) (defun c-electric-slash (arg) "Insert a slash character. @@ -956,39 +967,40 @@ If a numeric ARG is supplied, point is inside a literal, or `c-syntactic-indentation' is nil or `c-electric-flag' is nil, indentation is inhibited." (interactive "*P") - (let ((literal (c-save-buffer-state () (c-in-literal))) - indentp - ;; shut this up - (c-echo-syntactic-information-p nil)) + (c-with-string-fences + (let ((literal (c-save-buffer-state () (c-in-literal))) + indentp + ;; shut this up + (c-echo-syntactic-information-p nil)) - ;; comment-close-slash cleanup? This DOESN'T need `c-electric-flag' or - ;; `c-syntactic-indentation' set. - (when (and (not arg) - (eq literal 'c) - (memq 'comment-close-slash c-cleanup-list) - (eq (c-last-command-char) ?/) - (looking-at (concat "[ \t]*\\(" - (regexp-quote comment-end) "\\)?$")) - ; (eq c-block-comment-ender "*/") ; C-style comments ALWAYS end in */ - (save-excursion - (save-restriction - (narrow-to-region (point-min) (point)) - (back-to-indentation) - (looking-at (concat c-current-comment-prefix "[ \t]*$"))))) - (delete-region (progn (forward-line 0) (point)) - (progn (end-of-line) (point))) - (insert-char ?* 1)) ; the / comes later. ; Do I need a t (retain sticky properties) here? - - (setq indentp (and (not arg) - c-syntactic-indentation - c-electric-flag - (eq (c-last-command-char) ?/) - (eq (char-before) (if literal ?* ?/)))) - (let (post-self-insert-hook) ; Disable random functionality. - (self-insert-command (prefix-numeric-value arg))) - (if indentp - (indent-according-to-mode)) - (c--call-post-self-insert-hook-more-safely))) + ;; comment-close-slash cleanup? This DOESN'T need `c-electric-flag' or + ;; `c-syntactic-indentation' set. + (when (and (not arg) + (eq literal 'c) + (memq 'comment-close-slash c-cleanup-list) + (eq (c-last-command-char) ?/) + (looking-at (concat "[ \t]*\\(" + (regexp-quote comment-end) "\\)?$")) + ; (eq c-block-comment-ender "*/") ; C-style comments ALWAYS end in */ + (save-excursion + (save-restriction + (narrow-to-region (point-min) (point)) + (back-to-indentation) + (looking-at (concat c-current-comment-prefix "[ \t]*$"))))) + (delete-region (progn (forward-line 0) (point)) + (progn (end-of-line) (point))) + (insert-char ?* 1)) ; the / comes later. ; Do I need a t (retain sticky properties) here? + + (setq indentp (and (not arg) + c-syntactic-indentation + c-electric-flag + (eq (c-last-command-char) ?/) + (eq (char-before) (if literal ?* ?/)))) + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) + (if indentp + (indent-according-to-mode)))) + (c--call-post-self-insert-hook-more-safely)) (defun c-electric-star (arg) "Insert a star character. @@ -999,26 +1011,26 @@ supplied, point is inside a literal, or `c-syntactic-indentation' is nil, this indentation is inhibited." (interactive "*P") - (let (post-self-insert-hook) ; Disable random functionality. - (self-insert-command (prefix-numeric-value arg))) - ;; if we are in a literal, or if arg is given do not reindent the - ;; current line, unless this star introduces a comment-only line. - (if (c-save-buffer-state () - (and c-syntactic-indentation - c-electric-flag - (not arg) - (eq (c-in-literal) 'c) - (eq (char-before) ?*) - (save-excursion - (forward-char -1) - (skip-chars-backward "*") - (if (eq (char-before) ?/) - (forward-char -1)) - (skip-chars-backward " \t") - (bolp)))) - (let (c-echo-syntactic-information-p) ; shut this up - (indent-according-to-mode)) - ) + (c-with-string-fences + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) + ;; if we are in a literal, or if arg is given do not reindent the + ;; current line, unless this star introduces a comment-only line. + (if (c-save-buffer-state () + (and c-syntactic-indentation + c-electric-flag + (not arg) + (eq (c-in-literal) 'c) + (eq (char-before) ?*) + (save-excursion + (forward-char -1) + (skip-chars-backward "*") + (if (eq (char-before) ?/) + (forward-char -1)) + (skip-chars-backward " \t") + (bolp)))) + (let (c-echo-syntactic-information-p) ; shut this up + (indent-according-to-mode)))) (c--call-post-self-insert-hook-more-safely)) (defun c-electric-semi&comma (arg) @@ -1039,60 +1051,61 @@ reindented unless `c-syntactic-indentation' is nil. semicolon following a defun might be cleaned up, depending on the settings of `c-cleanup-list'." (interactive "*P") - (let* (lim literal c-syntactic-context - (here (point)) - ;; shut this up - (c-echo-syntactic-information-p nil)) - - (c-save-buffer-state () - (setq lim (c-most-enclosing-brace (c-parse-state)) - literal (c-in-literal lim))) - - (let (post-self-insert-hook) ; Disable random functionality. - (self-insert-command (prefix-numeric-value arg))) - - (if (and c-electric-flag (not literal) (not arg)) - ;; do all cleanups and newline insertions if c-auto-newline is on. - (if (or (not c-auto-newline) - (not (looking-at "[ \t]*\\\\?$"))) - (if c-syntactic-indentation - (c-indent-line)) - ;; clean ups: list-close-comma or defun-close-semi - (let ((pos (- (point-max) (point)))) - (if (c-save-buffer-state () - (and (or (and - (eq (c-last-command-char) ?,) - (memq 'list-close-comma c-cleanup-list)) - (and - (eq (c-last-command-char) ?\;) - (memq 'defun-close-semi c-cleanup-list))) - (progn - (forward-char -1) - (c-skip-ws-backward) - (eq (char-before) ?})) - ;; make sure matching open brace isn't in a comment - (not (c-in-literal lim)))) - (delete-region (point) here)) - (goto-char (- (point-max) pos))) - ;; reindent line - (when c-syntactic-indentation - (setq c-syntactic-context (c-guess-basic-syntax)) - (c-indent-line c-syntactic-context)) - ;; check to see if a newline should be added - (let ((criteria c-hanging-semi&comma-criteria) - answer add-newline-p) - (while criteria - (setq answer (funcall (car criteria))) - ;; only nil value means continue checking - (if (not answer) - (setq criteria (cdr criteria)) - (setq criteria nil) - ;; only 'stop specifically says do not add a newline - (setq add-newline-p (not (eq answer 'stop))) - )) - (if add-newline-p - (c-newline-and-indent))))) - (c--call-post-self-insert-hook-more-safely))) + (c-with-string-fences + (let* (lim literal c-syntactic-context + (here (point)) + ;; shut this up + (c-echo-syntactic-information-p nil)) + + (c-save-buffer-state () + (setq lim (c-most-enclosing-brace (c-parse-state)) + literal (c-in-literal lim))) + + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) + + (if (and c-electric-flag (not literal) (not arg)) + ;; do all cleanups and newline insertions if c-auto-newline is on. + (if (or (not c-auto-newline) + (not (looking-at "[ \t]*\\\\?$"))) + (if c-syntactic-indentation + (c-indent-line)) + ;; clean ups: list-close-comma or defun-close-semi + (let ((pos (- (point-max) (point)))) + (if (c-save-buffer-state () + (and (or (and + (eq (c-last-command-char) ?,) + (memq 'list-close-comma c-cleanup-list)) + (and + (eq (c-last-command-char) ?\;) + (memq 'defun-close-semi c-cleanup-list))) + (progn + (forward-char -1) + (c-skip-ws-backward) + (eq (char-before) ?})) + ;; make sure matching open brace isn't in a comment + (not (c-in-literal lim)))) + (delete-region (point) here)) + (goto-char (- (point-max) pos))) + ;; reindent line + (when c-syntactic-indentation + (setq c-syntactic-context (c-guess-basic-syntax)) + (c-indent-line c-syntactic-context)) + ;; check to see if a newline should be added + (let ((criteria c-hanging-semi&comma-criteria) + answer add-newline-p) + (while criteria + (setq answer (funcall (car criteria))) + ;; only nil value means continue checking + (if (not answer) + (setq criteria (cdr criteria)) + (setq criteria nil) + ;; only 'stop specifically says do not add a newline + (setq add-newline-p (not (eq answer 'stop))) + )) + (if add-newline-p + (c-newline-and-indent))))))) + (c--call-post-self-insert-hook-more-safely)) (defun c-electric-colon (arg) "Insert a colon. @@ -1113,89 +1126,90 @@ reindented unless `c-syntactic-indentation' is nil. `c-cleanup-list'." (interactive "*P") - (let* ((bod (c-point 'bod)) - (literal (c-save-buffer-state () (c-in-literal bod))) - newlines is-scope-op - ;; shut this up - (c-echo-syntactic-information-p nil)) - (let (post-self-insert-hook) ; Disable random functionality. - (self-insert-command (prefix-numeric-value arg))) - ;; Any electric action? - (if (and c-electric-flag (not literal) (not arg)) - ;; Unless we're at EOL, only re-indentation happens. - (if (not (looking-at "[ \t]*\\\\?$")) - (if c-syntactic-indentation - (indent-according-to-mode)) - - ;; scope-operator clean-up? - (let ((pos (- (point-max) (point))) - (here (point))) - (if (c-save-buffer-state () ; Why do we need this? [ACM, 2003-03-12] - (and c-auto-newline - (memq 'scope-operator c-cleanup-list) - (eq (char-before) ?:) - (progn - (forward-char -1) - (c-skip-ws-backward) - (eq (char-before) ?:)) - (not (c-in-literal)) - (not (eq (char-after (- (point) 2)) ?:)))) - (progn - (delete-region (point) (1- here)) - (setq is-scope-op t))) - (goto-char (- (point-max) pos))) - - ;; indent the current line if it's done syntactically. - (if c-syntactic-indentation - ;; Cannot use the same syntax analysis as we find below, - ;; since that's made with c-syntactic-indentation-in-macros - ;; always set to t. - (indent-according-to-mode)) - - ;; Calculate where, if anywhere, we want newlines. - (c-save-buffer-state - ((c-syntactic-indentation-in-macros t) - (c-auto-newline-analysis t) - ;; Turn on syntactic macro analysis to help with auto newlines - ;; only. - (syntax (c-guess-basic-syntax)) - (elem syntax)) - ;; Translate substatement-label to label for this operation. - (while elem - (if (eq (car (car elem)) 'substatement-label) - (setcar (car elem) 'label)) - (setq elem (cdr elem))) - ;; some language elements can only be determined by checking - ;; the following line. Let's first look for ones that can be - ;; found when looking on the line with the colon - (setq newlines - (and c-auto-newline - (or (c-lookup-lists '(case-label label access-label) - syntax c-hanging-colons-alist) - (c-lookup-lists '(member-init-intro inher-intro) - (progn - (insert ?\n) - (unwind-protect - (c-guess-basic-syntax) - (delete-char -1))) - c-hanging-colons-alist))))) - ;; does a newline go before the colon? Watch out for already - ;; non-hung colons. However, we don't unhang them because that - ;; would be a cleanup (and anti-social). - (if (and (memq 'before newlines) - (not is-scope-op) - (save-excursion - (skip-chars-backward ": \t") - (not (bolp)))) - (let ((pos (- (point-max) (point)))) - (forward-char -1) - (c-newline-and-indent) - (goto-char (- (point-max) pos)))) - ;; does a newline go after the colon? - (if (and (memq 'after (cdr-safe newlines)) - (not is-scope-op)) - (c-newline-and-indent)))) - (c--call-post-self-insert-hook-more-safely))) + (c-with-string-fences + (let* ((bod (c-point 'bod)) + (literal (c-save-buffer-state () (c-in-literal bod))) + newlines is-scope-op + ;; shut this up + (c-echo-syntactic-information-p nil)) + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) + ;; Any electric action? + (if (and c-electric-flag (not literal) (not arg)) + ;; Unless we're at EOL, only re-indentation happens. + (if (not (looking-at "[ \t]*\\\\?$")) + (if c-syntactic-indentation + (indent-according-to-mode)) + + ;; scope-operator clean-up? + (let ((pos (- (point-max) (point))) + (here (point))) + (if (c-save-buffer-state () ; Why do we need this? [ACM, 2003-03-12] + (and c-auto-newline + (memq 'scope-operator c-cleanup-list) + (eq (char-before) ?:) + (progn + (forward-char -1) + (c-skip-ws-backward) + (eq (char-before) ?:)) + (not (c-in-literal)) + (not (eq (char-after (- (point) 2)) ?:)))) + (progn + (delete-region (point) (1- here)) + (setq is-scope-op t))) + (goto-char (- (point-max) pos))) + + ;; indent the current line if it's done syntactically. + (if c-syntactic-indentation + ;; Cannot use the same syntax analysis as we find below, + ;; since that's made with c-syntactic-indentation-in-macros + ;; always set to t. + (indent-according-to-mode)) + + ;; Calculate where, if anywhere, we want newlines. + (c-save-buffer-state + ((c-syntactic-indentation-in-macros t) + (c-auto-newline-analysis t) + ;; Turn on syntactic macro analysis to help with auto newlines + ;; only. + (syntax (c-guess-basic-syntax)) + (elem syntax)) + ;; Translate substatement-label to label for this operation. + (while elem + (if (eq (car (car elem)) 'substatement-label) + (setcar (car elem) 'label)) + (setq elem (cdr elem))) + ;; some language elements can only be determined by checking + ;; the following line. Let's first look for ones that can be + ;; found when looking on the line with the colon + (setq newlines + (and c-auto-newline + (or (c-lookup-lists '(case-label label access-label) + syntax c-hanging-colons-alist) + (c-lookup-lists '(member-init-intro inher-intro) + (progn + (insert ?\n) + (unwind-protect + (c-guess-basic-syntax) + (delete-char -1))) + c-hanging-colons-alist))))) + ;; does a newline go before the colon? Watch out for already + ;; non-hung colons. However, we don't unhang them because that + ;; would be a cleanup (and anti-social). + (if (and (memq 'before newlines) + (not is-scope-op) + (save-excursion + (skip-chars-backward ": \t") + (not (bolp)))) + (let ((pos (- (point-max) (point)))) + (forward-char -1) + (c-newline-and-indent) + (goto-char (- (point-max) pos)))) + ;; does a newline go after the colon? + (if (and (memq 'after (cdr-safe newlines)) + (not is-scope-op)) + (c-newline-and-indent)))))) + (c--call-post-self-insert-hook-more-safely)) (defun c-electric-lt-gt (arg) "Insert a \"<\" or \">\" character. @@ -1209,74 +1223,75 @@ finishes a C++ style stream operator in C++ mode. Exceptions are when a numeric argument is supplied, or the point is inside a literal." (interactive "*P") - (let ((literal (c-save-buffer-state () (c-in-literal))) - template-delim include-delim + (let (template-delim include-delim (c-echo-syntactic-information-p nil) final-pos found-delim case-fold-search) - (let (post-self-insert-hook) ; Disable random functionality. - (self-insert-command (prefix-numeric-value arg))) - (setq final-pos (point)) + (c-with-string-fences + (let (post-self-insert-hook) ; Disable random functionality. + (self-insert-command (prefix-numeric-value arg))) + (setq final-pos (point)) ;;;; 2010-01-31: There used to be code here to put a syntax-table text ;;;; property on the new < or > and its mate (if any) when they are template ;;;; parens. This is now done in an after-change function. - (when (and (not arg) (not literal)) - ;; Have we got a delimiter on a #include directive? - (beginning-of-line) - (setq include-delim - (and - (looking-at c-cpp-include-key) - (if (eq (c-last-command-char) ?<) - (eq (match-end 0) (1- final-pos)) - (goto-char (1- final-pos)) - (skip-chars-backward "^<>" (c-point 'bol)) - (eq (char-before) ?<)))) - (goto-char final-pos) - - ;; Indent the line if appropriate. - (when (and c-electric-flag c-syntactic-indentation c-recognize-<>-arglists) - (setq found-delim + (when (and (not arg) + (not (c-save-buffer-state () (c-in-literal)))) + ;; Have we got a delimiter on a #include directive? + (beginning-of-line) + (setq include-delim + (and + (looking-at c-cpp-include-key) (if (eq (c-last-command-char) ?<) - ;; If a <, basically see if it's got "template" before it ..... - (or (and (progn - (backward-char) - (= (point) - (progn (c-beginning-of-current-token) (point)))) - (progn - (c-backward-token-2) - (looking-at c-opt-<>-sexp-key)) - (setq template-delim t)) - ;; ..... or is a C++ << operator. - (and (c-major-mode-is 'c++-mode) - (progn - (goto-char (1- final-pos)) - (c-beginning-of-current-token) - (looking-at "<<")) - (>= (match-end 0) final-pos))) - - ;; It's a >. Either a template/generic terminator ... - (or (and (c-get-char-property (1- final-pos) 'syntax-table) - (setq template-delim t)) - ;; or a C++ >> operator. - (and (c-major-mode-is 'c++-mode) - (progn - (goto-char (1- final-pos)) - (c-beginning-of-current-token) - (looking-at ">>")) - (>= (match-end 0) final-pos))))) - (goto-char final-pos) - - (when found-delim - (indent-according-to-mode))) - - ;; On the off chance that < and > are configured as pairs in - ;; electric-pair-mode. - (when (and (boundp 'electric-pair-mode) electric-pair-mode - (or template-delim include-delim)) - (let (post-self-insert-hook) - (electric-pair-post-self-insert-function)))) + (eq (match-end 0) (1- final-pos)) + (goto-char (1- final-pos)) + (skip-chars-backward "^<>" (c-point 'bol)) + (eq (char-before) ?<)))) + (goto-char final-pos) + + ;; Indent the line if appropriate. + (when (and c-electric-flag c-syntactic-indentation c-recognize-<>-arglists) + (setq found-delim + (if (eq (c-last-command-char) ?<) + ;; If a <, basically see if it's got "template" before it ..... + (or (and (progn + (backward-char) + (= (point) + (progn (c-beginning-of-current-token) (point)))) + (progn + (c-backward-token-2) + (looking-at c-opt-<>-sexp-key)) + (setq template-delim t)) + ;; ..... or is a C++ << operator. + (and (c-major-mode-is 'c++-mode) + (progn + (goto-char (1- final-pos)) + (c-beginning-of-current-token) + (looking-at "<<")) + (>= (match-end 0) final-pos))) + + ;; It's a >. Either a template/generic terminator ... + (or (and (c-get-char-property (1- final-pos) 'syntax-table) + (setq template-delim t)) + ;; or a C++ >> operator. + (and (c-major-mode-is 'c++-mode) + (progn + (goto-char (1- final-pos)) + (c-beginning-of-current-token) + (looking-at ">>")) + (>= (match-end 0) final-pos))))) + (goto-char final-pos) + + (when found-delim + (indent-according-to-mode))))) + + ;; On the off chance that < and > are configured as pairs in + ;; electric-pair-mode. + (when (and (boundp 'electric-pair-mode) electric-pair-mode + (or template-delim include-delim)) + (let (post-self-insert-hook) + (electric-pair-post-self-insert-function))) (when found-delim (when (and (eq (char-before) ?>) @@ -1301,12 +1316,13 @@ removed; see the variable `c-cleanup-list'. Also, if `c-electric-flag' and `c-auto-newline' are both non-nil, some newline cleanups are done if appropriate; see the variable `c-cleanup-list'." (interactive "*P") - (let ((literal (c-save-buffer-state () (c-in-literal))) + (let ((literal (c-save-buffer-state () + (c-with-string-fences (c-in-literal)))) ;; shut this up (c-echo-syntactic-information-p nil) case-fold-search) (let (post-self-insert-hook) ; The only way to get defined functionality - ; from `self-insert-command'. + ; from `self-insert-command'. (self-insert-command (prefix-numeric-value arg))) (if (and (not arg) (not literal)) @@ -1315,46 +1331,47 @@ newline cleanups are done if appropriate; see the variable `c-cleanup-list'." ;; afterwards. (old-blink-paren blink-paren-function) blink-paren-function) - (if (and c-syntactic-indentation c-electric-flag) - (indent-according-to-mode)) - - ;; If we're at EOL, check for new-line clean-ups. - (when (and c-electric-flag c-auto-newline - (looking-at "[ \t]*\\\\?$")) - - ;; clean up brace-elseif-brace - (when - (and (memq 'brace-elseif-brace c-cleanup-list) - (eq (c-last-command-char) ?\() - (re-search-backward - (concat "}" - "\\([ \t\n]\\|\\\\\n\\)*" - "else" - "\\([ \t\n]\\|\\\\\n\\)+" - "if" - "\\([ \t\n]\\|\\\\\n\\)*" - "(" - "\\=") - nil t) - (not (c-save-buffer-state () (c-in-literal)))) - (delete-region (match-beginning 0) (match-end 0)) - (insert-and-inherit "} else if (")) - - ;; clean up brace-catch-brace - (when - (and (memq 'brace-catch-brace c-cleanup-list) - (eq (c-last-command-char) ?\() - (re-search-backward - (concat "}" - "\\([ \t\n]\\|\\\\\n\\)*" - "catch" - "\\([ \t\n]\\|\\\\\n\\)*" - "(" - "\\=") - nil t) - (not (c-save-buffer-state () (c-in-literal)))) - (delete-region (match-beginning 0) (match-end 0)) - (insert-and-inherit "} catch ("))) + (c-with-string-fences + (if (and c-syntactic-indentation c-electric-flag) + (indent-according-to-mode)) + + ;; If we're at EOL, check for new-line clean-ups. + (when (and c-electric-flag c-auto-newline + (looking-at "[ \t]*\\\\?$")) + + ;; clean up brace-elseif-brace + (when + (and (memq 'brace-elseif-brace c-cleanup-list) + (eq (c-last-command-char) ?\() + (re-search-backward + (concat "}" + "\\([ \t\n]\\|\\\\\n\\)*" + "else" + "\\([ \t\n]\\|\\\\\n\\)+" + "if" + "\\([ \t\n]\\|\\\\\n\\)*" + "(" + "\\=") + nil t) + (not (c-save-buffer-state () (c-in-literal)))) + (delete-region (match-beginning 0) (match-end 0)) + (insert-and-inherit "} else if (")) + + ;; clean up brace-catch-brace + (when + (and (memq 'brace-catch-brace c-cleanup-list) + (eq (c-last-command-char) ?\() + (re-search-backward + (concat "}" + "\\([ \t\n]\\|\\\\\n\\)*" + "catch" + "\\([ \t\n]\\|\\\\\n\\)*" + "(" + "\\=") + nil t) + (not (c-save-buffer-state () (c-in-literal)))) + (delete-region (match-beginning 0) (match-end 0)) + (insert-and-inherit "} catch (")))) ;; Apply `electric-pair-mode' stuff. (when (and (boundp 'electric-pair-mode) @@ -1362,41 +1379,42 @@ newline cleanups are done if appropriate; see the variable `c-cleanup-list'." (let (post-self-insert-hook) (electric-pair-post-self-insert-function))) - ;; Check for clean-ups at function calls. These two DON'T need - ;; `c-electric-flag' or `c-syntactic-indentation' set. - ;; Point is currently just after the inserted paren. - (let (beg (end (1- (point)))) - (cond - - ;; space-before-funcall clean-up? - ((and (memq 'space-before-funcall c-cleanup-list) - (eq (c-last-command-char) ?\() - (save-excursion - (backward-char) - (skip-chars-backward " \t") - (setq beg (point)) - (and (c-save-buffer-state () (c-on-identifier)) - ;; Don't add a space into #define FOO().... - (not (and (c-beginning-of-macro) - (c-forward-over-cpp-define-id) - (eq (point) beg)))))) - (save-excursion - (delete-region beg end) - (goto-char beg) - (insert ?\ ))) - - ;; compact-empty-funcall clean-up? - ((c-save-buffer-state () - (and (memq 'compact-empty-funcall c-cleanup-list) - (eq (c-last-command-char) ?\)) - (save-excursion - (c-safe (backward-char 2)) - (when (looking-at "()") - (setq end (point)) - (skip-chars-backward " \t") - (setq beg (point)) - (c-on-identifier))))) - (delete-region beg end)))) + (c-with-string-fences + ;; Check for clean-ups at function calls. These two DON'T need + ;; `c-electric-flag' or `c-syntactic-indentation' set. + ;; Point is currently just after the inserted paren. + (let (beg (end (1- (point)))) + (cond + + ;; space-before-funcall clean-up? + ((and (memq 'space-before-funcall c-cleanup-list) + (eq (c-last-command-char) ?\() + (save-excursion + (backward-char) + (skip-chars-backward " \t") + (setq beg (point)) + (and (c-save-buffer-state () (c-on-identifier)) + ;; Don't add a space into #define FOO().... + (not (and (c-beginning-of-macro) + (c-forward-over-cpp-define-id) + (eq (point) beg)))))) + (save-excursion + (delete-region beg end) + (goto-char beg) + (insert ?\ ))) + + ;; compact-empty-funcall clean-up? + ((c-save-buffer-state () + (and (memq 'compact-empty-funcall c-cleanup-list) + (eq (c-last-command-char) ?\)) + (save-excursion + (c-safe (backward-char 2)) + (when (looking-at "()") + (setq end (point)) + (skip-chars-backward " \t") + (setq beg (point)) + (c-on-identifier))))) + (delete-region beg end))))) (and (eq last-input-event ?\)) (not executing-kbd-macro) old-blink-paren @@ -1405,8 +1423,8 @@ newline cleanups are done if appropriate; see the variable `c-cleanup-list'." ;; Apply `electric-pair-mode' stuff inside a string or comment. (when (and (boundp 'electric-pair-mode) electric-pair-mode) (let (post-self-insert-hook) - (electric-pair-post-self-insert-function)))) - (c--call-post-self-insert-hook-more-safely))) + (electric-pair-post-self-insert-function))))) + (c--call-post-self-insert-hook-more-safely)) (defun c-electric-continued-statement () "Reindent the current line if appropriate. @@ -1868,68 +1886,71 @@ defun." (c-region-is-active-p) (push-mark)) - (c-save-buffer-state - (beginning-of-defun-function - end-of-defun-function - (paren-state (c-parse-state)) - (orig-point-min (point-min)) (orig-point-max (point-max)) - lim ; Position of { which has been widened to. - where pos case-fold-search) - - (save-restriction - (if (eq c-defun-tactic 'go-outward) - (setq lim (c-widen-to-enclosing-decl-scope ; e.g. class, namespace. - paren-state orig-point-min orig-point-max))) - - ;; Move back out of any macro/comment/string we happen to be in. - (c-beginning-of-macro) - (setq pos (c-literal-start)) - (if pos (goto-char pos)) - - (setq where (c-where-wrt-brace-construct)) - - (if (< arg 0) - ;; Move forward to the closing brace of a function. - (progn - (if (memq where '(at-function-end outwith-function)) - (setq arg (1+ arg))) - (if (< arg 0) - (c-while-widening-to-decl-block - (< (setq arg (- (c-forward-to-nth-EOF-\;-or-} (- arg) where))) 0))) - ;; Move forward to the next opening brace.... - (when (and (= arg 0) - (progn - (c-while-widening-to-decl-block - (not (c-syntactic-re-search-forward "{" nil 'eob))) - (eq (char-before) ?{))) - (backward-char) - ;; ... and backward to the function header. - (c-beginning-of-decl-1) - t)) - - ;; Move backward to the opening brace of a function, making successively - ;; larger portions of the buffer visible as necessary. - (when (> arg 0) - (c-while-widening-to-decl-block - (> (setq arg (c-backward-to-nth-BOF-{ arg where)) 0))) - - (when (eq arg 0) - ;; Go backward to this function's header. - (c-beginning-of-decl-1) - - (setq pos (point)) - ;; We're now there, modulo comments and whitespace. - ;; Try to be line oriented; position point at the closest - ;; preceding boi that isn't inside a comment, but if we hit - ;; the previous declaration then we use the current point - ;; instead. - (while (and (/= (point) (c-point 'boi)) - (c-backward-single-comment))) - (if (/= (point) (c-point 'boi)) - (goto-char pos))) - - (c-keep-region-active) - (= arg 0))))) + (c-with-string-fences + (c-save-buffer-state + (beginning-of-defun-function + end-of-defun-function + (paren-state (c-parse-state)) + (orig-point-min (point-min)) (orig-point-max (point-max)) + lim ; Position of { which has been widened to. + where pos case-fold-search) + + (save-restriction + (if (eq c-defun-tactic 'go-outward) + (setq lim (c-widen-to-enclosing-decl-scope ; e.g. class, namespace. + paren-state orig-point-min orig-point-max))) + + ;; Move back out of any macro/comment/string we happen to be in. + (c-beginning-of-macro) + (setq pos (c-literal-start)) + (if pos (goto-char pos)) + + (setq where (c-where-wrt-brace-construct)) + + (if (< arg 0) + ;; Move forward to the closing brace of a function. + (progn + (if (memq where '(at-function-end outwith-function)) + (setq arg (1+ arg))) + (if (< arg 0) + (c-while-widening-to-decl-block + (< (setq arg (- (c-forward-to-nth-EOF-\;-or-} (- arg) where))) 0))) + (prog1 + ;; Move forward to the next opening brace.... + (when (and (= arg 0) + (progn + (c-while-widening-to-decl-block + (not (c-syntactic-re-search-forward "{" nil 'eob))) + (eq (char-before) ?{))) + (backward-char) + ;; ... and backward to the function header. + (c-beginning-of-decl-1) + t) + (c-keep-region-active))) + + ;; Move backward to the opening brace of a function, making successively + ;; larger portions of the buffer visible as necessary. + (when (> arg 0) + (c-while-widening-to-decl-block + (> (setq arg (c-backward-to-nth-BOF-{ arg where)) 0))) + + (when (eq arg 0) + ;; Go backward to this function's header. + (c-beginning-of-decl-1) + + (setq pos (point)) + ;; We're now there, modulo comments and whitespace. + ;; Try to be line oriented; position point at the closest + ;; preceding boi that isn't inside a comment, but if we hit + ;; the previous declaration then we use the current point + ;; instead. + (while (and (/= (point) (c-point 'boi)) + (c-backward-single-comment))) + (if (/= (point) (c-point 'boi)) + (goto-char pos))) + + (c-keep-region-active) + (= arg 0)))))) (defun c-forward-to-nth-EOF-\;-or-} (n where) ;; Skip to the closing brace or semicolon of the Nth function after point. @@ -1996,65 +2017,66 @@ the open-parenthesis that starts a defun; see `beginning-of-defun'." (c-region-is-active-p) (push-mark)) - (c-save-buffer-state - (beginning-of-defun-function - end-of-defun-function - (paren-state (c-parse-state)) - (orig-point-min (point-min)) (orig-point-max (point-max)) - lim - where pos case-fold-search) - - (save-restriction - (if (eq c-defun-tactic 'go-outward) - (setq lim (c-widen-to-enclosing-decl-scope ; e.g. class, namespace - paren-state orig-point-min orig-point-max))) - - ;; Move back out of any macro/comment/string we happen to be in. - (c-beginning-of-macro) - (setq pos (c-literal-start)) - (if pos (goto-char pos)) + (c-with-string-fences + (c-save-buffer-state + (beginning-of-defun-function + end-of-defun-function + (paren-state (c-parse-state)) + (orig-point-min (point-min)) (orig-point-max (point-max)) + lim + where pos case-fold-search) + + (save-restriction + (if (eq c-defun-tactic 'go-outward) + (setq lim (c-widen-to-enclosing-decl-scope ; e.g. class, namespace + paren-state orig-point-min orig-point-max))) + + ;; Move back out of any macro/comment/string we happen to be in. + (c-beginning-of-macro) + (setq pos (c-literal-start)) + (if pos (goto-char pos)) + + (setq where (c-where-wrt-brace-construct)) + + (if (< arg 0) + ;; Move backwards to the } of a function + (progn + (if (memq where '(at-header outwith-function)) + (setq arg (1+ arg))) + (if (< arg 0) + (c-while-widening-to-decl-block + (< (setq arg (- (c-backward-to-nth-BOF-{ (- arg) where))) 0))) + (if (= arg 0) + (c-while-widening-to-decl-block + (progn (c-syntactic-skip-backward "^}") + (not (eq (char-before) ?})))))) + + ;; Move forward to the } of a function + (if (> arg 0) + (c-while-widening-to-decl-block + (> (setq arg (c-forward-to-nth-EOF-\;-or-} arg where)) 0)))) + + ;; Do we need to move forward from the brace to the semicolon? + (when (eq arg 0) + (if (c-in-function-trailer-p) ; after "}" of struct/enum, etc. + (c-syntactic-re-search-forward ";")) - (setq where (c-where-wrt-brace-construct)) + (setq pos (point)) + ;; We're there now, modulo comments and whitespace. + ;; Try to be line oriented; position point after the next + ;; newline that isn't inside a comment, but if we hit the + ;; next declaration then we use the current point instead. + (while (and (not (bolp)) + (not (looking-at "\\s *$")) + (c-forward-single-comment))) + (cond ((bolp)) + ((looking-at "\\s *$") + (forward-line 1)) + (t + (goto-char pos)))) - (if (< arg 0) - ;; Move backwards to the } of a function - (progn - (if (memq where '(at-header outwith-function)) - (setq arg (1+ arg))) - (if (< arg 0) - (c-while-widening-to-decl-block - (< (setq arg (- (c-backward-to-nth-BOF-{ (- arg) where))) 0))) - (if (= arg 0) - (c-while-widening-to-decl-block - (progn (c-syntactic-skip-backward "^}") - (not (eq (char-before) ?})))))) - - ;; Move forward to the } of a function - (if (> arg 0) - (c-while-widening-to-decl-block - (> (setq arg (c-forward-to-nth-EOF-\;-or-} arg where)) 0)))) - - ;; Do we need to move forward from the brace to the semicolon? - (when (eq arg 0) - (if (c-in-function-trailer-p) ; after "}" of struct/enum, etc. - (c-syntactic-re-search-forward ";")) - - (setq pos (point)) - ;; We're there now, modulo comments and whitespace. - ;; Try to be line oriented; position point after the next - ;; newline that isn't inside a comment, but if we hit the - ;; next declaration then we use the current point instead. - (while (and (not (bolp)) - (not (looking-at "\\s *$")) - (c-forward-single-comment))) - (cond ((bolp)) - ((looking-at "\\s *$") - (forward-line 1)) - (t - (goto-char pos)))) - - (c-keep-region-active) - (= arg 0)))) + (c-keep-region-active) + (= arg 0))))) (defun c-defun-name-1 () "Return name of current defun, at current narrowing, or nil if there isn't one. @@ -2093,13 +2115,12 @@ with a brace block." (c-forward-syntactic-ws) (when (eq (char-after) ?\") (forward-sexp 1) + (c-forward-syntactic-ws) (c-forward-token-2)) ; over the comma and following WS. - (buffer-substring-no-properties - (point) - (progn - (c-forward-token-2) - (c-backward-syntactic-ws) - (point)))) + (setq pos (point)) + (and (zerop (c-forward-token-2)) + (progn (c-backward-syntactic-ws) t) + (buffer-substring-no-properties pos (point)))) ((and (c-major-mode-is 'objc-mode) (looking-at "[-+]\\s-*(")) ; Objective-C method ;; Move to the beginning of the method name. @@ -2340,18 +2361,19 @@ with a brace block, at the outermost level of nesting." "Display the name of the current CC mode defun and the position in it. With a prefix arg, push the name onto the kill ring too." (interactive "P") - (save-restriction - (widen) - (c-save-buffer-state ((name-and-limits (c-defun-name-and-limits nil)) - (name (car name-and-limits)) - (limits (cdr name-and-limits)) - (point-bol (c-point 'bol))) - (when name - (message "%s. Line %s/%s." name - (1+ (count-lines (car limits) (max point-bol (car limits)))) - (count-lines (car limits) (cdr limits))) - (if arg (kill-new name)) - (sit-for 3 t))))) + (c-with-string-fences + (save-restriction + (widen) + (c-save-buffer-state ((name-and-limits (c-defun-name-and-limits nil)) + (name (car name-and-limits)) + (limits (cdr name-and-limits)) + (point-bol (c-point 'bol))) + (when name + (message "%s. Line %s/%s." name + (1+ (count-lines (car limits) (max point-bol (car limits)))) + (count-lines (car limits) (cdr limits))) + (if arg (kill-new name)) + (sit-for 3 t)))))) (put 'c-display-defun-name 'isearch-scroll t) (defun c-mark-function () @@ -2367,34 +2389,35 @@ As opposed to \\[c-beginning-of-defun] and \\[c-end-of-defun], this function does not require the declaration to contain a brace block." (interactive) - (let (decl-limits case-fold-search) - (c-save-buffer-state nil - ;; We try to be line oriented, unless there are several - ;; declarations on the same line. - (if (looking-at c-syntactic-eol) - (c-backward-token-2 1 nil (c-point 'bol))) - (setq decl-limits (c-declaration-limits t))) - - (if (not decl-limits) - (error "Cannot find any declaration") - (let* ((extend-region-p - (and (eq this-command 'c-mark-function) - (eq last-command 'c-mark-function))) - (push-mark-p (and (eq this-command 'c-mark-function) - (not extend-region-p) - (not (c-region-is-active-p))))) - (if push-mark-p (push-mark)) - (if extend-region-p - (progn - (exchange-point-and-mark) - (setq decl-limits (c-declaration-limits t)) - (when (not decl-limits) - (exchange-point-and-mark) - (error "Cannot find any declaration")) - (goto-char (cdr decl-limits)) - (exchange-point-and-mark)) - (goto-char (car decl-limits)) - (push-mark (cdr decl-limits) nil t)))))) + (c-with-string-fences + (let (decl-limits case-fold-search) + (c-save-buffer-state nil + ;; We try to be line oriented, unless there are several + ;; declarations on the same line. + (if (looking-at c-syntactic-eol) + (c-backward-token-2 1 nil (c-point 'bol))) + (setq decl-limits (c-declaration-limits t))) + + (if (not decl-limits) + (error "Cannot find any declaration") + (let* ((extend-region-p + (and (eq this-command 'c-mark-function) + (eq last-command 'c-mark-function))) + (push-mark-p (and (eq this-command 'c-mark-function) + (not extend-region-p) + (not (c-region-is-active-p))))) + (if push-mark-p (push-mark)) + (if extend-region-p + (progn + (exchange-point-and-mark) + (setq decl-limits (c-declaration-limits t)) + (when (not decl-limits) + (exchange-point-and-mark) + (error "Cannot find any declaration")) + (goto-char (cdr decl-limits)) + (exchange-point-and-mark)) + (goto-char (car decl-limits)) + (push-mark (cdr decl-limits) nil t))))))) (defun c-cpp-define-name () "Return the name of the current CPP macro, or NIL if we're not in one." @@ -3031,85 +3054,86 @@ be more \"DWIM:ey\"." nil t)) (if (< count 0) (c-end-of-statement (- count) lim sentence-flag) - (c-save-buffer-state - ((count (or count 1)) - last ; start point for going back ONE chunk. Updated each chunk movement. - (macro-fence - (save-excursion (and (not (bobp)) (c-beginning-of-macro) (point)))) - res ; result from sub-function call - not-bos ; "not beginning-of-statement" - (range (c-collect-line-comments (c-literal-limits lim)))) ; (start.end) of current literal or NIL - - ;; Go back one statement at each iteration of the following loop. - (while (and (/= count 0) - (or (not lim) (> (point) lim))) - ;; Go back one "chunk" each time round the following loop, stopping - ;; when we reach a statement boundary, etc. - (setq last (point)) - (while - (cond ; Each arm of this cond returns NIL on reaching a desired - ; statement boundary, non-NIL otherwise. - ((bobp) - (setq count 0) - nil) - - (range ; point is within or approaching a literal. - (cond - ;; Single line string or sentence-flag is null => skip the - ;; entire literal. - ((or (null sentence-flag) - (c-one-line-string-p range)) - (goto-char (car range)) - (setq range (c-ascertain-preceding-literal)) - ;; N.B. The following is essentially testing for an AWK regexp - ;; at BOS: - ;; Was the previous non-ws thing an end of statement? - (save-excursion - (if macro-fence - (c-backward-comments) - (c-backward-syntactic-ws)) - (not (or (bobp) (c-after-statement-terminator-p))))) - - ;; Comment inside a statement or a multi-line string. - (t (when (setq res ; returns non-nil when we go out of the literal - (if (eq (c-literal-type range) 'string) - (c-beginning-of-sentence-in-string range) - (c-beginning-of-sentence-in-comment range))) - (setq range (c-ascertain-preceding-literal))) - res))) - - ;; Non-literal code. - (t (setq res (c-back-over-illiterals macro-fence)) - (setq not-bos ; "not reached beginning-of-statement". - (or (= (point) last) - (memq (char-after) '(?\) ?\})) - (and - (car res) - ;; We're at a tentative BOS. The next form goes - ;; back over WS looking for an end of previous - ;; statement. - (not (save-excursion - (if macro-fence - (c-backward-comments) - (c-backward-syntactic-ws)) - (or (bobp) (c-after-statement-terminator-p))))))) - ;; Are we about to move backwards into or out of a - ;; preprocessor command? If so, locate its beginning. - (when (eq (cdr res) 'macro-boundary) - (save-excursion - (beginning-of-line) - (setq macro-fence - (and (not (bobp)) - (progn (c-skip-ws-backward) (c-beginning-of-macro)) - (point))))) - ;; Are we about to move backwards into a literal? - (when (memq (cdr res) '(macro-boundary literal)) - (setq range (c-ascertain-preceding-literal))) - not-bos)) - (setq last (point))) - - (if (/= count 0) (setq count (1- count)))) - (c-keep-region-active)))) + (c-with-string-fences + (c-save-buffer-state + ((count (or count 1)) + last ; start point for going back ONE chunk. Updated each chunk movement. + (macro-fence + (save-excursion (and (not (bobp)) (c-beginning-of-macro) (point)))) + res ; result from sub-function call + not-bos ; "not beginning-of-statement" + (range (c-collect-line-comments (c-literal-limits lim)))) ; (start.end) of current literal or NIL + + ;; Go back one statement at each iteration of the following loop. + (while (and (/= count 0) + (or (not lim) (> (point) lim))) + ;; Go back one "chunk" each time round the following loop, stopping + ;; when we reach a statement boundary, etc. + (setq last (point)) + (while + (cond ; Each arm of this cond returns NIL on reaching a desired + ; statement boundary, non-NIL otherwise. + ((bobp) + (setq count 0) + nil) + + (range ; point is within or approaching a literal. + (cond + ;; Single line string or sentence-flag is null => skip the + ;; entire literal. + ((or (null sentence-flag) + (c-one-line-string-p range)) + (goto-char (car range)) + (setq range (c-ascertain-preceding-literal)) + ;; N.B. The following is essentially testing for an AWK regexp + ;; at BOS: + ;; Was the previous non-ws thing an end of statement? + (save-excursion + (if macro-fence + (c-backward-comments) + (c-backward-syntactic-ws)) + (not (or (bobp) (c-after-statement-terminator-p))))) + + ;; Comment inside a statement or a multi-line string. + (t (when (setq res ; returns non-nil when we go out of the literal + (if (eq (c-literal-type range) 'string) + (c-beginning-of-sentence-in-string range) + (c-beginning-of-sentence-in-comment range))) + (setq range (c-ascertain-preceding-literal))) + res))) + + ;; Non-literal code. + (t (setq res (c-back-over-illiterals macro-fence)) + (setq not-bos ; "not reached beginning-of-statement". + (or (= (point) last) + (memq (char-after) '(?\) ?\})) + (and + (car res) + ;; We're at a tentative BOS. The next form goes + ;; back over WS looking for an end of previous + ;; statement. + (not (save-excursion + (if macro-fence + (c-backward-comments) + (c-backward-syntactic-ws)) + (or (bobp) (c-after-statement-terminator-p))))))) + ;; Are we about to move backwards into or out of a + ;; preprocessor command? If so, locate its beginning. + (when (eq (cdr res) 'macro-boundary) + (save-excursion + (beginning-of-line) + (setq macro-fence + (and (not (bobp)) + (progn (c-skip-ws-backward) (c-beginning-of-macro)) + (point))))) + ;; Are we about to move backwards into a literal? + (when (memq (cdr res) '(macro-boundary literal)) + (setq range (c-ascertain-preceding-literal))) + not-bos)) + (setq last (point))) + + (if (/= count 0) (setq count (1- count)))) + (c-keep-region-active))))) (defun c-end-of-statement (&optional count lim sentence-flag) "Go to the end of the innermost C statement. @@ -3127,78 +3151,79 @@ sentence motion in or near comments and multiline strings." (setq count (or count 1)) (if (< count 0) (c-beginning-of-statement (- count) lim sentence-flag) - (c-save-buffer-state - (here ; start point for going forward ONE statement. Updated each statement. - (macro-fence - (save-excursion - (and (not (eobp)) (c-beginning-of-macro) - (progn (c-end-of-macro) (point))))) - res - (range (c-collect-line-comments (c-literal-limits lim)))) ; (start.end) of current literal or NIL - - ;; Go back/forward one statement at each iteration of the following loop. - (while (and (/= count 0) - (or (not lim) (< (point) lim))) - (setq here (point)) ; ONLY HERE is HERE updated - - ;; Go forward one "chunk" each time round the following loop, stopping - ;; when we reach a statement boundary, etc. - (while - (cond ; Each arm of this cond returns NIL on reaching a desired - ; statement boundary, non-NIL otherwise. - ((eobp) - (setq count 0) - nil) + (c-with-string-fences + (c-save-buffer-state + (here ; start point for going forward ONE statement. Updated each statement. + (macro-fence + (save-excursion + (and (not (eobp)) (c-beginning-of-macro) + (progn (c-end-of-macro) (point))))) + res + (range (c-collect-line-comments (c-literal-limits lim)))) ; (start.end) of current literal or NIL + + ;; Go back/forward one statement at each iteration of the following loop. + (while (and (/= count 0) + (or (not lim) (< (point) lim))) + (setq here (point)) ; ONLY HERE is HERE updated + + ;; Go forward one "chunk" each time round the following loop, stopping + ;; when we reach a statement boundary, etc. + (while + (cond ; Each arm of this cond returns NIL on reaching a desired + ; statement boundary, non-NIL otherwise. + ((eobp) + (setq count 0) + nil) + + (range ; point is within a literal. + (cond + ;; sentence-flag is null => skip the entire literal. + ;; or a Single line string. + ((or (null sentence-flag) + (c-one-line-string-p range)) + (goto-char (cdr range)) + (setq range (c-ascertain-following-literal)) + ;; Is there a virtual semicolon here (e.g. for AWK)? + (not (c-at-vsemi-p))) + + ;; Comment or multi-line string. + (t (when (setq res ; gets non-nil when we go out of the literal + (if (eq (c-literal-type range) 'string) + (c-end-of-sentence-in-string range) + (c-end-of-sentence-in-comment range))) + (setq range (c-ascertain-following-literal))) + ;; If we've just come forward out of a literal, check for + ;; vsemi. (N.B. AWK can't have a vsemi after a comment, but + ;; some other language may do in the future) + (and res + (not (c-at-vsemi-p)))))) + + ;; Non-literal code. + (t (setq res (c-forward-over-illiterals macro-fence + (> (point) here))) + ;; Are we about to move forward into or out of a + ;; preprocessor command? + (when (eq (cdr res) 'macro-boundary) + (setq macro-fence + (save-excursion + (if macro-fence + (progn + (end-of-line) + (and (not (eobp)) + (progn (c-skip-ws-forward) + (c-beginning-of-macro)) + (progn (c-end-of-macro) + (point)))) + (and (not (eobp)) + (c-beginning-of-macro) + (progn (c-end-of-macro) (point))))))) + ;; Are we about to move forward into a literal? + (when (memq (cdr res) '(macro-boundary literal)) + (setq range (c-ascertain-following-literal))) + (car res)))) - (range ; point is within a literal. - (cond - ;; sentence-flag is null => skip the entire literal. - ;; or a Single line string. - ((or (null sentence-flag) - (c-one-line-string-p range)) - (goto-char (cdr range)) - (setq range (c-ascertain-following-literal)) - ;; Is there a virtual semicolon here (e.g. for AWK)? - (not (c-at-vsemi-p))) - - ;; Comment or multi-line string. - (t (when (setq res ; gets non-nil when we go out of the literal - (if (eq (c-literal-type range) 'string) - (c-end-of-sentence-in-string range) - (c-end-of-sentence-in-comment range))) - (setq range (c-ascertain-following-literal))) - ;; If we've just come forward out of a literal, check for - ;; vsemi. (N.B. AWK can't have a vsemi after a comment, but - ;; some other language may do in the future) - (and res - (not (c-at-vsemi-p)))))) - - ;; Non-literal code. - (t (setq res (c-forward-over-illiterals macro-fence - (> (point) here))) - ;; Are we about to move forward into or out of a - ;; preprocessor command? - (when (eq (cdr res) 'macro-boundary) - (setq macro-fence - (save-excursion - (if macro-fence - (progn - (end-of-line) - (and (not (eobp)) - (progn (c-skip-ws-forward) - (c-beginning-of-macro)) - (progn (c-end-of-macro) - (point)))) - (and (not (eobp)) - (c-beginning-of-macro) - (progn (c-end-of-macro) (point))))))) - ;; Are we about to move forward into a literal? - (when (memq (cdr res) '(macro-boundary literal)) - (setq range (c-ascertain-following-literal))) - (car res)))) - - (if (/= count 0) (setq count (1- count)))) - (c-keep-region-active)))) + (if (/= count 0) (setq count (1- count)))) + (c-keep-region-active))))) ;; set up electric character functions to work with pending-del, @@ -3413,7 +3438,8 @@ to call `c-scan-conditionals' directly instead." (interactive "p") (let ((new-point (c-scan-conditionals count target-depth with-else))) (push-mark) - (goto-char new-point))) + (goto-char new-point)) + (c-keep-region-active)) (defun c-scan-conditionals (count &optional target-depth with-else) "Scan forward across COUNT preprocessor conditionals. @@ -3536,122 +3562,125 @@ prefix argument is equivalent to -1. depending on the variable `indent-tabs-mode'." (interactive "P") - (let ((indent-function - (if c-syntactic-indentation - (symbol-function 'indent-according-to-mode) - (lambda () - (let ((c-macro-start c-macro-start) - (steps (if (equal arg '(4)) - -1 - (prefix-numeric-value arg)))) - (c-shift-line-indentation (* steps c-basic-offset)) - (when (and c-auto-align-backslashes - (save-excursion - (end-of-line) - (eq (char-before) ?\\)) - (c-query-and-set-macro-start)) - ;; Realign the line continuation backslash if inside a macro. - (c-backslash-region (point) (point) nil t))) - )))) - (if (and c-syntactic-indentation arg) - ;; If c-syntactic-indentation and got arg, always indent this - ;; line as C and shift remaining lines of expression the same - ;; amount. - (let ((shift-amt (save-excursion - (back-to-indentation) - (current-column))) - beg end) - (c-indent-line) - (setq shift-amt (- (save-excursion - (back-to-indentation) - (current-column)) - shift-amt)) - (save-excursion - (if (eq c-tab-always-indent t) - (beginning-of-line)) ; FIXME!!! What is this here for? ACM 2005/10/31 - (setq beg (point)) - (c-forward-sexp 1) - (setq end (point)) - (goto-char beg) - (forward-line 1) - (setq beg (point))) - (if (> end beg) - (indent-code-rigidly beg end shift-amt "#"))) - ;; Else use c-tab-always-indent to determine behavior. - (cond - ;; CASE 1: indent when at column zero or in line's indentation, - ;; otherwise insert a tab - ((not c-tab-always-indent) - (if (save-excursion - (skip-chars-backward " \t") - (not (bolp))) - (funcall c-insert-tab-function) - (funcall indent-function))) - ;; CASE 2: just indent the line - ((eq c-tab-always-indent t) - (funcall indent-function)) - ;; CASE 3: if in a literal, insert a tab, but always indent the - ;; line - (t - (if (c-save-buffer-state () (c-in-literal)) - (funcall c-insert-tab-function)) - (funcall indent-function) - ))))) + (c-with-string-fences + (let ((indent-function + (if c-syntactic-indentation + (symbol-function 'indent-according-to-mode) + (lambda () + (let ((c-macro-start c-macro-start) + (steps (if (equal arg '(4)) + -1 + (prefix-numeric-value arg)))) + (c-shift-line-indentation (* steps c-basic-offset)) + (when (and c-auto-align-backslashes + (save-excursion + (end-of-line) + (eq (char-before) ?\\)) + (c-query-and-set-macro-start)) + ;; Realign the line continuation backslash if inside a macro. + (c-backslash-region (point) (point) nil t))) + )))) + (if (and c-syntactic-indentation arg) + ;; If c-syntactic-indentation and got arg, always indent this + ;; line as C and shift remaining lines of expression the same + ;; amount. + (let ((shift-amt (save-excursion + (back-to-indentation) + (current-column))) + beg end) + (c-indent-line) + (setq shift-amt (- (save-excursion + (back-to-indentation) + (current-column)) + shift-amt)) + (save-excursion + (if (eq c-tab-always-indent t) + (beginning-of-line)) ; FIXME!!! What is this here for? ACM 2005/10/31 + (setq beg (point)) + (c-forward-sexp 1) + (setq end (point)) + (goto-char beg) + (forward-line 1) + (setq beg (point))) + (if (> end beg) + (indent-code-rigidly beg end shift-amt "#"))) + ;; Else use c-tab-always-indent to determine behavior. + (cond + ;; CASE 1: indent when at column zero or in line's indentation, + ;; otherwise insert a tab + ((not c-tab-always-indent) + (if (save-excursion + (skip-chars-backward " \t") + (not (bolp))) + (funcall c-insert-tab-function) + (funcall indent-function))) + ;; CASE 2: just indent the line + ((eq c-tab-always-indent t) + (funcall indent-function)) + ;; CASE 3: if in a literal, insert a tab, but always indent the + ;; line + (t + (if (c-save-buffer-state () (c-in-literal)) + (funcall c-insert-tab-function)) + (funcall indent-function) + )))))) (defun c-indent-exp (&optional shutup-p) "Indent each line in the balanced expression following point syntactically. If optional SHUTUP-P is non-nil, no errors are signaled if no balanced expression is found." (interactive "*P") - (let ((here (point-marker)) - end) - (set-marker-insertion-type here t) - (unwind-protect - (let ((start (save-restriction - ;; Find the closest following open paren that - ;; ends on another line. - (narrow-to-region (point-min) (c-point 'eol)) - (let (beg (end (point))) - (while (and (setq beg (c-down-list-forward end)) - (setq end (c-up-list-forward beg)))) - (and beg - (eq (char-syntax (char-before beg)) ?\() - (1- beg)))))) - ;; sanity check - (if (not start) - (unless shutup-p - (error "Cannot find start of balanced expression to indent")) - (goto-char start) - (setq end (c-safe (scan-sexps (point) 1))) - (if (not end) - (unless shutup-p - (error "Cannot find end of balanced expression to indent")) - (forward-line) - (if (< (point) end) - (c-indent-region (point) end))))) - (goto-char here) - (set-marker here nil)))) + (c-with-string-fences + (let ((here (point-marker)) + end) + (set-marker-insertion-type here t) + (unwind-protect + (let ((start (save-restriction + ;; Find the closest following open paren that + ;; ends on another line. + (narrow-to-region (point-min) (c-point 'eol)) + (let (beg (end (point))) + (while (and (setq beg (c-down-list-forward end)) + (setq end (c-up-list-forward beg)))) + (and beg + (eq (char-syntax (char-before beg)) ?\() + (1- beg)))))) + ;; sanity check + (if (not start) + (unless shutup-p + (error "Cannot find start of balanced expression to indent")) + (goto-char start) + (setq end (c-safe (scan-sexps (point) 1))) + (if (not end) + (unless shutup-p + (error "Cannot find end of balanced expression to indent")) + (forward-line) + (if (< (point) end) + (c-indent-region (point) end))))) + (goto-char here) + (set-marker here nil))))) (defun c-indent-defun () "Indent the current top-level declaration or macro syntactically. In the macro case this also has the effect of realigning any line continuation backslashes, unless `c-auto-align-backslashes' is nil." (interactive "*") - (let ((here (point-marker)) decl-limits case-fold-search) - (unwind-protect - (progn - (c-save-buffer-state nil - ;; We try to be line oriented, unless there are several - ;; declarations on the same line. - (if (looking-at c-syntactic-eol) - (c-backward-token-2 1 nil (c-point 'bol)) - (c-forward-token-2 0 nil (c-point 'eol))) - (setq decl-limits (c-declaration-limits nil))) - (if decl-limits - (c-indent-region (car decl-limits) - (cdr decl-limits)))) - (goto-char here) - (set-marker here nil)))) + (c-with-string-fences + (let ((here (point-marker)) decl-limits case-fold-search) + (unwind-protect + (progn + (c-save-buffer-state nil + ;; We try to be line oriented, unless there are several + ;; declarations on the same line. + (if (looking-at c-syntactic-eol) + (c-backward-token-2 1 nil (c-point 'bol)) + (c-forward-token-2 0 nil (c-point 'eol))) + (setq decl-limits (c-declaration-limits nil))) + (if decl-limits + (c-indent-region (car decl-limits) + (cdr decl-limits)))) + (goto-char here) + (set-marker here nil))))) (defun c-indent-region (start end &optional quiet) "Indent syntactically lines whose first char is between START and END inclusive. @@ -3731,9 +3760,10 @@ starting on the current line. Otherwise reindent just the current line." (interactive (list current-prefix-arg (c-region-is-active-p))) - (if region - (c-indent-region (region-beginning) (region-end)) - (c-indent-command arg))) + (c-with-string-fences + (if region + (c-indent-region (region-beginning) (region-end)) + (c-indent-command arg)))) ;; for progress reporting (defvar c-progress-info nil) @@ -4820,15 +4850,16 @@ If point is in any other situation, i.e. in normal code, do nothing. Optional prefix ARG means justify paragraph as well." (interactive "*P") - (let ((fill-paragraph-function - ;; Avoid infinite recursion. - (if (not (eq fill-paragraph-function 'c-fill-paragraph)) - fill-paragraph-function))) - (c-mask-paragraph t nil 'fill-paragraph arg)) - ;; Always return t. This has the effect that if filling isn't done - ;; above, it isn't done at all, and it's therefore effectively - ;; disabled in normal code. - t) + (c-with-string-fences + (let ((fill-paragraph-function + ;; Avoid infinite recursion. + (if (not (eq fill-paragraph-function 'c-fill-paragraph)) + fill-paragraph-function))) + (c-mask-paragraph t nil 'fill-paragraph arg)) + ;; Always return t. This has the effect that if filling isn't done + ;; above, it isn't done at all, and it's therefore effectively + ;; disabled in normal code. + t)) (defun c-do-auto-fill () ;; Do automatic filling if not inside a context where it should be @@ -4860,181 +4891,170 @@ If a fill prefix is specified, it overrides all the above." ;; used from auto-fill itself, that's normally disabled to avoid ;; unnecessary recursion. (interactive) - (let ((fill-prefix fill-prefix) - (do-line-break - (lambda () - (delete-horizontal-space) - (if soft - (insert-and-inherit ?\n) - (newline (if allow-auto-fill nil 1))))) - ;; Already know the literal type and limits when called from - ;; c-context-line-break. - (c-lit-limits c-lit-limits) - (c-lit-type c-lit-type) - (c-macro-start c-macro-start)) - - (c-save-buffer-state () - (when (not (eq c-auto-fill-prefix t)) - ;; Called from do-auto-fill. - (unless c-lit-limits - (setq c-lit-limits (c-literal-limits nil nil t))) - (unless c-lit-type - (setq c-lit-type (c-literal-type c-lit-limits))) - (if (memq (cond ((c-query-and-set-macro-start) 'cpp) - ((null c-lit-type) 'code) - (t c-lit-type)) - c-ignore-auto-fill) - (setq fill-prefix t) ; Used as flag in the cond. - (if (and (null c-auto-fill-prefix) - (eq c-lit-type 'c) - (<= (c-point 'bol) (car c-lit-limits))) - ;; The adaptive fill function has generated a prefix, but - ;; we're on the first line in a block comment so it'll be - ;; wrong. Ignore it to guess a better one below. - (setq fill-prefix nil) - (when (and (eq c-lit-type 'c++) - (not (string-match (concat "\\`[ \t]*" - c-line-comment-starter) - (or fill-prefix "")))) - ;; Kludge: If the function that adapted the fill prefix - ;; doesn't produce the required comment starter for line - ;; comments, then we ignore it. - (setq fill-prefix nil))) - ))) - - (cond ((eq fill-prefix t) - ;; A call from do-auto-fill which should be ignored. - ) - (fill-prefix - ;; A fill-prefix overrides anything. - (funcall do-line-break) - (insert-and-inherit fill-prefix)) - ((c-save-buffer-state () - (unless c-lit-limits - (setq c-lit-limits (c-literal-limits))) - (unless c-lit-type - (setq c-lit-type (c-literal-type c-lit-limits))) - (memq c-lit-type '(c c++))) - ;; Some sort of comment. - (if (or comment-multi-line - (save-excursion - (goto-char (car c-lit-limits)) - (end-of-line) - (< (point) (cdr c-lit-limits)))) - ;; Inside a comment that should be continued. - (let ((fill (c-save-buffer-state nil - (c-guess-fill-prefix - (setq c-lit-limits - (c-collect-line-comments c-lit-limits)) - c-lit-type))) - (pos (point)) - (comment-text-end - (or (and (eq c-lit-type 'c) - (save-excursion - (goto-char (- (cdr c-lit-limits) 2)) - (if (looking-at "\\*/") (point)))) - (cdr c-lit-limits)))) - ;; Skip forward past the fill prefix in case - ;; we're standing in it. - ;; - ;; FIXME: This doesn't work well in cases like - ;; - ;; /* Bla bla bla bla bla - ;; bla bla - ;; - ;; If point is on the 'B' then the line will be - ;; broken after "Bla b". - ;; - ;; If we have an empty comment, /* */, the next - ;; lot of code pushes point to the */. We fix - ;; this by never allowing point to end up to the - ;; right of where it started. - (while (and (< (current-column) (cdr fill)) - (not (eolp))) - (forward-char 1)) - (if (and (> (point) comment-text-end) - (> (c-point 'bol) (car c-lit-limits))) - (progn - ;; The skip takes us out of the (block) - ;; comment; insert the fill prefix at bol - ;; instead and keep the position. - (setq pos (copy-marker pos t)) - (beginning-of-line) - (insert-and-inherit (car fill)) - (if soft (insert-and-inherit ?\n) (newline 1)) - (goto-char pos) - (set-marker pos nil)) - ;; Don't break in the middle of a comment starter - ;; or ender. - (cond ((> (point) comment-text-end) - (goto-char comment-text-end)) - ((< (point) (+ (car c-lit-limits) 2)) - (goto-char (+ (car c-lit-limits) 2)))) - (funcall do-line-break) - (insert-and-inherit (car fill)) - (if (and (looking-at c-block-comment-ender-regexp) - (memq (char-before) '(?\ ?\t))) - (backward-char)))) ; can this hit the - ; middle of a TAB? - ;; Inside a comment that should be broken. - (let ((comment-start comment-start) - (comment-end comment-end) - col) - (if (eq c-lit-type 'c) - (unless (string-match "[ \t]*/\\*" comment-start) - (setq comment-start "/* " comment-end " */")) - (unless (string-match "[ \t]*//" comment-start) - (setq comment-start "// " comment-end ""))) - (setq col (save-excursion - (back-to-indentation) - (current-column))) - (funcall do-line-break) - (when (and comment-end (not (equal comment-end ""))) - (forward-char -1) - (insert-and-inherit comment-end) - (forward-char 1)) - ;; c-comment-indent may look at the current - ;; indentation, so let's start out with the same - ;; indentation as the previous one. - (indent-to col) - (insert-and-inherit comment-start) - (indent-for-comment)))) - ((c-query-and-set-macro-start) - ;; In a macro. - (unless (looking-at "[ \t]*\\\\$") - ;; Do not clobber the alignment of the line continuation - ;; slash; c-backslash-region might look at it. - (delete-horizontal-space)) - ;; Got an asymmetry here: In normal code this command - ;; doesn't indent the next line syntactically, and otoh a - ;; normal syntactically indenting newline doesn't continue - ;; the macro. - (c-newline-and-indent (if allow-auto-fill nil 1))) - (t - ;; Somewhere else in the code. - (let ((col (save-excursion + (c-with-string-fences + (let ((fill-prefix fill-prefix) + (do-line-break + (lambda () + (delete-horizontal-space) + (if soft + (insert-and-inherit ?\n) + (newline (if allow-auto-fill nil 1))))) + ;; Already know the literal type and limits when called from + ;; c-context-line-break. + (c-lit-limits c-lit-limits) + (c-lit-type c-lit-type) + (c-macro-start c-macro-start)) + + (c-save-buffer-state () + (when (not (eq c-auto-fill-prefix t)) + ;; Called from do-auto-fill. + (unless c-lit-limits + (setq c-lit-limits (c-literal-limits nil nil t))) + (unless c-lit-type + (setq c-lit-type (c-literal-type c-lit-limits))) + (if (memq (cond ((c-query-and-set-macro-start) 'cpp) + ((null c-lit-type) 'code) + (t c-lit-type)) + c-ignore-auto-fill) + (setq fill-prefix t) ; Used as flag in the cond. + (if (and (null c-auto-fill-prefix) + (eq c-lit-type 'c) + (<= (c-point 'bol) (car c-lit-limits))) + ;; The adaptive fill function has generated a prefix, but + ;; we're on the first line in a block comment so it'll be + ;; wrong. Ignore it to guess a better one below. + (setq fill-prefix nil) + (when (and (eq c-lit-type 'c++) + (not (string-match (concat "\\`[ \t]*" + c-line-comment-starter) + (or fill-prefix "")))) + ;; Kludge: If the function that adapted the fill prefix + ;; doesn't produce the required comment starter for line + ;; comments, then we ignore it. + (setq fill-prefix nil))) + ))) + + (cond ((eq fill-prefix t) + ;; A call from do-auto-fill which should be ignored. + ) + (fill-prefix + ;; A fill-prefix overrides anything. + (funcall do-line-break) + (insert-and-inherit fill-prefix)) + ((c-save-buffer-state () + (unless c-lit-limits + (setq c-lit-limits (c-literal-limits))) + (unless c-lit-type + (setq c-lit-type (c-literal-type c-lit-limits))) + (memq c-lit-type '(c c++))) + ;; Some sort of comment. + (if (or comment-multi-line + (save-excursion + (goto-char (car c-lit-limits)) + (end-of-line) + (< (point) (cdr c-lit-limits)))) + ;; Inside a comment that should be continued. + (let ((fill (c-save-buffer-state nil + (c-guess-fill-prefix + (setq c-lit-limits + (c-collect-line-comments c-lit-limits)) + c-lit-type))) + (pos (point)) + (comment-text-end + (or (and (eq c-lit-type 'c) + (save-excursion + (goto-char (- (cdr c-lit-limits) 2)) + (if (looking-at "\\*/") (point)))) + (cdr c-lit-limits)))) + ;; Skip forward past the fill prefix in case + ;; we're standing in it. + ;; + ;; FIXME: This doesn't work well in cases like + ;; + ;; /* Bla bla bla bla bla + ;; bla bla + ;; + ;; If point is on the 'B' then the line will be + ;; broken after "Bla b". + ;; + ;; If we have an empty comment, /* */, the next + ;; lot of code pushes point to the */. We fix + ;; this by never allowing point to end up to the + ;; right of where it started. + (while (and (< (current-column) (cdr fill)) + (not (eolp))) + (forward-char 1)) + (if (and (> (point) comment-text-end) + (> (c-point 'bol) (car c-lit-limits))) + (progn + ;; The skip takes us out of the (block) + ;; comment; insert the fill prefix at bol + ;; instead and keep the position. + (setq pos (copy-marker pos t)) (beginning-of-line) - (while (and (looking-at "[ \t]*\\\\?$") - (= (forward-line -1) 0))) - (current-indentation)))) - (funcall do-line-break) - (indent-to col)))))) + (insert-and-inherit (car fill)) + (if soft (insert-and-inherit ?\n) (newline 1)) + (goto-char pos) + (set-marker pos nil)) + ;; Don't break in the middle of a comment starter + ;; or ender. + (cond ((> (point) comment-text-end) + (goto-char comment-text-end)) + ((< (point) (+ (car c-lit-limits) 2)) + (goto-char (+ (car c-lit-limits) 2)))) + (funcall do-line-break) + (insert-and-inherit (car fill)) + (if (and (looking-at c-block-comment-ender-regexp) + (memq (char-before) '(?\ ?\t))) + (backward-char)))) ; can this hit the + ; middle of a TAB? + ;; Inside a comment that should be broken. + (let ((comment-start comment-start) + (comment-end comment-end) + col) + (if (eq c-lit-type 'c) + (unless (string-match "[ \t]*/\\*" comment-start) + (setq comment-start "/* " comment-end " */")) + (unless (string-match "[ \t]*//" comment-start) + (setq comment-start "// " comment-end ""))) + (setq col (save-excursion + (back-to-indentation) + (current-column))) + (funcall do-line-break) + (when (and comment-end (not (equal comment-end ""))) + (forward-char -1) + (insert-and-inherit comment-end) + (forward-char 1)) + ;; c-comment-indent may look at the current + ;; indentation, so let's start out with the same + ;; indentation as the previous one. + (indent-to col) + (insert-and-inherit comment-start) + (indent-for-comment)))) + ((c-query-and-set-macro-start) + ;; In a macro. + (unless (looking-at "[ \t]*\\\\$") + ;; Do not clobber the alignment of the line continuation + ;; slash; c-backslash-region might look at it. + (delete-horizontal-space)) + ;; Got an asymmetry here: In normal code this command + ;; doesn't indent the next line syntactically, and otoh a + ;; normal syntactically indenting newline doesn't continue + ;; the macro. + (c-newline-and-indent (if allow-auto-fill nil 1))) + (t + ;; Somewhere else in the code. + (let ((col (save-excursion + (beginning-of-line) + (while (and (looking-at "[ \t]*\\\\?$") + (= (forward-line -1) 0))) + (current-indentation)))) + (funcall do-line-break) + (indent-to col))))))) (defalias 'c-comment-line-break-function 'c-indent-new-comment-line) (make-obsolete 'c-comment-line-break-function 'c-indent-new-comment-line "21.1") -;; Advice for Emacsen older than 21.1 (!), released 2001/10 -(unless (boundp 'comment-line-break-function) - (defvar c-inside-line-break-advice nil) - (defadvice indent-new-comment-line (around c-line-break-advice - activate preactivate) - "Call `c-indent-new-comment-line' if in CC Mode." - (if (or c-inside-line-break-advice - (not c-buffer-is-cc-mode)) - ad-do-it - (let ((c-inside-line-break-advice t)) - (c-indent-new-comment-line (ad-get-arg 0)))))) - (defun c-context-line-break () "Do a line break suitable to the context. @@ -5057,58 +5077,59 @@ When point is inside a string, only insert a backslash when it is also inside a preprocessor directive." (interactive "*") - (let* (c-lit-limits c-lit-type - (c-macro-start c-macro-start) - case-fold-search) - - (c-save-buffer-state () - (setq c-lit-limits (c-literal-limits nil nil t) - c-lit-type (c-literal-type c-lit-limits)) - (when (eq c-lit-type 'c++) - (setq c-lit-limits (c-collect-line-comments c-lit-limits))) - (c-query-and-set-macro-start)) - - (cond - ((or (eq c-lit-type 'c) - (and (eq c-lit-type 'c++) ; C++ comment, but not at the very end of it. - (< (save-excursion - (skip-chars-forward " \t") - (point)) - (1- (cdr c-lit-limits)))) - (and (numberp c-macro-start) ; Macro, but not at the very end of + (c-with-string-fences + (let* (c-lit-limits c-lit-type + (c-macro-start c-macro-start) + case-fold-search) + + (c-save-buffer-state () + (setq c-lit-limits (c-literal-limits nil nil t) + c-lit-type (c-literal-type c-lit-limits)) + (when (eq c-lit-type 'c++) + (setq c-lit-limits (c-collect-line-comments c-lit-limits))) + (c-query-and-set-macro-start)) + + (cond + ((or (eq c-lit-type 'c) + (and (eq c-lit-type 'c++) ; C++ comment, but not at the very end of it. + (< (save-excursion + (skip-chars-forward " \t") + (point)) + (1- (cdr c-lit-limits)))) + (and (numberp c-macro-start) ; Macro, but not at the very end of ; it, not in a string, and not in the ; cpp keyword. - (not (eq c-lit-type 'string)) - (or (not (looking-at "\\s *$")) - (eq (char-before) ?\\)) - (<= (save-excursion - (goto-char c-macro-start) - (if (looking-at c-opt-cpp-start) - (goto-char (match-end 0))) - (point)) - (point)))) - (let ((comment-multi-line t) - (fill-prefix nil)) - (c-indent-new-comment-line nil t))) - - ((eq c-lit-type 'string) - (if (and (numberp c-macro-start) - (not (eq (char-before) ?\\))) - (insert ?\\)) - (newline)) - - (t (delete-horizontal-space) - (newline) - ;; c-indent-line may look at the current indentation, so let's - ;; start out with the same indentation as the previous line. - (let ((col (save-excursion - (backward-char) - (forward-line 0) - (while (and (looking-at "[ \t]*\\\\?$") - (= (forward-line -1) 0))) - (current-indentation)))) - (indent-to col)) - (indent-according-to-mode))))) + (not (eq c-lit-type 'string)) + (or (not (looking-at "\\s *$")) + (eq (char-before) ?\\)) + (<= (save-excursion + (goto-char c-macro-start) + (if (looking-at c-opt-cpp-start) + (goto-char (match-end 0))) + (point)) + (point)))) + (let ((comment-multi-line t) + (fill-prefix nil)) + (c-indent-new-comment-line nil t))) + + ((eq c-lit-type 'string) + (if (and (numberp c-macro-start) + (not (eq (char-before) ?\\))) + (insert ?\\)) + (newline)) + + (t (delete-horizontal-space) + (newline) + ;; c-indent-line may look at the current indentation, so let's + ;; start out with the same indentation as the previous line. + (let ((col (save-excursion + (backward-char) + (forward-line 0) + (while (and (looking-at "[ \t]*\\\\?$") + (= (forward-line -1) 0))) + (current-indentation)))) + (indent-to col)) + (indent-according-to-mode)))))) (defun c-context-open-line () "Insert a line break suitable to the context and leave point before it. diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el index a1270243550..9edaf465346 100644 --- a/lisp/progmodes/cc-defs.el +++ b/lisp/progmodes/cc-defs.el @@ -1563,6 +1563,28 @@ with value CHAR in the region [FROM to)." (forward-char))))) +;; Miscellaneous macro(s) +(defvar c-string-fences-set-flag nil) +;; Non-nil when we have set string fences with `c-restore-string-fences'. +(defmacro c-with-string-fences (&rest forms) + ;; Restore the string fences, evaluate FORMS, then remove them again. It + ;; should only be used at the top level of "boundary" functions in CC Mode, + ;; i.e. those called from outside CC Mode which directly or indirectly need + ;; unbalanced string markers to have their string-fence syntax-table text + ;; properties. This includes all calls to `c-parse-state'. This macro will + ;; be invoked recursively; however the `c-string-fences-set-flag' mechanism + ;; should ensure consistency, when this happens. + (declare (debug t)) + `(unwind-protect + (progn + (unless c-string-fences-set-flag + (c-restore-string-fences)) + (let ((c-string-fences-set-flag t)) + ,@forms)) + (unless c-string-fences-set-flag + (c-clear-string-fences)))) + + ;; Macros to put overlays (Emacs) or extents (XEmacs) on buffer text. ;; For our purposes, these are characterized by being possible to ;; remove again without affecting the other text properties in the diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 3068c41a57e..cfbb668baeb 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -165,12 +165,16 @@ (defvar c-doc-line-join-end-ch) (defvar c-syntactic-context) (defvar c-syntactic-element) +(defvar c-new-id-start) +(defvar c-new-id-end) +(defvar c-new-id-is-type) (cc-bytecomp-defvar c-min-syn-tab-mkr) (cc-bytecomp-defvar c-max-syn-tab-mkr) (cc-bytecomp-defun c-clear-syn-tab) (cc-bytecomp-defun c-clear-string-fences) (cc-bytecomp-defun c-restore-string-fences) (cc-bytecomp-defun c-remove-string-fences) +(cc-bytecomp-defun c-fontify-new-found-type) ;; Make declarations for all the `c-lang-defvar' variables in cc-langs. @@ -1235,7 +1239,7 @@ comment at the start of cc-engine.el for more info." (not comma-delimited) (not (c-looking-at-inexpr-block lim nil t)) (save-excursion - (c-backward-token-2 1 t nil) + (c-backward-token-2 1 t nil) ; Don't test the value (not (looking-at "=\\([^=]\\|$\\)"))) (or (not c-opt-block-decls-with-vars-key) @@ -3418,7 +3422,9 @@ initializing CC Mode. Currently (2020-06) these are `js-mode' and ;; Return a good pos (in the sense of `c-state-cache-good-pos') at the ;; lowest[*] position between POS and HERE which is syntactically equivalent ;; to HERE. This position may be HERE itself. POS is before HERE in the - ;; buffer. + ;; buffer. If POS and HERE are both in the same literal, return the start + ;; of the literal. STATE is the parsing state at POS. + ;; ;; [*] We don't actually always determine this exact position, since this ;; would require a disproportionate amount of work, given that this function ;; deals only with a corner condition, and POS and HERE are typically on @@ -3434,7 +3440,7 @@ initializing CC Mode. Currently (2020-06) these are `js-mode' and (setq pos (point) state s))) (if (eq (point) here) ; HERE is in the same literal as POS - pos + (nth 8 state) ; A valid good pos cannot be in a literal. (setq s (parse-partial-sexp pos here (1+ (car state)) nil state nil)) (cond ((> (car s) (car state)) ; Moved into a paren between POS and HERE @@ -3880,7 +3886,10 @@ initializing CC Mode. Currently (2020-06) these are `js-mode' and (cons (if (and ce (< bra ce) (> ce here)) ; {..} straddling HERE? bra (point-min)) - (min here from))))))))) + (progn + (goto-char (min here from)) + (c-beginning-of-macro) + (point)))))))))) (defsubst c-state-push-any-brace-pair (bra+1 macro-start-or-here) ;; If BRA+1 is nil, do nothing. Otherwise, BRA+1 is the buffer position @@ -6135,7 +6144,7 @@ comment at the start of cc-engine.el for more info." (setq s (cons -1 (cdr s)))) ((and (equal match ",") (eq (car s) -1))) ; at "," in "class foo : bar, ..." - ((member match '(";" "*" "," "(")) + ((member match '(";" "*" "," ")")) (when (and s (cdr s) (<= (car s) 0)) (setq s (cdr s)))) ((c-keyword-member kwd-sym 'c-flat-decl-block-kwds) @@ -6808,26 +6817,47 @@ comment at the start of cc-engine.el for more info." (defvar c-found-types nil) (make-variable-buffer-local 'c-found-types) +;; Dynamically bound variable that instructs `c-forward-type' to +;; record the ranges of types that only are found. Behaves otherwise +;; like `c-record-type-identifiers'. Also when this variable is non-nil, +;; `c-fontify-new-found-type' doesn't get called (yet) for the purported +;; type. +(defvar c-record-found-types nil) + (defsubst c-clear-found-types () ;; Clears `c-found-types'. (setq c-found-types (make-hash-table :test #'equal :weakness nil))) -(defun c-add-type (from to) - ;; Add the given region as a type in `c-found-types'. If the region - ;; doesn't match an existing type but there is a type which is equal - ;; to the given one except that the last character is missing, then - ;; the shorter type is removed. That's done to avoid adding all - ;; prefixes of a type as it's being entered and font locked. This - ;; doesn't cover cases like when characters are removed from a type - ;; or added in the middle. We'd need the position of point when the - ;; font locking is invoked to solve this well. +(defun c-add-type-1 (from to) + ;; Add the given region as a type in `c-found-types'. Prepare occurrences + ;; of this new type for fontification throughout the buffer. ;; ;; This function might do hidden buffer changes. (let ((type (c-syntactic-content from to c-recognize-<>-arglists))) (unless (gethash type c-found-types) - (remhash (substring type 0 -1) c-found-types) - (puthash type t c-found-types)))) + (puthash type t c-found-types) + (when (and (not c-record-found-types) ; Only call `c-fontify-new-found-type' + ; when we haven't "bound" c-found-types + ; to itself in c-forward-<>-arglist. + (eq (string-match c-symbol-key type) 0) + (eq (match-end 0) (length type))) + (c-fontify-new-found-type type))))) + +(defun c-add-type (from to) + ;; Add the given region as a type in `c-found-types'. Also perform the + ;; actions of `c-add-type-1'. If the region is or overlaps an identifier + ;; which might be being typed in, don't record it. This is tested by + ;; checking `c-new-id-start' and `c-new-id-end'. That's done to avoid + ;; adding all prefixes of a type as it's being entered and font locked. + ;; This is a bit rough and ready, but now covers adding characters into the + ;; middle of an identifier. + ;; + ;; This function might do hidden buffer changes. + (if (and c-new-id-start c-new-id-end + (<= from c-new-id-end) (>= to c-new-id-start)) + (setq c-new-id-is-type t) + (c-add-type-1 from to))) (defun c-unfind-type (name) ;; Remove the "NAME" from c-found-types, if present. @@ -8210,11 +8240,6 @@ multi-line strings (but not C++, for example)." (setq c-record-ref-identifiers (cons range c-record-ref-identifiers)))))) -;; Dynamically bound variable that instructs `c-forward-type' to -;; record the ranges of types that only are found. Behaves otherwise -;; like `c-record-type-identifiers'. -(defvar c-record-found-types nil) - (defmacro c-forward-keyword-prefixed-id (type) ;; Used internally in `c-forward-keyword-clause' to move forward ;; over a type (if TYPE is 'type) or a name (otherwise) which @@ -8264,9 +8289,10 @@ multi-line strings (but not C++, for example)." (defun c-forward-noise-clause () ;; Point is at a c-noise-macro-with-parens-names macro identifier. Go ;; forward over this name, any parenthesis expression which follows it, and - ;; any syntactic WS, ending up at the next token. If there is an unbalanced - ;; paren expression, leave point at it. Always Return t. - (c-forward-token-2) + ;; any syntactic WS, ending up at the next token or EOB. If there is an + ;; unbalanced paren expression, leave point at it. Always Return t. + (or (zerop (c-forward-token-2)) + (goto-char (point-max))) (if (and (eq (char-after) ?\() (c-go-list-forward)) (c-forward-syntactic-ws)) @@ -8444,6 +8470,11 @@ multi-line strings (but not C++, for example)." (c-forward-<>-arglist-recur all-types))) (progn (when (consp c-record-found-types) + (let ((cur c-record-found-types)) + (while (consp (car-safe cur)) + (c-fontify-new-found-type + (buffer-substring-no-properties (caar cur) (cdar cur))) + (setq cur (cdr cur)))) (setq c-record-type-identifiers ;; `nconc' doesn't mind that the tail of ;; `c-record-found-types' is t. @@ -9169,6 +9200,12 @@ multi-line strings (but not C++, for example)." (when (and (eq res t) (consp c-record-found-types)) + ;; Cause the confirmed types to get fontified. + (let ((cur c-record-found-types)) + (while (consp (car-safe cur)) + (c-fontify-new-found-type + (buffer-substring-no-properties (caar cur) (cdar cur))) + (setq cur (cdr cur)))) ;; Merge in the ranges of any types found by the second ;; `c-forward-type'. (setq c-record-type-identifiers @@ -9906,6 +9943,10 @@ This function might do hidden buffer changes." ;; Set when we have encountered a keyword (e.g. "extern") which ;; causes the following declaration to be treated as though top-level. make-top + ;; A list of found types in this declaration. This is an association + ;; list, the car being the buffer position, the cdr being the + ;; identifier. + found-type-list ;; Save `c-record-type-identifiers' and ;; `c-record-ref-identifiers' since ranges are recorded ;; speculatively and should be thrown away if it turns out @@ -9975,10 +10016,17 @@ This function might do hidden buffer changes." ;; If the previous identifier is a found type we ;; record it as a real one; it might be some sort of ;; alias for a prefix like "unsigned". - (save-excursion - (goto-char type-start) - (let ((c-promote-possible-types t)) - (c-forward-type)))) + ;; We postpone entering the new found type into c-found-types + ;; until we are sure of it, thus preventing rapid alternation + ;; of the fontification of the token throughout the buffer. + (push (cons type-start + (buffer-substring-no-properties + type-start + (save-excursion + (goto-char type-start) + (c-end-of-token) + (point)))) + found-type-list)) ;; Signal a type declaration for "struct foo {". (when (and backup-at-type-decl @@ -10224,13 +10272,10 @@ This function might do hidden buffer changes." (when (eq at-type 'found) ;; Remove the ostensible type from the found types list. (when type-start - (c-unfind-type - (buffer-substring-no-properties - type-start - (save-excursion - (goto-char type-start) - (c-end-of-token) - (point))))) + (let ((discard-t (assq type-start found-type-list))) + (when discard-t + (setq found-type-list + (remq discard-t found-type-list))))) t)) ;; The token which we assumed to be a type is actually the ;; identifier, and we have no explicit type. @@ -10844,6 +10889,14 @@ This function might do hidden buffer changes." ;; interactive refontification. (c-put-c-type-property (point) 'c-decl-arg-start)) + ;; Enter all the found types into `c-found-types'. + (when found-type-list + (save-excursion + (let ((c-promote-possible-types t)) + (dolist (ft found-type-list) + (goto-char (car ft)) + (c-forward-type))))) + ;; Record the type's coordinates in `c-record-type-identifiers' for ;; later fontification. (when (and c-record-type-identifiers at-type ;; (not (eq at-type t)) @@ -12092,7 +12145,10 @@ comment at the start of cc-engine.el for more info." (and (c-major-mode-is 'pike-mode) c-decl-block-key))) (while (eq braceassignp 'dontknow) - (cond ((eq (char-after) ?\;) + (cond ((or (eq (char-after) ?\;) + (save-excursion + (progn (c-backward-syntactic-ws) + (c-at-vsemi-p)))) (setq braceassignp nil)) ((and class-key (looking-at class-key)) @@ -14016,7 +14072,8 @@ comment at the start of cc-engine.el for more info." ;; clause - we assume only C++ needs it. (c-syntactic-skip-backward "^;,=" lim t)) (setq placeholder (point)) - (memq (char-before) '(?, ?= ?<))) + (and (memq (char-before) '(?, ?= ?<)) + (not (c-crosses-statement-barrier-p (point) indent-point)))) (cond ;; CASE 5D.6: Something like C++11's "using foo = <type-exp>" diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index 49e8763a28e..625010b04b2 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -97,6 +97,7 @@ (cc-bytecomp-defun c-font-lock-declarators) (cc-bytecomp-defun c-font-lock-objc-method) (cc-bytecomp-defun c-font-lock-invalid-string) +(cc-bytecomp-defun c-font-lock-fontify-region) ;; Note that font-lock in XEmacs doesn't expand face names as @@ -919,13 +920,6 @@ casts and declarations are fontified. Used on level 2 and higher." ;; This function does hidden buffer changes. ;;(message "c-font-lock-complex-decl-prepare %s %s" (point) limit) - - ;; Clear the list of found types if we start from the start of the - ;; buffer, to make it easier to get rid of misspelled types and - ;; variables that have gotten recognized as types in malformed code. - (when (bobp) - (c-clear-found-types)) - (c-skip-comments-and-strings limit) (when (< (point) limit) @@ -2258,6 +2252,49 @@ higher." ;; defvar will install its default value later on. (makunbound def-var))) +;; `c-re-redisplay-timer' is a timer which, when triggered, causes a +;; redisplay. +(defvar c-re-redisplay-timer nil) + +(defun c-force-redisplay (buffer start end) + ;; Force redisplay immediately. This assumes `font-lock-support-mode' is + ;; 'jit-lock-mode. Set the variable `c-re-redisplay-timer' to nil. + (with-current-buffer buffer + (save-excursion (c-font-lock-fontify-region start end)) + (jit-lock-force-redisplay (copy-marker start) (copy-marker end)) + (setq c-re-redisplay-timer nil))) + +(defun c-fontify-new-found-type (type) + ;; Cause the fontification of TYPE, a string, wherever it occurs in the + ;; buffer. If TYPE is currently displayed in a window, cause redisplay to + ;; happen "instantaneously". These actions are done only when jit-lock-mode + ;; is active. + (when (and font-lock-mode + (boundp 'font-lock-support-mode) + (eq font-lock-support-mode 'jit-lock-mode)) + (c-save-buffer-state + ((window-boundaries + (mapcar (lambda (win) + (cons (window-start win) + (window-end win))) + (get-buffer-window-list (current-buffer) 'no-mini t))) + (target-re (concat "\\_<" type "\\_>"))) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (while (re-search-forward target-re nil t) + (put-text-property (match-beginning 0) (match-end 0) + 'fontified nil) + (dolist (win-boundary window-boundaries) + (when (and (< (match-beginning 0) (cdr win-boundary)) + (> (match-end 0) (car win-boundary)) + (not c-re-redisplay-timer)) + (setq c-re-redisplay-timer + (run-with-timer 0 nil #'c-force-redisplay + (current-buffer) + (match-beginning 0) (match-end 0))))))))))) + ;;; C. diff --git a/lisp/progmodes/cc-guess.el b/lisp/progmodes/cc-guess.el index ea5dd48986c..584db86539e 100644 --- a/lisp/progmodes/cc-guess.el +++ b/lisp/progmodes/cc-guess.el @@ -76,6 +76,8 @@ (cc-require 'cc-engine) (cc-require 'cc-styles) +(cc-bytecomp-defun c-restore-string-fences) +(cc-bytecomp-defun c-clear-string-fences) (defcustom c-guess-offset-threshold 10 @@ -225,11 +227,12 @@ guess is made from scratch. Note that the larger the region to guess in, the slower the guessing. So you can limit the region with `c-guess-region-max'." (interactive "r\nP") - (let ((accumulator (when accumulate c-guess-accumulator))) - (setq c-guess-accumulator (c-guess-examine start end accumulator)) - (let ((pair (c-guess-guess c-guess-accumulator))) - (setq c-guess-guessed-basic-offset (car pair) - c-guess-guessed-offsets-alist (cdr pair))))) + (c-with-string-fences + (let ((accumulator (when accumulate c-guess-accumulator))) + (setq c-guess-accumulator (c-guess-examine start end accumulator)) + (let ((pair (c-guess-guess c-guess-accumulator))) + (setq c-guess-guessed-basic-offset (car pair) + c-guess-guessed-offsets-alist (cdr pair)))))) (defun c-guess-examine (start end accumulator) diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index 68070cd0581..c5964165c8d 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -458,12 +458,14 @@ so that all identifiers are recognized as words.") c-before-change-check-<>-operators c-truncate-bs-cache c-before-change-check-unbalanced-strings - c-parse-quotes-before-change) + c-parse-quotes-before-change + c-before-change-fix-comment-escapes) (c objc) '(c-extend-region-for-CPP c-depropertize-CPP c-truncate-bs-cache c-before-change-check-unbalanced-strings - c-parse-quotes-before-change) + c-parse-quotes-before-change + c-before-change-fix-comment-escapes) java '(c-parse-quotes-before-change c-before-change-check-unbalanced-strings c-before-change-check-<>-operators) @@ -500,6 +502,7 @@ parameters \(point-min) and \(point-max).") c-after-change-mark-abnormal-strings c-change-expand-fl-region) (c objc) '(c-depropertize-new-text + c-after-change-fix-comment-escapes c-after-change-escape-NL-in-string c-parse-quotes-after-change c-after-change-mark-abnormal-strings @@ -507,6 +510,7 @@ parameters \(point-min) and \(point-max).") c-neutralize-syntax-in-CPP c-change-expand-fl-region) c++ '(c-depropertize-new-text + c-after-change-fix-comment-escapes c-after-change-escape-NL-in-string c-after-change-unmark-ml-strings c-parse-quotes-after-change diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 22ab277781a..70fc1cb73a9 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -179,6 +179,15 @@ (when c-buffer-is-cc-mode (save-restriction (widen) + (let ((lst (buffer-list))) + (catch 'found + (dolist (b lst) + (if (and (not (eq b (current-buffer))) + (with-current-buffer b + c-buffer-is-cc-mode)) + (throw 'found nil))) + (remove-hook 'post-command-hook 'c-post-command) + (remove-hook 'post-gc-hook 'c-post-gc-hook))) (c-save-buffer-state () (c-clear-char-properties (point-min) (point-max) 'category) (c-clear-char-properties (point-min) (point-max) 'syntax-table) @@ -745,6 +754,8 @@ that requires a literal mode spec at compile time." ;; would do since font-lock uses a(n implicit) depth of 0) so we don't need ;; c-after-font-lock-init. (add-hook 'after-change-functions 'c-after-change nil t) + (add-hook 'post-command-hook 'c-post-command) + (when (boundp 'font-lock-extend-after-change-region-function) (set (make-local-variable 'font-lock-extend-after-change-region-function) 'c-extend-after-change-region))) ; Currently (2009-05) used by all @@ -986,7 +997,8 @@ Note that the style variables are always made local to the buffer." ;; `c-before/after-change', frame 3 is the primitive invoking the change ;; hook. (memq (cadr (backtrace-frame 3)) - '(put-text-property remove-list-of-text-properties))) + '(put-text-property remove-text-properties + remove-list-of-text-properties))) (defun c-depropertize-CPP (beg end) ;; Remove the punctuation syntax-table text property from the CPP parts of @@ -1308,7 +1320,8 @@ Note that the style variables are always made local to the buffer." ;; balanced by another " is left with a '(1) syntax-table property. (when (and c-min-syn-tab-mkr c-max-syn-tab-mkr) - (let (s pos) + (c-save-buffer-state (s pos) ; Prevent text property stuff causing change + ; function invocation. (setq pos c-min-syn-tab-mkr) (while (and @@ -1331,7 +1344,8 @@ Note that the style variables are always made local to the buffer." (c-search-backward-char-property-with-value-on-char 'c-fl-syn-tab '(15) ?\" (max (- (point) 500) (point-min)))) - (not (equal (c-get-char-property (point) 'syntax-table) '(1)))) + (not (equal (c-get-char-property (point) 'syntax-table) + '(1)))) (setq pos (1+ pos)))) (while (< pos c-max-syn-tab-mkr) (setq pos @@ -1361,7 +1375,9 @@ Note that the style variables are always made local to the buffer." ;; Restore any syntax-table text properties which are "mirrored" by ;; c-fl-syn-tab text properties. (when (and c-min-syn-tab-mkr c-max-syn-tab-mkr) - (let ((pos c-min-syn-tab-mkr)) + (c-save-buffer-state ; Prevent text property stuff causing change function + ; invocation. + ((pos c-min-syn-tab-mkr)) (while (and (< pos c-max-syn-tab-mkr) @@ -1951,6 +1967,123 @@ Note that this is a strict tail, so won't match, e.g. \"0x....\".") ;; confused by already processed single quotes. (narrow-to-region (point) (point-max)))))) +;; The next two variables record the bounds of an identifier currently being +;; typed in. These are used to prevent such a partial identifier being +;; recorded as a found type by c-add-type. +(defvar c-new-id-start nil) +(make-variable-buffer-local 'c-new-id-start) +(defvar c-new-id-end nil) +(make-variable-buffer-local 'c-new-id-end) +;; The next variable, when non-nil, records that the previous two variables +;; define a type. +(defvar c-new-id-is-type nil) +(make-variable-buffer-local 'c-new-id-is-type) + +(defun c-before-change-fix-comment-escapes (beg end) + "Remove punctuation syntax-table text properties from C/C++ comment markers. +This is to handle the rare case of two or more backslashes at an +end of line in a // comment or the equally rare case of a +backslash preceding the terminator of a /* comment, as \\*/. + +This function is used solely as a member of +`c-get-state-before-change-functions', where it should appear +late in that variable, and it must be used only together with +`c-after-change-fix-comment-escapes'. + +Note that the function currently only handles comments beginning +with // and /*, not more generic line and block comments." + (c-save-buffer-state (end-state) + (setq end-state (c-full-pp-to-literal end)) + (when (memq (cadr end-state) '(c c++)) + (goto-char (max (- beg 2) (point-min))) + (if (eq (cadr end-state) 'c) + (when (search-forward "\\*/" + (or (cdr (caddr end-state)) (point-max)) t) + (c-clear-char-property (match-beginning 0) 'syntax-table) + (c-truncate-lit-pos-cache (match-beginning 0))) + (while (search-forward "\\\\\n" + (or (cdr (caddr end-state)) (point-max)) t) + (c-clear-char-property (match-beginning 0) 'syntax-table) + (c-truncate-lit-pos-cache (match-beginning 0))))))) + +(defun c-after-change-fix-comment-escapes (beg end _old-len) + "Apply punctuation syntax-table text properties to C/C++ comment markers. +This is to handle the rare case of two or more backslashes at an +end of line in a // comment or the equally rare case of a +backslash preceding the terminator of a /* comment, as \\*/. + +This function is used solely as a member of +`c-before-font-lock-functions', where it should appear early in +that variable, and it must be used only together with +`c-before-change-fix-comment-escapes'. + +Note that the function currently only handles comments beginning +with // and /*, not more generic line and block comments." + (c-save-buffer-state (state) + ;; We cannot use `c-full-pp-to-literal' in this function, since the + ;; `syntax-table' text properties after point are not yet in a consistent + ;; state. + (setq state (c-semi-pp-to-literal beg)) + (goto-char (if (memq (cadr state) '(c c++)) + (caddr state) + (max (- beg 2) (point-min)))) + (while + (re-search-forward "\\\\\\(\\(\\\\\n\\)\\|\\(\\*/\\)\\)" + (min (+ end 2) (point-max)) t) + (setq state (c-semi-pp-to-literal (match-beginning 0))) + (when (cond + ((eq (cadr state) 'c) + (match-beginning 3)) + ((eq (cadr state) 'c++) + (match-beginning 2))) + (c-put-char-property (match-beginning 0) 'syntax-table '(1)) + (c-truncate-lit-pos-cache (match-beginning 0)))) + + (goto-char end) + (setq state (c-semi-pp-to-literal (point))) + (cond + ((eq (cadr state) 'c) + (when (search-forward "*/" nil t) + (when (eq (char-before (match-beginning 0)) ?\\) + (c-put-char-property (1- (match-beginning 0)) 'syntax-table '(1)) + (c-truncate-lit-pos-cache (1- (match-beginning 0)))))) + ((eq (cadr state) 'c++) + (while + (progn + (end-of-line) + (and (eq (char-before) ?\\) + (progn + (when (eq (char-before (1- (point))) ?\\) + (c-put-char-property (- (point) 2) 'syntax-table '(1)) + (c-truncate-lit-pos-cache (1- (point)))) + t) + (not (eobp)))) + (forward-char)))))) + +(defun c-update-new-id (end) + ;; Note the bounds of any identifier that END is in or just after, in + ;; `c-new-id-start' and `c-new-id-end'. Otherwise set these variables to + ;; nil. + (save-excursion + (goto-char end) + (let ((id-beg (c-on-identifier))) + (setq c-new-id-start id-beg + c-new-id-end (and id-beg + (progn (c-end-of-current-token) (point))))))) + +(defun c-post-command () + ;; If point was inside of a new identifier and no longer is, record that + ;; fact. + (when (and c-buffer-is-cc-mode + c-new-id-start c-new-id-end + (or (> (point) c-new-id-end) + (< (point) c-new-id-start))) + (when c-new-id-is-type + (c-add-type-1 c-new-id-start c-new-id-end)) + (setq c-new-id-start nil + c-new-id-end nil + c-new-id-is-type nil))) + (defun c-before-change (beg end) ;; Function to be put on `before-change-functions'. Primarily, this calls ;; the language dependent `c-get-state-before-change-functions'. It is @@ -1968,115 +2101,116 @@ Note that this is a strict tail, so won't match, e.g. \"0x....\".") ;; or a comment - "wrongly" removing a symbol from `c-found-types' ;; isn't critical. (unless (c-called-from-text-property-change-p) - (save-restriction - (widen) - (if c-just-done-before-change - ;; We have two consecutive calls to `before-change-functions' without - ;; an intervening `after-change-functions'. An example of this is bug - ;; #38691. To protect CC Mode, assume that the entire buffer has - ;; changed. - (setq beg (point-min) - end (point-max) - c-just-done-before-change 'whole-buffer) - (setq c-just-done-before-change t)) - ;; (c-new-BEG c-new-END) will be the region to fontify. - (setq c-new-BEG beg c-new-END end) - (setq c-maybe-stale-found-type nil) - ;; A workaround for syntax-ppss's failure to notice syntax-table text - ;; property changes. - (when (fboundp 'syntax-ppss) - (setq c-syntax-table-hwm most-positive-fixnum)) - (save-match-data - (widen) - (unwind-protect - (progn - (c-restore-string-fences) - (save-excursion - ;; Are we inserting/deleting stuff in the middle of an - ;; identifier? - (c-unfind-enclosing-token beg) - (c-unfind-enclosing-token end) - ;; Are we coalescing two tokens together, e.g. "fo o" - ;; -> "foo"? - (when (< beg end) - (c-unfind-coalesced-tokens beg end)) - (c-invalidate-sws-region-before beg end) - ;; Are we (potentially) disrupting the syntactic - ;; context which makes a type a type? E.g. by - ;; inserting stuff after "foo" in "foo bar;", or - ;; before "foo" in "typedef foo *bar;"? - ;; - ;; We search for appropriate c-type properties "near" - ;; the change. First, find an appropriate boundary - ;; for this property search. - (let (lim lim-2 - type type-pos - marked-id term-pos - (end1 - (or (and (eq (get-text-property end 'face) - 'font-lock-comment-face) - (previous-single-property-change end 'face)) - end))) - (when (>= end1 beg) ; Don't hassle about changes entirely in + (c-with-string-fences + (save-restriction + (widen) + ;; Clear the list of found types if we make a change at the start of the + ;; buffer, to make it easier to get rid of misspelled types and + ;; variables that have gotten recognized as types in malformed code. + (when (eq beg (point-min)) + (c-clear-found-types)) + (if c-just-done-before-change + ;; We have two consecutive calls to `before-change-functions' + ;; without an intervening `after-change-functions'. An example of + ;; this is bug #38691. To protect CC Mode, assume that the entire + ;; buffer has changed. + (setq beg (point-min) + end (point-max) + c-just-done-before-change 'whole-buffer) + (setq c-just-done-before-change t)) + ;; (c-new-BEG c-new-END) will be the region to fontify. + (setq c-new-BEG beg c-new-END end) + (setq c-maybe-stale-found-type nil) + ;; A workaround for syntax-ppss's failure to notice syntax-table text + ;; property changes. + (when (fboundp 'syntax-ppss) + (setq c-syntax-table-hwm most-positive-fixnum)) + (save-match-data + (save-excursion + ;; Are we inserting/deleting stuff in the middle of an + ;; identifier? + (c-unfind-enclosing-token beg) + (c-unfind-enclosing-token end) + ;; Are we coalescing two tokens together, e.g. "fo o" + ;; -> "foo"? + (when (< beg end) + (c-unfind-coalesced-tokens beg end)) + (c-invalidate-sws-region-before beg end) + ;; Are we (potentially) disrupting the syntactic + ;; context which makes a type a type? E.g. by + ;; inserting stuff after "foo" in "foo bar;", or + ;; before "foo" in "typedef foo *bar;"? + ;; + ;; We search for appropriate c-type properties "near" + ;; the change. First, find an appropriate boundary + ;; for this property search. + (let (lim lim-2 + type type-pos + marked-id term-pos + (end1 + (or (and (eq (get-text-property end 'face) + 'font-lock-comment-face) + (previous-single-property-change end 'face)) + end))) + (when (>= end1 beg) ; Don't hassle about changes entirely in ; comments. - ;; Find a limit for the search for a `c-type' property - ;; Point is currently undefined. A `goto-char' somewhere is needed. (2020-12-06). - (setq lim-2 (c-determine-limit 1000 (point) ; that is wrong. FIXME!!! (2020-12-06) - )) - (while - (and (/= (skip-chars-backward "^;{}" lim-2) 0) - (> (point) (point-min)) - (memq (c-get-char-property (1- (point)) 'face) - '(font-lock-comment-face font-lock-string-face)))) - (setq lim (max (point-min) (1- (point)))) - - ;; Look for the latest `c-type' property before end1 - (when (and (> end1 (point-min)) - (setq type-pos - (if (get-text-property (1- end1) 'c-type) - end1 - (previous-single-property-change end1 'c-type - nil lim)))) - (setq type (get-text-property (max (1- type-pos) lim) 'c-type)) - - (when (memq type '(c-decl-id-start c-decl-type-start)) - ;; Get the identifier, if any, that the property is on. - (goto-char (1- type-pos)) - (setq marked-id - (when (looking-at "\\(\\sw\\|\\s_\\)") - (c-beginning-of-current-token) - (buffer-substring-no-properties (point) type-pos))) - - (goto-char end1) - (setq lim-2 (c-determine-+ve-limit 1000)) - (skip-chars-forward "^;{}" lim-2) ; FIXME!!! loop for + ;; Find a limit for the search for a `c-type' property + ;; Point is currently undefined. A `goto-char' somewhere is needed. (2020-12-06). + (setq lim-2 (c-determine-limit 1000 (point) ; that is wrong. FIXME!!! (2020-12-06) + )) + (while + (and (/= (skip-chars-backward "^;{}" lim-2) 0) + (> (point) (point-min)) + (memq (c-get-char-property (1- (point)) 'face) + '(font-lock-comment-face font-lock-string-face)))) + (setq lim (max (point-min) (1- (point)))) + + ;; Look for the latest `c-type' property before end1 + (when (and (> end1 (point-min)) + (setq type-pos + (if (get-text-property (1- end1) 'c-type) + end1 + (previous-single-property-change end1 'c-type + nil lim)))) + (setq type (get-text-property (max (1- type-pos) lim) 'c-type)) + + (when (memq type '(c-decl-id-start c-decl-type-start)) + ;; Get the identifier, if any, that the property is on. + (goto-char (1- type-pos)) + (setq marked-id + (when (looking-at "\\(\\sw\\|\\s_\\)") + (c-beginning-of-current-token) + (buffer-substring-no-properties (point) type-pos))) + + (goto-char end1) + (setq lim-2 (c-determine-+ve-limit 1000)) + (skip-chars-forward "^;{}" lim-2) ; FIXME!!! loop for ; comment, maybe - (setq lim (point)) - (setq term-pos - (or (c-next-single-property-change end 'c-type nil lim) lim)) - (setq c-maybe-stale-found-type - (list type marked-id - type-pos term-pos - (buffer-substring-no-properties type-pos - term-pos) - (buffer-substring-no-properties beg end))))))) - - (if c-get-state-before-change-functions - (mapc (lambda (fn) - (funcall fn beg end)) - c-get-state-before-change-functions)) - - (c-laomib-invalidate-cache beg end))) - (c-clear-string-fences)))) - (c-truncate-lit-pos-cache beg) - ;; The following must be done here rather than in `c-after-change' - ;; because newly inserted parens would foul up the invalidation - ;; algorithm. - (c-invalidate-state-cache beg) - ;; The following must happen after the previous, which likely alters - ;; the macro cache. - (when c-opt-cpp-symbol - (c-invalidate-macro-cache beg end)))) + (setq lim (point)) + (setq term-pos + (or (c-next-single-property-change end 'c-type nil lim) lim)) + (setq c-maybe-stale-found-type + (list type marked-id + type-pos term-pos + (buffer-substring-no-properties type-pos + term-pos) + (buffer-substring-no-properties beg end))))))) + + (if c-get-state-before-change-functions + (mapc (lambda (fn) + (funcall fn beg end)) + c-get-state-before-change-functions)) + + (c-laomib-invalidate-cache beg end)))) + (c-truncate-lit-pos-cache beg) + ;; The following must be done here rather than in `c-after-change' + ;; because newly inserted parens would foul up the invalidation + ;; algorithm. + (c-invalidate-state-cache beg) + ;; The following must happen after the previous, which likely alters + ;; the macro cache. + (when c-opt-cpp-symbol + (c-invalidate-macro-cache beg end))))) (defvar c-in-after-change-fontification nil) (make-variable-buffer-local 'c-in-after-change-fontification) @@ -2128,50 +2262,48 @@ Note that this is a strict tail, so won't match, e.g. \"0x....\".") (save-restriction (save-match-data ; c-recognize-<>-arglists changes match-data (widen) - (unwind-protect - (progn - (c-restore-string-fences) - (when (> end (point-max)) - ;; Some emacsen might return positions past the end. This - ;; has been observed in Emacs 20.7 when rereading a buffer - ;; changed on disk (haven't been able to minimize it, but - ;; Emacs 21.3 appears to work). - (setq end (point-max)) - (when (> beg end) - (setq beg end))) - - ;; C-y is capable of spuriously converting category - ;; properties c-</>-as-paren-syntax and - ;; c-cpp-delimiter into hard syntax-table properties. - ;; Remove these when it happens. - (when (eval-when-compile (memq 'category-properties c-emacs-features)) - (c-save-buffer-state () - (c-clear-char-property-with-value beg end 'syntax-table - c-<-as-paren-syntax) - (c-clear-char-property-with-value beg end 'syntax-table - c->-as-paren-syntax) - (c-clear-char-property-with-value beg end 'syntax-table nil))) - - (c-trim-found-types beg end old-len) ; maybe we don't - ; need all of these. - (c-invalidate-sws-region-after beg end old-len) - ;; (c-invalidate-state-cache beg) ; moved to - ;; `c-before-change'. - (c-invalidate-find-decl-cache beg) - - (when c-recognize-<>-arglists - (c-after-change-check-<>-operators beg end)) - - (setq c-in-after-change-fontification t) - (save-excursion - (mapc (lambda (fn) - (funcall fn beg end old-len)) - c-before-font-lock-functions))) - (c-clear-string-fences)))))) + (c-with-string-fences + (when (> end (point-max)) + ;; Some emacsen might return positions past the end. This + ;; has been observed in Emacs 20.7 when rereading a buffer + ;; changed on disk (haven't been able to minimize it, but + ;; Emacs 21.3 appears to work). + (setq end (point-max)) + (when (> beg end) + (setq beg end))) + + ;; C-y is capable of spuriously converting category + ;; properties c-</>-as-paren-syntax and + ;; c-cpp-delimiter into hard syntax-table properties. + ;; Remove these when it happens. + (when (eval-when-compile (memq 'category-properties c-emacs-features)) + (c-save-buffer-state () + (c-clear-char-property-with-value beg end 'syntax-table + c-<-as-paren-syntax) + (c-clear-char-property-with-value beg end 'syntax-table + c->-as-paren-syntax) + (c-clear-char-property-with-value beg end 'syntax-table nil))) + + (c-update-new-id end) + (c-trim-found-types beg end old-len) ; maybe we don't + ; need all of these. + (c-invalidate-sws-region-after beg end old-len) + ;; (c-invalidate-state-cache beg) ; moved to + ;; `c-before-change'. + (c-invalidate-find-decl-cache beg) + + (when c-recognize-<>-arglists + (c-after-change-check-<>-operators beg end)) + + (setq c-in-after-change-fontification t) + (save-excursion + (mapc (lambda (fn) + (funcall fn beg end old-len)) + c-before-font-lock-functions))))) ;; A workaround for syntax-ppss's failure to notice syntax-table text ;; property changes. - (when (fboundp 'syntax-ppss) - (syntax-ppss-flush-cache c-syntax-table-hwm))) + (when (fboundp 'syntax-ppss) + (syntax-ppss-flush-cache c-syntax-table-hwm))))) (defun c-doc-fl-decl-start (pos) ;; If the line containing POS is in a doc comment continued line (as defined @@ -2403,46 +2535,42 @@ Note that this is a strict tail, so won't match, e.g. \"0x....\".") (widen) (let (new-beg new-end new-region case-fold-search) (c-save-buffer-state nil - ;; Temporarily reapply the string fence syntax-table properties. - (unwind-protect - (progn - (c-restore-string-fences) - (if (and c-in-after-change-fontification - (< beg c-new-END) (> end c-new-BEG)) - ;; Region and the latest after-change fontification region overlap. - ;; Determine the upper and lower bounds of our adjusted region - ;; separately. - (progn - (if (<= beg c-new-BEG) - (setq c-in-after-change-fontification nil)) - (setq new-beg - (if (and (>= beg (c-point 'bol c-new-BEG)) - (<= beg c-new-BEG)) - ;; Either jit-lock has accepted `c-new-BEG', or has - ;; (probably) extended the change region spuriously - ;; to BOL, which position likely has a - ;; syntactically different position. To ensure - ;; correct fontification, we start at `c-new-BEG', - ;; assuming any characters to the left of - ;; `c-new-BEG' on the line do not require - ;; fontification. - c-new-BEG - (setq new-region (c-before-context-fl-expand-region beg end) - new-end (cdr new-region)) - (car new-region))) - (setq new-end - (if (and (>= end (c-point 'bol c-new-END)) - (<= end c-new-END)) - c-new-END - (or new-end - (cdr (c-before-context-fl-expand-region beg end)))))) - ;; Context (etc.) fontification. - (setq new-region (c-before-context-fl-expand-region beg end) - new-beg (car new-region) new-end (cdr new-region))) - ;; Finally invoke font lock's functionality. - (funcall (default-value 'font-lock-fontify-region-function) - new-beg new-end verbose)) - (c-clear-string-fences)))))) + (c-with-string-fences + (if (and c-in-after-change-fontification + (< beg c-new-END) (> end c-new-BEG)) + ;; Region and the latest after-change fontification region overlap. + ;; Determine the upper and lower bounds of our adjusted region + ;; separately. + (progn + (if (<= beg c-new-BEG) + (setq c-in-after-change-fontification nil)) + (setq new-beg + (if (and (>= beg (c-point 'bol c-new-BEG)) + (<= beg c-new-BEG)) + ;; Either jit-lock has accepted `c-new-BEG', or has + ;; (probably) extended the change region spuriously + ;; to BOL, which position likely has a + ;; syntactically different position. To ensure + ;; correct fontification, we start at `c-new-BEG', + ;; assuming any characters to the left of + ;; `c-new-BEG' on the line do not require + ;; fontification. + c-new-BEG + (setq new-region (c-before-context-fl-expand-region beg end) + new-end (cdr new-region)) + (car new-region))) + (setq new-end + (if (and (>= end (c-point 'bol c-new-END)) + (<= end c-new-END)) + c-new-END + (or new-end + (cdr (c-before-context-fl-expand-region beg end)))))) + ;; Context (etc.) fontification. + (setq new-region (c-before-context-fl-expand-region beg end) + new-beg (car new-region) new-end (cdr new-region))) + ;; Finally invoke font lock's functionality. + (funcall (default-value 'font-lock-fontify-region-function) + new-beg new-end verbose)))))) (defun c-after-font-lock-init () ;; Put on `font-lock-mode-hook'. This function ensures our after-change @@ -2550,17 +2678,24 @@ This function is called from `c-common-init', once per mode initialization." At the time of call, point is just after the newly inserted CHAR. -When CHAR is \", t will be returned unless the \" is marked with -a string fence syntax-table text property. For other characters, -the default value of `electric-pair-inhibit-predicate' is called -and its value returned. +When CHAR is \" and not within a comment, t will be returned if +the quotes on the current line are already balanced (i.e. if the +last \" is not marked with a string fence syntax-table text +property). For other cases, the default value of +`electric-pair-inhibit-predicate' is called and its value +returned. This function is the appropriate value of `electric-pair-inhibit-predicate' for CC Mode modes, which mark invalid strings with such a syntax table text property on the opening \" and the next unescaped end of line." - (if (eq char ?\") - (not (equal (get-text-property (1- (point)) 'c-fl-syn-tab) '(15))) + (if (and (eq char ?\") + (not (memq (cadr (c-semi-pp-to-literal (1- (point)))) '(c c++)))) + (let ((last-quote (save-match-data + (save-excursion + (goto-char (c-point 'eoll)) + (search-backward "\""))))) + (not (equal (c-get-char-property last-quote 'c-fl-syn-tab) '(15)))) (funcall (default-value 'electric-pair-inhibit-predicate) char))) diff --git a/lisp/progmodes/cc-styles.el b/lisp/progmodes/cc-styles.el index a66f91e0eb3..1cf14d52d55 100644 --- a/lisp/progmodes/cc-styles.el +++ b/lisp/progmodes/cc-styles.el @@ -180,6 +180,7 @@ (inclass . +) (inline-open . 0)))) ("linux" + (indent-tabs-mode . t) (c-basic-offset . 8) (c-comment-only-line-offset . 0) (c-hanging-braces-alist . ((brace-list-open) @@ -444,17 +445,19 @@ STYLE using `c-set-style' if the optional SET-P flag is non-nil." defstr)) (prompt (concat symname " offset " defstr)) (keymap (make-sparse-keymap)) - (minibuffer-completion-table obarray) - (minibuffer-completion-predicate 'fboundp) offset input) ;; In principle completing-read is used here, but SPC is unbound ;; to make it less annoying to enter lists. (set-keymap-parent keymap minibuffer-local-completion-map) (define-key keymap " " 'self-insert-command) (while (not offset) - (setq input (read-from-minibuffer prompt nil keymap t - 'c-read-offset-history - (format "%s" oldoff))) + (minibuffer-with-setup-hook + (lambda () + (setq-local minibuffer-completion-table obarray) + (setq-local minibuffer-completion-predicate 'fboundp)) + (setq input (read-from-minibuffer prompt nil keymap t + 'c-read-offset-history + (format "%s" oldoff)))) (if (c-valid-offset input) (setq offset input) ;; error, but don't signal one, keep trying diff --git a/lisp/progmodes/cc-vars.el b/lisp/progmodes/cc-vars.el index 45521d50218..e0f5a7ee021 100644 --- a/lisp/progmodes/cc-vars.el +++ b/lisp/progmodes/cc-vars.el @@ -179,7 +179,7 @@ STYLE stands for the choice where the value is taken from some style setting. PREAMBLE is optionally prepended to FOO; that is, if FOO contains :tag or :value, the respective two-element list component is ignored." - (declare (debug (symbolp form stringp &rest))) + (declare (debug (symbolp form stringp &rest)) (indent defun)) (let* ((expanded-doc (concat doc " This is a style variable. Apart from the valid values described diff --git a/lisp/progmodes/cfengine.el b/lisp/progmodes/cfengine.el index 6fc898d95be..32031d19462 100644 --- a/lisp/progmodes/cfengine.el +++ b/lisp/progmodes/cfengine.el @@ -793,14 +793,6 @@ bundle agent rcfiles (cdr (assq 'functions cfengine3-fallback-syntax))) 'symbols)) -(defcustom cfengine-mode-abbrevs nil - "Abbrevs for CFEngine2 mode." - :type '(repeat (list (string :tag "Name") - (string :tag "Expansion") - (choice :tag "Hook" (const nil) function)))) - -(make-obsolete-variable 'cfengine-mode-abbrevs 'edit-abbrevs "24.1") - ;; Taken from the doc for pre-release 2.1. (eval-and-compile (defconst cfengine2-actions @@ -989,13 +981,7 @@ Intended as the value of `indent-line-function'." (defun cfengine-fill-paragraph (&optional justify) "Fill `paragraphs' in Cfengine code." (interactive "P") - (or (if (fboundp 'fill-comment-paragraph) - (fill-comment-paragraph justify) - ;; else do nothing in a comment - (nth 4 (parse-partial-sexp (save-excursion - (beginning-of-defun) - (point)) - (point)))) + (or (fill-comment-paragraph justify) (let ((paragraph-start ;; Include start of parenthesized block. "\f\\|[ \t]*$\\|.*(") @@ -1415,7 +1401,6 @@ to the action header." (setq-local outline-regexp "[ \t]*\\(\\sw\\|\\s_\\)+:+") (setq-local outline-level #'cfengine2-outline-level) (setq-local fill-paragraph-function #'cfengine-fill-paragraph) - (define-abbrev-table 'cfengine2-mode-abbrev-table cfengine-mode-abbrevs) (setq font-lock-defaults '(cfengine2-font-lock-keywords nil nil nil beginning-of-line)) ;; Fixme: set the args of functions in evaluated classes to string diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el index 7d4a8ffc6fc..9f33186d8b1 100644 --- a/lisp/progmodes/compile.el +++ b/lisp/progmodes/compile.el @@ -82,6 +82,25 @@ after `call-process' inserts the grep output into the buffer.") "Position of the start of the text inserted by `compilation-filter'. This is bound before running `compilation-filter-hook'.") +(defcustom compilation-hidden-output nil + "Regexp to match output from the compilation that should be hidden. +This can also be a list of regexps. + +The text matched by this variable will be made invisible, which +means that it'll still be present in the buffer, so that +navigation commands (for instance, `next-error') can still make +use of the hidden text to determine the current directory and the +like. + +For instance, to hide the verbose output from recursive +makefiles, you can say something like: + + (setq compilation-hidden-output + \\='(\"^make[^\n]+\n\"))" + :type '(choice regexp + (repeat regexp)) + :version "29.1") + (defvar compilation-first-column 1 "This is how compilers number the first column, usually 1 or 0. If this is buffer-local in the destination buffer, Emacs obeys @@ -257,7 +276,16 @@ of[ \t]+\"?\\([a-zA-Z]?:?[^\":\n]+\\)\"?:" 3 2 nil (1)) "): ") 3 4 5 (1 . 2)) - (iar + (gradle-android + ,(rx bol (* " ") "ERROR:" + (group-n 1 ; file + (+ (not (in ":\n")))) + ":" + (group-n 2 (+ digit)) ; line + ": ") + 1 2) + + (iar "^\"\\(.*\\)\",\\([0-9]+\\)\\s-+\\(?:Error\\|Warnin\\(g\\)\\)\\[[0-9]+\\]:" 1 2 nil (3)) @@ -340,69 +368,73 @@ of[ \t]+\"?\\([a-zA-Z]?:?[^\":\n]+\\)\"?:" 3 2 nil (1)) ": \\*\\*\\* \\[\\(\\(.+?\\):\\([0-9]+\\): .+\\)\\]" 2 3 nil 0 1) (gnu + ;; The `gnu' message syntax is + ;; [PROGRAM:]FILE:LINE[-ENDLINE]:[COL[-ENDCOL]:] MESSAGE + ;; or + ;; [PROGRAM:]FILE:LINE[.COL][-ENDLINE[.ENDCOL]]: MESSAGE ,(rx bol - ;; Match an optional program name in the format - ;; PROGRAM:SOURCE-FILE-NAME:LINENO: MESSAGE - ;; which is used for non-interactive programs other than - ;; compilers (e.g. the "jade:" entry in compilation.txt). - (? (| (regexp "[[:alpha:]][-[:alnum:].]+: ?") - ;; FIXME: This pattern was added for handling messages - ;; from Ruby, but it is unclear whether it is actually - ;; used since the gcc-include rule above seems to cover - ;; it. - (regexp "[ \t]+\\(?:in \\|from\\)"))) + ;; Match an optional program name which is used for + ;; non-interactive programs other than compilers (e.g. the + ;; "jade:" entry in compilation.txt). + (? (| (: alpha (+ (in ?. ?- alnum)) ":" (? " ")) + ;; Skip indentation generated by GCC's -fanalyzer. + (: (+ " ") "|"))) ;; File name group. (group-n 1 - ;; Avoid matching the file name as a program in the pattern - ;; above by disallow file names entirely composed of digits. - (: (regexp "[0-9]*[^0-9\n]") - ;; This rule says that a file name can be composed - ;; of any non-newline char, but it also rules out - ;; some valid but unlikely cases, such as a - ;; trailing space or a space followed by a -, or a - ;; colon followed by a space. - (*? (| (regexp "[^\n :]") - (regexp " [^-/\n]") - (regexp ":[^ \n]"))))) - (regexp ": ?") + ;; Avoid matching the file name as a program in the pattern + ;; above by disallowing file names entirely composed of digits. + ;; Do not allow file names beginning with a space. + (| (not (in "0-9" "\n\t ")) + (: (+ (in "0-9")) + (not (in "0-9" "\n")))) + ;; A file name can be composed of any non-newline char, but + ;; rule out some valid but unlikely cases, such as a trailing + ;; space or a space followed by a -, or a colon followed by a + ;; space. + (*? (| (not (in "\n :")) + (: " " (not (in ?- "/\n"))) + (: ":" (not (in " \n")))))) + ":" (? " ") ;; Line number group. - (group-n 2 (regexp "[0-9]+")) + (group-n 2 (+ (in "0-9"))) (? (| (: "-" - (group-n 4 (regexp "[0-9]+")) ; ending line - (? "." (group-n 5 (regexp "[0-9]+")))) ; ending column + (group-n 4 (+ (in "0-9"))) ; ending line + (? "." (group-n 5 (+ (in "0-9"))))) ; ending column (: (in ".:") - (group-n 3 (regexp "[0-9]+")) ; starting column + (group-n 3 (+ (in "0-9"))) ; starting column (? "-" - (? (group-n 4 (regexp "[0-9]+")) ".") ; ending line - (group-n 5 (regexp "[0-9]+")))))) ; ending column + (? (group-n 4 (+ (in "0-9"))) ".") ; ending line + (group-n 5 (+ (in "0-9"))))))) ; ending column ":" (| (: (* " ") (group-n 6 (| "FutureWarning" "RuntimeWarning" - "Warning" - "warning" + "Warning" "warning" "W:"))) (: (* " ") - (group-n 7 (| (regexp "[Ii]nfo\\(?:\\>\\|rmationa?l?\\)") - "I:" - (: "[ skipping " (+ nonl) " ]") - "instantiated from" - "required from" - (regexp "[Nn]ote")))) + (group-n 7 + (| (| "Info" "info" + "Information" "information" + "Informational" "informational" + "I:" + "instantiated from" + "required from" + "Note" "note") + (: "[ skipping " (+ nonl) " ]")))) (: (* " ") - (regexp "[Ee]rror")) + (| "Error" "error")) ;; Avoid matching time stamps on the form "HH:MM:SS" where ;; MM is interpreted as a line number by trying to rule out ;; messages where the text after the line number starts with ;; a 2-digit number. - (: (regexp "[0-9]?") - (| (regexp "[^0-9\n]") + (: (? (in "0-9")) + (| (not (in "0-9\n")) eol)) - (regexp "[0-9][0-9][0-9]"))) + (: (in "0-9") (in "0-9") (in "0-9")))) 1 (2 . 4) (3 . 5) (6 . 7)) (cucumber @@ -954,7 +986,10 @@ Faces `compilation-error-face', `compilation-warning-face', (defcustom compilation-auto-jump-to-first-error nil "If non-nil, automatically jump to the first error during compilation." - :type 'boolean + :type '(choice (const :tag "Never" nil) + (const :tag "Always" t) + (const :tag "If location known" if-location-known) + (const :tag "First known location" first-known)) :version "23.1") (defvar-local compilation-auto-jump-to-next nil @@ -1185,21 +1220,46 @@ POS and RES.") l2 (setcdr l1 (cons (list ,key) l2))))))) +(defun compilation--file-known-p () + "Say whether the file under point can be found." + (when-let* ((msg (get-text-property (point) 'compilation-message)) + (loc (compilation--message->loc msg)) + (elem (compilation-find-file-1 + (point-marker) + (caar (compilation--loc->file-struct loc)) + (cadr (car (compilation--loc->file-struct loc))) + (compilation--file-struct->formats + (compilation--loc->file-struct loc))))) + (car elem))) + (defun compilation-auto-jump (buffer pos) (when (buffer-live-p buffer) (with-current-buffer buffer (goto-char pos) (let ((win (get-buffer-window buffer 0))) (if win (set-window-point win pos))) - (if compilation-auto-jump-to-first-error - (compile-goto-error))))) + (when compilation-auto-jump-to-first-error + (cl-case compilation-auto-jump-to-first-error + ('if-location-known + (when (compilation--file-known-p) + (compile-goto-error))) + ('first-known + (let (match) + (while (and (not (compilation--file-known-p)) + (setq match (text-property-search-forward + 'compilation-message nil nil t))) + (goto-char (prop-match-beginning match)))) + (when (compilation--file-known-p) + (compile-goto-error))) + (otherwise + (compile-goto-error))))))) ;; This function is the central driver, called when font-locking to gather ;; all information needed to later jump to corresponding source code. ;; Return a property list with all meta information on this error location. (defun compilation-error-properties (file line end-line col end-col type fmt - rule) + rule) (unless (text-property-not-all (match-beginning 0) (point) 'compilation-message nil) (if file @@ -1523,7 +1583,8 @@ to `compilation-error-regexp-alist' if RULES is nil." ;; FIXME-omake: Doing it here seems wrong, at least it should depend on ;; whether or not omake's own error messages are recognized. (cond - ((not omake-included) nil) + ((or (not omake-included) (not pat)) + nil) ((string-match "\\`\\([^^]\\|\\^\\( \\*\\|\\[\\)\\)" pat) nil) ;; Not anchored or anchored but already allows empty spaces. (t (setq pat (concat "^\\(?: \\)?" (substring pat 1))))) @@ -1542,7 +1603,7 @@ to `compilation-error-regexp-alist' if RULES is nil." (error "HYPERLINK should be an integer: %s" (nth 5 item))) (goto-char start) - (while (re-search-forward pat end t) + (while (and pat (re-search-forward pat end t)) (when (setq props (compilation-error-properties file line end-line col end-col (or type 2) fmt rule)) @@ -1755,13 +1816,21 @@ If nil, ask to kill it." :type 'boolean :version "24.3") +(defcustom compilation-max-output-line-length 400 + "Output lines that are longer than this value will be hidden. +If nil, don't hide anything." + :type '(choice (const :tag "Hide nothing" nil) + integer) + :version "29.1") + (defun compilation--update-in-progress-mode-line () ;; `compilation-in-progress' affects the mode-line of all ;; buffers when it changes from nil to non-nil or vice-versa. (unless compilation-in-progress (force-mode-line-update t))) ;;;###autoload -(defun compilation-start (command &optional mode name-function highlight-regexp) +(defun compilation-start (command &optional mode name-function highlight-regexp + continue) "Run compilation command COMMAND (low level interface). If COMMAND starts with a cd command, that becomes the `default-directory'. The rest of the arguments are optional; for them, nil means use the default. @@ -1778,6 +1847,12 @@ If HIGHLIGHT-REGEXP is non-nil, `next-error' will temporarily highlight the matching section of the visited source line; the default is to use the global value of `compilation-highlight-regexp'. +If CONTINUE is non-nil, the buffer won't be emptied before +compilation is started. This can be useful if you wish to +combine the output from several compilation commands in the same +buffer. The new output will be at the end of the buffer, and +point is not changed. + Returns the compilation buffer created." (or mode (setq mode 'compilation-mode)) (let* ((name-of-mode @@ -1841,7 +1916,12 @@ Returns the compilation buffer created." (if (= (length expanded-dir) 1) (car expanded-dir) substituted-dir))))) - (erase-buffer) + (if continue + (progn + ;; Save the point so we can restore it. + (setq continue (point)) + (goto-char (point-max))) + (erase-buffer)) ;; Select the desired mode. (if (not (eq mode t)) (progn @@ -1867,12 +1947,13 @@ Returns the compilation buffer created." (if (or compilation-auto-jump-to-first-error (eq compilation-scroll-output 'first-error)) (setq-local compilation-auto-jump-to-next t)) - ;; Output a mode setter, for saving and later reloading this buffer. - (insert "-*- mode: " name-of-mode - "; default-directory: " - (prin1-to-string (abbreviate-file-name default-directory)) - " -*-\n" - (format "%s started at %s\n\n" + (when (zerop (buffer-size)) + ;; Output a mode setter, for saving and later reloading this buffer. + (insert "-*- mode: " name-of-mode + "; default-directory: " + (prin1-to-string (abbreviate-file-name default-directory)) + " -*-\n")) + (insert (format "%s started at %s\n\n" mode-name (substring (current-time-string) 0 19)) command "\n") @@ -1891,28 +1972,33 @@ Returns the compilation buffer created." (and (derived-mode-p 'comint-mode) (comint-term-environment)) (list (format "INSIDE_EMACS=%s,compile" emacs-version)) + ;; Some external programs (like "git grep") use a pager; + ;; defeat that. + (list "PAGER=") (copy-sequence process-environment)))) (setq-local compilation-arguments (list command mode name-function highlight-regexp)) (setq-local revert-buffer-function 'compilation-revert-buffer) - (and outwin - ;; Forcing the window-start overrides the usual redisplay - ;; feature of bringing point into view, so setting the - ;; window-start to top of the buffer risks losing the - ;; effect of moving point to EOB below, per - ;; compilation-scroll-output, if the command is long - ;; enough to push point outside of the window. This - ;; could happen, e.g., in `rgrep'. - (not compilation-scroll-output) - (set-window-start outwin (point-min))) + (when (and outwin + (not continue) + ;; Forcing the window-start overrides the usual redisplay + ;; feature of bringing point into view, so setting the + ;; window-start to top of the buffer risks losing the + ;; effect of moving point to EOB below, per + ;; compilation-scroll-output, if the command is long + ;; enough to push point outside of the window. This + ;; could happen, e.g., in `rgrep'. + (not compilation-scroll-output)) + (set-window-start outwin (point-min))) ;; Position point as the user will see it. (let ((desired-visible-point - ;; Put it at the end if `compilation-scroll-output' is set. - (if compilation-scroll-output - (point-max) - ;; Normally put it at the top. - (point-min)))) + (cond + (continue continue) + ;; Put it at the end if `compilation-scroll-output' is set. + (compilation-scroll-output (point-max)) + ;; Normally put it at the top. + (t (point-min))))) (goto-char desired-visible-point) (when (and outwin (not (eq outwin (selected-window)))) (set-window-point outwin desired-visible-point))) @@ -2228,6 +2314,7 @@ The parent is always `compilation-mode' and the customizable `compilation-...' variables are also set from the name of the mode you have chosen, by replacing the first word, e.g., `compilation-scroll-output' from `grep-scroll-output' if that variable exists." + (declare (indent defun)) (let ((mode-name (replace-regexp-in-string "-mode\\'" "" (symbol-name mode)))) `(define-derived-mode ,mode compilation-mode ,name ,doc @@ -2407,8 +2494,8 @@ commands of Compilation major mode are available. See (defun compilation-filter (proc string) "Process filter for compilation buffers. -Just inserts the text, -handles carriage motion (see `comint-inhibit-carriage-motion'), +Just inserts the text, handles carriage motion (see +`comint-inhibit-carriage-motion'), `compilation-hidden-output', and runs `compilation-filter-hook'." (when (buffer-live-p (process-buffer proc)) (with-current-buffer (process-buffer proc) @@ -2428,13 +2515,18 @@ and runs `compilation-filter-hook'." ;; We used to use `insert-before-markers', so that windows with ;; point at `process-mark' scroll along with the output, but we ;; now use window-point-insertion-type instead. - (insert string) + (if (not compilation-max-output-line-length) + (insert string) + (dolist (line (string-lines string nil t)) + (compilation--insert-abbreviated-line + line compilation-max-output-line-length))) + (when compilation-hidden-output + (compilation--hide-output compilation-filter-start)) (unless comint-inhibit-carriage-motion (comint-carriage-motion (process-mark proc) (point))) (set-marker (process-mark proc) (point)) ;; Update the number of errors in compilation-mode-line-errors (compilation--ensure-parse (point)) - ;; (setq-local compilation-buffer-modtime (current-time)) (run-hooks 'compilation-filter-hook)) (goto-char pos) (narrow-to-region min max) @@ -2442,6 +2534,58 @@ and runs `compilation-filter-hook'." (set-marker min nil) (set-marker max nil)))))) +(defun compilation--hide-output (start) + (save-excursion + (goto-char start) + (beginning-of-line) + ;; Apply the match to each line, but wait until we have a complete + ;; line. + (let ((start (point))) + (while (search-forward "\n" nil t) + (save-restriction + (narrow-to-region start (point)) + (dolist (regexp (ensure-list compilation-hidden-output)) + (goto-char start) + (while (re-search-forward regexp nil t) + (add-text-properties (match-beginning 0) (match-end 0) + '( invisible t + rear-nonsticky t)))) + (goto-char (point-max))))))) + +(defun compilation--insert-abbreviated-line (string width) + (if (and (> (current-column) 0) + (get-text-property (1- (point)) 'button)) + ;; We already have an abbreviation; just add the string to it. + (let ((beg (point))) + (insert string) + (add-text-properties + beg + ;; Don't make the final newline invisible. + (if (= (aref string (1- (length string))) ?\n) + (1- (point)) + (point)) + (text-properties-at (1- beg)))) + (insert string) + ;; If we exceeded the limit, hide the last portion of the line. + (when (> (current-column) width) + (let ((start (save-excursion + (move-to-column width) + (point)))) + (buttonize-region + start (point) + (lambda (start) + (let ((inhibit-read-only t)) + (remove-text-properties start (save-excursion + (goto-char start) + (line-end-position)) + (text-properties-at start))))) + (put-text-property + start (if (= (aref string (1- (length string))) ?\n) + ;; Don't hide the final newline. + (1- (point)) + (point)) + 'display (if (char-displayable-p ?…) "[…]" "[...]")))))) + (defsubst compilation-buffer-internal-p () "Test if inside a compilation buffer." (local-variable-p 'compilation-locs)) @@ -2931,19 +3075,7 @@ and overlay is highlighted between MK and END-MK." (remove-hook 'pre-command-hook #'compilation-goto-locus-delete-o)) -(defun compilation-find-file (marker filename directory &rest formats) - "Find a buffer for file FILENAME. -If FILENAME is not found at all, ask the user where to find it. -Pop up the buffer containing MARKER and scroll to MARKER if we ask -the user where to find the file. -Search the directories in `compilation-search-path'. -A nil in `compilation-search-path' means to try the -\"current\" directory, which is passed in DIRECTORY. -If DIRECTORY is relative, it is combined with `default-directory'. -If DIRECTORY is nil, that means use `default-directory'. -FORMATS, if given, is a list of formats to reformat FILENAME when -looking for it: for each element FMT in FORMATS, this function -attempts to find a file whose name is produced by (format FMT FILENAME)." +(defun compilation-find-file-1 (marker filename directory &optional formats) (or formats (setq formats '("%s"))) (let ((dirs compilation-search-path) (spec-dir (if directory @@ -2992,6 +3124,23 @@ attempts to find a file whose name is produced by (format FMT FILENAME)." (find-file-noselect name)) fmts (cdr fmts))) (setq dirs (cdr dirs)))) + (list buffer spec-dir))) + +(defun compilation-find-file (marker filename directory &rest formats) + "Find a buffer for file FILENAME. +If FILENAME is not found at all, ask the user where to find it. +Pop up the buffer containing MARKER and scroll to MARKER if we ask +the user where to find the file. +Search the directories in `compilation-search-path'. +A nil in `compilation-search-path' means to try the +\"current\" directory, which is passed in DIRECTORY. +If DIRECTORY is relative, it is combined with `default-directory'. +If DIRECTORY is nil, that means use `default-directory'. +FORMATS, if given, is a list of formats to reformat FILENAME when +looking for it: for each element FMT in FORMATS, this function +attempts to find a file whose name is produced by (format FMT FILENAME)." + (pcase-let ((`(,buffer ,spec-dir) + (compilation-find-file-1 marker filename directory formats))) (while (null buffer) ;Repeat until the user selects an existing file. ;; The file doesn't exist. Ask the user where to find it. (save-excursion ;This save-excursion is probably not right. diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el index ae36789af82..f51d2fcb115 100644 --- a/lisp/progmodes/cperl-mode.el +++ b/lisp/progmodes/cperl-mode.el @@ -64,7 +64,7 @@ ;; This mode supports font-lock, imenu and mode-compile. In the ;; hairy version font-lock is on, but you should activate imenu ;; yourself (note that mode-compile is not standard yet). Well, you -;; can use imenu from keyboard anyway (M-x imenu), but it is better +;; can use imenu from keyboard anyway (M-g i), but it is better ;; to bind it like that: ;; (define-key global-map [M-S-down-mouse-3] 'imenu) @@ -558,6 +558,20 @@ This way enabling/disabling of menu items is more correct." :type 'boolean :group 'cperl-speed) +(defcustom cperl-file-style nil + "Indentation style to use in cperl-mode." + :type '(choice (const "CPerl") + (const "PBP") + (const "PerlStyle") + (const "GNU") + (const "C++") + (const "K&R") + (const "BSD") + (const "Whitesmith") + (const :tag "Default" nil)) + :version "29.1") +;;;###autoload(put 'cperl-file-style 'safe-local-variable 'stringp) + (defcustom cperl-ps-print-face-properties '((font-lock-keyword-face nil nil bold shadow) (font-lock-variable-name-face nil nil bold) @@ -1019,15 +1033,9 @@ Unless KEEP, removes the old indentation." (define-key map [(control ?c) (control ?h) ?v] ;;(concat (char-to-string help-char) "v") ; does not work 'cperl-get-help)) - (substitute-key-definition - 'indent-sexp 'cperl-indent-exp - map global-map) - (substitute-key-definition - 'indent-region 'cperl-indent-region - map global-map) - (substitute-key-definition - 'indent-for-comment 'cperl-indent-for-comment - map global-map) + (define-key map [remap indent-sexp] #'cperl-indent-exp) + (define-key map [remap indent-region] #'cperl-indent-region) + (define-key map [remap indent-for-comment] #'cperl-indent-for-comment) map) "Keymap used in CPerl mode.") @@ -1083,7 +1091,7 @@ Unless KEEP, removes the old indentation." ["Debugger" cperl-db t] "----" ("Tools" - ["Imenu" imenu (fboundp 'imenu)] + ["Imenu" imenu] ["Imenu on Perl Info" cperl-imenu-on-info (featurep 'imenu)] "----" ["Ispell PODs" cperl-pod-spell @@ -1314,7 +1322,7 @@ name, and one for the discovery of a following BLOCK.") ,cperl--ws+-rx (group-n 2 ,cperl--normal-identifier-rx)) "A regular expression to detect a subroutine start. -Contains three groups: One one to distinguish lexical from +Contains three groups: One to distinguish lexical from \"normal\" subroutines, for the keyword \"sub\", and one for the subroutine name.") @@ -1666,9 +1674,11 @@ Settings for classic indent-styles: K&R BSD=C++ GNU PBP PerlStyle=Whitesmith `cperl-continued-statement-offset' 5 4 2 4 4 CPerl knows several indentation styles, and may bulk set the -corresponding variables. Use \\[cperl-set-style] to do this. Use -\\[cperl-set-style-back] to restore the memorized preexisting values -\(both available from menu). See examples in `cperl-style-examples'. +corresponding variables. Use \\[cperl-set-style] to do this or +set the `cperl-file-style' user option. Use +\\[cperl-set-style-back] to restore the memorized preexisting +values \(both available from menu). See examples in +`cperl-style-examples'. Part of the indentation style is how different parts of if/elsif/else statements are broken into lines; in CPerl, this is reflected on how @@ -1801,8 +1811,15 @@ or as help on variables `cperl-tips', `cperl-problems', (when (and cperl-pod-here-scan (not cperl-syntaxify-by-font-lock)) (cperl-find-pods-heres)) + (when cperl-file-style + (cperl-set-style cperl-file-style)) + (add-hook 'hack-local-variables-hook #'cperl--set-file-style nil t) ;; Setup Flymake (add-hook 'flymake-diagnostic-functions #'perl-flymake nil t)) + +(defun cperl--set-file-style () + (when cperl-file-style + (cperl-set-style cperl-file-style))) ;; Fix for perldb - make default reasonable (defun cperl-db () @@ -3840,7 +3857,7 @@ recursive calls in starting lines of here-documents." "\\<" cperl-sub-regexp "\\>" ; sub with proto/attr "\\(" cperl-white-and-comment-rex - (rx (group (eval cperl--normal-identifier-rx))) + (rx (opt (group (eval cperl--normal-identifier-rx)))) "\\)" "\\(" cperl-maybe-white-and-comment-rex @@ -5951,7 +5968,7 @@ default function." (eval cperl--basic-identifier-rx))) (0+ blank) "(") ;; '("\\<for\\(each\\)?\\([ \t]+\\(state\\|my\\|local\\|our\\)\\)?[ \t]*\\(\\$[a-zA-Z_][a-zA-Z_0-9]*\\)[ \t]*(" - 4 font-lock-variable-name-face) + 1 font-lock-variable-name-face) ;; Avoid $!, and s!!, qq!! etc. when not fontifying syntactically '("\\(?:^\\|[^smywqrx$]\\)\\(!\\)" 1 font-lock-negation-char-face) '("\\[\\(\\^\\)" 1 font-lock-negation-char-face prepend))) @@ -6319,7 +6336,7 @@ else ) ("Current")) "List of variables to set to get a particular indentation style. -Should be used via `cperl-set-style' or via Perl menu. +Should be used via `cperl-set-style', `cperl-file-style' or via Perl menu. See examples in `cperl-style-examples'.") @@ -6365,7 +6382,8 @@ side-effect of memorizing only. Examples in `cperl-style-examples'." (eval '(mode-compile)))) ; Avoid a warning (declare-function Info-find-node "info" - (filename nodename &optional no-going-back strict-case)) + (filename nodename &optional no-going-back strict-case + noerror)) (defun cperl-info-buffer (type) ;; Return buffer with documentation. Creates if missing. @@ -7062,9 +7080,7 @@ One may build such TAGS files from CPerl mode menu." (error "No items found")) (setq update ;; (imenu-choose-buffer-index "Packages: " (nth 2 cperl-hierarchy)) - (if (if (fboundp 'display-popup-menus-p) - (display-popup-menus-p) - window-system) + (if (display-popup-menus-p) (x-popup-menu t (nth 2 cperl-hierarchy)) (require 'tmm) (tmm-prompt (nth 2 cperl-hierarchy)))) diff --git a/lisp/progmodes/cpp.el b/lisp/progmodes/cpp.el index 5cdcd7d32e3..f4584b63113 100644 --- a/lisp/progmodes/cpp.el +++ b/lisp/progmodes/cpp.el @@ -702,11 +702,8 @@ BRANCH should be either nil (false branch), t (true branch) or `both'." (x-popup-menu cpp-button-event (list prompt (cons prompt cpp-face-default-list))) (let ((name (car (rassq default cpp-face-default-list)))) - (cdr (assoc (completing-read (if name - (concat prompt - " (default " name "): ") - (concat prompt ": ")) - cpp-face-default-list nil t) + (cdr (assoc (completing-read (format-prompt "%s" name prompt) + cpp-face-default-list nil t) cpp-face-all-list)))) default)) diff --git a/lisp/progmodes/cwarn.el b/lisp/progmodes/cwarn.el index 971e3f6174d..03469b9f55b 100644 --- a/lisp/progmodes/cwarn.el +++ b/lisp/progmodes/cwarn.el @@ -180,9 +180,6 @@ C++ modes are included." (cwarn-font-lock-keywords cwarn-mode) (font-lock-flush)) -;;;###autoload -(define-obsolete-function-alias 'turn-on-cwarn-mode 'cwarn-mode "24.1") - ;;}}} ;;{{{ Help functions diff --git a/lisp/progmodes/ebrowse.el b/lisp/progmodes/ebrowse.el index dacb2a5f011..16b2f3ff503 100644 --- a/lisp/progmodes/ebrowse.el +++ b/lisp/progmodes/ebrowse.el @@ -996,7 +996,7 @@ if for some reason a circle is in the inheritance graph." Each line corresponds to a class in a class tree. Letters do not insert themselves, they are commands. File operations in the tree buffer work on class tree data structures. -E.g.\\[save-buffer] writes the tree to the file it was loaded from. +E.g. \\[save-buffer] writes the tree to the file it was loaded from. Tree mode key bindings: \\{ebrowse-tree-mode-map}" @@ -1330,9 +1330,9 @@ Pop to member buffer if no prefix ARG, to tree buffer otherwise." "Set the indentation width of the tree display." (interactive) (let ((width (string-to-number (read-string - (concat "Indentation (default " - (int-to-string ebrowse--indentation) - "): ") + (format-prompt + "Indentation" + (int-to-string ebrowse--indentation)) nil nil ebrowse--indentation)))) (when (cl-plusp width) (setq-local ebrowse--indentation width) @@ -4050,23 +4050,27 @@ NUMBER-OF-STATIC-VARIABLES:" (defvar ebrowse-global-map nil "Keymap for Ebrowse commands.") - (defvar ebrowse-global-prefix-key "\C-c\C-m" "Prefix key for Ebrowse commands.") - -(defvar ebrowse-global-submap-4 nil - "Keymap used for `ebrowse-global-prefix' followed by `4'.") - - -(defvar ebrowse-global-submap-5 nil - "Keymap used for `ebrowse-global-prefix' followed by `5'.") - +(defvar-keymap ebrowse-global-submap-4 + :doc "Keymap used for `ebrowse-global-prefix' followed by `4'." + "." #'ebrowse-tags-find-definition-other-window + "f" #'ebrowse-tags-find-definition-other-window + "v" #'ebrowse-tags-find-declaration-other-window + "F" #'ebrowse-tags-view-definition-other-window + "V" #'ebrowse-tags-view-declaration-other-window) + +(defvar-keymap ebrowse-global-submap-5 + :doc "Keymap used for `ebrowse-global-prefix' followed by `5'." + "." #'ebrowse-tags-find-definition-other-frame + "f" #'ebrowse-tags-find-definition-other-frame + "v" #'ebrowse-tags-find-declaration-other-frame + "F" #'ebrowse-tags-view-definition-other-frame + "V" #'ebrowse-tags-view-declaration-other-frame) (unless ebrowse-global-map (setq ebrowse-global-map (make-sparse-keymap)) - (setq ebrowse-global-submap-4 (make-sparse-keymap)) - (setq ebrowse-global-submap-5 (make-sparse-keymap)) (define-key ebrowse-global-map "a" 'ebrowse-tags-apropos) (define-key ebrowse-global-map "b" 'ebrowse-pop-to-browser-buffer) (define-key ebrowse-global-map "-" 'ebrowse-back-in-position-stack) @@ -4087,17 +4091,7 @@ NUMBER-OF-STATIC-VARIABLES:" (define-key ebrowse-global-map " " 'ebrowse-electric-buffer-list) (define-key ebrowse-global-map "\t" 'ebrowse-tags-complete-symbol) (define-key ebrowse-global-map "4" ebrowse-global-submap-4) - (define-key ebrowse-global-submap-4 "." 'ebrowse-tags-find-definition-other-window) - (define-key ebrowse-global-submap-4 "f" 'ebrowse-tags-find-definition-other-window) - (define-key ebrowse-global-submap-4 "v" 'ebrowse-tags-find-declaration-other-window) - (define-key ebrowse-global-submap-4 "F" 'ebrowse-tags-view-definition-other-window) - (define-key ebrowse-global-submap-4 "V" 'ebrowse-tags-view-declaration-other-window) (define-key ebrowse-global-map "5" ebrowse-global-submap-5) - (define-key ebrowse-global-submap-5 "." 'ebrowse-tags-find-definition-other-frame) - (define-key ebrowse-global-submap-5 "f" 'ebrowse-tags-find-definition-other-frame) - (define-key ebrowse-global-submap-5 "v" 'ebrowse-tags-find-declaration-other-frame) - (define-key ebrowse-global-submap-5 "F" 'ebrowse-tags-view-definition-other-frame) - (define-key ebrowse-global-submap-5 "V" 'ebrowse-tags-view-declaration-other-frame) (define-key global-map ebrowse-global-prefix-key ebrowse-global-map)) diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index bdd7751fc0c..0c4a9bfdbea 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -31,6 +31,7 @@ (require 'cl-generic) (require 'lisp-mode) (eval-when-compile (require 'cl-lib)) +(eval-when-compile (require 'subr-x)) (define-abbrev-table 'emacs-lisp-mode-abbrev-table () "Abbrev table for Emacs Lisp mode. @@ -45,15 +46,16 @@ It has `lisp-mode-abbrev-table' as its parent." table) "Syntax table used in `emacs-lisp-mode'.") -(defvar emacs-lisp-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map lisp-mode-shared-map) - (define-key map "\e\t" 'completion-at-point) - (define-key map "\e\C-x" 'eval-defun) - (define-key map "\e\C-q" 'indent-pp-sexp) - map) - "Keymap for Emacs Lisp mode. -All commands in `lisp-mode-shared-map' are inherited by this map.") +(defvar-keymap emacs-lisp-mode-map + :doc "Keymap for Emacs Lisp mode. +All commands in `lisp-mode-shared-map' are inherited by this map." + :parent lisp-mode-shared-map + "M-TAB" #'completion-at-point + "C-M-x" #'eval-defun + "C-c C-e" #'elisp-eval-buffer + "C-c C-f" #'elisp-byte-compile-file + "C-c C-b" #'elisp-byte-compile-buffer + "C-M-q" #'indent-pp-sexp) (easy-menu-define emacs-lisp-mode-menu emacs-lisp-mode-map "Menu for Emacs Lisp mode." @@ -239,6 +241,26 @@ Comments in the form will be lost." (if (bolp) (delete-char -1)) (indent-region start (point))))) +(defun elisp-mode-syntax-propertize (start end) + (goto-char start) + (let ((case-fold-search nil)) + (funcall + (syntax-propertize-rules + ;; Empty symbol. + ("##" (0 (unless (nth 8 (syntax-ppss)) + (string-to-syntax "_")))) + ;; Unicode character names. (The longest name is 88 characters + ;; long.) + ("\\?\\\\N{[-A-Za-z0-9 ]\\{,100\\}}" + (0 (unless (nth 8 (syntax-ppss)) + (string-to-syntax "_")))) + ((rx "#" (or (seq (group-n 1 "&" (+ digit)) ?\") ; Bool-vector. + (seq (group-n 1 "s") "(") ; Record. + (seq (group-n 1 (+ "^")) "["))) ; Char-table. + (1 (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0)))) + (string-to-syntax "'"))))) + start end))) + (defcustom emacs-lisp-mode-hook nil "Hook run when entering Emacs Lisp mode." :options '(eldoc-mode imenu-add-menubar-index checkdoc-minor-mode) @@ -270,10 +292,8 @@ Comments in the form will be lost." (setq-local lexical-binding t) (add-file-local-variable-prop-line 'lexical-binding t interactive)))) -(defvar elisp--dynlex-modeline-map - (let ((map (make-sparse-keymap))) - (define-key map [mode-line mouse-1] 'elisp-enable-lexical-binding) - map)) +(defvar-keymap elisp--dynlex-modeline-map + "<mode-line> <mouse-1>" #'elisp-enable-lexical-binding) ;;;###autoload (define-derived-mode emacs-lisp-mode lisp-data-mode @@ -314,6 +334,7 @@ be used instead. #'elisp-eldoc-var-docstring nil t) (add-hook 'xref-backend-functions #'elisp--xref-backend nil t) (setq-local project-vc-external-roots-function #'elisp-load-path-roots) + (setq-local syntax-propertize-function #'elisp-mode-syntax-propertize) (add-hook 'completion-at-point-functions #'elisp-completion-at-point nil 'local) (add-hook 'flymake-diagnostic-functions #'elisp-flymake-checkdoc nil t) @@ -610,13 +631,13 @@ functions are annotated with \"<f>\" via the ;; t if in function position. (funpos (eq (char-before beg) ?\()) (quoted (elisp--form-quoted-p beg)) - (fun-sym (condition-case nil - (save-excursion - (up-list -1) - (forward-char 1) - (and (memq (char-syntax (char-after)) '(?w ?_)) - (read (current-buffer)))) - (error nil)))) + (is-ignore-error + (condition-case nil + (save-excursion + (up-list -1) + (forward-char 1) + (looking-at-p "ignore-error\\>")) + (error nil)))) (when (and end (or (not (nth 8 (syntax-ppss))) (memq (char-before beg) '(?` ?‘)))) (let ((table-etc @@ -625,7 +646,7 @@ functions are annotated with \"<f>\" via the ;; FIXME: We could look at the first element of ;; the current form and use it to provide a more ;; specific completion table in more cases. - ((eq fun-sym 'ignore-error) + (is-ignore-error (list t (elisp--completion-local-symbols) :predicate (lambda (sym) (get sym 'error-conditions)))) @@ -636,7 +657,8 @@ functions are annotated with \"<f>\" via the :company-kind #'elisp--company-kind :company-doc-buffer #'elisp--company-doc-buffer :company-docsig #'elisp--company-doc-string - :company-location #'elisp--company-location)) + :company-location #'elisp--company-location + :company-deprecated #'elisp--company-deprecated)) (quoted (list nil (elisp--completion-local-symbols) ;; Don't include all symbols (bug#16646). @@ -652,7 +674,8 @@ functions are annotated with \"<f>\" via the :company-kind #'elisp--company-kind :company-doc-buffer #'elisp--company-doc-buffer :company-docsig #'elisp--company-doc-string - :company-location #'elisp--company-location)) + :company-location #'elisp--company-location + :company-deprecated #'elisp--company-deprecated)) (t (list nil (completion-table-merge elisp--local-variables-completion-table @@ -667,7 +690,8 @@ functions are annotated with \"<f>\" via the 'variable)) :company-doc-buffer #'elisp--company-doc-buffer :company-docsig #'elisp--company-doc-string - :company-location #'elisp--company-location))) + :company-location #'elisp--company-location + :company-deprecated #'elisp--company-deprecated))) ;; Looks like a funcall position. Let's double check. (save-excursion (goto-char (1- beg)) @@ -677,7 +701,10 @@ functions are annotated with \"<f>\" via the (let ((c (char-after))) (if (eq c ?\() ?\( (if (memq (char-syntax c) '(?w ?_)) - (read (current-buffer)))))) + (let ((pt (point))) + (forward-sexp) + (intern-soft + (buffer-substring pt (point)))))))) (error nil)))) (pcase parent ;; FIXME: Rather than hardcode special cases here, @@ -714,13 +741,15 @@ functions are annotated with \"<f>\" via the :company-kind (lambda (_) 'variable) :company-doc-buffer #'elisp--company-doc-buffer :company-docsig #'elisp--company-doc-string - :company-location #'elisp--company-location)) + :company-location #'elisp--company-location + :company-deprecated #'elisp--company-deprecated)) (_ (list nil (elisp--completion-local-symbols) :predicate #'elisp--shorthand-aware-fboundp :company-kind #'elisp--company-kind :company-doc-buffer #'elisp--company-doc-buffer :company-docsig #'elisp--company-doc-string :company-location #'elisp--company-location + :company-deprecated #'elisp--company-deprecated )))))))) (nconc (list beg end) (if (null (car table-etc)) @@ -743,14 +772,19 @@ functions are annotated with \"<f>\" via the ((facep sym) 'color) (t 'text)))) +(defun elisp--company-deprecated (str) + (let ((sym (intern-soft str))) + (or (get sym 'byte-obsolete-variable) + (get sym 'byte-obsolete-info)))) + (defun lisp-completion-at-point (&optional _predicate) (declare (obsolete elisp-completion-at-point "25.1")) (elisp-completion-at-point)) ;;; Xref backend -(declare-function xref-make "xref" (summary location)) -(declare-function xref-item-location "xref" (this)) +(declare-function xref-make "progmodes/xref" (summary location)) +(declare-function xref-item-location "progmodes/xref" (this)) (defun elisp--xref-backend () 'elisp) @@ -773,7 +807,7 @@ functions are annotated with \"<f>\" via the (defun elisp--xref-make-xref (type symbol file &optional summary) "Return an xref for TYPE SYMBOL in FILE. TYPE must be a type in `find-function-regexp-alist' (use nil for -'defun). If SUMMARY is non-nil, use it for the summary; +`defun'). If SUMMARY is non-nil, use it for the summary; otherwise build the summary from TYPE and SYMBOL." (xref-make (or summary (format elisp--xref-format (or type 'defun) symbol)) @@ -1190,16 +1224,16 @@ namespace but with lower confidence." ;;; Elisp Interaction mode -(defvar lisp-interaction-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map lisp-mode-shared-map) - (define-key map "\e\C-x" 'eval-defun) - (define-key map "\e\C-q" 'indent-pp-sexp) - (define-key map "\e\t" 'completion-at-point) - (define-key map "\n" 'eval-print-last-sexp) - map) - "Keymap for Lisp Interaction mode. -All commands in `lisp-mode-shared-map' are inherited by this map.") +(defvar-keymap lisp-interaction-mode-map + :doc "Keymap for Lisp Interaction mode. +All commands in `lisp-mode-shared-map' are inherited by this map." + :parent lisp-mode-shared-map + "C-M-x" #'eval-defun + "C-M-q" #'indent-pp-sexp + "C-c C-e" #'elisp-eval-buffer + "C-c C-b" #'elisp-byte-compile-buffer + "M-TAB" #'completion-at-point + "C-j" #'eval-print-last-sexp) (easy-menu-define lisp-interaction-mode-menu lisp-interaction-mode-map "Menu for Lisp Interaction mode." @@ -1610,8 +1644,6 @@ Return the result of evaluation." ;; printing, not while evaluating. (defvar elisp--eval-defun-result) (let ((debug-on-error eval-expression-debug-on-error) - (print-length eval-expression-print-length) - (print-level eval-expression-print-level) elisp--eval-defun-result) (save-excursion ;; Arrange for eval-region to "read" the (possibly) altered form. @@ -1626,10 +1658,17 @@ Return the result of evaluation." (setq beg (point)) (setq form (funcall load-read-function (current-buffer))) (setq end (point))) - ;; Alter the form if necessary. - (let ((form (eval-sexp-add-defvars - (elisp--eval-defun-1 - (macroexpand form))))) + ;; Alter the form if necessary. We bind `print-level' (etc.) + ;; in the form itself, because we want evalling the form to + ;; use the original values, while we want the printing to use + ;; `eval-expression-print-length' (etc.). + (let ((form `(let ((print-level ,print-level) + (print-length ,print-length)) + ,(eval-sexp-add-defvars + (elisp--eval-defun-1 + (macroexpand form))))) + (print-length eval-expression-print-length) + (print-level eval-expression-print-level)) (eval-region beg end standard-output (lambda (_ignore) ;; Skipping to the end of the specified region @@ -1645,7 +1684,10 @@ Return the result of evaluation." elisp--eval-defun-result)) (defun eval-defun (edebug-it) - "Evaluate the top-level form containing point, or after point. + "Evaluate the top-level form containing point. +If point isn't in a top-level form, evaluate the first top-level +form after point. If there is no top-level form after point, +eval the first preceeding top-level form. If the current defun is actually a call to `defvar' or `defcustom', evaluating it this way resets the variable using its initial value @@ -1733,7 +1775,8 @@ Intended for `eldoc-documentation-functions' (which see)." (defun elisp-eldoc-var-docstring (callback &rest _ignored) "Document variable at point. -Intended for `eldoc-documentation-functions' (which see)." +Intended for `eldoc-documentation-functions' (which see). +Also see `elisp-eldoc-var-docstring-with-value'." (let* ((sym (elisp--current-symbol)) (docstring (and sym (elisp-get-var-docstring sym)))) (when docstring @@ -1741,6 +1784,33 @@ Intended for `eldoc-documentation-functions' (which see)." :thing sym :face 'font-lock-variable-name-face)))) +(defun elisp-eldoc-var-docstring-with-value (callback &rest _) + "Document variable at point. +Intended for `eldoc-documentation-functions' (which see). +Compared to `elisp-eldoc-var-docstring', this also includes the +current variable value and a bigger chunk of the docstring." + (when-let ((cs (elisp--current-symbol))) + (when (and (boundp cs) + ;; nil and t are boundp! + (not (null cs)) + (not (eq cs t))) + (funcall callback + (format "%.100S %s" + (symbol-value cs) + (let* ((doc (documentation-property + cs 'variable-documentation t)) + (more (- (length doc) 1000))) + (concat (propertize + (string-limit (if (string= doc "nil") + "Undocumented." + doc) + 1000) + 'face 'font-lock-doc-face) + (when (> more 0) + (format "[%sc more]" more))))) + :thing cs + :face 'font-lock-variable-name-face)))) + (defun elisp-get-fnsym-args-string (sym &optional index) "Return a string containing the parameter list of the function SYM. If SYM is a subr and no arglist is obtainable from the docstring @@ -2058,7 +2128,9 @@ current buffer state and calls REPORT-FN when done." (when (process-live-p elisp-flymake--byte-compile-process) (kill-process elisp-flymake--byte-compile-process))) (let ((temp-file (make-temp-file "elisp-flymake-byte-compile")) - (source-buffer (current-buffer))) + (source-buffer (current-buffer)) + (coding-system-for-write 'utf-8-unix) + (coding-system-for-read 'utf-8)) (save-restriction (widen) (write-region (point-min) (point-max) temp-file nil 'nomessage)) @@ -2079,7 +2151,7 @@ current buffer state and calls REPORT-FN when done." :connection-type 'pipe :sentinel (lambda (proc _event) - (when (eq (process-status proc) 'exit) + (unless (process-live-p proc) (unwind-protect (cond ((not (and (buffer-live-p source-buffer) @@ -2108,6 +2180,8 @@ Runs in a batch-mode Emacs. Interactively use variable (interactive (list buffer-file-name)) (let* ((file (or file (car command-line-args-left))) + (coding-system-for-read 'utf-8-unix) + (coding-system-for-write 'utf-8) (byte-compile-log-buffer (generate-new-buffer " *dummy-byte-compile-log-buffer*")) (byte-compile-dest-file-function #'ignore) @@ -2125,6 +2199,67 @@ Runs in a batch-mode Emacs. Interactively use variable (terpri) (pp collected))) +(defun elisp-eval-buffer () + "Evaluate the forms in the current buffer." + (interactive) + (eval-buffer) + (message "Evaluated the %s buffer" (buffer-name))) + +(defun elisp-byte-compile-file (&optional load) + "Byte compile the file the current buffer is visiting. +If LOAD is non-nil, load the resulting .elc file. When called +interactively, this is the prefix argument." + (interactive "P") + (unless buffer-file-name + (error "This buffer is not visiting a file")) + (byte-compile-file buffer-file-name) + (when load + (load (funcall byte-compile-dest-file-function buffer-file-name)))) + +(defun elisp-byte-compile-buffer (&optional load) + "Byte compile the current buffer, but don't write a file. +If LOAD is non-nil, load byte-compiled data. When called +interactively, this is the prefix argument." + (interactive "P") + (let ((bfn buffer-file-name) + file elc) + (require 'bytecomp) + (unwind-protect + (progn + (setq file (make-temp-file "compile" nil ".el") + elc (funcall byte-compile-dest-file-function file)) + (write-region (point-min) (point-max) file nil 'silent) + (let ((set-message-function + (lambda (message) + (when (string-match-p "\\`Wrote " message) + 'ignore))) + (byte-compile-log-warning-function + (lambda (string position &optional fill level) + (if bfn + ;; Massage the warnings to that they point to + ;; this file, not the one in /tmp. + (let ((byte-compile-current-file bfn) + (byte-compile-root-dir (file-name-directory bfn))) + (byte-compile--log-warning-for-byte-compile + string position fill level)) + ;; We don't have a file name, so the warnings + ;; will point to a file that doesn't exist. This + ;; should be fixed in some way. + (byte-compile--log-warning-for-byte-compile + string position fill level))))) + (byte-compile-file file)) + (when (and bfn (get-buffer "*Compile-Log*")) + (with-current-buffer "*Compile-Log*" + (setq default-directory (file-name-directory bfn)))) + (if load + (load elc) + (message "Byte-compiled the current buffer"))) + (when file + (when (file-exists-p file) + (delete-file file)) + (when (file-exists-p elc) + (delete-file elc)))))) + (put 'read-symbol-shorthands 'safe-local-variable #'consp) diff --git a/lisp/progmodes/erts-mode.el b/lisp/progmodes/erts-mode.el new file mode 100644 index 00000000000..13da1d478d6 --- /dev/null +++ b/lisp/progmodes/erts-mode.el @@ -0,0 +1,223 @@ +;;; erts-mode.el --- major mode to edit erts files -*- lexical-binding: t; -*- + +;; Copyright (C) 2021-2022 Free Software Foundation, Inc. + +;; Keywords: tools + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: + +(eval-when-compile (require 'cl-lib)) +(require 'ert) + +(defgroup erts-mode nil + "Major mode for editing Emacs test files." + :group 'lisp) + +(defface erts-mode-specification-name + '((((class color) + (background dark)) + :foreground "green") + (((class color) + (background light)) + :foreground "cornflower blue") + (t + :bold t)) + "Face used for displaying specification names." + :group 'erts-mode) + +(defface erts-mode-specification-value + '((((class color) + (background dark)) + :foreground "DeepSkyBlue1") + (((class color) + (background light)) + :foreground "blue") + (t + :bold t)) + "Face used for displaying specification values." + :group 'erts-mode) + +(defface erts-mode-start-test + '((t :inherit font-lock-keyword-face)) + "Face used for displaying specification test start markers." + :group 'erts-mode) + +(defface erts-mode-end-test + '((t :inherit font-lock-comment-face)) + "Face used for displaying specification test start markers." + :group 'erts-mode) + +(defvar-keymap erts-mode-map + :parent prog-mode-map + "C-c C-r" #'erts-tag-region + "C-c C-c" #'erts-run-test) + +(defvar erts-mode-font-lock-keywords + ;; Specifications. + `((erts-mode--match-not-in-test + ("^\\([^ \t\n:]+:\\)[ \t]*\\(.*\\(\n[ \t].*\\)*\\)\n?" + (progn (goto-char (match-beginning 0)) (match-end 0)) nil + (1 'erts-mode-specification-name) + (2 'erts-mode-specification-value))) + ("^=-=$" 0 'erts-mode-start-test) + ("^=-=-=$" 0 'erts-mode-end-test))) + +(defun erts-mode--match-not-in-test (_limit) + (when (erts-mode--in-test-p (point)) + (erts-mode--end-of-test)) + (let ((start (point))) + (goto-char + (if (re-search-forward "^=-=$" nil t) + (match-beginning 0) + (point-max))) + (if (< (point) start) + nil + ;; Here we disregard LIMIT so that we may extend the area again. + (set-match-data (list start (point))) + (point)))) + +(defun erts-mode--end-of-test () + (search-forward "^=-=-=\n" nil t)) + +(defun erts-mode--in-test-p (point) + "Say whether POINT is in a test." + (save-excursion + (goto-char point) + (beginning-of-line) + (if (looking-at "=-=\\(-=\\)?$") + t + (let ((test-start (save-excursion + (re-search-backward "^=-=\n" nil t)))) + ;; Before the first test. + (and test-start + (let ((test-end (re-search-backward "^=-=-=\n" nil t))) + (or (null test-end) + ;; Between tests. + (> test-start test-end)))))))) + +;;;###autoload +(define-derived-mode erts-mode prog-mode "erts" + "Major mode for editing erts (Emacs testing) files. +This mode mainly provides some font locking. + +\\{erts-mode-map}" + (setq-local font-lock-defaults '(erts-mode-font-lock-keywords t))) + +(defun erts-tag-region (start end name) + "Tag the region between START and END as a test. +Interactively, this is the region. + +NAME should be a string appropriate for output by ert if the test fails. +If NAME is nil or the empty string, a name will be auto-generated." + (interactive "r\nsTest name: " erts-mode) + ;; Automatically make a name. + (when (zerop (length name)) + (save-excursion + (goto-char (point-min)) + (let ((names nil)) + (while (re-search-forward "^Name:[ \t]*\\(.*\\)" nil t) + (let ((name (match-string 1))) + (unless (erts-mode--in-test-p (point)) + (push name names)))) + (setq name + (cl-loop with base = (file-name-sans-extension (buffer-name)) + for i from 1 + for name = (format "%s%d" base i) + unless (member name names) + return name))))) + (save-excursion + (goto-char end) + (unless (bolp) + (insert "\n")) + (insert "=-=-=\n") + (goto-char start) + (insert "Name: " name "\n\n") + (insert "=-=\n"))) + +(defun erts-mode--preceding-spec (name) + (save-excursion + ;; Find the name, but skip if it's in a test. + (while (and (re-search-backward (format "^%s:" name) nil t) + (erts-mode--in-test-p (point)))) + (and (not (erts-mode--in-test-p (point))) + (re-search-forward "^=-=$" nil t) + (progn + (goto-char (match-beginning 0)) + (cdr (assq (intern (downcase name)) + (ert--erts-specifications (point)))))))) + +(defun erts-run-test (test-function &optional verbose) + "Run the current test. +If the current erts file doesn't define a test function, the user +will be prompted for one. + +If VERBOSE (interactively, the prefix), display a diff of the +expected results and the actual results in a separate buffer." + (interactive + (list (or (erts-mode--preceding-spec "Code") + (read-string "Transformation function: ")) + current-prefix-arg) + erts-mode) + (save-excursion + (erts-mode--goto-start-of-test) + (condition-case arg + (ert-test--erts-test + (list (cons 'dummy t) + (cons 'code (car (read-from-string test-function))) + (cons 'point-char (erts-mode--preceding-spec "Point-Char"))) + (buffer-file-name)) + (:success (message "Test successful")) + (ert-test-failed + (if (not verbose) + (message "Test failure; result: \n%s" + (substring-no-properties (cadr (cadr arg)))) + (message "Test failure") + (let (expected got) + (unwind-protect + (progn + (with-current-buffer + (setq expected (generate-new-buffer "erts expected")) + (insert (nth 1 (cadr arg)))) + (with-current-buffer + (setq got (generate-new-buffer "erts results")) + (insert (nth 2 (cadr arg)))) + (diff-buffers expected got)) + (kill-buffer expected) + (kill-buffer got)))))))) + +(defun erts-mode--goto-start-of-test () + (if (not (erts-mode--in-test-p (point))) + (re-search-forward "^=-=\n" nil t) + (re-search-backward "^=-=\n" nil t) + (let ((potential-start (match-end 0))) + ;; See if we're in a two-clause ("before" and "after") test or not. + (if-let ((start (and (save-excursion (re-search-backward "^=-=\n" nil t)) + (match-end 0)))) + (let ((end (save-excursion (re-search-backward "^=-=-=\n" nil t)))) + (if (or (not end) + (> start end)) + ;; We are, so go to the real start. + (goto-char start) + (goto-char potential-start))) + (goto-char potential-start))))) + +(provide 'erts-mode) + +;;; erts-mode.el ends here diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el index 124817ffda4..7766694edff 100644 --- a/lisp/progmodes/etags.el +++ b/lisp/progmodes/etags.el @@ -145,7 +145,9 @@ Otherwise, `find-tag-default' is used." :type '(choice (const nil) function)) (define-obsolete-variable-alias 'find-tag-marker-ring-length - 'xref-marker-ring-length "25.1") + 'tags-location-ring-length "25.1") + +(defvar tags-location-ring-length 16) (defcustom tags-tag-face 'default "Face for tags in the output of `tags-apropos'." @@ -180,10 +182,11 @@ Example value: (sexp :tag "Tags to search"))) :version "21.1") -(defvaralias 'find-tag-marker-ring 'xref--marker-ring) +;; Obsolete variable kept for compatibility. We don't use it in any way. +(defvar find-tag-marker-ring (make-ring 16)) (make-obsolete-variable 'find-tag-marker-ring - "use `xref-push-marker-stack' or `xref-pop-marker-stack' instead." + "use `xref-push-marker-stack' or `xref-go-back' instead." "25.1") (defvar default-tags-table-function nil @@ -191,7 +194,7 @@ Example value: This function receives no arguments and should return the default tags table file to use for the current buffer.") -(defvar tags-location-ring (make-ring xref-marker-ring-length) +(defvar tags-location-ring (make-ring tags-location-ring-length) "Ring of markers which are locations visited by \\[find-tag]. Pop back to the last location with \\[negative-argument] \\[find-tag].") @@ -292,7 +295,7 @@ file the tag was in." (or (locate-dominating-file default-directory "TAGS") default-directory))) (list (read-file-name - "Visit tags table (default TAGS): " + (format-prompt "Visit tags table" "TAGS") ;; default to TAGS from default-directory up to root. default-tag-dir (expand-file-name "TAGS" default-tag-dir) @@ -625,7 +628,7 @@ Returns t if it visits a tags table, or nil if there are no more in the list." (car list)) ;; Finally, prompt the user for a file name. (expand-file-name - (read-file-name "Visit tags table (default TAGS): " + (read-file-name (format-prompt "Visit tags table" "TAGS") default-directory "TAGS" t)))))) @@ -731,13 +734,13 @@ Returns t if it visits a tags table, or nil if there are no more in the list." (interactive) ;; Clear out the markers we are throwing away. (let ((i 0)) - (while (< i xref-marker-ring-length) + (while (< i tags-location-ring-length) (if (aref (cddr tags-location-ring) i) (set-marker (aref (cddr tags-location-ring) i) nil)) (setq i (1+ i)))) (xref-clear-marker-stack) (setq tags-file-name nil - tags-location-ring (make-ring xref-marker-ring-length) + tags-location-ring (make-ring tags-location-ring-length) tags-table-list nil tags-table-computed-list nil tags-table-computed-list-for nil @@ -1068,7 +1071,7 @@ See documentation of variable `tags-file-name'." regexp next-p t)) ;;;###autoload -(defalias 'pop-tag-mark 'xref-pop-marker-stack) +(defalias 'pop-tag-mark 'xref-go-back) (defvar tag-lines-already-matched nil @@ -1995,7 +1998,8 @@ see the doc of that variable if you want to add names to the list." (setq set-list (delete (car set-list) set-list))) (goto-char (point-min)) (insert-before-markers - "Type `t' to select a tags table or set of tags tables:\n\n") + (substitute-command-keys + "Type \\`t' to select a tags table or set of tags tables:\n\n")) (if desired-point (goto-char desired-point)) (set-window-start (selected-window) 1 t)) diff --git a/lisp/progmodes/executable.el b/lisp/progmodes/executable.el index d7c093444ed..670b6e7e898 100644 --- a/lisp/progmodes/executable.el +++ b/lisp/progmodes/executable.el @@ -240,12 +240,13 @@ executable." (not (string= argument (buffer-substring (point) (match-end 1)))) (if (or (not executable-query) no-query-flag - (save-window-excursion - ;; Make buffer visible before question. - (switch-to-buffer (current-buffer)) - (y-or-n-p (format-message - "Replace magic number by `#!%s'? " - argument)))) + (save-match-data + (save-window-excursion + ;; Make buffer visible before question. + (switch-to-buffer (current-buffer)) + (y-or-n-p (format-message + "Replace magic number by `#!%s'? " + argument))))) (progn (replace-match argument t t nil 1) (message "Magic number changed to `#!%s'" argument)))) diff --git a/lisp/progmodes/f90.el b/lisp/progmodes/f90.el index 526865e6f61..dcd74f0369c 100644 --- a/lisp/progmodes/f90.el +++ b/lisp/progmodes/f90.el @@ -345,6 +345,7 @@ The options are `downcase-word', `upcase-word', `capitalize-word' and nil." ;; there are spaces. "contiguous" "submodule" "concurrent" "codimension" "sync all" "sync memory" "critical" "image_index" "error stop" + "impure" )) "\\_>") "Regexp used by the function `f90-change-keywords'.") @@ -599,6 +600,7 @@ and variable-name parts, respectively." (append f90-font-lock-keywords-1 (list + '("\\(&\\)[ \t]*\\(!\\|$\\)" (1 font-lock-keyword-face)) ;; Variable declarations (avoid the real function call). ;; NB by accident (?), this correctly fontifies the "integer" in: ;; integer () function foo () @@ -610,8 +612,8 @@ and variable-name parts, respectively." '("^[ \t0-9]*\\(?:pure\\|elemental\\)?[ \t]*\ \\(real\\|integer\\|c\\(haracter\\|omplex\\)\\|\ enumerator\\|generic\\|procedure\\|logical\\|double[ \t]*precision\\)\ -\\(.*::\\|[ \t]*(.*)\\)?\\([^&!\n]*\\)" - (1 font-lock-type-face t) (4 font-lock-variable-name-face t)) +\\(.*::\\|[ \t]*(.*)\\)?\\([^&!\n]*\\(?:&\n[^&!\n]*\\)*\\)" + (1 font-lock-type-face t) (4 font-lock-variable-name-face append)) ;; Derived type/class variables. ;; TODO ? If we just highlighted the "type" part, rather than ;; "type(...)", this could be in the previous expression. And this @@ -646,18 +648,19 @@ do\\([ \t]*while\\)?\\|select[ \t]*\\(?:case\\|type\\)\\|where\\|\ forall\\|block\\|critical\\)\\)\\_>" (2 font-lock-constant-face nil t) (3 font-lock-keyword-face)) ;; Implicit declaration. - '("\\_<\\(implicit\\)[ \t]*\\(real\\|integer\\|c\\(haracter\\|omplex\\)\ + '("\\_<\\(implicit\\)[ \t]+\\(real\\|integer\\|c\\(haracter\\|omplex\\)\ \\|enumerator\\|procedure\\|\ logical\\|double[ \t]*precision\\|type[ \t]*(\\(?:\\sw\\|\\s_\\)+)\\|none\\)[ \t]*" (1 font-lock-keyword-face) (2 font-lock-type-face)) '("\\_<\\(namelist\\|common\\)[ \t]*/\\(\\(?:\\sw\\|\\s_\\)+\\)?/" (1 font-lock-keyword-face) (2 font-lock-constant-face nil t)) "\\_<else\\([ \t]*if\\|where\\)?\\_>" - '("\\(&\\)[ \t]*\\(!\\|$\\)" (1 font-lock-keyword-face)) "\\_<\\(then\\|continue\\|format\\|include\\|\\(?:error[ \t]+\\)?stop\\|\ return\\)\\_>" - '("\\_<\\(exit\\|cycle\\)[ \t]*\\(\\(?:\\sw\\|\\s_\\)+\\)?\\_>" + '("\\_<\\(exit\\|cycle\\)[ \t]+\\(\\(?:\\sw\\|\\s_\\)+\\)?\\_>" (1 font-lock-keyword-face) (2 font-lock-constant-face nil t)) + '("\\_<\\(exit\\|cycle\\)\\_>" + (1 font-lock-keyword-face)) '("\\_<\\(case\\)[ \t]*\\(default\\|(\\)" . 1) ;; F2003 "class default". '("\\_<\\(class\\)[ \t]*default" . 1) @@ -822,9 +825,7 @@ Can be overridden by the value of `font-lock-maximum-decoration'.") :style toggle :help "Expand abbreviations while typing in this buffer"] ["Add Imenu Menu" f90-add-imenu-menu :active (not (lookup-key (current-local-map) [menu-bar index])) - :included (fboundp 'imenu-add-to-menubar) - :help "Add an index menu to the menu-bar" - ])) + :help "Add an index menu to the menu-bar"])) map) "Keymap used in F90 mode.") diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index eebfa70e348..4ab16831bc1 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -903,7 +903,7 @@ can also be executed interactively independently of (defun flymake-proc--delete-temp-directory (dir-name) "Attempt to delete temp dir DIR-NAME, do not fail on error." - (let* ((temp-dir temporary-file-directory) + (let* ((temp-dir (file-truename temporary-file-directory)) (suffix (substring dir-name (1+ (length (directory-file-name temp-dir)))))) (while (> (length suffix) 0) diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 83d7bc8641c..0b7958e52fb 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -303,7 +303,7 @@ generated it." (defun flymake-error (text &rest args) "Format TEXT with ARGS and signal an error for Flymake." (let ((msg (apply #'format-message text args))) - (flymake-log :error msg) + (flymake-log :error "%s" msg) (error (concat "[Flymake] " msg)))) (cl-defstruct (flymake--diag @@ -1102,6 +1102,13 @@ The commands `flymake-goto-next-error' and `flymake-goto-prev-error' can be used to navigate among Flymake diagnostics annotated in the buffer. +By default, `flymake-mode' doesn't override the \\[next-error] command, but +if you're using Flymake a lot (and don't use the regular compilation +mechanisms that often), it can be useful to put something like +the following in your init file: + + (setq next-error-function \\='flymake-goto-next-error) + The visual appearance of each type of diagnostic can be changed by setting properties `flymake-overlay-control', `flymake-bitmap' and `flymake-severity' on the symbols of diagnostic types (like @@ -1358,6 +1365,11 @@ This is a suitable place for placing the `flymake-error-counter', Separating each of these with space is not necessary." :type '(repeat (choice string symbol))) +(defcustom flymake-mode-line-lighter "Flymake" + "The string to use in the Flymake mode line." + :type 'string + :version "29.1") + (defvar flymake-mode-line-title '(:eval (flymake--mode-line-title)) "Mode-line construct to show Flymake's mode name and menu.") @@ -1386,7 +1398,7 @@ correctly.") (defun flymake--mode-line-title () `(:propertize - "Flymake" + ,flymake-mode-line-lighter mouse-face mode-line-highlight help-echo ,(lambda (&rest _) @@ -1637,6 +1649,8 @@ buffer." (defun flymake-show-buffer-diagnostics () "Show a list of Flymake diagnostics for current buffer." (interactive) + (unless flymake-mode + (user-error "Flymake mode is not enabled in the current buffer")) (let* ((name (flymake--diagnostics-buffer-name)) (source (current-buffer)) (target (or (get-buffer name) diff --git a/lisp/progmodes/fortran.el b/lisp/progmodes/fortran.el index 86f0be7320e..786c5ae8042 100644 --- a/lisp/progmodes/fortran.el +++ b/lisp/progmodes/fortran.el @@ -2213,7 +2213,6 @@ arg DO-SPACE prevents stripping the whitespace." :style toggle :help "Expand abbreviations while typing in this buffer"] ["Add Imenu Menu" imenu-add-menubar-index :active (not (lookup-key (current-local-map) [menu-bar index])) - :included (fboundp 'imenu-add-to-menubar) :help "Add an index menu to the menu-bar"])) (provide 'fortran) diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el index a1385b0dea8..21bb75ae0cf 100644 --- a/lisp/progmodes/gdb-mi.el +++ b/lisp/progmodes/gdb-mi.el @@ -90,6 +90,7 @@ (require 'gud) (require 'cl-lib) (require 'cl-seq) +(require 'bindat) (eval-when-compile (require 'pcase)) (declare-function speedbar-change-initial-expansion-list @@ -104,6 +105,7 @@ ;; at toplevel, so the compiler doesn't know under which circumstances ;; they're defined. (declare-function gud-until "gud" (arg)) +(declare-function gud-go "gud" (arg)) (declare-function gud-print "gud" (arg)) (declare-function gud-down "gud" (arg)) (declare-function gud-up "gud" (arg)) @@ -283,8 +285,8 @@ Possible values are: :type '(choice (const :tag "Always restore" t) (const :tag "Don't restore" nil) - (const :tag "Depends on `gdb-show-main'" 'if-gdb-show-main) - (const :tag "Depends on `gdb-many-windows'" 'if-gdb-many-windows)) + (const :tag "Depends on `gdb-show-main'" if-gdb-show-main) + (const :tag "Depends on `gdb-many-windows'" if-gdb-many-windows)) :group 'gdb :version "28.1") @@ -682,7 +684,7 @@ Note that this variable only takes effect when variable Until there are such number of source windows on screen, GDB tries to open a new window when visiting a new source file; after that GDB starts to reuse existing source windows." - :type 'number + :type 'natnum :group 'gdb :version "28.1") @@ -954,12 +956,16 @@ detailed description of this mode. (forward-char 2) (gud-call "-exec-until *%a" arg))) "\C-u" "Continue to current line or address.") - ;; TODO Why arg here? (gud-def - gud-go (gud-call (if gdb-active-process - (gdb-gud-context-command "-exec-continue") - "-exec-run") arg) - nil "Start or continue execution.") + gud-go (progn + (when arg + (gud-call (concat "-exec-arguments " + (read-string "Arguments to exec-run: ")))) + (gud-call + (if gdb-active-process + (gdb-gud-context-command "-exec-continue") + "-exec-run"))) + "C-v" "Start or continue execution. Use a prefix to specify arguments.") ;; For debugging Emacs only. (gud-def gud-pp @@ -1138,7 +1144,8 @@ no input, and GDB is waiting for input." (setq name (nth 1 (split-string define "[( ]"))) (push (cons name define) gdb-define-alist)))) -(declare-function tooltip-show "tooltip" (text &optional use-echo-area)) +(declare-function tooltip-show "tooltip" (text &optional use-echo-area + text-face default-face)) (defconst gdb--string-regexp (rx "\"" (* (or (seq "\\" nonl) @@ -1266,7 +1273,7 @@ Used by Speedbar." :version "22.1") (define-key gud-minor-mode-map "\C-c\C-w" 'gud-watch) -(define-key global-map (vconcat gud-key-prefix "\C-w") 'gud-watch) +(keymap-set gud-global-map "C-w" 'gud-watch) (declare-function tooltip-identifier-from-point "tooltip" (point)) @@ -1580,7 +1587,7 @@ Buffer mode and name are selected according to buffer type. If buffer has trigger associated with it in `gdb-buffer-rules', this trigger is subscribed to `gdb-buf-publisher' and called with -'update argument." +`update' argument." (or (gdb-get-buffer buffer-type thread) (let ((rules (assoc buffer-type gdb-buffer-rules)) (new (generate-new-buffer "limbo"))) @@ -1612,6 +1619,7 @@ this trigger is subscribed to `gdb-buf-publisher' and called with ;; Used to display windows with thread-bound buffers (defmacro def-gdb-preempt-display-buffer (name buffer &optional doc split-horizontal) + (declare (indent defun)) `(defun ,name (&optional thread) ,(when doc doc) (message "%s" thread) @@ -2104,7 +2112,7 @@ is running." (not (null gdb-running-threads-count)) (> gdb-running-threads-count 0)))) -;; GUD displays the selected GDB frame. This might might not be the current +;; GUD displays the selected GDB frame. This might not be the current ;; GDB frame (after up, down etc). If no GDB frame is visible but the last ;; visited breakpoint is, use that window. (defun gdb-display-source-buffer (buffer) @@ -3012,6 +3020,7 @@ calling `gdb-current-context-command'). Triggers defined by this command are meant to be used as a trigger argument when describing buffer types with `gdb-set-buffer-rules'." + (declare (indent defun)) `(defun ,trigger-name (&optional signal) (when (or (not ,signal-list) @@ -3032,6 +3041,7 @@ Erase current buffer and evaluate CUSTOM-DEFUN. Then call `gdb-update-buffer-name'. If NOPRESERVE is non-nil, window point is not restored after CUSTOM-DEFUN." + (declare (indent defun)) `(defun ,handler-name () (let* ((inhibit-read-only t) ,@(unless nopreserve @@ -3055,6 +3065,7 @@ See `def-gdb-auto-update-trigger'. HANDLER-NAME handler uses customization of CUSTOM-DEFUN. See `def-gdb-auto-update-handler'." + (declare (indent defun)) `(progn (def-gdb-auto-update-trigger ,trigger-name ,gdb-command @@ -3489,6 +3500,7 @@ corresponding to the mode line clicked." CUSTOM-DEFUN may use locally bound `thread' variable, which will be the value of `gdb-thread' property of the current line. If `gdb-thread' is nil, error is signaled." + (declare (indent defun)) `(defun ,name (&optional event) ,(when doc doc) (interactive (list last-input-event)) @@ -3504,6 +3516,7 @@ If `gdb-thread' is nil, error is signaled." &optional doc) "Define a NAME which will call BUFFER-COMMAND with id of thread on the current line." + (declare (indent defun)) `(def-gdb-thread-buffer-command ,name (,buffer-command (gdb-mi--field thread 'id)) ,doc)) @@ -3559,6 +3572,7 @@ on the current line." "Define a NAME which will execute GUD-COMMAND with `gdb-thread-number' locally bound to id of thread on the current line." + (declare (indent defun)) `(def-gdb-thread-buffer-command ,name (if gdb-non-stop (let ((gdb-thread-number (gdb-mi--field thread 'id)) @@ -3727,6 +3741,7 @@ in `gdb-memory-format'." (defmacro def-gdb-set-positive-number (name variable echo-string &optional doc) "Define a function NAME which reads new VAR value from minibuffer." + (declare (indent defun)) `(defun ,name (event) ,(when doc doc) (interactive "e") @@ -3755,6 +3770,7 @@ in `gdb-memory-format'." "Define a function NAME to switch memory buffer to use FORMAT. DOC is an optional documentation string." + (declare (indent defun)) `(defun ,name () ,(when doc doc) (interactive) (customize-set-variable 'gdb-memory-format ,format) @@ -3824,6 +3840,7 @@ DOC is an optional documentation string." "Define a function NAME to switch memory unit size to UNIT-SIZE. DOC is an optional documentation string." + (declare (indent defun)) `(defun ,name () ,(when doc doc) (interactive) (customize-set-variable 'gdb-memory-unit ,unit-size) @@ -3848,6 +3865,7 @@ The defined function switches Memory buffer to show address stored in ADDRESS-VAR variable. DOC is an optional documentation string." + (declare (indent defun)) `(defun ,name ,(when doc doc) (interactive) @@ -4293,7 +4311,7 @@ member." ;; uses "-stack-list-locals --simple-values". Needs GDB 6.1 onwards. (def-gdb-trigger-and-handler gdb-invalidate-locals - (concat (gdb-current-context-command "-stack-list-locals") + (concat (gdb-current-context-command "-stack-list-variables") " --simple-values") gdb-locals-handler gdb-locals-handler-custom '(start update)) @@ -4304,6 +4322,48 @@ member." 'gdb-locals-mode 'gdb-invalidate-locals) + +;; Retrieve the values of all variables before invalidating locals. +(def-gdb-trigger-and-handler + gdb-locals-values + (concat (gdb-current-context-command "-stack-list-variables") + " --all-values") + gdb-locals-values-handler gdb-locals-values-handler-custom + '(start update)) + +(gdb-set-buffer-rules + 'gdb-locals-values-buffer + 'gdb-locals-values-buffer-name + 'gdb-locals-mode + 'gdb-locals-values) + +(defun gdb-locals-values-buffer-name () + (gdb-current-context-buffer-name + (concat "local values of " (gdb-get-target-string)))) + +(defcustom gdb-locals-simple-values-only nil + "Only display simple values in the Locals buffer." + :type 'boolean + :group 'gud + :version "29.1") + +(defcustom gdb-locals-value-limit 100 + "Maximum length the value of a local variable is allowed to be." + :type 'integer + :group 'gud + :version "29.1") + +(defvar gdb-locals-values-table (make-hash-table :test #'equal) + "Mapping of local variable names to a string with their value.") + +(defun gdb-locals-values-handler-custom () + "Store the values of local variables in `gdb-locals-value-map'." + (let ((locals-list (bindat-get-field (gdb-mi--partial-output) 'variables))) + (dolist (local locals-list) + (let ((name (bindat-get-field local 'name)) + (value (bindat-get-field local 'value))) + (puthash name value gdb-locals-values-table))))) + (defvar gdb-locals-watch-map (let ((map (make-sparse-keymap))) (suppress-keymap map) @@ -4320,6 +4380,15 @@ member." map) "Keymap to edit value of a simple data type local variable.") +(defun gdb-locals-value-filter (value) + "Filter function for the local variable VALUE." + (let* ((no-nl (replace-regexp-in-string "\n" " " value)) + (str (replace-regexp-in-string "[[:space:]]+" " " no-nl)) + (limit gdb-locals-value-limit)) + (if (>= (length str) limit) + (concat (substring str 0 limit) "...") + str))) + (defun gdb-edit-locals-value (&optional event) "Assign a value to a variable displayed in the locals buffer." (interactive (list last-input-event)) @@ -4332,17 +4401,22 @@ member." (gud-basic-call (concat "-gdb-set variable " var " = " value))))) -;; Don't display values of arrays or structures. -;; These can be expanded using gud-watch. +;; Complex data types are looked up in `gdb-locals-values-table'. (defun gdb-locals-handler-custom () - (let ((locals-list (gdb-mi--field (gdb-mi--partial-output) 'locals)) + "Handler to rebuild the local variables table buffer." + (let ((locals-list (bindat-get-field (gdb-mi--partial-output) 'variables)) (table (make-gdb-table))) (dolist (local locals-list) (let ((name (gdb-mi--field local 'name)) (value (gdb-mi--field local 'value)) (type (gdb-mi--field local 'type))) (when (not value) - (setq value "<complex data type>")) + (setq value + (if gdb-locals-simple-values-only + "<complex data type>" + (gethash name gdb-locals-values-table "<unavailable>")))) + (setq value (gdb-locals-value-filter value)) + (if (or (not value) (string-match "0x" value)) (add-text-properties 0 (length name) @@ -4865,6 +4939,8 @@ file\" where the GDB session starts (see `gdb-main-file')." (expand-file-name gdb-default-window-configuration-file gdb-window-configuration-directory))) ;; Create default layout as before. + ;; Make sure that local values are updated before locals. + (gdb-get-buffer-create 'gdb-locals-values-buffer) (gdb-get-buffer-create 'gdb-locals-buffer) (gdb-get-buffer-create 'gdb-stack-buffer) (gdb-get-buffer-create 'gdb-breakpoints-buffer) diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el index bbcb644b73f..423de7d5818 100644 --- a/lisp/progmodes/grep.el +++ b/lisp/progmodes/grep.el @@ -48,8 +48,8 @@ to avoid computing them again.") "Set SYMBOL to VALUE, and update `grep-host-defaults-alist'. SYMBOL should be one of `grep-command', `grep-template', `grep-use-null-device', `grep-find-command' `grep-find-template', -`grep-find-use-xargs', `grep-use-null-filename-separator', or -`grep-highlight-matches'." +`grep-find-use-xargs', `grep-use-null-filename-separator', +`grep-highlight-matches', or `grep-quoting-style'." (when grep-host-defaults-alist (let* ((host-id (intern (or (file-remote-p default-directory) "localhost"))) @@ -202,6 +202,9 @@ by `grep-compute-defaults'; to change the default value, use :set #'grep-apply-setting :version "22.1") +(defvar grep-quoting-style nil + "Whether to use POSIX-like shell argument quoting.") + (defcustom grep-files-aliases '(("all" . "* .*") ("el" . "*.el") @@ -212,6 +215,7 @@ by `grep-compute-defaults'; to change the default value, use ("hh" . "*.hxx *.hpp *.[Hh] *.HH *.h++") ("h" . "*.h") ("l" . "[Cc]hange[Ll]og*") + ("am" . "Makefile.am GNUmakefile *.mk") ("m" . "[Mm]akefile*") ("tex" . "*.tex") ("texi" . "*.texi") @@ -269,16 +273,16 @@ See `compilation-error-screen-columns'." (defvar grep-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map compilation-minor-mode-map) - (define-key map " " 'scroll-up-command) - (define-key map [?\S-\ ] 'scroll-down-command) - (define-key map "\^?" 'scroll-down-command) - (define-key map "\C-c\C-f" 'next-error-follow-minor-mode) - - (define-key map "\r" 'compile-goto-error) ;; ? - (define-key map "{" 'compilation-previous-file) - (define-key map "}" 'compilation-next-file) - (define-key map "\t" 'compilation-next-error) - (define-key map [backtab] 'compilation-previous-error) + (define-key map " " #'scroll-up-command) + (define-key map [?\S-\ ] #'scroll-down-command) + (define-key map "\^?" #'scroll-down-command) + (define-key map "\C-c\C-f" #'next-error-follow-minor-mode) + + (define-key map "\r" #'compile-goto-error) ;; ? + (define-key map "{" #'compilation-previous-file) + (define-key map "}" #'compilation-next-file) + (define-key map "\t" #'compilation-next-error) + (define-key map [backtab] #'compilation-previous-error) map) "Keymap for grep buffers. `compilation-minor-mode-map' is a cdr of this.") @@ -322,24 +326,24 @@ See `compilation-error-screen-columns'." ;; FIXME: Nowadays the last button is not "help" but "search"! (help (last tool-bar-map))) ;; Keep Help last in tool bar (tool-bar-local-item - "left-arrow" 'previous-error-no-select 'previous-error-no-select map + "left-arrow" #'previous-error-no-select #'previous-error-no-select map :rtl "right-arrow" :help "Goto previous match") (tool-bar-local-item - "right-arrow" 'next-error-no-select 'next-error-no-select map + "right-arrow" #'next-error-no-select #'next-error-no-select map :rtl "left-arrow" :help "Goto next match") (tool-bar-local-item - "cancel" 'kill-compilation 'kill-compilation map + "cancel" #'kill-compilation #'kill-compilation map :enable '(let ((buffer (compilation-find-buffer))) (get-buffer-process buffer)) :help "Stop grep") (tool-bar-local-item - "refresh" 'recompile 'recompile map + "refresh" #'recompile #'recompile map :help "Restart grep") (append map help)))) -(defalias 'kill-grep 'kill-compilation) +(defalias 'kill-grep #'kill-compilation) ;; override compilation-last-buffer (defvar grep-last-buffer nil @@ -443,9 +447,9 @@ buffer `default-directory'." (defvar grep-find-abbreviate-properties (let ((ellipsis (if (char-displayable-p ?…) "[…]" "[...]")) (map (make-sparse-keymap))) - (define-key map [down-mouse-2] 'mouse-set-point) - (define-key map [mouse-2] 'grep-find-toggle-abbreviation) - (define-key map "\C-m" 'grep-find-toggle-abbreviation) + (define-key map [down-mouse-2] #'mouse-set-point) + (define-key map [mouse-2] #'grep-find-toggle-abbreviation) + (define-key map "\C-m" #'grep-find-toggle-abbreviation) `(face nil display ,ellipsis mouse-face highlight help-echo "RET, mouse-2: show unabbreviated command" keymap ,map abbreviated-command t)) @@ -453,7 +457,7 @@ buffer `default-directory'." (defvar grep-mode-font-lock-keywords '(;; Command output lines. - (": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or address\\)\\)$" + (": \\(.\\{,200\\}\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or address\\)\\)$" 1 grep-error-face) ;; remove match from grep-regexp-alist before fontifying ("^Grep[/a-zA-Z]* started.*" @@ -616,8 +620,8 @@ This function is called from `compilation-filter-hook'." "Compute the defaults for the `grep' command. The value depends on `grep-command', `grep-template', `grep-use-null-device', `grep-find-command', `grep-find-template', -`grep-use-null-filename-separator', `grep-find-use-xargs' and -`grep-highlight-matches'." +`grep-use-null-filename-separator', `grep-find-use-xargs', +`grep-highlight-matches', and `grep-quoting-style'." ;; Keep default values. (unless grep-host-defaults-alist (add-to-list @@ -631,13 +635,14 @@ The value depends on `grep-command', `grep-template', (grep-use-null-filename-separator ,grep-use-null-filename-separator) (grep-find-use-xargs ,grep-find-use-xargs) - (grep-highlight-matches ,grep-highlight-matches))))) - (let* ((host-id - (intern (or (file-remote-p default-directory) "localhost"))) + (grep-highlight-matches ,grep-highlight-matches) + (grep-quoting-style ,grep-quoting-style))))) + (let* ((remote (file-remote-p default-directory)) + (host-id (intern (or remote "localhost"))) (host-defaults (assq host-id grep-host-defaults-alist)) (defaults (assq nil grep-host-defaults-alist)) - (quot-braces (shell-quote-argument "{}")) - (quot-scolon (shell-quote-argument ";"))) + (quot-braces (shell-quote-argument "{}" remote)) + (quot-scolon (shell-quote-argument ";" remote))) ;; There are different defaults on different hosts. They must be ;; computed for every host once. (dolist (setting '(grep-command grep-template @@ -791,8 +796,11 @@ The value depends on `grep-command', `grep-template', find-program gcmd null quot-braces)) (t (format "%s -H <D> <X> -type f <F> -print | \"%s\" %s" - find-program xargs-program gcmd)))))))) - ;; Save defaults for this host. + find-program xargs-program gcmd)))))) + + (setq grep-quoting-style (and remote 'posix)))) + + ;; Save defaults for this host. (setq grep-host-defaults-alist (delete (assq host-id grep-host-defaults-alist) grep-host-defaults-alist)) @@ -807,7 +815,8 @@ The value depends on `grep-command', `grep-template', (grep-use-null-filename-separator ,grep-use-null-filename-separator) (grep-find-use-xargs ,grep-find-use-xargs) - (grep-highlight-matches ,grep-highlight-matches)))))) + (grep-highlight-matches ,grep-highlight-matches) + (grep-quoting-style ,grep-quoting-style)))))) (defun grep-tag-default () (or (and transient-mark-mode mark-active @@ -820,7 +829,8 @@ The value depends on `grep-command', `grep-template', (defun grep-default-command () "Compute the default grep command for \\[universal-argument] \\[grep] to offer." - (let ((tag-default (shell-quote-argument (grep-tag-default))) + (let ((tag-default + (shell-quote-argument (grep-tag-default) grep-quoting-style)) ;; This a regexp to match single shell arguments. ;; Could someone please add comments explaining it? (sh-arg-re @@ -875,6 +885,14 @@ The value depends on `grep-command', `grep-template', (setq-local compilation-disable-input t) (setq-local compilation-error-screen-columns grep-error-screen-columns) + ;; We normally use a nul byte to separate the file name from the + ;; contents, but display it as ":". That's fine, but when yanking + ;; to other buffers, it's annoying to have the nul byte there. + (unless kill-transform-function + (setq-local kill-transform-function #'identity)) + (add-function :filter-return (local 'kill-transform-function) + (lambda (string) + (string-replace "\0" ":" string))) (add-hook 'compilation-filter-hook #'grep-filter nil t)) (defun grep--save-buffers () @@ -952,8 +970,7 @@ easily repeat a find command." (grep command-args)))) ;;;###autoload -(defalias 'find-grep 'grep-find) - +(defalias 'find-grep #'grep-find) ;; User-friendly interactive API. @@ -963,7 +980,7 @@ easily repeat a find command." ("<F>" . files) ("<N>" . (null-device)) ("<X>" . excl) - ("<R>" . (shell-quote-argument (or regexp "")))) + ("<R>" . (shell-quote-argument (or regexp "") grep-quoting-style))) "List of substitutions performed by `grep-expand-template'. If car of an element matches, the cdr is evalled in order to get the substitution string. @@ -1010,7 +1027,7 @@ these include `opts', `dir', `files', `null-device', `excl' and ;; Instead of a `grep-read-files-function' variable, we used to lookup ;; mode-specific functions in the major mode's symbol properties, so preserve ;; this behavior for backward compatibility. - (let ((old-function (get major-mode 'grep-read-files))) ;Obsolete since 28.1 + (let ((old-function (get major-mode #'grep-read-files))) ;Obsolete since 28.1 (if old-function (funcall old-function) (let ((file-name-at-point @@ -1057,17 +1074,18 @@ REGEXP is used as a string in the prompt." default-extension (car grep-files-history) (car (car grep-files-aliases)))) - (files (completing-read - (concat "Search for \"" regexp - "\" in files matching wildcard" - (if default (concat " (default " default ")")) - ": ") - #'read-file-name-internal - nil nil nil 'grep-files-history - (delete-dups - (delq nil - (append (list default default-alias default-extension) - (mapcar #'car grep-files-aliases))))))) + (defaults + (delete-dups + (delq nil + (append (list default default-alias default-extension) + (mapcar #'car grep-files-aliases))))) + (files (completing-read + (format-prompt "Search for \"%s\" in files matching wildcard" + default regexp) + (completion-table-merge + (lambda (_string _pred _action) defaults) + #'read-file-name-internal) + nil nil nil 'grep-files-history defaults))) (and files (or (cdr (assoc files grep-files-aliases)) files)))) @@ -1114,6 +1132,9 @@ command before it's run." (when (and (stringp regexp) (> (length regexp) 0)) (unless (and dir (file-accessible-directory-p dir)) (setq dir default-directory)) + (unless (string-equal (file-remote-p dir) (file-remote-p default-directory)) + (let ((default-directory dir)) + (grep-compute-defaults))) (let ((command regexp)) (if (null files) (if (string= command grep-command) @@ -1136,11 +1157,13 @@ command before it's run." (mapconcat (lambda (ignore) (cond ((stringp ignore) - (shell-quote-argument ignore)) + (shell-quote-argument + ignore grep-quoting-style)) ((consp ignore) (and (funcall (car ignore) dir) (shell-quote-argument - (cdr ignore)))))) + (cdr ignore) + grep-quoting-style))))) grep-find-ignored-files " --exclude="))) (and (eq grep-use-directories-skip t) @@ -1160,7 +1183,7 @@ command before it's run." (if (and grep-use-null-device null-device (null-device)) (concat command " " (null-device)) command) - 'grep-mode)) + #'grep-mode)) ;; Set default-directory if we started lgrep in the *grep* buffer. (if (eq next-error-last-buffer (current-buffer)) (setq default-directory dir)))))) @@ -1193,7 +1216,11 @@ When called programmatically and FILES is nil, REGEXP is expected to specify a command to run. If CONFIRM is non-nil, the user will be given an opportunity to edit the -command before it's run." +command before it's run. + +Interactively, the user can use the \\`M-c' command while entering +the regexp to indicate whether the grep should be case sensitive +or not." (interactive (progn (grep-compute-defaults) @@ -1212,13 +1239,17 @@ command before it's run." (when (and (stringp regexp) (> (length regexp) 0)) (unless (and dir (file-accessible-directory-p dir)) (setq dir default-directory)) + (unless (string-equal (file-remote-p dir) (file-remote-p default-directory)) + (let ((default-directory dir)) + (grep-compute-defaults))) (if (null files) (if (not (string= regexp (if (consp grep-find-command) (car grep-find-command) grep-find-command))) - (compilation-start regexp 'grep-mode)) + (compilation-start regexp #'grep-mode)) (setq dir (file-name-as-directory (expand-file-name dir))) - (let ((command (rgrep-default-command regexp files nil))) + (let* ((case-fold-search (read-regexp-case-fold-search regexp)) + (command (rgrep-default-command regexp files nil))) (when command (if confirm (setq command @@ -1227,7 +1258,7 @@ command before it's run." (add-to-history 'grep-find-history command)) (grep--save-buffers) (let ((default-directory dir)) - (compilation-start command 'grep-mode)) + (compilation-start command #'grep-mode)) ;; Set default-directory if we started rgrep in the *grep* buffer. (if (eq next-error-last-buffer (current-buffer)) (setq default-directory dir))))))) @@ -1247,44 +1278,46 @@ command before it's run." (grep-expand-template grep-find-template regexp - (concat (shell-quote-argument "(") + (concat (shell-quote-argument "(" grep-quoting-style) " " find-name-arg " " (mapconcat - #'shell-quote-argument + (lambda (x) (shell-quote-argument x grep-quoting-style)) (split-string files) (concat " -o " find-name-arg " ")) " " - (shell-quote-argument ")")) + (shell-quote-argument ")" grep-quoting-style)) dir (concat (and grep-find-ignored-directories (concat "-type d " - (shell-quote-argument "(") + (shell-quote-argument "(" grep-quoting-style) ;; we should use shell-quote-argument here " -path " - (mapconcat (lambda (d) (shell-quote-argument (concat "*/" d))) - (rgrep-find-ignored-directories dir) - " -o -path ") + (mapconcat + (lambda (d) + (shell-quote-argument (concat "*/" d) grep-quoting-style)) + (rgrep-find-ignored-directories dir) + " -o -path ") " " - (shell-quote-argument ")") + (shell-quote-argument ")" grep-quoting-style) " -prune -o ")) (and grep-find-ignored-files - (concat (shell-quote-argument "!") " -type d " - (shell-quote-argument "(") + (concat (shell-quote-argument "!" grep-quoting-style) " -type d " + (shell-quote-argument "(" grep-quoting-style) ;; we should use shell-quote-argument here " -name " (mapconcat (lambda (ignore) (cond ((stringp ignore) - (shell-quote-argument ignore)) + (shell-quote-argument ignore grep-quoting-style)) ((consp ignore) (and (funcall (car ignore) dir) (shell-quote-argument - (cdr ignore)))))) + (cdr ignore) grep-quoting-style))))) grep-find-ignored-files " -o -name ") " " - (shell-quote-argument ")") + (shell-quote-argument ")" grep-quoting-style) " -prune -o "))))) (defun grep-find-toggle-abbreviation () @@ -1354,7 +1387,7 @@ The returned file name is relative." (caar (compilation--loc->file-struct loc)))) ;;;###autoload -(defalias 'rzgrep 'zrgrep) +(defalias 'rzgrep #'zrgrep) (provide 'grep) diff --git a/lisp/progmodes/gud.el b/lisp/progmodes/gud.el index 085cd9b7e66..be43effed7d 100644 --- a/lisp/progmodes/gud.el +++ b/lisp/progmodes/gud.el @@ -54,8 +54,8 @@ (declare-function gdb-tooltip-print-1 "gdb-mi" (expr)) (declare-function gud-pp "gdb-mi" (arg)) (declare-function gdb-var-delete "gdb-mi" ()) -(declare-function speedbar-toggle-line-expansion "gud" ()) -(declare-function speedbar-edit-line "gud" ()) +(declare-function speedbar-toggle-line-expansion "speedbar" ()) +(declare-function speedbar-edit-line "speedbar" ()) ;; FIXME: The declares below are necessary because we don't call `gud-def' ;; at toplevel, so the compiler doesn't know under which circumstances ;; they're defined. @@ -90,8 +90,10 @@ pdb (Python), and jdb." "Prefix of all GUD commands valid in C buffers." :type 'key-sequence) -(global-set-key (vconcat gud-key-prefix "\C-l") #'gud-refresh) -;; (define-key ctl-x-map " " 'gud-break); backward compatibility hack +(defvar-keymap gud-global-map + "C-l" #'gud-refresh) + +(global-set-key gud-key-prefix gud-global-map) (defvar gud-marker-filter nil) (put 'gud-marker-filter 'permanent-local t) @@ -332,7 +334,7 @@ Used to gray out relevant toolbar icons.") (">" . gud-down))) (define-key map key cmd)) map) - "Keymap to repeat `gud-gdb' stepping instructions `C-x C-a C-n n n'. + "Keymap to repeat `gud-gdb' stepping instructions \\`C-x C-a C-n n n'. Used in `repeat-mode'.") (defun gud-set-repeat-map-property (keymap-symbol) @@ -433,7 +435,7 @@ we're in the GUD buffer)." ;; Unused lexical warning if cmd does not use "arg". cmd)))) ,(if key `(local-set-key ,(concat "\C-c" key) #',func)) - ,(if key `(global-set-key (vconcat gud-key-prefix ,key) #',func)))) + ,(if key `(define-key gud-global-map ,key #',func)))) ;; Where gud-display-frame should put the debugging arrow; a cons of ;; (filename . line-number). This is set by the marker-filter, which scans @@ -742,10 +744,10 @@ The option \"--fullname\" must be included in this value." output)) -(easy-mmode-defmap gud-minibuffer-local-map - '(("\C-i" . comint-dynamic-complete-filename)) - "Keymap for minibuffer prompting of gud startup command." - :inherit minibuffer-local-map) +(defvar-keymap gud-minibuffer-local-map + :doc "Keymap for minibuffer prompting of gud startup command." + :parent minibuffer-local-map + "C-i" #'comint-dynamic-complete-filename) (defun gud-query-cmdline (minor-mode &optional init) (let* ((hist-sym (gud-symbol 'history nil minor-mode)) @@ -757,13 +759,18 @@ The option \"--fullname\" must be included in this value." (concat (or cmd-name (symbol-name minor-mode)) " " (or init - (let ((file nil)) - (dolist (f (directory-files default-directory) file) - (if (and (file-executable-p f) - (not (file-directory-p f)) - (or (not file) - (file-newer-than-file-p f file))) - (setq file f))))))) + (let ((file nil) + (files (directory-files default-directory))) + ;; On remote systems, this may be slow, so avoid it. + (when (or (not (file-remote-p default-directory)) + (length< files 50)) + (dolist (f files) + (if (and (file-executable-p f) + (not (file-directory-p f)) + (or (not file) + (file-newer-than-file-p f file))) + (setq file f))) + file))))) gud-minibuffer-local-map nil hist-sym))) @@ -867,7 +874,8 @@ the buffer in which this command was invoked." COMMAND is the prefix for which we seek completion. CONTEXT is the text before COMMAND on the line." (let* ((complete-list - (gud-gdb-run-command-fetch-lines (concat "complete " context command) + (gud-gdb-run-command-fetch-lines (concat "server complete " + context command) (current-buffer) ;; From string-match above. (length context)))) @@ -1046,7 +1054,7 @@ SKIP is the number of chars to skip on each line, it defaults to 0." ("l" . gud-refresh))) (define-key map key cmd)) map) - "Keymap to repeat `sdb' stepping instructions `C-x C-a C-n n n'. + "Keymap to repeat `sdb' stepping instructions \\`C-x C-a C-n n n'. Used in `repeat-mode'.") (defun gud-sdb-marker-filter (string) @@ -1293,7 +1301,7 @@ whereby $stopformat=1 produces an output format compatible with gud-irix-p) (define-key map "f" 'gud-finish)) map) - "Keymap to repeat `dbx' stepping instructions `C-x C-a C-n n n'. + "Keymap to repeat `dbx' stepping instructions \\`C-x C-a C-n n n'. Used in `repeat-mode'.") ;; The process filter is also somewhat @@ -1468,7 +1476,7 @@ and source-file directory for your debugger." (">" . gud-down))) (define-key map key cmd)) map) - "Keymap to repeat `xdb' stepping instructions `C-x C-a C-n n n'. + "Keymap to repeat `xdb' stepping instructions \\`C-x C-a C-n n n'. Used in `repeat-mode'.") (defcustom gud-xdb-directories nil @@ -1556,7 +1564,7 @@ directories if your program contains sources from more than one directory." ("l" . gud-refresh))) (define-key map key cmd)) map) - "Keymap to repeat `perldb' stepping instructions `C-x C-a C-n n n'. + "Keymap to repeat `perldb' stepping instructions \\`C-x C-a C-n n n'. Used in `repeat-mode'.") (defun gud-perldb-massage-args (_file args) @@ -1746,7 +1754,7 @@ working directory and source-file directory for your debugger." (">" . gud-down))) (define-key map key cmd)) map) - "Keymap to repeat `pdb' stepping instructions `C-x C-a C-n n n'. + "Keymap to repeat `pdb' stepping instructions \\`C-x C-a C-n n n'. Used in `repeat-mode'.") ;; There's no guarantee that Emacs will hand the filter the entire @@ -1863,7 +1871,7 @@ directory and source-file directory for your debugger." (">" . gud-down))) (define-key map key cmd)) map) - "Keymap to repeat `guiler' stepping instructions `C-x C-a C-n n n'. + "Keymap to repeat `guiler' stepping instructions \\`C-x C-a C-n n n'. Used in `repeat-mode'.") (defun gud-guiler-marker-filter (string) @@ -2390,7 +2398,7 @@ extension EXTN. Normally EXTN is given as the regular expression ("l" . gud-refresh))) (define-key map key cmd)) map) - "Keymap to repeat `jdb' stepping instructions `C-x C-a C-n n n'. + "Keymap to repeat `jdb' stepping instructions \\`C-x C-a C-n n n'. Used in `repeat-mode'.") (defun gud-jdb-find-source-using-classpath (p) @@ -2452,7 +2460,7 @@ during jdb initialization depending on the value of ;; not supported/followed) (if (and gud-jdb-use-classpath (not gud-jdb-classpath-string) - (or (string-match "classpath:[ \t[]+\\([^]]+\\)" gud-marker-acc) + (or (string-match "classpath:[ \t[]+\\([^]]*\\)" gud-marker-acc) (string-match "-classpath[ \t\"]+\\([^ \"]+\\)" gud-marker-acc))) (setq gud-jdb-classpath (gud-jdb-parse-classpath-string @@ -3539,8 +3547,8 @@ Treats actions as defuns." #'gdb-script-end-of-defun) (setq-local font-lock-defaults '(gdb-script-font-lock-keywords nil nil ((?_ . "w")) nil - (font-lock-syntactic-face-function - . gdb-script-font-lock-syntactic-face))) + (font-lock-syntactic-face-function + . gdb-script-font-lock-syntactic-face))) ;; Recognize docstrings. (setq-local syntax-propertize-function gdb-script-syntax-propertize-function) @@ -3686,7 +3694,6 @@ With arg, dereference expr if ARG is positive, otherwise do not dereference." (message "Dereferencing is now %s." (if gud-tooltip-dereference "on" "off"))) -(defvar tooltip-use-echo-area) (declare-function tooltip-show "tooltip" (text &optional use-echo-area)) (declare-function tooltip-strip-prompt "tooltip" (process output)) @@ -3700,8 +3707,7 @@ With arg, dereference expr if ARG is positive, otherwise do not dereference." "Process debugger output and show it in a tooltip window." (remove-function (process-filter process) #'gud-tooltip-process-output) (tooltip-show (tooltip-strip-prompt process output) - (or gud-tooltip-echo-area tooltip-use-echo-area - (not tooltip-mode)))) + (or gud-tooltip-echo-area (not tooltip-mode)))) (defun gud-tooltip-print-command (expr) "Return a suitable command to print the expression EXPR." @@ -3745,8 +3751,7 @@ This function must return nil if it doesn't handle EVENT." (unless (null define-elt) (tooltip-show (cdr define-elt) - (or gud-tooltip-echo-area tooltip-use-echo-area - (not tooltip-mode))) + (or gud-tooltip-echo-area (not tooltip-mode))) expr)))) (when gud-tooltip-dereference (setq expr (concat "*" expr))) diff --git a/lisp/progmodes/hideif.el b/lisp/progmodes/hideif.el index f6a4711e244..f2ada676ab7 100644 --- a/lisp/progmodes/hideif.el +++ b/lisp/progmodes/hideif.el @@ -181,30 +181,24 @@ Effective only if `hide-ifdef-expand-reinclusion-guard' is t." :type 'regexp :version "25.1") -(defvar hide-ifdef-mode-submap +(defvar-keymap hide-ifdef-mode-submap + :doc "Keymap used by `hide-ifdef-mode' under `hide-ifdef-mode-prefix-key'." ;; Set up the submap that goes after the prefix key. - (let ((map (make-sparse-keymap))) - (define-key map "d" 'hide-ifdef-define) - (define-key map "u" 'hide-ifdef-undef) - (define-key map "D" 'hide-ifdef-set-define-alist) - (define-key map "U" 'hide-ifdef-use-define-alist) - - (define-key map "h" 'hide-ifdefs) - (define-key map "s" 'show-ifdefs) - (define-key map "\C-d" 'hide-ifdef-block) - (define-key map "\C-s" 'show-ifdef-block) - (define-key map "e" 'hif-evaluate-macro) - (define-key map "C" 'hif-clear-all-ifdef-defined) - - (define-key map "\C-q" 'hide-ifdef-toggle-read-only) - (define-key map "\C-w" 'hide-ifdef-toggle-shadowing) - (substitute-key-definition - 'read-only-mode 'hide-ifdef-toggle-outside-read-only map) - ;; `toggle-read-only' is obsoleted by `read-only-mode'. - (substitute-key-definition - 'toggle-read-only 'hide-ifdef-toggle-outside-read-only map) - map) - "Keymap used by `hide-ifdef-mode' under `hide-ifdef-mode-prefix-key'.") + "d" #'hide-ifdef-define + "u" #'hide-ifdef-undef + "D" #'hide-ifdef-set-define-alist + "U" #'hide-ifdef-use-define-alist + "h" #'hide-ifdefs + "s" #'show-ifdefs + "C-d" #'hide-ifdef-block + "C-s" #'show-ifdef-block + "e" #'hif-evaluate-macro + "C" #'hif-clear-all-ifdef-defined + "C-q" #'hide-ifdef-toggle-read-only + "C-w" #'hide-ifdef-toggle-shadowing + "<remap> <read-only-mode>" #'hide-ifdef-toggle-outside-read-only + ;; `toggle-read-only' is obsoleted by `read-only-mode'. + "<remap> <toggle-read-only>" #'hide-ifdef-toggle-outside-read-only) (defcustom hide-ifdef-mode-prefix-key "\C-c@" "Prefix key for all Hide-Ifdef mode commands." @@ -2456,7 +2450,7 @@ This allows #ifdef VAR to be hidden." (t nil)))) (var (read-minibuffer "Define what? " default)) - (val (read-from-minibuffer (format "Set %s to? (default 1): " var) + (val (read-from-minibuffer (format-prompt "Set %s to?" "1" var) nil nil t nil "1"))) (list var val))) (hif-set-var var (or val 1)) diff --git a/lisp/progmodes/icon.el b/lisp/progmodes/icon.el index e1ee9efc54b..ec281f3a496 100644 --- a/lisp/progmodes/icon.el +++ b/lisp/progmodes/icon.el @@ -31,17 +31,16 @@ "Abbrev table in use in Icon-mode buffers.") (define-abbrev-table 'icon-mode-abbrev-table ()) -(defvar icon-mode-map - (let ((map (make-sparse-keymap "Icon"))) - (define-key map "{" 'electric-icon-brace) - (define-key map "}" 'electric-icon-brace) - (define-key map "\e\C-h" 'mark-icon-function) - (define-key map "\e\C-a" 'beginning-of-icon-defun) - (define-key map "\e\C-e" 'end-of-icon-defun) - (define-key map "\e\C-q" 'indent-icon-exp) - (define-key map "\177" 'backward-delete-char-untabify) - map) - "Keymap used in Icon mode.") +(defvar-keymap icon-mode-map + :doc "Keymap used in Icon mode." + :name "Icon" + "{" #'electric-icon-brace + "}" #'electric-icon-brace + "C-M-h" #'mark-icon-function + "C-M-a" #'beginning-of-icon-defun + "C-M-e" #'end-of-icon-defun + "C-M-q" #'indent-icon-exp + "DEL" #'backward-delete-char-untabify) (easy-menu-define icon-mode-menu icon-mode-map "Menu for Icon mode." diff --git a/lisp/progmodes/idlw-shell.el b/lisp/progmodes/idlw-shell.el index af09cab1258..d21a9faec9d 100644 --- a/lisp/progmodes/idlw-shell.el +++ b/lisp/progmodes/idlw-shell.el @@ -231,7 +231,7 @@ because these are used as separators by IDL." (defcustom idlwave-shell-graphics-window-size '(500 400) "Size of IDL graphics windows popped up by special IDLWAVE command. -The command is `C-c C-d C-f' and accepts as a prefix the window nr. +The command is \\`C-c C-d C-f' and accepts as a prefix the window nr. A command like `WINDOW,N,xsize=XX,ysize=YY' is sent to IDL." :group 'idlwave-shell-general-setup :type '(list @@ -817,7 +817,7 @@ IDL has currently stepped.") Command history, searching of previous commands, command line editing are available via the comint-mode key bindings, by default - mostly on the key `C-c'. Command history is also available with + mostly on the key \\`C-c'. Command history is also available with the arrow keys UP and DOWN. 2. Completion @@ -844,7 +844,7 @@ IDL has currently stepped.") --------- A complete set of commands for compiling and debugging IDL programs is available from the menu. Also keybindings starting with a - `C-c C-d' prefix are available for most commands in the *idl* buffer + \\`C-c C-d' prefix are available for most commands in the *idl* buffer and also in source buffers. The best place to learn about the keybindings is again the menu. @@ -1327,7 +1327,7 @@ See also the variable `idlwave-shell-input-mode-spells'." Characters are sent one by one, without newlines. The loop is blocking and intercepts all input events to Emacs. You can use this command to interact with the IDL command GET_KBRD. -The loop can be aborted by typing `C-g'. The loop also exits automatically +The loop can be aborted by typing \\[keyboard-quit]. The loop also exits automatically when the IDL prompt gets displayed again after the current IDL command." (interactive) @@ -1342,7 +1342,8 @@ when the IDL prompt gets displayed again after the current IDL command." (funcall errf "No IDL program seems to be waiting for input")) ;; OK, start the loop - (message "Character mode on: Sending single chars (`C-g' to exit)") + (message (substitute-command-keys + "Character mode on: Sending single chars (\\[keyboard-quit] to exit)")) (message (catch 'exit (while t diff --git a/lisp/progmodes/idlwave.el b/lisp/progmodes/idlwave.el index e3985db64ab..a2061fde762 100644 --- a/lisp/progmodes/idlwave.el +++ b/lisp/progmodes/idlwave.el @@ -1001,9 +1001,9 @@ Obsolete, if the IDL Assistant is being used for help." "List of modifiers to be used for the debugging commands. Will be used to bind debugging commands in the shell buffer and in all source buffers. These are additional convenience bindings, the debugging -commands are always available with the `C-c C-d' prefix. +commands are always available with the \\`C-c C-d' prefix. If you set this to (control shift), this means setting a breakpoint will -be on `C-S-b', compiling a source file on `C-S-c' etc. Possible modifiers +be on \\`C-S-b', compiling a source file on \\`C-S-c' etc. Possible modifiers are `control', `meta', `super', `hyper', `alt', and `shift'." :group 'idlwave-shell-general-setup :type '(set :tag "Specify modifiers" @@ -1353,7 +1353,7 @@ the leftover unidentified statements containing an equal sign.") ;; Note that this is documented in the v18 manuals as being a string ;; of length one rather than a single character. ;; The code in this file accepts either format for compatibility. -(defvar idlwave-comment-indent-char ?\ +(defvar idlwave-comment-indent-char ?\s "Character to be inserted for IDL comment indentation. Normally a space.") @@ -3247,7 +3247,7 @@ ignored." ;; In the following while statements, after one iteration ;; point will be at the beginning of a line in which case ;; the while will not be executed for the - ;; the first paragraph line and thus will not affect the + ;; first paragraph line and thus will not affect the ;; indentation. ;; ;; First check to see if indentation is based on hanging indent. @@ -8421,7 +8421,7 @@ was pressed." (defun idlwave-list-shell-load-path-shadows (&optional _arg) "List the load path shadows of all routines compiled under the shell. This is very useful for checking an IDL application. Just compile the -application, do RESOLVE_ALL, and `C-c C-i' to compile all referenced +application, do RESOLVE_ALL, and \\`C-c C-i' to compile all referenced routines and update IDLWAVE internal info. Then check for shadowing with this command." (interactive) diff --git a/lisp/progmodes/inf-lisp.el b/lisp/progmodes/inf-lisp.el index c952e449810..b9042e66c6b 100644 --- a/lisp/progmodes/inf-lisp.el +++ b/lisp/progmodes/inf-lisp.el @@ -308,7 +308,7 @@ quoted using shell quote syntax. "inferior-lisp" (car cmdlist) nil (cdr cmdlist))) (inferior-lisp-mode))) (setq inferior-lisp-buffer "*inferior-lisp*") - (pop-to-buffer-same-window "*inferior-lisp*")) + (pop-to-buffer "*inferior-lisp*" display-comint-buffer-action)) ;;;###autoload (defalias 'run-lisp 'inferior-lisp) diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 812b3b98e3c..eb2a1e4fccc 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -33,7 +33,7 @@ ;; The main features of this JavaScript mode are syntactic ;; highlighting (enabled with `font-lock-mode' or ;; `global-font-lock-mode'), automatic indentation and filling of -;; comments, C preprocessor fontification, and MozRepl integration. +;; comments, and C preprocessor fontification. ;; ;; General Remarks: ;; @@ -51,7 +51,6 @@ (require 'cc-fonts)) (require 'newcomment) (require 'imenu) -(require 'moz nil t) (require 'json) (require 'prog-mode) @@ -59,12 +58,9 @@ (require 'cl-lib) (require 'ido)) -(defvar inferior-moz-buffer) -(defvar moz-repl-name) (defvar ido-cur-list) (defvar electric-layout-rules) (declare-function ido-mode "ido" (&optional arg)) -(declare-function inferior-moz-process "ext:mozrepl" ()) ;;; Constants @@ -95,7 +91,7 @@ name.") (defconst js--plain-method-re (concat "^\\s-*?\\(" js--dotted-name-re "\\)\\.prototype" - "\\.\\(" js--name-re "\\)\\s-*?=\\s-*?\\(function\\)\\_>") + "\\.\\(" js--name-re "\\)\\s-*?=\\s-*?\\(\\(:?async[ \t\n]+\\)function\\)\\_>") "Regexp matching an explicit JavaScript prototype \"method\" declaration. Group 1 is a (possibly-dotted) class name, group 2 is a method name, and group 3 is the `function' keyword.") @@ -485,25 +481,22 @@ seldom use, either globally or on a per-buffer basis." (list 'const x)) js--available-frameworks))) -(defcustom js-js-switch-tabs - (and (memq system-type '(darwin)) t) +(defvar js-js-switch-tabs (and (memq system-type '(darwin)) t) "Whether `js-mode' should display tabs while selecting them. This is useful only if the windowing system has a good mechanism -for preventing Firefox from stealing the keyboard focus." - :type 'boolean) +for preventing Firefox from stealing the keyboard focus.") +(make-obsolete-variable 'js-js-switch-tabs "MozRepl no longer exists" "28.1") -(defcustom js-js-tmpdir - (locate-user-emacs-file "js/js") +(defvar js-js-tmpdir (locate-user-emacs-file "js/js") "Temporary directory used by `js-mode' to communicate with Mozilla. -This directory must be readable and writable by both Mozilla and Emacs." - :type 'directory - :version "28.1") +This directory must be readable and writable by both Mozilla and Emacs.") +(make-obsolete-variable 'js-js-tmpdir "MozRepl no longer exists" "28.1") -(defcustom js-js-timeout 5 +(defvar js-js-timeout 5 "Reply timeout for executing commands in Mozilla via `js-mode'. The value is given in seconds. Increase this value if you are -getting timeout messages." - :type 'integer) +getting timeout messages.") +(make-obsolete-variable 'js-js-timeout "MozRepl no longer exists" "28.1") (defcustom js-indent-first-init nil "Non-nil means specially indent the first variable declaration's initializer. @@ -667,24 +660,11 @@ This variable is like `sgml-attribute-offset'." :type 'integer :safe 'integerp) -;;; KeyMap - -(defvar js-mode-map - (let ((keymap (make-sparse-keymap))) - (define-key keymap [(control ?c) (meta ?:)] #'js-eval) - (define-key keymap [(control ?c) (control ?j)] #'js-set-js-context) - (define-key keymap [(control meta ?x)] #'js-eval-defun) - (define-key keymap [(meta ?.)] #'js-find-symbol) - (easy-menu-define nil keymap "JavaScript Menu" - '("JavaScript" - ["Select New Mozilla Context..." js-set-js-context - (fboundp #'inferior-moz-process)] - ["Evaluate Expression in Mozilla Context..." js-eval - (fboundp #'inferior-moz-process)] - ["Send Current Function to Mozilla..." js-eval-defun - (fboundp #'inferior-moz-process)])) - keymap) - "Keymap for `js-mode'.") +;;; Keymap + +(defvar-keymap js-mode-map + :doc "Keymap for `js-mode'." + "M-." #'js-find-symbol) ;;; Syntax table and parsing @@ -932,9 +912,10 @@ This puts point at the `function' keyword. If this is a syntactically-correct non-expression function, return the name of the function, or t if the name could not be determined. Otherwise, return nil." - (cl-assert (looking-at "\\_<function\\_>")) + (unless (looking-at "\\(\\_<async\\_>[ \t\n]+\\)?\\_<function\\_>") + (error "Invalid position")) (let ((name t)) - (forward-word-strictly) + (goto-char (match-end 0)) (forward-comment most-positive-fixnum) (when (eq (char-after) ?*) (forward-char) @@ -970,14 +951,17 @@ If POS is not in a function prologue, return nil." (goto-char (match-end 0)))) (skip-syntax-backward "w_") - (and (or (looking-at "\\_<function\\_>") - (js--re-search-backward "\\_<function\\_>" nil t)) - - (save-match-data (goto-char (match-beginning 0)) - (js--forward-function-decl)) - - (<= pos (point)) - (or prologue-begin (match-beginning 0)))))) + (let ((start nil)) + (and (or (looking-at "\\_<function\\_>") + (js--re-search-backward "\\_<function\\_>" nil t)) + (progn + (setq start (match-beginning 0)) + (goto-char start) + (when (looking-back "\\_<async\\_>[ \t\n]+" (- (point) 30)) + (setq start (match-beginning 0))) + (js--forward-function-decl)) + (<= pos (point)) + (or prologue-begin start)))))) (defun js--beginning-of-defun-raw () "Helper function for `js-beginning-of-defun'. @@ -1247,7 +1231,6 @@ LIMIT defaults to point." ;; Regular function declaration ((and (looking-at "\\_<function\\_>") (setq name (js--forward-function-decl))) - (when (eq name t) (setq name (js--guess-function-name orig-match-end)) (if name @@ -1259,6 +1242,11 @@ LIMIT defaults to point." (cl-assert (eq (char-after) ?{)) (forward-char) + (save-excursion + (goto-char orig-match-start) + (when (looking-back "\\_<async\\_>[ \t\n]+" + (- (point) 30)) + (setq orig-match-start (match-beginning 0)))) (make-js--pitem :paren-depth orig-depth :h-begin orig-match-start @@ -3308,10 +3296,7 @@ marker." (setf (car bounds) (point)))) (buffer-substring (car bounds) (cdr bounds))))) -(defvar find-tag-marker-ring) ; etags - -;; etags loads ring. -(declare-function ring-insert "ring" (ring item)) +(declare-function xref-push-marker-stack "xref" (&optional m)) (defun js-find-symbol (&optional arg) "Read a JavaScript symbol and jump to it. @@ -3319,7 +3304,7 @@ With a prefix argument, restrict symbols to those from the current buffer. Pushes a mark onto the tag ring just like `find-tag'." (interactive "P") - (require 'etags) + (require 'xref) (let (symbols marker) (if (not arg) (setq symbols (js--get-all-known-symbols)) @@ -3331,1111 +3316,11 @@ current buffer. Pushes a mark onto the tag ring just like symbols "Jump to: " (js--guess-symbol-at-point)))) - (ring-insert find-tag-marker-ring (point-marker)) + (xref-push-marker-stack) (switch-to-buffer (marker-buffer marker)) (push-mark) (goto-char marker))) -;;; MozRepl integration - -(define-error 'js-moz-bad-rpc "Mozilla RPC Error") ;; '(timeout error)) -(define-error 'js-js-error "JavaScript Error") ;; '(js-error error)) - -(defun js--wait-for-matching-output - (process regexp timeout &optional start) - "Wait TIMEOUT seconds for PROCESS to output a match for REGEXP. -On timeout, return nil. On success, return t with match data -set. If START is non-nil, look for output starting from START. -Otherwise, use the current value of `process-mark'." - (with-current-buffer (process-buffer process) - (cl-loop with start-pos = (or start - (marker-position (process-mark process))) - with end-time = (time-add nil timeout) - for time-left = (float-time (time-subtract end-time nil)) - do (goto-char (point-max)) - if (looking-back regexp start-pos) return t - while (> time-left 0) - do (accept-process-output process time-left nil t) - do (goto-char (process-mark process)) - finally do (signal - 'js-moz-bad-rpc - (list (format "Timed out waiting for output matching %S" regexp)))))) - -(cl-defstruct js--js-handle - ;; Integer, mirrors the value we see in JS - (id nil :read-only t) - - ;; Process to which this thing belongs - (process nil :read-only t)) - -(defun js--js-handle-expired-p (x) - (not (eq (js--js-handle-process x) - (inferior-moz-process)))) - -(defvar js--js-references nil - "Maps Elisp JavaScript proxy objects to their JavaScript IDs.") - -(defvar js--js-process nil - "The most recent MozRepl process object.") - -(defvar js--js-gc-idle-timer nil - "Idle timer for cleaning up JS object references.") - -(defvar js--js-last-gcs-done nil) - -(defconst js--moz-interactor - (replace-regexp-in-string - "[ \n]+" " " - ; */" Make Emacs happy -"(function(repl) { - repl.defineInteractor('js', { - onStart: function onStart(repl) { - if(!repl._jsObjects) { - repl._jsObjects = {}; - repl._jsLastID = 0; - repl._jsGC = this._jsGC; - } - this._input = ''; - }, - - _jsGC: function _jsGC(ids_in_use) { - var objects = this._jsObjects; - var keys = []; - var num_freed = 0; - - for(var pn in objects) { - keys.push(Number(pn)); - } - - keys.sort(function(x, y) x - y); - ids_in_use.sort(function(x, y) x - y); - var i = 0; - var j = 0; - - while(i < ids_in_use.length && j < keys.length) { - var id = ids_in_use[i++]; - while(j < keys.length && keys[j] !== id) { - var k_id = keys[j++]; - delete objects[k_id]; - ++num_freed; - } - ++j; - } - - while(j < keys.length) { - var k_id = keys[j++]; - delete objects[k_id]; - ++num_freed; - } - - return num_freed; - }, - - _mkArray: function _mkArray() { - var result = []; - for(var i = 0; i < arguments.length; ++i) { - result.push(arguments[i]); - } - return result; - }, - - _parsePropDescriptor: function _parsePropDescriptor(parts) { - if(typeof parts === 'string') { - parts = [ parts ]; - } - - var obj = parts[0]; - var start = 1; - - if(typeof obj === 'string') { - obj = window; - start = 0; - } else if(parts.length < 2) { - throw new Error('expected at least 2 arguments'); - } - - for(var i = start; i < parts.length - 1; ++i) { - obj = obj[parts[i]]; - } - - return [obj, parts[parts.length - 1]]; - }, - - _getProp: function _getProp(/*...*/) { - if(arguments.length === 0) { - throw new Error('no arguments supplied to getprop'); - } - - if(arguments.length === 1 && - (typeof arguments[0]) !== 'string') - { - return arguments[0]; - } - - var [obj, propname] = this._parsePropDescriptor(arguments); - return obj[propname]; - }, - - _putProp: function _putProp(properties, value) { - var [obj, propname] = this._parsePropDescriptor(properties); - obj[propname] = value; - }, - - _delProp: function _delProp(propname) { - var [obj, propname] = this._parsePropDescriptor(arguments); - delete obj[propname]; - }, - - _typeOf: function _typeOf(thing) { - return typeof thing; - }, - - _callNew: function(constructor) { - if(typeof constructor === 'string') - { - constructor = window[constructor]; - } else if(constructor.length === 1 && - typeof constructor[0] !== 'string') - { - constructor = constructor[0]; - } else { - var [obj,propname] = this._parsePropDescriptor(constructor); - constructor = obj[propname]; - } - - /* Hacky, but should be robust */ - var s = 'new constructor('; - for(var i = 1; i < arguments.length; ++i) { - if(i != 1) { - s += ','; - } - - s += 'arguments[' + i + ']'; - } - - s += ')'; - return eval(s); - }, - - _callEval: function(thisobj, js) { - return eval.call(thisobj, js); - }, - - getPrompt: function getPrompt(repl) { - return 'EVAL>' - }, - - _lookupObject: function _lookupObject(repl, id) { - if(typeof id === 'string') { - switch(id) { - case 'global': - return window; - case 'nil': - return null; - case 't': - return true; - case 'false': - return false; - case 'undefined': - return undefined; - case 'repl': - return repl; - case 'interactor': - return this; - case 'NaN': - return NaN; - case 'Infinity': - return Infinity; - case '-Infinity': - return -Infinity; - default: - throw new Error('No object with special id:' + id); - } - } - - var ret = repl._jsObjects[id]; - if(ret === undefined) { - throw new Error('No object with id:' + id + '(' + typeof id + ')'); - } - return ret; - }, - - _findOrAllocateObject: function _findOrAllocateObject(repl, value) { - if(typeof value !== 'object' && typeof value !== 'function') { - throw new Error('_findOrAllocateObject called on non-object(' - + typeof(value) + '): ' - + value) - } - - for(var id in repl._jsObjects) { - id = Number(id); - var obj = repl._jsObjects[id]; - if(obj === value) { - return id; - } - } - - var id = ++repl._jsLastID; - repl._jsObjects[id] = value; - return id; - }, - - _fixupList: function _fixupList(repl, list) { - for(var i = 0; i < list.length; ++i) { - if(list[i] instanceof Array) { - this._fixupList(repl, list[i]); - } else if(typeof list[i] === 'object') { - var obj = list[i]; - if(obj.funcall) { - var parts = obj.funcall; - this._fixupList(repl, parts); - var [thisobj, func] = this._parseFunc(parts[0]); - list[i] = func.apply(thisobj, parts.slice(1)); - } else if(obj.objid) { - list[i] = this._lookupObject(repl, obj.objid); - } else { - throw new Error('Unknown object type: ' + obj.toSource()); - } - } - } - }, - - _parseFunc: function(func) { - var thisobj = null; - - if(typeof func === 'string') { - func = window[func]; - } else if(func instanceof Array) { - if(func.length === 1 && typeof func[0] !== 'string') { - func = func[0]; - } else { - [thisobj, func] = this._parsePropDescriptor(func); - func = thisobj[func]; - } - } - - return [thisobj,func]; - }, - - _encodeReturn: function(value, array_as_mv) { - var ret; - - if(value === null) { - ret = ['special', 'null']; - } else if(value === true) { - ret = ['special', 'true']; - } else if(value === false) { - ret = ['special', 'false']; - } else if(value === undefined) { - ret = ['special', 'undefined']; - } else if(typeof value === 'number') { - if(isNaN(value)) { - ret = ['special', 'NaN']; - } else if(value === Infinity) { - ret = ['special', 'Infinity']; - } else if(value === -Infinity) { - ret = ['special', '-Infinity']; - } else { - ret = ['atom', value]; - } - } else if(typeof value === 'string') { - ret = ['atom', value]; - } else if(array_as_mv && value instanceof Array) { - ret = ['array', value.map(this._encodeReturn, this)]; - } else { - ret = ['objid', this._findOrAllocateObject(repl, value)]; - } - - return ret; - }, - - _handleInputLine: function _handleInputLine(repl, line) { - var ret; - var array_as_mv = false; - - try { - if(line[0] === '*') { - array_as_mv = true; - line = line.substring(1); - } - var parts = eval(line); - this._fixupList(repl, parts); - var [thisobj, func] = this._parseFunc(parts[0]); - ret = this._encodeReturn( - func.apply(thisobj, parts.slice(1)), - array_as_mv); - } catch(x) { - ret = ['error', x.toString() ]; - } - - var JSON = Components.classes['@mozilla.org/dom/json;1'].createInstance(Components.interfaces.nsIJSON); - repl.print(JSON.encode(ret)); - repl._prompt(); - }, - - handleInput: function handleInput(repl, chunk) { - this._input += chunk; - var match, line; - while(match = this._input.match(/.*\\n/)) { - line = match[0]; - - if(line === 'EXIT\\n') { - repl.popInteractor(); - repl._prompt(); - return; - } - - this._input = this._input.substring(line.length); - this._handleInputLine(repl, line); - } - } - }); -}) -") - - "String to set MozRepl up into a simple-minded evaluation mode.") - -(defun js--js-encode-value (x) - "Marshall the given value for JS. -Strings and numbers are JSON-encoded. Lists (including nil) are -made into JavaScript array literals and their contents encoded -with `js--js-encode-value'." - (cond ((or (stringp x) (numberp x)) (json-encode x)) - ((symbolp x) (format "{objid:%S}" (symbol-name x))) - ((js--js-handle-p x) - - (when (js--js-handle-expired-p x) - (error "Stale JS handle")) - - (format "{objid:%s}" (js--js-handle-id x))) - - ((sequencep x) - (if (eq (car-safe x) 'js--funcall) - (format "{funcall:[%s]}" - (mapconcat #'js--js-encode-value (cdr x) ",")) - (concat - "[" (mapconcat #'js--js-encode-value x ",") "]"))) - (t - (error "Unrecognized item: %S" x)))) - -(defconst js--js-prompt-regexp "\\(repl[0-9]*\\)> $") -(defconst js--js-repl-prompt-regexp "^EVAL>$") -(defvar js--js-repl-depth 0) - -(defun js--js-wait-for-eval-prompt () - (js--wait-for-matching-output - (inferior-moz-process) - js--js-repl-prompt-regexp js-js-timeout - - ;; start matching against the beginning of the line in - ;; order to catch a prompt that's only partially arrived - (save-excursion (forward-line 0) (point)))) - -;; Presumably "inferior-moz-process" loads comint. -(declare-function comint-send-string "comint" (process string)) -(declare-function comint-send-input "comint" - (&optional no-newline artificial)) - -(defun js--js-enter-repl () - (inferior-moz-process) ; called for side-effect - (with-current-buffer inferior-moz-buffer - (goto-char (point-max)) - - ;; Do some initialization the first time we see a process - (unless (eq (inferior-moz-process) js--js-process) - (setq js--js-process (inferior-moz-process)) - (setq js--js-references (make-hash-table :test 'eq :weakness t)) - (setq js--js-repl-depth 0) - - ;; Send interactor definition - (comint-send-string js--js-process js--moz-interactor) - (comint-send-string js--js-process - (concat "(" moz-repl-name ")\n")) - (js--wait-for-matching-output - (inferior-moz-process) js--js-prompt-regexp - js-js-timeout)) - - ;; Sanity check - (when (looking-back js--js-prompt-regexp - (save-excursion (forward-line 0) (point))) - (setq js--js-repl-depth 0)) - - (if (> js--js-repl-depth 0) - ;; If js--js-repl-depth > 0, we *should* be seeing an - ;; EVAL> prompt. If we don't, give Mozilla a chance to catch - ;; up with us. - (js--js-wait-for-eval-prompt) - - ;; Otherwise, tell Mozilla to enter the interactor mode - (insert (match-string-no-properties 1) - ".pushInteractor('js')") - (comint-send-input nil t) - (js--wait-for-matching-output - (inferior-moz-process) js--js-repl-prompt-regexp - js-js-timeout)) - - (cl-incf js--js-repl-depth))) - -(defun js--js-leave-repl () - (cl-assert (> js--js-repl-depth 0)) - (when (= 0 (cl-decf js--js-repl-depth)) - (with-current-buffer inferior-moz-buffer - (goto-char (point-max)) - (js--js-wait-for-eval-prompt) - (insert "EXIT") - (comint-send-input nil t) - (js--wait-for-matching-output - (inferior-moz-process) js--js-prompt-regexp - js-js-timeout)))) - -(defsubst js--js-not (value) - (memq value '(nil null false undefined))) - -(defsubst js--js-true (value) - (not (js--js-not value))) - -(eval-and-compile - (defun js--optimize-arglist (arglist) - "Convert immediate js< and js! references to deferred ones." - (cl-loop for item in arglist - if (eq (car-safe item) 'js<) - collect (append (list 'list ''js--funcall - '(list 'interactor "_getProp")) - (js--optimize-arglist (cdr item))) - else if (eq (car-safe item) 'js>) - collect (append (list 'list ''js--funcall - '(list 'interactor "_putProp")) - - (if (atom (cadr item)) - (list (cadr item)) - (list - (append - (list 'list ''js--funcall - '(list 'interactor "_mkArray")) - (js--optimize-arglist (cadr item))))) - (js--optimize-arglist (cddr item))) - else if (eq (car-safe item) 'js!) - collect (pcase-let ((`(,_ ,function . ,body) item)) - (append (list 'list ''js--funcall - (if (consp function) - (cons 'list - (js--optimize-arglist function)) - function)) - (js--optimize-arglist body))) - else - collect item))) - -(defmacro js--js-get-service (class-name interface-name) - `(js! ("Components" "classes" ,class-name "getService") - (js< "Components" "interfaces" ,interface-name))) - -(defmacro js--js-create-instance (class-name interface-name) - `(js! ("Components" "classes" ,class-name "createInstance") - (js< "Components" "interfaces" ,interface-name))) - -(defmacro js--js-qi (object interface-name) - `(js! (,object "QueryInterface") - (js< "Components" "interfaces" ,interface-name))) - -(defmacro with-js (&rest forms) - "Run FORMS with the Mozilla repl set up for js commands. -Inside the lexical scope of `with-js', `js?', `js!', -`js-new', `js-eval', `js-list', `js<', `js>', `js-get-service', -`js-create-instance', and `js-qi' are defined." - (declare (indent 0) (debug t)) - `(progn - (js--js-enter-repl) - (unwind-protect - (cl-macrolet ((js? (&rest body) `(js--js-true ,@body)) - (js! (function &rest body) - `(js--js-funcall - ,(if (consp function) - (cons 'list - (js--optimize-arglist function)) - function) - ,@(js--optimize-arglist body))) - - (js-new (function &rest body) - `(js--js-new - ,(if (consp function) - (cons 'list - (js--optimize-arglist function)) - function) - ,@body)) - - (js-eval (thisobj js) - `(js--js-eval - ,@(js--optimize-arglist - (list thisobj js)))) - - (js-list (&rest args) - `(js--js-list - ,@(js--optimize-arglist args))) - - (js-get-service (&rest args) - `(js--js-get-service - ,@(js--optimize-arglist args))) - - (js-create-instance (&rest args) - `(js--js-create-instance - ,@(js--optimize-arglist args))) - - (js-qi (&rest args) - `(js--js-qi - ,@(js--optimize-arglist args))) - - (js< (&rest body) `(js--js-get - ,@(js--optimize-arglist body))) - (js> (props value) - `(js--js-funcall - '(interactor "_putProp") - ,(if (consp props) - (cons 'list - (js--optimize-arglist props)) - props) - ,@(js--optimize-arglist (list value)) - )) - (js-handle? (arg) `(js--js-handle-p ,arg))) - ,@forms) - (js--js-leave-repl)))) - -(defvar js--js-array-as-list nil - "Whether to listify any Array returned by a Mozilla function. -If nil, the whole Array is treated as a JS symbol.") - -(defun js--js-decode-retval (result) - (pcase (intern (cl-first result)) - ('atom (cl-second result)) - ('special (intern (cl-second result))) - ('array - (mapcar #'js--js-decode-retval (cl-second result))) - ('objid - (or (gethash (cl-second result) - js--js-references) - (puthash (cl-second result) - (make-js--js-handle - :id (cl-second result) - :process (inferior-moz-process)) - js--js-references))) - - ('error (signal 'js-js-error (list (cl-second result)))) - (x (error "Unmatched case in js--js-decode-retval: %S" x)))) - -(defvar comint-last-input-end) - -(defun js--js-funcall (function &rest arguments) - "Call the Mozilla function FUNCTION with arguments ARGUMENTS. -If function is a string, look it up as a property on the global -object and use the global object for `this'. -If FUNCTION is a list with one element, use that element as the -function with the global object for `this', except that if that -single element is a string, look it up on the global object. -If FUNCTION is a list with more than one argument, use the list -up to the last value as a property descriptor and the last -argument as a function." - - (with-js - (let ((argstr (js--js-encode-value - (cons function arguments)))) - - (with-current-buffer inferior-moz-buffer - ;; Actual funcall - (when js--js-array-as-list - (insert "*")) - (insert argstr) - (comint-send-input nil t) - (js--wait-for-matching-output - (inferior-moz-process) "EVAL>" - js-js-timeout) - (goto-char comint-last-input-end) - - ;; Read the result - (let* ((json-array-type 'list) - (result (prog1 (json-read) - (goto-char (point-max))))) - (js--js-decode-retval result)))))) - -(defun js--js-new (constructor &rest arguments) - "Call CONSTRUCTOR as a constructor, with arguments ARGUMENTS. -CONSTRUCTOR is a JS handle, a string, or a list of these things." - (apply #'js--js-funcall - '(interactor "_callNew") - constructor arguments)) - -(defun js--js-eval (thisobj js) - (js--js-funcall '(interactor "_callEval") thisobj js)) - -(defun js--js-list (&rest arguments) - "Return a Lisp array resulting from evaluating each of ARGUMENTS." - (let ((js--js-array-as-list t)) - (apply #'js--js-funcall '(interactor "_mkArray") - arguments))) - -(defun js--js-get (&rest props) - (apply #'js--js-funcall '(interactor "_getProp") props)) - -(defun js--js-put (props value) - (js--js-funcall '(interactor "_putProp") props value)) - -(defun js-gc (&optional force) - "Tell the repl about any objects we don't reference anymore. -With argument, run even if no intervening GC has happened." - (interactive) - - (when force - (setq js--js-last-gcs-done nil)) - - (let ((this-gcs-done gcs-done) keys num) - (when (and js--js-references - (boundp 'inferior-moz-buffer) - (buffer-live-p inferior-moz-buffer) - - ;; Don't bother running unless we've had an intervening - ;; garbage collection; without a gc, nothing is deleted - ;; from the weak hash table, so it's pointless telling - ;; MozRepl about that references we still hold - (not (eq js--js-last-gcs-done this-gcs-done)) - - ;; Are we looking at a normal prompt? Make sure not to - ;; interrupt the user if he's doing something - (with-current-buffer inferior-moz-buffer - (save-excursion - (goto-char (point-max)) - (looking-back js--js-prompt-regexp - (save-excursion (forward-line 0) (point)))))) - - (setq keys (cl-loop for x being the hash-keys - of js--js-references - collect x)) - (setq num (js--js-funcall '(repl "_jsGC") (or keys []))) - - (setq js--js-last-gcs-done this-gcs-done) - (when (called-interactively-p 'interactive) - (message "Cleaned %s entries" num)) - - num))) - -(run-with-idle-timer 30 t #'js-gc) - -(defun js-eval (js) - "Evaluate the JavaScript in JS and return JSON-decoded result." - (interactive "MJavaScript to evaluate: ") - (with-js - (let* ((content-window (js--js-content-window - (js--get-js-context))) - (result (js-eval content-window js))) - (when (called-interactively-p 'interactive) - (message "%s" (js! "String" result))) - result))) - -(defun js--get-tabs () - "Enumerate all JavaScript contexts available. -Each context is a list: - (TITLE URL BROWSER TAB TABBROWSER) for content documents - (TITLE URL WINDOW) for windows - -All tabs of a given window are grouped together. The most recent -window is first. Within each window, the tabs are returned -left-to-right." - (with-js - (let (windows) - - (cl-loop with window-mediator = (js! ("Components" "classes" - "@mozilla.org/appshell/window-mediator;1" - "getService") - (js< "Components" "interfaces" - "nsIWindowMediator")) - with enumerator = (js! (window-mediator "getEnumerator") nil) - - while (js? (js! (enumerator "hasMoreElements"))) - for window = (js! (enumerator "getNext")) - for window-info = (js-list window - (js< window "document" "title") - (js! (window "location" "toString")) - (js< window "closed") - (js< window "windowState")) - - unless (or (js? (cl-fourth window-info)) - (eq (cl-fifth window-info) 2)) - do (push window-info windows)) - - (cl-loop for (window title location) in windows - collect (list title location window) - - for gbrowser = (js< window "gBrowser") - if (js-handle? gbrowser) - nconc (cl-loop - for x below (js< gbrowser "browsers" "length") - collect (js-list (js< gbrowser - "browsers" - x - "contentDocument" - "title") - - (js! (gbrowser - "browsers" - x - "contentWindow" - "location" - "toString")) - (js< gbrowser - "browsers" - x) - - (js! (gbrowser - "tabContainer" - "childNodes" - "item") - x) - - gbrowser)))))) - -(defvar js-read-tab-history nil) - -(declare-function ido-chop "ido" (items elem)) - -(defun js--read-tab (prompt) - "Read a Mozilla tab with prompt PROMPT. -Return a cons of (TYPE . OBJECT). TYPE is either `window' or -`tab', and OBJECT is a JavaScript handle to a ChromeWindow or a -browser, respectively." - - ;; Prime IDO - (unless ido-mode - (ido-mode 1) - (ido-mode -1)) - - (with-js - (let ((tabs (js--get-tabs)) selected-tab-cname - selected-tab prev-hitab) - - ;; Disambiguate names - (setq tabs - (cl-loop with tab-names = (make-hash-table :test 'equal) - for tab in tabs - for cname = (format "%s (%s)" - (cl-second tab) (cl-first tab)) - for num = (cl-incf (gethash cname tab-names -1)) - if (> num 0) - do (setq cname (format "%s <%d>" cname num)) - collect (cons cname tab))) - - (cl-labels - ((find-tab-by-cname - (cname) - (cl-loop for tab in tabs - if (equal (car tab) cname) - return (cdr tab))) - - (mogrify-highlighting - (hitab unhitab) - - ;; Hack to reduce the number of - ;; round-trips to mozilla - (let (cmds) - (cond - ;; Highlighting tab - ((cl-fourth hitab) - (push '(js! ((cl-fourth hitab) "setAttribute") - "style" - "color: red; font-weight: bold") - cmds) - - ;; Highlight window proper - (push '(js! ((cl-third hitab) - "setAttribute") - "style" - "border: 8px solid red") - cmds) - - ;; Select tab, when appropriate - (when js-js-switch-tabs - (push - '(js> ((cl-fifth hitab) "selectedTab") (cl-fourth hitab)) - cmds))) - - ;; Highlighting whole window - ((cl-third hitab) - (push '(js! ((cl-third hitab) "document" - "documentElement" "setAttribute") - "style" - (concat "-moz-appearance: none;" - "border: 8px solid red;")) - cmds))) - - (cond - ;; Unhighlighting tab - ((cl-fourth unhitab) - (push '(js! ((cl-fourth unhitab) "setAttribute") "style" "") - cmds) - (push '(js! ((cl-third unhitab) "setAttribute") "style" "") - cmds)) - - ;; Unhighlighting window - ((cl-third unhitab) - (push '(js! ((cl-third unhitab) "document" - "documentElement" "setAttribute") - "style" "") - cmds))) - - (eval `(with-js - (js-list ,@(nreverse cmds))) - t))) - - (command-hook - () - (let* ((tab (find-tab-by-cname (car ido-matches)))) - (mogrify-highlighting tab prev-hitab) - (setq prev-hitab tab))) - - (setup-hook - () - ;; Fiddle with the match list a bit: if our first match - ;; is a tabbrowser window, rotate the match list until - ;; the active tab comes up - (let ((matched-tab (find-tab-by-cname (car ido-matches)))) - (when (and matched-tab - (null (cl-fourth matched-tab)) - (equal "navigator:browser" - (js! ((cl-third matched-tab) - "document" - "documentElement" - "getAttribute") - "windowtype"))) - - (cl-loop with tab-to-match = (js< (cl-third matched-tab) - "gBrowser" - "selectedTab") - - for match in ido-matches - for candidate-tab = (find-tab-by-cname match) - if (eq (cl-fourth candidate-tab) tab-to-match) - do (setq ido-cur-list - (ido-chop ido-cur-list match)) - and return t))) - - (add-hook 'post-command-hook #'command-hook t t))) - - - (unwind-protect - ;; FIXME: Don't impose IDO on the user. - (setq selected-tab-cname - (let ((ido-minibuffer-setup-hook - (cons #'setup-hook ido-minibuffer-setup-hook))) - (ido-completing-read - prompt - (mapcar #'car tabs) - nil t nil - 'js-read-tab-history))) - - (when prev-hitab - (mogrify-highlighting nil prev-hitab) - (setq prev-hitab nil))) - - (add-to-history 'js-read-tab-history selected-tab-cname) - - (setq selected-tab (cl-loop for tab in tabs - if (equal (car tab) selected-tab-cname) - return (cdr tab))) - - (cons (if (cl-fourth selected-tab) 'browser 'window) - (cl-third selected-tab)))))) - -(defun js--guess-eval-defun-info (pstate) - "Helper function for `js-eval-defun'. -Return a list (NAME . CLASSPARTS), where CLASSPARTS is a list of -strings making up the class name and NAME is the name of the -function part." - (cond ((and (= (length pstate) 3) - (eq (js--pitem-type (cl-first pstate)) 'function) - (= (length (js--pitem-name (cl-first pstate))) 1) - (consp (js--pitem-type (cl-second pstate)))) - - (append (js--pitem-name (cl-second pstate)) - (list (cl-first (js--pitem-name (cl-first pstate)))))) - - ((and (= (length pstate) 2) - (eq (js--pitem-type (cl-first pstate)) 'function)) - - (append - (butlast (js--pitem-name (cl-first pstate))) - (list (car (last (js--pitem-name (cl-first pstate))))))) - - (t (error "Function not a toplevel defun or class member")))) - -(defvar js--js-context nil - "The current JavaScript context. -This is a cons like the one returned from `js--read-tab'. -Change with `js-set-js-context'.") - -(defconst js--js-inserter - "(function(func_info,func) { - func_info.unshift('window'); - var obj = window; - for(var i = 1; i < func_info.length - 1; ++i) { - var next = obj[func_info[i]]; - if(typeof next !== 'object' && typeof next !== 'function') { - next = obj.prototype && obj.prototype[func_info[i]]; - if(typeof next !== 'object' && typeof next !== 'function') { - alert('Could not find ' + func_info.slice(0, i+1).join('.') + - ' or ' + func_info.slice(0, i+1).join('.') + '.prototype'); - return; - } - - func_info.splice(i+1, 0, 'prototype'); - ++i; - } - } - - obj[func_info[i]] = func; - alert('Successfully updated '+func_info.join('.')); - })") - -(defun js-set-js-context (context) - "Set the JavaScript context to CONTEXT. -When called interactively, prompt for CONTEXT." - (interactive (list (js--read-tab "JavaScript Context: "))) - (setq js--js-context context)) - -(defun js--get-js-context () - "Return a valid JavaScript context. -If one hasn't been set, or if it's stale, prompt for a new one." - (with-js - (when (or (null js--js-context) - (js--js-handle-expired-p (cdr js--js-context)) - (pcase (car js--js-context) - ('window (js? (js< (cdr js--js-context) "closed"))) - ('browser (not (js? (js< (cdr js--js-context) - "contentDocument")))) - (x (error "Unmatched case in js--get-js-context: %S" x)))) - (setq js--js-context (js--read-tab "JavaScript Context: "))) - js--js-context)) - -(defun js--js-content-window (context) - (with-js - (pcase (car context) - ('window (cdr context)) - ('browser (js< (cdr context) - "contentWindow" "wrappedJSObject")) - (x (error "Unmatched case in js--js-content-window: %S" x))))) - -(defun js--make-nsilocalfile (path) - (with-js - (let ((file (js-create-instance "@mozilla.org/file/local;1" - "nsILocalFile"))) - (js! (file "initWithPath") path) - file))) - -(defun js--js-add-resource-alias (alias path) - (with-js - (let* ((io-service (js-get-service "@mozilla.org/network/io-service;1" - "nsIIOService")) - (res-prot (js! (io-service "getProtocolHandler") "resource")) - (res-prot (js-qi res-prot "nsIResProtocolHandler")) - (path-file (js--make-nsilocalfile path)) - (path-uri (js! (io-service "newFileURI") path-file))) - (js! (res-prot "setSubstitution") alias path-uri)))) - -(cl-defun js-eval-defun () - "Update a Mozilla tab using the JavaScript defun at point." - (interactive) - - ;; This function works by generating a temporary file that contains - ;; the function we'd like to insert. We then use the elisp-js bridge - ;; to command mozilla to load this file by inserting a script tag - ;; into the document we set. This way, debuggers and such will have - ;; a way to find the source of the just-inserted function. - ;; - ;; We delete the temporary file if there's an error, but otherwise - ;; we add an unload event listener on the Mozilla side to delete the - ;; file. - - (save-excursion - (let (begin end pstate defun-info temp-name defun-body) - (js-end-of-defun) - (setq end (point)) - (js--ensure-cache) - (js-beginning-of-defun) - (re-search-forward "\\_<function\\_>") - (setq begin (match-beginning 0)) - (setq pstate (js--forward-pstate)) - - (when (or (null pstate) - (> (point) end)) - (error "Could not locate function definition")) - - (setq defun-info (js--guess-eval-defun-info pstate)) - - (let ((overlay (make-overlay begin end))) - (overlay-put overlay 'face 'highlight) - (unwind-protect - (unless (y-or-n-p (format "Send %s to Mozilla? " - (mapconcat #'identity defun-info "."))) - (message "") ; question message lingers until next command - (cl-return-from js-eval-defun)) - (delete-overlay overlay))) - - (setq defun-body (buffer-substring-no-properties begin end)) - - (make-directory js-js-tmpdir t) - - ;; (Re)register a Mozilla resource URL to point to the - ;; temporary directory - (js--js-add-resource-alias "js" js-js-tmpdir) - - (setq temp-name (make-temp-file (concat js-js-tmpdir - "/js-") - nil ".js")) - (unwind-protect - (with-js - (with-temp-buffer - (insert js--js-inserter) - (insert "(") - (let ((standard-output (current-buffer))) - (json--print-list defun-info)) - (insert ",\n") - (insert defun-body) - (insert "\n)") - (write-region (point-min) (point-max) temp-name - nil 1)) - - ;; Give Mozilla responsibility for deleting this file - (let* ((content-window (js--js-content-window - (js--get-js-context))) - (content-document (js< content-window "document")) - (head (if (js? (js< content-document "body")) - ;; Regular content - (js< (js! (content-document "getElementsByTagName") - "head") - 0) - ;; Chrome - (js< content-document "documentElement"))) - (elem (js! (content-document "createElementNS") - "http://www.w3.org/1999/xhtml" "script"))) - - (js! (elem "setAttribute") "type" "text/javascript") - (js! (elem "setAttribute") "src" - (format "resource://js/%s" - (file-name-nondirectory temp-name))) - - (js! (head "appendChild") elem) - - (js! (content-window "addEventListener") "unload" - (js! ((js-new - "Function" "file" - "return function() { file.remove(false) }")) - (js--make-nsilocalfile temp-name)) - 'false) - (setq temp-name nil) - - - - )) - - ;; temp-name is set to nil on success - (when temp-name - (delete-file temp-name)))))) - ;;; Syntax extensions (defvar js-syntactic-mode-name t diff --git a/lisp/progmodes/m4-mode.el b/lisp/progmodes/m4-mode.el index b9fcd033bbb..a18c8bcce44 100644 --- a/lisp/progmodes/m4-mode.el +++ b/lisp/progmodes/m4-mode.el @@ -121,13 +121,11 @@ If m4 is not in your PATH, set this to an absolute file name." ("#" (0 (when (m4--quoted-p (match-beginning 0)) (string-to-syntax ".")))))) -(defvar m4-mode-map - (let ((map (make-sparse-keymap))) - (define-key map "\C-c\C-b" 'm4-m4-buffer) - (define-key map "\C-c\C-r" 'm4-m4-region) - (define-key map "\C-c\C-c" 'comment-region) - map) - "Keymap for M4 Mode.") +(defvar-keymap m4-mode-map + :doc "Keymap for M4 Mode." + "C-c C-b" #'m4-m4-buffer + "C-c C-r" #'m4-m4-region + "C-c C-c" #'comment-region) (easy-menu-define m4-mode-menu m4-mode-map "Menu for M4 Mode." diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el index 9f08f39e1c0..cbbcf1c2b7c 100644 --- a/lisp/progmodes/make-mode.el +++ b/lisp/progmodes/make-mode.el @@ -220,8 +220,8 @@ to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \"it seems necessary\"." "List of special targets. You will be offered to complete on one of those in the minibuffer whenever you enter a \".\" at the beginning of a line in `makefile-mode'." - :type '(repeat string)) -(put 'makefile-special-targets-list 'risky-local-variable t) + :type '(repeat string) + :risky t) (defcustom makefile-runtime-macros-list '(("@") ("&") (">") ("<") ("*") ("^") ("+") ("?") ("%") ("$")) @@ -542,8 +542,8 @@ not be enclosed in { } or ( )." This should identify a `make' command that can handle the `-q' option." :type 'string) -(defvaralias 'makefile-query-one-target-method - 'makefile-query-one-target-method-function) +(define-obsolete-variable-alias 'makefile-query-one-target-method + 'makefile-query-one-target-method-function "29.1") (defcustom makefile-query-one-target-method-function 'makefile-query-by-make-minus-q @@ -1170,7 +1170,6 @@ and adds all qualifying names to the list of known targets." (goto-char (match-end 0)) (insert suffix)))))))) -(define-obsolete-function-alias 'makefile-complete 'completion-at-point "24.1") ;; Backslashification. Stolen from cc-mode.el. diff --git a/lisp/progmodes/meta-mode.el b/lisp/progmodes/meta-mode.el index 5aaa277431a..f0fd23f3bc3 100644 --- a/lisp/progmodes/meta-mode.el +++ b/lisp/progmodes/meta-mode.el @@ -108,30 +108,27 @@ (macro-keywords-2 "\\(primarydef\\|secondarydef\\|tertiarydef\\)") (args-keywords - (eval-when-compile - (regexp-opt - '("expr" "suffix" "text" "primary" "secondary" "tertiary") - t))) + (regexp-opt + '("expr" "suffix" "text" "primary" "secondary" "tertiary") + t)) (type-keywords - (eval-when-compile - (regexp-opt - '("boolean" "color" "numeric" "pair" "path" "pen" "picture" - "string" "transform" "newinternal") - t))) + (regexp-opt + '("boolean" "color" "numeric" "pair" "path" "pen" "picture" + "string" "transform" "newinternal") + t)) (syntactic-keywords - (eval-when-compile - (regexp-opt - '("for" "forever" "forsuffixes" "endfor" - "step" "until" "upto" "downto" "thru" "within" - "iff" "if" "elseif" "else" "fi" "exitif" "exitunless" - "let" "def" "vardef" "enddef" "mode_def" - "true" "false" "known" "unknown" "and" "or" "not" - "save" "interim" "inner" "outer" "relax" - "begingroup" "endgroup" "expandafter" "scantokens" - "generate" "input" "endinput" "end" "bye" - "message" "errmessage" "errhelp" "special" "numspecial" - "readstring" "readfrom" "write") - t))) + (regexp-opt + '("for" "forever" "forsuffixes" "endfor" + "step" "until" "upto" "downto" "thru" "within" + "iff" "if" "elseif" "else" "fi" "exitif" "exitunless" + "let" "def" "vardef" "enddef" "mode_def" + "true" "false" "known" "unknown" "and" "or" "not" + "save" "interim" "inner" "outer" "relax" + "begingroup" "endgroup" "expandafter" "scantokens" + "generate" "input" "endinput" "end" "bye" + "message" "errmessage" "errhelp" "special" "numspecial" + "readstring" "readfrom" "write") + t)) ) (list ;; embedded TeX code in btex ... etex @@ -441,8 +438,6 @@ If the list was changed, sort the list and remove duplicates first." (insert close))))))) (nth 1 entry)))) -(define-obsolete-function-alias 'meta-complete-symbol - 'completion-at-point "24.1") ;;; Indentation. @@ -806,7 +801,6 @@ The environment marked is the one that contains point or follows point." st) "Syntax table used in Metafont or MetaPost mode.") -(define-obsolete-variable-alias 'meta-mode-map 'meta-common-mode-map "24.1") (defvar meta-common-mode-map (let ((map (make-sparse-keymap))) ;; Comment Paragraphs: diff --git a/lisp/progmodes/mixal-mode.el b/lisp/progmodes/mixal-mode.el index 97a218fcfa3..9d1ceaa55a8 100644 --- a/lisp/progmodes/mixal-mode.el +++ b/lisp/progmodes/mixal-mode.el @@ -78,16 +78,13 @@ ;;; Code: (defvar compile-command) -;;; Key map -(defvar mixal-mode-map - (let ((map (make-sparse-keymap))) - (define-key map "\C-c\C-c" 'compile) - (define-key map "\C-c\C-r" 'mixal-run) - (define-key map "\C-c\C-d" 'mixal-debug) - (define-key map "\C-h\C-o" 'mixal-describe-operation-code) - map) - "Keymap for `mixal-mode'.") -;; (makunbound 'mixal-mode-map) +;;; Keymap +(defvar-keymap mixal-mode-map + :doc "Keymap for `mixal-mode'." + "C-c C-c" #'compile + "C-c C-r" #'mixal-run + "C-c C-d" #'mixal-debug + "C-h C-o" #'mixal-describe-operation-code) ;;; Syntax table (defvar mixal-mode-syntax-table diff --git a/lisp/progmodes/modula2.el b/lisp/progmodes/modula2.el index a8d644dba0e..e668570ba17 100644 --- a/lisp/progmodes/modula2.el +++ b/lisp/progmodes/modula2.el @@ -101,9 +101,8 @@ (defcustom m2-indent 5 "This variable gives the indentation in Modula-2 mode." - :type 'integer) -(put 'm2-indent 'safe-local-variable - (lambda (v) (or (null v) (integerp v)))) + :type 'integer + :safe (lambda (v) (or (null v) (integerp v)))) (defconst m2-smie-grammar ;; An official definition can be found as "M2R10.pdf". This grammar does diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el index a45909537ad..721dfa51ad3 100644 --- a/lisp/progmodes/octave.el +++ b/lisp/progmodes/octave.el @@ -197,8 +197,8 @@ newline or semicolon after an else or end keyword." (defcustom octave-block-offset 2 "Extra indentation applied to statements in Octave block structures." - :type 'integer) -(put 'octave-block-offset 'safe-local-variable 'integerp) + :type 'integer + :safe #'integerp) (defvar octave-block-comment-start (concat (make-string 2 octave-comment-char) " ") @@ -879,7 +879,8 @@ startup file, `~/.emacs-octave'." (set-process-filter proc 'comint-output-filter) ;; Just in case, to be sure a cd in the startup file won't have ;; detrimental effects. - (with-demoted-errors (inferior-octave-resync-dirs)) + (with-demoted-errors "Octave resync error: %S" + (inferior-octave-resync-dirs)) ;; Generate a proper prompt, which is critical to ;; `comint-history-isearch-backward-regexp'. Bug#14433. (comint-send-string proc "\n"))) @@ -1814,18 +1815,18 @@ If the environment variable OCTAVE_SRCDIR is set, it is searched first." (user-error "Aborted"))) (_ name))) -(defvar find-tag-marker-ring) +(declare-function xref-push-marker-stack "xref" (&optional m)) (defun octave-find-definition (fn) "Find the definition of FN. Functions implemented in C++ can be found if variable `octave-source-directories' is set correctly." (interactive (list (octave-completing-read))) - (require 'etags) + (require 'xref) (let ((orig (point))) (if (and (derived-mode-p 'octave-mode) (octave-goto-function-definition fn)) - (ring-insert find-tag-marker-ring (copy-marker orig)) + (xref-push-marker-stack (copy-marker orig)) (inferior-octave-send-list-and-digest ;; help NAME is more verbose (list (format "\ @@ -1840,7 +1841,7 @@ if iskeyword('%s') disp('`%s'' is a keyword') else which('%s') endif\n" (setq file (match-string 1 line)))) (if (not file) (user-error "%s" (or line (format-message "`%s' not found" fn))) - (ring-insert find-tag-marker-ring (point-marker)) + (xref-push-marker-stack) (setq file (funcall octave-find-definition-filename-function file)) (when file (find-file file) diff --git a/lisp/progmodes/opascal.el b/lisp/progmodes/opascal.el index 4ab9b4a9962..63399adf3ae 100644 --- a/lisp/progmodes/opascal.el +++ b/lisp/progmodes/opascal.el @@ -1641,10 +1641,10 @@ An error is raised if not in a comment." (defun opascal-new-comment-line () "If in a // comment, do a newline, indented such that one is still in the comment block. If not in a // comment, just does a normal newline." - (interactive) (declare (obsolete "use comment-indent-new-line with comment-multi-line instead" "27.1")) + (interactive) (let ((comment (opascal-current-token))) (if (not (eq 'comment-single-line (opascal-token-kind comment))) ;; Not in a // comment. Just do the normal newline. diff --git a/lisp/progmodes/pascal.el b/lisp/progmodes/pascal.el index 422ee9bb6bd..8d3194e6a47 100644 --- a/lisp/progmodes/pascal.el +++ b/lisp/progmodes/pascal.el @@ -47,8 +47,8 @@ ;; "reset" "rewrite" "write" "writeln") ;; pascal-separator-keywords '("downto" "else" "mod" "div" "then")) -;; KNOWN BUGS / BUGREPORTS -;; ======================= +;; KNOWN BUGS / BUG REPORTS +;; ======================== ;; As far as I know, there are no bugs in the current version of this ;; package. This may not be true however, since I never use this mode ;; myself and therefore would never notice them anyway. If you do @@ -239,14 +239,6 @@ will do all lineups." (const :tag "Declarations" declaration) (const :tag "Case statements" case))) -(defvar pascal-toggle-completions nil - "If non-nil, `pascal-complete-word' tries all possible completions. -Repeated use of \\[pascal-complete-word] then shows all -completions in turn, instead of displaying a list of all possible -completions.") -(make-obsolete-variable 'pascal-toggle-completions - 'completion-cycle-threshold "24.1") - (defcustom pascal-type-keywords '("array" "file" "packed" "char" "integer" "real" "string" "record") "Keywords for types used when completing a word in a declaration or parmlist. @@ -1297,13 +1289,6 @@ indent of the current line in parameterlist." (when (> e b) (list b e #'pascal-completion)))) -(define-obsolete-function-alias 'pascal-complete-word - 'completion-at-point "24.1") - -(define-obsolete-function-alias 'pascal-show-completions - 'completion-help-at-point "24.1") - - (defun pascal-get-default-symbol () "Return symbol around current point as a string." (save-excursion @@ -1357,9 +1342,7 @@ The default is a name found in the buffer around point." default "")) (label ;; Do completion with default. - (completing-read (if (not (string= default "")) - (concat "Label (default " default "): ") - "Label: ") + (completing-read (format-prompt "Label" default) ;; Complete with the defuns found in the ;; current-buffer. (let ((buf (current-buffer))) @@ -1384,8 +1367,6 @@ The default is a name found in the buffer around point." ;;; (defvar pascal-outline-map (let ((map (make-sparse-keymap))) - (if (fboundp 'set-keymap-name) - (set-keymap-name map 'pascal-outline-map)) (define-key map "\M-\C-a" 'pascal-outline-prev-defun) (define-key map "\M-\C-e" 'pascal-outline-next-defun) (define-key map "\C-c\C-d" 'pascal-outline-goto-defun) diff --git a/lisp/progmodes/perl-mode.el b/lisp/progmodes/perl-mode.el index 6f468192a90..92b47ce88f6 100644 --- a/lisp/progmodes/perl-mode.el +++ b/lisp/progmodes/perl-mode.el @@ -191,7 +191,9 @@ ,(concat "\\<" (regexp-opt '("if" "until" "while" "elsif" "else" "unless" "do" "dump" "for" "foreach" "exit" "die" - "BEGIN" "END" "return" "exec" "eval") t) + "BEGIN" "END" "return" "exec" "eval" + "when" "given" "default") + t) "\\>") ;; ;; Fontify declarators and prefixes as types. @@ -212,7 +214,7 @@ (eval-and-compile (defconst perl--syntax-exp-intro-keywords - '("split" "if" "unless" "until" "while" "print" + '("split" "if" "unless" "until" "while" "print" "printf" "grep" "map" "not" "or" "and" "for" "foreach" "return")) (defconst perl--syntax-exp-intro-regexp diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el index 20685354890..7738de6a745 100644 --- a/lisp/progmodes/prog-mode.el +++ b/lisp/progmodes/prog-mode.el @@ -49,9 +49,15 @@ (define-key-after menu [prog-separator] menu-bar-separator 'middle-separator) + (unless (xref-forward-history-empty-p) + (define-key-after menu [xref-forward] + '(menu-item "Go Forward" xref-go-forward + :help "Forward to the position gone Back from") + 'prog-separator)) + (unless (xref-marker-stack-empty-p) (define-key-after menu [xref-pop] - '(menu-item "Go Back" xref-pop-marker-stack + '(menu-item "Go Back" xref-go-back :help "Back to the position of the last search") 'prog-separator)) diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 07093d61474..30f51704dca 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2015-2022 Free Software Foundation, Inc. ;; Version: 0.8.1 -;; Package-Requires: ((emacs "26.1") (xref "1.0.2")) +;; Package-Requires: ((emacs "26.1") (xref "1.4.0")) ;; This is a GNU ELPA :core package. Avoid using functionality that ;; not compatible with the version of Emacs recorded above. @@ -322,7 +322,15 @@ to find the list of ignores for each directory." (process-file-shell-command command nil t)) (pt (point-min))) (unless (zerop status) - (error "File listing failed: %s" (buffer-string))) + (goto-char (point-min)) + (if (and + (not (eql status 127)) + (search-forward "Permission denied\n" nil t)) + (let ((end (1- (point)))) + (re-search-backward "\\`\\|\0") + (error "File listing failed: %s" + (buffer-substring (1+ (point)) end))) + (error "File listing failed: %s" (buffer-string)))) (goto-char pt) (while (search-forward "\0" nil t) (push (buffer-substring-no-properties (1+ pt) (1- (point))) @@ -374,6 +382,12 @@ you might have to restart Emacs to see the effect." :package-version '(project . "0.2.0") :safe #'booleanp) +(defcustom project-vc-include-untracked t + "When non-nil, the VC project backend includes untracked files." + :type 'boolean + :version "29.1" + :safe #'booleanp) + ;; FIXME: Using the current approach, major modes are supposed to set ;; this variable to a buffer-local value. So we don't have access to ;; the "external roots" of language A from buffers of language B, which @@ -410,30 +424,33 @@ The directory names should be absolute. Used in the VC project backend implementation of `project-external-roots'.") (defun project-try-vc (dir) - (let* ((backend - ;; FIXME: This is slow. Cache it. - (ignore-errors (vc-responsible-backend dir))) - (root - (pcase backend - ('Git - ;; Don't stop at submodule boundary. - ;; FIXME: Cache for a shorter time. - (or (vc-file-getprop dir 'project-git-root) - (let ((root (vc-call-backend backend 'root dir))) - (vc-file-setprop - dir 'project-git-root - (if (and - ;; FIXME: Invalidate the cache when the value - ;; of this variable changes. - (project--vc-merge-submodules-p root) - (project--submodule-p root)) - (let* ((parent (file-name-directory - (directory-file-name root)))) - (vc-call-backend backend 'root parent)) - root))))) - ('nil nil) - (_ (ignore-errors (vc-call-backend backend 'root dir)))))) - (and root (cons 'vc root)))) + (or (vc-file-getprop dir 'project-vc) + (let* ((backend (ignore-errors (vc-responsible-backend dir))) + (root + (pcase backend + ('Git + ;; Don't stop at submodule boundary. + (or (vc-file-getprop dir 'project-git-root) + (let ((root (vc-call-backend backend 'root dir))) + (vc-file-setprop + dir 'project-git-root + (if (and + ;; FIXME: Invalidate the cache when the value + ;; of this variable changes. + (project--vc-merge-submodules-p root) + (project--submodule-p root)) + (let* ((parent (file-name-directory + (directory-file-name root)))) + (vc-call-backend backend 'root parent)) + root))))) + ('nil nil) + (_ (ignore-errors (vc-call-backend backend 'root dir))))) + project) + (when root + (setq project (list 'vc backend root)) + ;; FIXME: Cache for a shorter time. + (vc-file-setprop dir 'project-vc project) + project)))) (defun project--submodule-p (root) ;; XXX: We only support Git submodules for now. @@ -459,7 +476,7 @@ backend implementation of `project-external-roots'.") (t nil)))) (cl-defmethod project-root ((project (head vc))) - (cdr project)) + (nth 2 project)) (cl-defmethod project-external-roots ((project (head vc))) (project-subtract-directories @@ -474,8 +491,8 @@ backend implementation of `project-external-roots'.") (lambda (dir) (let ((ignores (project--value-in-dir 'project-vc-ignores dir)) backend) - (if (and (file-equal-p dir (cdr project)) - (setq backend (vc-responsible-backend dir)) + (if (and (file-equal-p dir (nth 2 project)) + (setq backend (cadr project)) (cond ((eq backend 'Hg)) ((and (eq backend 'Git) @@ -501,8 +518,9 @@ backend implementation of `project-external-roots'.") (args '("-z")) (vc-git-use-literal-pathspecs nil) files) - ;; Include unregistered. - (setq args (append args '("-c" "-o" "--exclude-standard"))) + (setq args (append args + '("-c" "--exclude-standard") + (and project-vc-include-untracked '("-o")))) (when extra-ignores (setq args (append args (cons "--" @@ -554,9 +572,9 @@ backend implementation of `project-external-roots'.") (delete-consecutive-dups files))) (`Hg (let ((default-directory (expand-file-name (file-name-as-directory dir))) - args) - ;; Include unregistered. - (setq args (nconc args '("-mcardu" "--no-status" "-0"))) + (args (list (concat "-mcard" (and project-vc-include-untracked "u")) + "--no-status" + "-0"))) (when extra-ignores (setq args (nconc args (mapcan @@ -581,17 +599,17 @@ backend implementation of `project-external-roots'.") (insert-file-contents ".gitmodules") (let (res) (goto-char (point-min)) - (while (re-search-forward "path *= *\\(.+\\)" nil t) + (while (re-search-forward "^[ \t]*path *= *\\(.+\\)" nil t) (push (match-string 1) res)) (nreverse res))) (file-missing nil))) (cl-defmethod project-ignores ((project (head vc)) dir) - (let* ((root (cdr project)) + (let* ((root (nth 2 project)) backend) (append (when (file-equal-p dir root) - (setq backend (vc-responsible-backend root)) + (setq backend (cadr project)) (delq nil (mapcar @@ -768,7 +786,6 @@ The following commands are available: (define-key tab-prefix-map "p" #'project-other-tab-command)) (declare-function grep-read-files "grep") -(declare-function xref--show-xrefs "xref") (declare-function xref--find-ignores-arguments "xref") ;;;###autoload @@ -794,7 +811,7 @@ requires quoting, e.g. `\\[quoted-insert]<space>'." (project--files-in-directory dir nil (grep-read-files regexp)))))) - (xref--show-xrefs + (xref-show-xrefs (apply-partially #'project--find-regexp-in-files regexp files) nil))) @@ -822,7 +839,7 @@ pattern to search for." (project-files pr (cons (project-root pr) (project-external-roots pr))))) - (xref--show-xrefs + (xref-show-xrefs (apply-partially #'project--find-regexp-in-files regexp files) nil))) @@ -842,28 +859,40 @@ pattern to search for." project-regexp-history-variable))) ;;;###autoload -(defun project-find-file () +(defun project-find-file (&optional include-all) "Visit a file (with completion) in the current project. The filename at point (determined by `thing-at-point'), if any, -is available as part of \"future history\"." - (interactive) +is available as part of \"future history\". + +If INCLUDE-ALL is non-nil, or with prefix argument when called +interactively, include all files under the project root, except +for VCS directories listed in `vc-directory-exclusion-list'." + (interactive "P") (let* ((pr (project-current t)) - (dirs (list (project-root pr)))) - (project-find-file-in (thing-at-point 'filename) dirs pr))) + (root (project-root pr)) + (dirs (list root))) + (project-find-file-in + (or (thing-at-point 'filename) + (and buffer-file-name (file-relative-name buffer-file-name root))) + dirs pr include-all))) ;;;###autoload -(defun project-or-external-find-file () +(defun project-or-external-find-file (&optional include-all) "Visit a file (with completion) in the current project or external roots. The filename at point (determined by `thing-at-point'), if any, -is available as part of \"future history\"." - (interactive) +is available as part of \"future history\". + +If INCLUDE-ALL is non-nil, or with prefix argument when called +interactively, include all files under the project root, except +for VCS directories listed in `vc-directory-exclusion-list'." + (interactive "P") (let* ((pr (project-current t)) (dirs (cons (project-root pr) (project-external-roots pr)))) - (project-find-file-in (thing-at-point 'filename) dirs pr))) + (project-find-file-in (thing-at-point 'filename) dirs pr include-all))) (defcustom project-read-file-name-function #'project--read-file-cpd-relative "Function to call to read a file name from a list. @@ -916,15 +945,28 @@ by the user at will." predicate hist mb-default)) -(defun project-find-file-in (suggested-filename dirs project) +(defun project-find-file-in (suggested-filename dirs project &optional include-all) "Complete a file name in DIRS in PROJECT and visit the result. SUGGESTED-FILENAME is a relative file name, or part of it, which -is used as part of \"future history\"." - (let* ((all-files (project-files project dirs)) +is used as part of \"future history\". + +If INCLUDE-ALL is non-nil, or with prefix argument when called +interactively, include all files from DIRS, except for VCS +directories listed in `vc-directory-exclusion-list'." + (let* ((vc-dirs-ignores (mapcar + (lambda (dir) + (concat dir "/")) + vc-directory-exclusion-list)) + (all-files + (if include-all + (mapcan + (lambda (dir) (project--files-in-directory dir vc-dirs-ignores)) + dirs) + (project-files project dirs))) (completion-ignore-case read-file-name-completion-ignore-case) (file (funcall project-read-file-name-function - "Find file" all-files nil nil + "Find file" all-files nil 'file-name-history suggested-filename))) (if (string= file "") (user-error "You didn't specify the file") @@ -961,7 +1003,7 @@ is used as part of \"future history\"." "Dired" ;; Some completion UIs show duplicates. (delete-dups all-dirs) - nil nil))) + nil 'file-name-history))) (dired dir))) ;;;###autoload @@ -976,6 +1018,8 @@ is used as part of \"future history\"." (interactive) (vc-dir (project-root (project-current t)))) +(declare-function comint-check-proc "comint") + ;;;###autoload (defun project-shell () "Start an inferior shell in the current project's root directory. @@ -984,11 +1028,14 @@ switch to it. Otherwise, create a new shell buffer. With \\[universal-argument] prefix arg, create a new inferior shell buffer even if one already exists." (interactive) + (require 'comint) (let* ((default-directory (project-root (project-current t))) (default-project-shell-name (project-prefixed-buffer-name "shell")) (shell-buffer (get-buffer default-project-shell-name))) (if (and shell-buffer (not current-prefix-arg)) - (pop-to-buffer-same-window shell-buffer) + (if (comint-check-proc shell-buffer) + (pop-to-buffer shell-buffer (bound-and-true-p display-comint-buffer-action)) + (shell shell-buffer)) (shell (generate-new-buffer-name default-project-shell-name))))) ;;;###autoload @@ -1004,7 +1051,7 @@ if one already exists." (eshell-buffer-name (project-prefixed-buffer-name "eshell")) (eshell-buffer (get-buffer eshell-buffer-name))) (if (and eshell-buffer (not current-prefix-arg)) - (pop-to-buffer-same-window eshell-buffer) + (pop-to-buffer eshell-buffer (bound-and-true-p display-comint-buffer-action)) (eshell t)))) ;;;###autoload @@ -1047,11 +1094,17 @@ type \\[help-command] at that time. If you exit the `query-replace', you can later continue the `query-replace' loop using the command \\[fileloop-continue]." (interactive - (pcase-let ((`(,from ,to) - (query-replace-read-args "Query replace (regexp)" t t))) - (list from to))) + (let ((query-replace-read-from-regexp-default 'find-tag-default-as-regexp)) + (pcase-let ((`(,from ,to) + (query-replace-read-args "Query replace (regexp)" t t))) + (list from to)))) (fileloop-initialize-replace - from to (project-files (project-current t)) 'default) + from to + ;; XXX: Filter out Git submodules, which are not regular files. + ;; `project-files' can return those, which is arguably suboptimal, + ;; but removing them eagerly has performance cost. + (cl-delete-if-not #'file-regular-p (project-files (project-current t))) + 'default) (fileloop-continue)) (defvar compilation-read-command) @@ -1087,6 +1140,29 @@ If non-nil, it overrides `compilation-buffer-name-function' for compilation-buffer-name-function))) (call-interactively #'compile))) +(defcustom project-ignore-buffer-conditions nil + "List of conditions to filter the buffers to be switched to. +If any of these conditions are satisfied for a buffer in the +current project, `project-switch-to-buffer', +`project-display-buffer' and `project-display-buffer-other-frame' +ignore it. +See the doc string of `project-kill-buffer-conditions' for the +general form of conditions." + :type '(repeat (choice regexp function symbol + (cons :tag "Major mode" + (const major-mode) symbol) + (cons :tag "Derived mode" + (const derived-mode) symbol) + (cons :tag "Negation" + (const not) sexp) + (cons :tag "Conjunction" + (const and) sexp) + (cons :tag "Disjunction" + (const or) sexp))) + :version "29.1" + :group 'project + :package-version '(project . "0.8.2")) + (defun project--read-project-buffer () (let* ((pr (project-current t)) (current-buffer (current-buffer)) @@ -1096,7 +1172,10 @@ If non-nil, it overrides `compilation-buffer-name-function' for (predicate (lambda (buffer) ;; BUFFER is an entry (BUF-NAME . BUF-OBJ) of Vbuffer_alist. - (memq (cdr buffer) buffers)))) + (and (memq (cdr buffer) buffers) + (not + (project--buffer-check + (cdr buffer) project-ignore-buffer-conditions)))))) (read-buffer "Switch to buffer: " (when (funcall predicate (cons other-name other-buffer)) @@ -1150,7 +1229,10 @@ displayed." (not (major-mode . help-mode))) (derived-mode . compilation-mode) (derived-mode . dired-mode) - (derived-mode . diff-mode)) + (derived-mode . diff-mode) + (derived-mode . comint-mode) + (derived-mode . eshell-mode) + (derived-mode . change-log-mode)) "List of conditions to kill buffers related to a project. This list is used by `project-kill-buffers'. Each condition is either: @@ -1160,10 +1242,9 @@ Each condition is either: - a cons-cell, where the car describes how to interpret the cdr. The car can be one of the following: * `major-mode': the buffer is killed if the buffer's major - mode is eq to the cons-cell's cdr + mode is eq to the cons-cell's cdr. * `derived-mode': the buffer is killed if the buffer's major - mode is derived from the major mode denoted by the cons-cell's - cdr + mode is derived from the major mode in the cons-cell's cdr. * `not': the cdr is interpreted as a negation of a condition. * `and': the cdr is a list of recursive conditions, that all have to be met. @@ -1183,9 +1264,18 @@ current project, it will be killed." (const and) sexp) (cons :tag "Disjunction" (const or) sexp))) - :version "28.1" + :version "29.1" + :group 'project + :package-version '(project . "0.8.2")) + +(defcustom project-kill-buffers-display-buffer-list nil + "Non-nil to display list of buffers to kill before killing project buffers. +Used by `project-kill-buffers'." + :type 'boolean + :version "29.1" :group 'project - :package-version '(project . "0.6.0")) + :package-version '(project . "0.8.2") + :safe #'booleanp) (defun project--buffer-list (pr) "Return the list of all buffers in project PR." @@ -1202,16 +1292,17 @@ current project, it will be killed." (push buf bufs))) (nreverse bufs))) -(defun project--kill-buffer-check (buf conditions) +(defun project--buffer-check (buf conditions) "Check if buffer BUF matches any element of the list CONDITIONS. -See `project-kill-buffer-conditions' for more details on the form -of CONDITIONS." - (catch 'kill +See `project-kill-buffer-conditions' or +`project-ignore-buffer-conditions' for more details on the +form of CONDITIONS." + (catch 'match (dolist (c conditions) (when (cond ((stringp c) (string-match-p c (buffer-name buf))) - ((symbolp c) + ((functionp c) (funcall c buf)) ((eq (car-safe c) 'major-mode) (eq (buffer-local-value 'major-mode buf) @@ -1221,15 +1312,15 @@ of CONDITIONS." (buffer-local-value 'major-mode buf) (cdr c))) ((eq (car-safe c) 'not) - (not (project--kill-buffer-check buf (cdr c)))) + (not (project--buffer-check buf (cdr c)))) ((eq (car-safe c) 'or) - (project--kill-buffer-check buf (cdr c))) + (project--buffer-check buf (cdr c))) ((eq (car-safe c) 'and) (seq-every-p - (apply-partially #'project--kill-buffer-check + (apply-partially #'project--buffer-check buf) (mapcar #'list (cdr c))))) - (throw 'kill t))))) + (throw 'match t))))) (defun project--buffers-to-kill (pr) "Return list of buffers in project PR to kill. @@ -1237,7 +1328,7 @@ What buffers should or should not be killed is described in `project-kill-buffer-conditions'." (let (bufs) (dolist (buf (project-buffers pr)) - (when (project--kill-buffer-check buf project-kill-buffer-conditions) + (when (project--buffer-check buf project-kill-buffer-conditions) (push buf bufs))) bufs)) @@ -1250,17 +1341,40 @@ identical. Only the buffers that match a condition in `project-kill-buffer-conditions' will be killed. If NO-CONFIRM is non-nil, the command will not ask the user for confirmation. NO-CONFIRM is always nil when the command is invoked -interactively." +interactively. + +Also see the `project-kill-buffers-display-buffer-list' variable." (interactive) (let* ((pr (project-current t)) - (bufs (project--buffers-to-kill pr))) + (bufs (project--buffers-to-kill pr)) + (query-user (lambda () + (yes-or-no-p + (format "Kill %d buffers in %s? " + (length bufs) + (project-root pr)))))) (cond (no-confirm (mapc #'kill-buffer bufs)) ((null bufs) (message "No buffers to kill")) - ((yes-or-no-p (format "Kill %d buffers in %s? " - (length bufs) - (project-root pr))) + (project-kill-buffers-display-buffer-list + (when + (with-current-buffer-window + (get-buffer-create "*Buffer List*") + `(display-buffer--maybe-at-bottom + (dedicated . t) + (window-height . (fit-window-to-buffer)) + (preserve-size . (nil . t)) + (body-function + . ,#'(lambda (_window) + (list-buffers-noselect nil bufs)))) + #'(lambda (window _value) + (with-selected-window window + (unwind-protect + (funcall query-user) + (when (window-live-p window) + (quit-restore-window window 'kill)))))) + (mapc #'kill-buffer bufs))) + ((funcall query-user) (mapc #'kill-buffer bufs))))) diff --git a/lisp/progmodes/prolog.el b/lisp/progmodes/prolog.el index 6bc7ee408d5..5aba95d4c79 100644 --- a/lisp/progmodes/prolog.el +++ b/lisp/progmodes/prolog.el @@ -742,14 +742,6 @@ Relevant only when `prolog-imenu-flag' is non-nil." :group 'prolog-other :type 'boolean) -(defcustom prolog-char-quote-workaround nil - "If non-nil, declare 0 as a quote character to handle 0'<char>. -This is really kludgy, and unneeded (i.e. obsolete) in Emacs>=24." - :version "24.1" - :group 'prolog-other - :type 'boolean) -(make-obsolete-variable 'prolog-char-quote-workaround nil "24.1") - ;;------------------------------------------------------------------- ;; Internal variables @@ -1303,7 +1295,7 @@ To find out what version of Prolog mode you are running, enter (t t))) ;; This statement was missing in Emacs 24.1, 24.2, 24.3. -(define-obsolete-function-alias 'switch-to-prolog 'run-prolog "24.1") +(define-obsolete-function-alias 'switch-to-prolog 'run-prolog "24.1") ; "24.4" ; for grep ;;;###autoload (defun run-prolog (arg) "Run an inferior Prolog process, input and output via buffer *prolog*. @@ -1355,8 +1347,6 @@ the variable `prolog-prompt-regexp'." (error "This Prolog system has defined no interpreter")) (unless (comint-check-proc "*prolog*") (with-current-buffer (get-buffer-create "*prolog*") - (prolog-inferior-mode) - ;; The "INFERIOR=yes" hack is for SWI-Prolog 7.2.3 and earlier, ;; which assumes it is running under Emacs if either INFERIOR=yes or ;; if EMACS is set to a nonempty value. The EMACS setting is @@ -1369,6 +1359,7 @@ the variable `prolog-prompt-regexp'." (cons "INFERIOR=yes" process-environment)))) (apply 'make-comint-in-buffer "prolog" (current-buffer) pname nil pswitches)) + (prolog-inferior-mode) (unless prolog-system ;; Setup auto-detection. @@ -2484,11 +2475,8 @@ Interaction supports completion." (if (eq (try-completion default prolog-info-alist) nil) (setq default nil)) ;; Read the PredSpec from the user - (completing-read - (if (zerop (length default)) - "Help on predicate: " - (concat "Help on predicate (default " default "): ")) - prolog-info-alist nil t nil nil default))) + (completing-read (format-prompt "Help on predicate" default) + prolog-info-alist nil t nil nil default))) (defun prolog-build-info-alist (&optional verbose) "Build an alist of all builtins and library predicates. diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f7f1784b172..1c99937c4b9 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -5,7 +5,7 @@ ;; Author: Fabián E. Gallina <fgallina@gnu.org> ;; URL: https://github.com/fgallina/python.el ;; Version: 0.28 -;; Package-Requires: ((emacs "24.2") (cl-lib "1.0")) +;; Package-Requires: ((emacs "24.4") (cl-lib "1.0")) ;; Maintainer: emacs-devel@gnu.org ;; Created: Jul 2010 ;; Keywords: languages @@ -92,7 +92,7 @@ ;; Operating Systems' pipe buffering (e.g. CPython 3.3.4 in Windows 7. ;; See URL `https://debbugs.gnu.org/cgi/bugreport.cgi?bug=17304'). To ;; avoid this, the `python-shell-unbuffered' defaults to non-nil and -;; controls whether `python-shell-calculate-process-environment' +;; controls whether `python-shell--calculate-process-environment' ;; should set the "PYTHONUNBUFFERED" environment variable on startup: ;; See URL `https://docs.python.org/3/using/cmdline.html#cmdoption-u'. @@ -149,7 +149,7 @@ ;; (setq python-shell-process-environment ;; (list ;; (format "PATH=%s" (mapconcat -;; 'identity +;; #'identity ;; (reverse ;; (cons (getenv "PATH") ;; '("/path/to/env/bin/"))) @@ -245,10 +245,9 @@ (require 'ansi-color) (require 'cl-lib) (require 'comint) -(require 'tramp-sh) +(eval-when-compile (require 'subr-x)) ;For `string-empty-p'. ;; Avoid compiler warnings -(defvar view-return-to-alist) (defvar compilation-error-regexp-alist) (defvar outline-heading-end-regexp) @@ -273,39 +272,39 @@ (defvar python-mode-map (let ((map (make-sparse-keymap))) ;; Movement - (define-key map [remap backward-sentence] 'python-nav-backward-block) - (define-key map [remap forward-sentence] 'python-nav-forward-block) - (define-key map [remap backward-up-list] 'python-nav-backward-up-list) - (define-key map [remap mark-defun] 'python-mark-defun) - (define-key map "\C-c\C-j" 'imenu) + (define-key map [remap backward-sentence] #'python-nav-backward-block) + (define-key map [remap forward-sentence] #'python-nav-forward-block) + (define-key map [remap backward-up-list] #'python-nav-backward-up-list) + (define-key map [remap mark-defun] #'python-mark-defun) + (define-key map "\C-c\C-j" #'imenu) ;; Indent specific - (define-key map "\177" 'python-indent-dedent-line-backspace) - (define-key map (kbd "<backtab>") 'python-indent-dedent-line) - (define-key map "\C-c<" 'python-indent-shift-left) - (define-key map "\C-c>" 'python-indent-shift-right) + (define-key map "\177" #'python-indent-dedent-line-backspace) + (define-key map (kbd "<backtab>") #'python-indent-dedent-line) + (define-key map "\C-c<" #'python-indent-shift-left) + (define-key map "\C-c>" #'python-indent-shift-right) ;; Skeletons - (define-key map "\C-c\C-tc" 'python-skeleton-class) - (define-key map "\C-c\C-td" 'python-skeleton-def) - (define-key map "\C-c\C-tf" 'python-skeleton-for) - (define-key map "\C-c\C-ti" 'python-skeleton-if) - (define-key map "\C-c\C-tm" 'python-skeleton-import) - (define-key map "\C-c\C-tt" 'python-skeleton-try) - (define-key map "\C-c\C-tw" 'python-skeleton-while) + (define-key map "\C-c\C-tc" #'python-skeleton-class) + (define-key map "\C-c\C-td" #'python-skeleton-def) + (define-key map "\C-c\C-tf" #'python-skeleton-for) + (define-key map "\C-c\C-ti" #'python-skeleton-if) + (define-key map "\C-c\C-tm" #'python-skeleton-import) + (define-key map "\C-c\C-tt" #'python-skeleton-try) + (define-key map "\C-c\C-tw" #'python-skeleton-while) ;; Shell interaction - (define-key map "\C-c\C-p" 'run-python) - (define-key map "\C-c\C-s" 'python-shell-send-string) - (define-key map "\C-c\C-e" 'python-shell-send-statement) - (define-key map "\C-c\C-r" 'python-shell-send-region) - (define-key map "\C-\M-x" 'python-shell-send-defun) - (define-key map "\C-c\C-c" 'python-shell-send-buffer) - (define-key map "\C-c\C-l" 'python-shell-send-file) - (define-key map "\C-c\C-z" 'python-shell-switch-to-shell) + (define-key map "\C-c\C-p" #'run-python) + (define-key map "\C-c\C-s" #'python-shell-send-string) + (define-key map "\C-c\C-e" #'python-shell-send-statement) + (define-key map "\C-c\C-r" #'python-shell-send-region) + (define-key map "\C-\M-x" #'python-shell-send-defun) + (define-key map "\C-c\C-c" #'python-shell-send-buffer) + (define-key map "\C-c\C-l" #'python-shell-send-file) + (define-key map "\C-c\C-z" #'python-shell-switch-to-shell) ;; Some util commands - (define-key map "\C-c\C-v" 'python-check) - (define-key map "\C-c\C-f" 'python-eldoc-at-point) - (define-key map "\C-c\C-d" 'python-describe-at-point) + (define-key map "\C-c\C-v" #'python-check) + (define-key map "\C-c\C-f" #'python-eldoc-at-point) + (define-key map "\C-c\C-d" #'python-describe-at-point) ;; Utilities - (substitute-key-definition 'complete-symbol 'completion-at-point + (substitute-key-definition #'complete-symbol #'completion-at-point map global-map) (easy-menu-define python-menu map "Python Mode menu" '("Python" @@ -359,9 +358,12 @@ (defmacro python-rx (&rest regexps) "Python mode specialized rx macro. This variant of `rx' supports common Python named REGEXPS." - `(rx-let ((block-start (seq symbol-start + `(rx-let ((sp-bsnl (or space (and ?\\ ?\n))) + (block-start (seq symbol-start (or "def" "class" "if" "elif" "else" "try" "except" "finally" "for" "while" "with" + ;; Python 3.10+ PEP634 + "match" "case" ;; Python 3.5+ PEP492 (and "async" (+ space) (or "def" "for" "with"))) @@ -394,7 +396,7 @@ This variant of `rx' supports common Python named REGEXPS." (open-paren (or "{" "[" "(")) (close-paren (or "}" "]" ")")) (simple-operator (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)) - (not-simple-operator (not simple-operator)) + (not-simple-operator (not (or simple-operator ?\n))) (operator (or "==" ">=" "is" "not" "**" "//" "<<" ">>" "<=" "!=" "+" "-" "/" "&" "^" "~" "|" "*" "<" ">" @@ -538,9 +540,9 @@ the {...} holes that appear within f-strings." (setq ppss (syntax-ppss)))))) (defvar python-font-lock-keywords-level-1 - `((,(rx symbol-start "def" (1+ space) (group (1+ (or word ?_)))) + `((,(python-rx symbol-start "def" (1+ space) (group symbol-name)) (1 font-lock-function-name-face)) - (,(rx symbol-start "class" (1+ space) (group (1+ (or word ?_)))) + (,(python-rx symbol-start "class" (1+ space) (group symbol-name)) (1 font-lock-type-face))) "Font lock keywords to use in `python-mode' for level 1 decoration. @@ -563,6 +565,8 @@ class declarations.") ;; Python 3.5+ PEP492 (and "async" (+ space) (or "def" "for" "with")) "await" + ;; Python 3.10+ + "match" "case" ;; Extra: "self") symbol-end) @@ -601,15 +605,18 @@ builtins.") (defun python-font-lock-assignment-matcher (regexp) "Font lock matcher for assignments based on REGEXP. -Return nil if REGEXP matched within a `paren' context (to avoid, -e.g., default values for arguments or passing arguments by name -being treated as assignments) or is followed by an '=' sign (to -avoid '==' being treated as an assignment." +Search for next occurrence if REGEXP matched within a `paren' +context (to avoid, e.g., default values for arguments or passing +arguments by name being treated as assignments) or is followed by +an '=' sign (to avoid '==' being treated as an assignment. Set +point to the position one character before the end of the +occurrence found so that subsequent searches can detect the '=' +sign in chained assignment." (lambda (limit) - (let ((res (re-search-forward regexp limit t))) - (unless (or (python-syntax-context 'paren) - (equal (char-after (point)) ?=)) - res)))) + (cl-loop while (re-search-forward regexp limit t) + unless (or (python-syntax-context 'paren) + (equal (char-after) ?=)) + return (progn (backward-char) t)))) (defvar python-font-lock-keywords-maximum-decoration `((python--font-lock-f-strings) @@ -671,7 +678,7 @@ avoid '==' being treated as an assignment." ;; and variants thereof ;; the cases ;; (a) = 5 - ;; [a] = 5 + ;; [a] = 5, ;; [*a] = 5, 6 ;; are handled separately below (,(python-font-lock-assignment-matcher @@ -701,10 +708,11 @@ avoid '==' being treated as an assignment." (1 font-lock-variable-name-face)) ;; special cases ;; (a) = 5 - ;; [a] = 5 + ;; [a] = 5, ;; [*a] = 5, 6 (,(python-font-lock-assignment-matcher - (python-rx (or "[" "(") (* space) + (python-rx (or line-start ?\; ?=) (* space) + (or "[" "(") (* space) grouped-assignment-target (* space) (or ")" "]") (* space) assignment-operator)) @@ -825,7 +833,6 @@ It makes underscores and dots word constituent chars.") (defcustom python-indent-offset 4 "Default indentation offset for Python." - :group 'python :type 'integer :safe 'integerp) @@ -835,21 +842,18 @@ It makes underscores and dots word constituent chars.") (defcustom python-indent-guess-indent-offset t "Non-nil tells Python mode to guess `python-indent-offset' value." :type 'boolean - :group 'python :safe 'booleanp) (defcustom python-indent-guess-indent-offset-verbose t "Non-nil means to emit a warning when indentation guessing fails." :version "25.1" :type 'boolean - :group 'python :safe' booleanp) (defcustom python-indent-trigger-commands '(indent-for-tab-command yas-expand yas/expand) "Commands that might trigger a `python-indent-line' call." - :type '(repeat symbol) - :group 'python) + :type '(repeat symbol)) (defcustom python-indent-def-block-scale 2 "Multiplier applied to indentation inside multi-line def blocks." @@ -1298,7 +1302,7 @@ Called from a program, START and END specify the region to indent." ;; Don't mess with strings, unless it's the ;; enclosing set of quotes or a docstring. (or (not (python-syntax-context 'string)) - (eq + (equal (syntax-after (+ (1- (point)) (current-indentation) @@ -1427,8 +1431,15 @@ marks the next defun after the ones already marked." ;;; Navigation +(defcustom python-forward-sexp-function #'python-nav-forward-sexp + "Function to use when navigating between expressions." + :version "28.1" + :type '(choice (const :tag "Python blocks" python-nav-forward-sexp) + (const :tag "CC-mode like" nil) + function)) + (defvar python-nav-beginning-of-defun-regexp - (python-rx line-start (* space) defun (+ space) (group symbol-name)) + (python-rx line-start (* space) defun (+ sp-bsnl) (group symbol-name)) "Regexp matching class or function definition. The name of the defun should be grouped so it can be retrieved via `match-string'.") @@ -1443,26 +1454,34 @@ With positive ARG search backwards, else search forwards." (line-beg-pos (line-beginning-position)) (line-content-start (+ line-beg-pos (current-indentation))) (pos (point-marker)) + (min-indentation (+ (current-indentation) + (if (python-info-looking-at-beginning-of-defun) + python-indent-offset 0))) (body-indentation (and (> arg 0) (save-excursion (while (and - (not (python-info-looking-at-beginning-of-defun)) + (or (not (python-info-looking-at-beginning-of-defun)) + (>= (current-indentation) min-indentation)) + (setq min-indentation + (min min-indentation (current-indentation))) (python-nav-backward-block))) (or (and (python-info-looking-at-beginning-of-defun) (+ (current-indentation) python-indent-offset)) 0)))) (found (progn - (when (and (python-info-looking-at-beginning-of-defun) + (when (and (python-info-looking-at-beginning-of-defun nil t) (or (< arg 0) ;; If looking at beginning of defun, and if ;; pos is > line-content-start, ensure a ;; backward re search match this defun by ;; going to end of line before calling ;; re-search-fn bug#40563 - (and (> arg 0) (> pos line-content-start)))) - (end-of-line 1)) + (and (> arg 0) + (or (python-info-continuation-line-p) + (> pos line-content-start))))) + (python-nav-end-of-statement)) (while (and (funcall re-search-fn python-nav-beginning-of-defun-regexp nil t) @@ -1472,14 +1491,18 @@ With positive ARG search backwards, else search forwards." (and (> arg 0) (not (= (current-indentation) 0)) (>= (current-indentation) body-indentation))))) - (and (python-info-looking-at-beginning-of-defun) + (and (python-info-looking-at-beginning-of-defun nil t) (or (not (= (line-number-at-pos pos) (line-number-at-pos))) (and (>= (point) line-beg-pos) (<= (point) line-content-start) (> pos line-content-start))))))) (if found - (or (beginning-of-line 1) t) + (progn + (when (< arg 0) + (python-nav-beginning-of-statement)) + (beginning-of-line 1) + t) (and (goto-char pos) nil)))) (defun python-nav-beginning-of-defun (&optional arg) @@ -1518,7 +1541,10 @@ Returns nil if point is not in a def or class." (python-util-forward-comment -1) (forward-line 1) ;; Ensure point moves forward. - (and (> beg-pos (point)) (goto-char beg-pos))))) + (and (> beg-pos (point)) (goto-char beg-pos)) + ;; Return non-nil if we did something (because then we were in a + ;; def/class). + (/= beg-pos (point))))) (defun python-nav--syntactically (fn poscompfn &optional contextfn) "Move point using FN avoiding places with specific context. @@ -1615,11 +1641,15 @@ of the statement." (while (and (or noend (goto-char (line-end-position))) (not (eobp)) (cond ((setq string-start (python-syntax-context 'string)) - ;; The assertion can only fail if syntax table + ;; The condition can be nil if syntax table ;; text properties and the `syntax-ppss' cache ;; are somehow out of whack. This has been ;; observed when using `syntax-ppss' during ;; narrowing. + ;; It can also fail in cases where the buffer is in + ;; the process of being modified, e.g. when creating + ;; a string with `electric-pair-mode' disabled such + ;; that there can be an unmatched single quote (when (>= string-start last-string-end) (goto-char string-start) (if (python-syntax-context 'paren) @@ -1702,7 +1732,10 @@ backward to previous statement." (while (and (forward-line 1) (not (eobp)) (or (and (> (current-indentation) block-indentation) - (or (python-nav-end-of-statement) t)) + (let ((start (point))) + (python-nav-end-of-statement) + ;; must move forward otherwise infinite loop + (> (point) start))) (python-info-current-line-comment-p) (python-info-current-line-empty-p)))) (python-util-forward-comment -1) @@ -2018,7 +2051,6 @@ position, else returns nil." (defcustom python-shell-buffer-name "Python" "Default buffer name for Python interpreter." :type 'string - :group 'python :safe 'stringp) (defcustom python-shell-interpreter @@ -2032,19 +2064,16 @@ Some Python interpreters also require changes to `python-shell-interpreter' to \"ipython3\" requires setting `python-shell-interpreter-args' to \"--simple-prompt\"." :version "28.1" - :type 'string - :group 'python) + :type 'string) (defcustom python-shell-internal-buffer-name "Python Internal" "Default buffer name for the Internal Python interpreter." :type 'string - :group 'python :safe 'stringp) (defcustom python-shell-interpreter-args "-i" "Default arguments for the Python interpreter." - :type 'string - :group 'python) + :type 'string) (defcustom python-shell-interpreter-interactive-arg "-i" "Interpreter argument to force it to run interactively." @@ -2109,7 +2138,6 @@ It should not contain a caret (^) at the beginning." "Should syntax highlighting be enabled in the Python shell buffer? Restart the Python shell after changing this variable for it to take effect." :type 'boolean - :group 'python :safe 'booleanp) (defcustom python-shell-unbuffered t @@ -2117,7 +2145,6 @@ Restart the Python shell after changing this variable for it to take effect." When non-nil, this may prevent delayed and missing output in the Python shell. See commentary for details." :type 'boolean - :group 'python :safe 'booleanp) (defcustom python-shell-process-environment nil @@ -2127,8 +2154,7 @@ When this variable is non-nil, values are exported into the process environment before starting it. Any variables already present in the current environment are superseded by variables set here." - :type '(repeat string) - :group 'python) + :type '(repeat string)) (defcustom python-shell-extra-pythonpaths nil "List of extra pythonpaths for Python shell. @@ -2137,8 +2163,7 @@ the PYTHONPATH before starting processes. Any values present here that already exists in PYTHONPATH are moved to the beginning of the list so that they are prioritized when looking for modules." - :type '(repeat string) - :group 'python) + :type '(repeat string)) (defcustom python-shell-exec-path nil "List of paths for searching executables. @@ -2146,8 +2171,7 @@ When this variable is non-nil, values added at the beginning of the PATH before starting processes. Any values present here that already exists in PATH are moved to the beginning of the list so that they are prioritized when looking for executables." - :type '(repeat string) - :group 'python) + :type '(repeat string)) (defcustom python-shell-remote-exec-path nil "List of paths to be ensured remotely for searching executables. @@ -2158,8 +2182,7 @@ here. Normally you won't use this variable directly unless you plan to ensure a particular set of paths to all Python shell executed through tramp connections." :version "25.1" - :type '(repeat string) - :group 'python) + :type '(repeat string)) (define-obsolete-variable-alias 'python-shell-virtualenv-path 'python-shell-virtualenv-root "25.1") @@ -2169,13 +2192,11 @@ executed through tramp connections." This variable, when set to a string, makes the environment to be modified such that shells are started within the specified virtualenv." - :type '(choice (const nil) directory) - :group 'python) + :type '(choice (const nil) directory)) (defcustom python-shell-setup-codes nil "List of code run by `python-shell-send-setup-code'." - :type '(repeat symbol) - :group 'python) + :type '(repeat symbol)) (defcustom python-shell-compilation-regexp-alist `((,(rx line-start (1+ (any " \t")) "File \"" @@ -2189,8 +2210,7 @@ virtualenv." "(" (group (1+ digit)) ")" (1+ (not (any "("))) "()") 1 2)) "`compilation-error-regexp-alist' for inferior Python." - :type '(alist regexp) - :group 'python) + :type '(alist regexp)) (defvar python-shell-output-filter-in-progress nil) (defvar python-shell-output-filter-buffer nil) @@ -2208,33 +2228,34 @@ virtualenv." (or (getenv "PYTHONPATH") "") path-separator 'omit))) (python-shell--add-to-path-with-priority pythonpath python-shell-extra-pythonpaths) - (mapconcat 'identity pythonpath path-separator))) + (mapconcat #'identity pythonpath path-separator))) (defun python-shell-calculate-process-environment () - "Calculate `process-environment' or `tramp-remote-process-environment'. + (declare (obsolete python-shell--calculate-process-environment "29.1")) + (defvar tramp-remote-process-environment) + (let* ((remote-p (file-remote-p default-directory))) + (append (python-shell--calculate-process-environment) + (if remote-p + tramp-remote-process-environment + process-environment)))) + +(defun python-shell--calculate-process-environment () + "Return a list of entries to add to the `process-environment'. Prepends `python-shell-process-environment', sets extra pythonpaths from `python-shell-extra-pythonpaths' and sets a few -virtualenv related vars. If `default-directory' points to a -remote host, the returned value is intended for -`tramp-remote-process-environment'." - (let* ((remote-p (file-remote-p default-directory)) - (process-environment (if remote-p - tramp-remote-process-environment - process-environment)) - (virtualenv (when python-shell-virtualenv-root - (directory-file-name python-shell-virtualenv-root)))) - (dolist (env python-shell-process-environment) - (pcase-let ((`(,key ,value) (split-string env "="))) - (setenv key value))) +virtualenv related vars." + (let* ((virtualenv (when python-shell-virtualenv-root + (directory-file-name python-shell-virtualenv-root))) + (res python-shell-process-environment)) (when python-shell-unbuffered - (setenv "PYTHONUNBUFFERED" "1")) + (push "PYTHONUNBUFFERED=1" res)) (when python-shell-extra-pythonpaths - (setenv "PYTHONPATH" (python-shell-calculate-pythonpath))) + (push (concat "PYTHONPATH=" (python-shell-calculate-pythonpath)) res)) (if (not virtualenv) - process-environment - (setenv "PYTHONHOME" nil) - (setenv "VIRTUAL_ENV" virtualenv)) - process-environment)) + nil + (push "PYTHONHOME" res) + (push (concat "VIRTUAL_ENV=" virtualenv) res)) + res)) (defun python-shell-calculate-exec-path () "Calculate `exec-path'. @@ -2262,14 +2283,26 @@ of `exec-path'." (defun python-shell-tramp-refresh-remote-path (vec paths) "Update VEC's remote-path giving PATHS priority." + (cl-assert (featurep 'tramp)) + (declare-function tramp-set-remote-path "tramp-sh") + (declare-function tramp-set-connection-property "tramp-cache") + (declare-function tramp-get-connection-property "tramp-cache") (let ((remote-path (tramp-get-connection-property vec "remote-path" nil))) (when remote-path + ;; FIXME: This part of the Tramp code still knows about Python! (python-shell--add-to-path-with-priority remote-path paths) (tramp-set-connection-property vec "remote-path" remote-path) (tramp-set-remote-path vec)))) + (defun python-shell-tramp-refresh-process-environment (vec env) "Update VEC's process environment with ENV." + (cl-assert (featurep 'tramp)) + (defvar tramp-end-of-heredoc) + (defvar tramp-end-of-output) + ;; Do we even know that `tramp-sh' is loaded at this point? + ;; What about files accessed via FTP, sudo, ...? + (declare-function tramp-send-command "tramp-sh") ;; Stolen from `tramp-open-connection-setup-interactive-shell'. (let ((env (append (when (fboundp 'tramp-get-remote-locale) ;; Emacs<24.4 compat. @@ -2282,7 +2315,7 @@ of `exec-path'." unset vars item) (while env (setq item (split-string (car env) "=" 'omit)) - (setcdr item (mapconcat 'identity (cdr item) "=")) + (setcdr item (mapconcat #'identity (cdr item) "=")) (if (and (stringp (cdr item)) (not (string-equal (cdr item) ""))) (push (format "%s %s" (car item) (cdr item)) vars) (push (car item) unset)) @@ -2292,12 +2325,12 @@ of `exec-path'." vec (format "while read var val; do export $var=$val; done <<'%s'\n%s\n%s" tramp-end-of-heredoc - (mapconcat 'identity vars "\n") + (mapconcat #'identity vars "\n") tramp-end-of-heredoc) t)) (when unset (tramp-send-command - vec (format "unset %s" (mapconcat 'identity unset " ")) t)))) + vec (format "unset %s" (mapconcat #'identity unset " ")) t)))) (defmacro python-shell-with-environment (&rest body) "Modify shell environment during execution of BODY. @@ -2306,41 +2339,49 @@ execution of body. If `default-directory' points to a remote machine then modifies `tramp-remote-process-environment' and `python-shell-remote-exec-path' instead." (declare (indent 0) (debug (body))) - (let ((vec (make-symbol "vec"))) - `(progn - (let* ((,vec - (when (file-remote-p default-directory) - (ignore-errors - (tramp-dissect-file-name default-directory 'noexpand)))) - (process-environment - (if ,vec - process-environment - (python-shell-calculate-process-environment))) - (exec-path - (if ,vec - exec-path - (python-shell-calculate-exec-path))) - (tramp-remote-process-environment - (if ,vec - (python-shell-calculate-process-environment) - tramp-remote-process-environment))) - (when (tramp-get-connection-process ,vec) - ;; For already existing connections, the new exec path must - ;; be re-set, otherwise it won't take effect. One example - ;; of such case is when remote dir-locals are read and - ;; *then* subprocesses are triggered within the same - ;; connection. - (python-shell-tramp-refresh-remote-path - ,vec (python-shell-calculate-exec-path)) - ;; The `tramp-remote-process-environment' variable is only - ;; effective when the started process is an interactive - ;; shell, otherwise (like in the case of processes started - ;; with `process-file') the environment is not changed. - ;; This makes environment modifications effective - ;; unconditionally. - (python-shell-tramp-refresh-process-environment - ,vec tramp-remote-process-environment)) - ,(macroexp-progn body))))) + `(python-shell--with-environment + (python-shell--calculate-process-environment) + (lambda () ,@body))) + +(defun python-shell--with-environment (extraenv bodyfun) + ;; FIXME: This is where the generic code delegates to Tramp. + (let* ((vec + (and (file-remote-p default-directory) + (fboundp 'tramp-dissect-file-name) + (ignore-errors + (tramp-dissect-file-name default-directory 'noexpand))))) + (if vec + (python-shell--tramp-with-environment vec extraenv bodyfun) + (let ((process-environment + (append extraenv process-environment)) + (exec-path + ;; FIXME: This is still Python-specific. + (python-shell-calculate-exec-path))) + (funcall bodyfun))))) + +(defun python-shell--tramp-with-environment (vec extraenv bodyfun) + (defvar tramp-remote-process-environment) + (declare-function tramp-get-connection-process "tramp" (vec)) + (let* ((tramp-remote-process-environment + (append extraenv tramp-remote-process-environment))) + (when (tramp-get-connection-process vec) + ;; For already existing connections, the new exec path must + ;; be re-set, otherwise it won't take effect. One example + ;; of such case is when remote dir-locals are read and + ;; *then* subprocesses are triggered within the same + ;; connection. + (python-shell-tramp-refresh-remote-path + ;; FIXME: This is still Python-specific. + vec (python-shell-calculate-exec-path)) + ;; The `tramp-remote-process-environment' variable is only + ;; effective when the started process is an interactive + ;; shell, otherwise (like in the case of processes started + ;; with `process-file') the environment is not changed. + ;; This makes environment modifications effective + ;; unconditionally. + (python-shell-tramp-refresh-process-environment + vec tramp-remote-process-environment)) + (funcall bodyfun))) (defvar python-shell--prompt-calculated-input-regexp nil "Calculated input prompt regexp for inferior python shell. @@ -2623,12 +2664,13 @@ banner and the initial prompt are received separately." (define-obsolete-function-alias 'python-comint-output-filter-function - 'ansi-color-filter-apply + #'ansi-color-filter-apply "25.1") (defun python-comint-postoutput-scroll-to-bottom (output) "Faster version of `comint-postoutput-scroll-to-bottom'. Avoids `recenter' calls until OUTPUT is completely sent." + (declare (obsolete nil "29.1")) ; Not used. (when (and (not (string= "" output)) (python-shell-comint-end-of-output-p (ansi-color-filter-apply output))) @@ -2721,20 +2763,12 @@ goes wrong and syntax highlighting in the shell gets messed up." (deactivate-mark nil) (start-pos prompt-end) (buffer-undo-list t) - (font-lock-buffer-pos nil) (replacement (python-shell-font-lock-with-font-lock-buffer - (delete-region (line-beginning-position) - (point-max)) - (setq font-lock-buffer-pos (point)) + (delete-region (point-min) (point-max)) (insert input) - ;; Ensure buffer is fontified, keeping it - ;; compatible with Emacs < 24.4. - (if (fboundp 'font-lock-ensure) - (funcall 'font-lock-ensure) - (font-lock-default-fontify-buffer)) - (buffer-substring font-lock-buffer-pos - (point-max)))) + (font-lock-ensure) + (buffer-string))) (replacement-length (length replacement)) (i 0)) ;; Inject text properties to get input fontified. @@ -2816,8 +2850,7 @@ current process to not hang while waiting. This is useful to safely attach setup code for long-running processes that eventually provide a shell." :version "25.1" - :type 'hook - :group 'python) + :type 'hook) (defconst python-shell-eval-setup-code "\ @@ -2943,15 +2976,15 @@ variable. (setq-local comint-output-filter-functions '(ansi-color-process-output python-shell-comint-watch-for-first-prompt-output-filter - python-comint-postoutput-scroll-to-bottom comint-watch-for-password-prompt)) (setq-local comint-highlight-input nil) (setq-local compilation-error-regexp-alist python-shell-compilation-regexp-alist) + (setq-local scroll-conservatively 1) (add-hook 'completion-at-point-functions #'python-shell-completion-at-point nil 'local) (define-key inferior-python-mode-map "\t" - 'python-shell-completion-complete-or-indent) + #'python-shell-completion-complete-or-indent) (make-local-variable 'python-shell-internal-last-output) (when python-shell-font-lock-enable (python-shell-font-lock-turn-on)) @@ -2977,7 +3010,8 @@ killed." (let* ((cmdlist (split-string-and-unquote cmd)) (interpreter (car cmdlist)) (args (cdr cmdlist)) - (buffer (apply #'make-comint-in-buffer proc-name proc-buffer-name + (buffer (apply #'make-comint-in-buffer proc-name + proc-buffer-name interpreter nil args)) (python-shell--parent-buffer (current-buffer)) (process (get-buffer-process buffer)) @@ -3075,7 +3109,8 @@ of `error' with a user-friendly message." (or (python-shell-get-process) (if interactivep (user-error - "Start a Python process first with `M-x run-python' or `%s'" + (substitute-command-keys + "Start a Python process first with \\`M-x run-python' or `%s'") ;; Get the binding. (key-description (where-is-internal @@ -3126,7 +3161,7 @@ there for compatibility with CEDET.") (run-python-internal)))) (define-obsolete-function-alias - 'python-proc 'python-shell-internal-get-or-create-process "24.3") + 'python-proc #'python-shell-internal-get-or-create-process "24.3") (defun python-shell--save-temp-file (string) (let* ((temporary-file-directory @@ -3211,11 +3246,13 @@ detecting a prompt at the end of the buffer." (defun python-shell-send-string-no-output (string &optional process) "Send STRING to PROCESS and inhibit output. Return the output." - (let ((process (or process (python-shell-get-process-or-error))) - (comint-preoutput-filter-functions - '(python-shell-output-filter)) - (python-shell-output-filter-in-progress t) - (inhibit-quit t)) + (or process (setq process (python-shell-get-process-or-error))) + (cl-letf (((process-filter process) + (lambda (_proc str) + (with-current-buffer (process-buffer process) + (python-shell-output-filter str)))) + (python-shell-output-filter-in-progress t) + (inhibit-quit t)) (or (with-local-quit (python-shell-send-string string process) @@ -3243,10 +3280,10 @@ Returns the output. See `python-shell-send-string-no-output'." (python-shell-internal-get-or-create-process)))) (define-obsolete-function-alias - 'python-send-receive 'python-shell-internal-send-string "24.3") + 'python-send-receive #'python-shell-internal-send-string "24.3") (define-obsolete-function-alias - 'python-send-string 'python-shell-internal-send-string "24.3") + 'python-send-string #'python-shell-internal-send-string "24.3") (defun python-shell-buffer-substring (start end &optional nomain no-cookie) "Send buffer substring from START to END formatted for shell. @@ -3281,22 +3318,25 @@ the python shell: (goto-char start) (python-util-forward-comment 1) (current-indentation)))) - (fillstr (and (not no-cookie) - (not starts-at-point-min-p) - (concat - (format "# -*- coding: %s -*-\n" encoding) - (make-string - ;; Subtract 2 because of the coding cookie. - (- (line-number-at-pos start) 2) ?\n))))) + (fillstr (cond (starts-at-point-min-p + nil) + ((not no-cookie) + (concat + (format "# -*- coding: %s -*-\n" encoding) + (make-string + ;; Subtract 2 because of the coding cookie. + (- (line-number-at-pos start) 2) ?\n))) + (t + (make-string (- (line-number-at-pos start) 1) ?\n))))) (with-temp-buffer (python-mode) (when fillstr (insert fillstr)) - (insert substring) - (goto-char (point-min)) (when (not toplevel-p) - (insert "if True:") + (forward-line -1) + (insert "if True:\n") (delete-region (point) (line-end-position))) + (insert substring) (when nomain (let* ((if-name-main-start-end (and nomain @@ -3542,8 +3582,7 @@ def __PYTHON_EL_get_completions(text): completer.print_mode = True return completions" "Code used to setup completion in inferior Python processes." - :type 'string - :group 'python) + :type 'string) (define-obsolete-variable-alias 'python-shell-completion-module-string-code @@ -3760,7 +3799,8 @@ With argument MSG show activation/deactivation message." (format "was t and %S is not part of the " (file-name-nondirectory python-shell-interpreter)) "`python-shell-completion-native-disabled-interpreters' " - "list. Native completions have been disabled locally. ")) + "list. Native completions have been disabled locally. " + "Consider installing the python package \"readline\". ")) (python-shell-completion-native-turn-off msg)))))) (defun python-shell-completion-native-turn-on-maybe-with-msg () @@ -3807,7 +3847,7 @@ With argument MSG show activation/deactivation message." (comint-redirect-perform-sanity-check nil) (comint-redirect-insert-matching-regexp t) (comint-redirect-finished-regexp - "1__dummy_completion__[[:space:]]*\n") + "1__dummy_completion__.*\n") (comint-redirect-output-buffer redirect-buffer)) ;; Compatibility with Emacs 24.x. Comint changed and ;; now `comint-redirect-filter' gets 3 args. This @@ -3815,7 +3855,8 @@ With argument MSG show activation/deactivation message." ;; in use based on its args and uses `apply-partially' ;; to make it up for the 3 args case. (if (= (length - (help-function-arglist 'comint-redirect-filter)) 3) + (help-function-arglist 'comint-redirect-filter)) + 3) (set-process-filter process (apply-partially #'comint-redirect-filter original-filter-fn)) @@ -3924,7 +3965,7 @@ using that one instead of current buffer's process." (define-obsolete-function-alias 'python-shell-completion-complete-at-point - 'python-shell-completion-at-point + #'python-shell-completion-at-point "25.1") (defun python-shell-completion-complete-or-indent () @@ -3953,7 +3994,6 @@ considered over. The overlay arrow will be removed from the currently tracked buffer. Additionally, if `python-pdbtrack-kill-buffers' is non-nil, all files opened by pdbtracking will be killed." :type 'boolean - :group 'python :safe 'booleanp) (defcustom python-pdbtrack-stacktrace-info-regexp @@ -4162,7 +4202,7 @@ inferior Python process is updated properly." (define-obsolete-function-alias 'python-completion-complete-at-point - 'python-completion-at-point + #'python-completion-at-point "25.1") @@ -4172,29 +4212,25 @@ inferior Python process is updated properly." "Function to fill comments. This is the function used by `python-fill-paragraph' to fill comments." - :type 'symbol - :group 'python) + :type 'symbol) (defcustom python-fill-string-function 'python-fill-string "Function to fill strings. This is the function used by `python-fill-paragraph' to fill strings." - :type 'symbol - :group 'python) + :type 'symbol) (defcustom python-fill-decorator-function 'python-fill-decorator "Function to fill decorators. This is the function used by `python-fill-paragraph' to fill decorators." - :type 'symbol - :group 'python) + :type 'symbol) (defcustom python-fill-paren-function 'python-fill-paren "Function to fill parens. This is the function used by `python-fill-paragraph' to fill parens." - :type 'symbol - :group 'python) + :type 'symbol) (defcustom python-fill-docstring-style 'pep-257 "Style used to fill docstrings. @@ -4264,7 +4300,6 @@ value may result in one of the following docstring styles: (const :tag "PEP-257 with 2 newlines at end of string." pep-257) (const :tag "PEP-257 with 1 newline at end of string." pep-257-nn) (const :tag "Symmetric style." symmetric)) - :group 'python :safe (lambda (val) (memq val '(django onetwo pep-257 pep-257-nn symmetric nil)))) @@ -4423,7 +4458,6 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'." This happens when pressing \"if<SPACE>\", for example, to prompt for the if condition." :type 'boolean - :group 'python :safe 'booleanp) (defvar python-skeleton-available '() @@ -4548,7 +4582,7 @@ The skeleton will be bound to python-skeleton-NAME." (defun python-skeleton-add-menu-items () "Add menu items to Python->Skeletons menu." - (let ((skeletons (sort python-skeleton-available 'string<))) + (let ((skeletons (sort python-skeleton-available #'string<))) (dolist (skeleton skeletons) (easy-menu-add-item nil '("Python" "Skeletons") @@ -4578,8 +4612,7 @@ def __FFAP_get_module_path(objstr): except: return ''" "Python code to get a module path." - :type 'string - :group 'python) + :type 'string) (defun python-ffap-module-path (module) "Function for `ffap-alist' to return path for MODULE." @@ -4607,14 +4640,12 @@ def __FFAP_get_module_path(objstr): (executable-find "epylint") "install pyflakes, pylint or something else") "Command used to check a Python file." - :type 'string - :group 'python) + :type 'string) (defcustom python-check-buffer-name "*Python check: %s*" "Buffer name used for check commands." - :type 'string - :group 'python) + :type 'string) (defvar python-check-custom-command nil "Internal use.") @@ -4667,7 +4698,10 @@ See `python-check-command' for the default." target = obj objtype = 'def' if target: - args = inspect.formatargspec(*argspec_function(target)) + if hasattr(inspect, 'signature'): + args = str(inspect.signature(target)) + else: + args = inspect.formatargspec(*argspec_function(target)) name = obj.__name__ doc = '{objtype} {name}{args}'.format( objtype=objtype, name=name, args=args @@ -4678,8 +4712,7 @@ See `python-check-command' for the default." doc = '' return doc" "Python code to setup documentation retrieval." - :type 'string - :group 'python) + :type 'string) (defun python-eldoc--get-symbol-at-point () "Get the current symbol for eldoc. @@ -4726,14 +4759,13 @@ Set to nil by `python-eldoc-function' if (defcustom python-eldoc-function-timeout 1 "Timeout for `python-eldoc-function' in seconds." - :group 'python :type 'integer :version "25.1") (defcustom python-eldoc-function-timeout-permanent t - "Non-nil means that when `python-eldoc-function' times out -`python-eldoc-get-doc' will be set to nil." - :group 'python + "If non-nil, a timeout in Python-Eldoc will disable it permanently. +Python-Eldoc can be re-enabled manually by setting `python-eldoc-get-doc' +back to t in the affected buffer." :type 'boolean :version "25.1") @@ -4766,10 +4798,14 @@ Interactively, prompt for symbol." (interactive (let ((symbol (python-eldoc--get-symbol-at-point)) (enable-recursive-minibuffers t)) - (list (read-string (if symbol - (format "Describe symbol (default %s): " symbol) - "Describe symbol: ") - nil nil symbol)))) + (list (read-string + ;; `format-prompt' is new in Emacs 28.1. + (if (fboundp 'format-prompt) + (format-prompt "Describe symbol" symbol) + (if symbol + (format "Describe symbol (default %s): " symbol) + "Describe symbol: ")) + nil nil symbol)))) (message (python-eldoc--get-doc-at-point symbol))) (defun python-describe-at-point (symbol process) @@ -4921,7 +4957,7 @@ To this: (\"decorator.wrapped_f\" . 393))" ;; Inspired by imenu--flatten-index-alist removed in revno 21853. (apply - 'nconc + #'nconc (mapcar (lambda (item) (let ((name (if prefix @@ -5004,7 +5040,7 @@ since it returns nil if point is not inside a defun." (and (= (current-indentation) 0) (throw 'exit t)))) (and names (concat (and type (format "%s " type)) - (mapconcat 'identity names "."))))))) + (mapconcat #'identity names "."))))))) (defun python-info-current-symbol (&optional replace-self) "Return current symbol using dotty syntax. @@ -5025,9 +5061,10 @@ parent defun name." (replace-regexp-in-string (python-rx line-start word-start "self" word-end ?.) (concat - (mapconcat 'identity + (mapconcat #'identity (butlast (split-string current-defun "\\.")) - ".") ".") + ".") + ".") name))))))) (defun python-info-statement-starts-block-p () @@ -5069,7 +5106,7 @@ parent defun name." (define-obsolete-function-alias 'python-info-closing-block - 'python-info-dedenter-opening-block-position "24.4") + #'python-info-dedenter-opening-block-position "24.4") (defun python-info-dedenter-opening-block-position () "Return the point of the closest block the current line closes. @@ -5114,7 +5151,8 @@ likely an invalid python file." (let ((indentation (current-indentation))) (when (and (not (memq indentation collected-indentations)) (or (not collected-indentations) - (< indentation (apply #'min collected-indentations))) + (< indentation + (apply #'min collected-indentations))) ;; There must be no line with indentation ;; smaller than `indentation' (except for ;; blank lines) between the found opening @@ -5142,7 +5180,7 @@ likely an invalid python file." (define-obsolete-function-alias 'python-info-closing-block-message - 'python-info-dedenter-opening-block-message "24.4") + #'python-info-dedenter-opening-block-message "24.4") (defun python-info-dedenter-opening-block-message () "Message the first line of the block the current statement closes." @@ -5267,10 +5305,15 @@ operator." (forward-line -1) (python-info-assignment-statement-p t)))) -(defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss) - "Check if point is at `beginning-of-defun' using SYNTAX-PPSS." +(defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss + check-statement) + "Check if point is at `beginning-of-defun' using SYNTAX-PPSS. +When CHECK-STATEMENT is non-nil, the current statement is checked +instead of the current physical line." (and (not (python-syntax-context-type (or syntax-ppss (syntax-ppss)))) (save-excursion + (when check-statement + (python-nav-beginning-of-statement)) (beginning-of-line 1) (looking-at python-nav-beginning-of-defun-regexp)))) @@ -5444,10 +5487,12 @@ allowed files." (let ((dir-name (file-name-as-directory dir))) (apply #'nconc (mapcar (lambda (file-name) - (let ((full-file-name (expand-file-name file-name dir-name))) + (let ((full-file-name + (expand-file-name file-name dir-name))) (when (and (not (member file-name '("." ".."))) - (funcall (or predicate #'identity) full-file-name)) + (funcall (or predicate #'identity) + full-file-name)) (list full-file-name)))) (directory-files dir-name))))) @@ -5515,7 +5560,6 @@ required arguments. Once launched it will receive the Python source to be checked as its standard input. To use `flake8' you would set this to (\"flake8\" \"-\")." :version "26.1" - :group 'python-flymake :type '(repeat string)) ;; The default regexp accommodates for older pyflakes, which did not @@ -5537,7 +5581,6 @@ If COLUMN or TYPE are nil or that index didn't match, that information is not present on the matched line and a default will be used." :version "26.1" - :group 'python-flymake :type '(list regexp (integer :tag "Line's index") (choice @@ -5562,19 +5605,9 @@ configuration could be: By default messages are considered errors." :version "26.1" - :group 'python-flymake :type '(alist :key-type (regexp) :value-type (symbol))) -(defcustom python-forward-sexp-function #'python-nav-forward-sexp - "Function to use when navigating between expressions." - :version "28.1" - :group 'python - :group 'python-flymake - :type '(choice (const :tag "Python blocks" python-nav-forward-sexp) - (const :tag "CC-mode like" nil) - function)) - (defvar-local python--flymake-proc nil) (defun python--flymake-parse-output (source proc report-fn) diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index 72631a6557f..87bb92908d1 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -70,7 +70,7 @@ "Regexp to match modifiers.") (defconst ruby-block-mid-keywords - '("then" "else" "elsif" "when" "rescue" "ensure") + '("then" "else" "elsif" "when" "in" "rescue" "ensure") "Keywords where the indentation gets shallower in middle of block statements.") (defconst ruby-block-mid-re @@ -325,6 +325,13 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." "Use `ruby-encoding-map' to set encoding magic comment if this is non-nil." :type 'boolean :group 'ruby) +(defcustom ruby-toggle-block-space-before-parameters t + "When non-nil, ensure space between the \"toggled\" curly and parameters. +This only affects the output of the command `ruby-toggle-block'." + :type 'boolean + :safe 'booleanp + :version "29.1") + ;;; SMIE support (require 'smie) @@ -362,7 +369,9 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (for-body (for-head ";" insts)) (for-head (id "in" exp)) (cases (exp "then" insts) - (cases "when" cases) (insts "else" insts)) + (cases "when" cases) + (cases "in" cases) + (insts "else" insts)) (expseq (exp) );;(expseq "," expseq) (hashvals (exp1 "=>" exp1) (hashvals "," hashvals)) (insts-rescue-insts (insts) @@ -373,7 +382,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (if-body (ielsei) (if-body "elsif" if-body))) '((nonassoc "in") (assoc ";") (right " @ ") (assoc ",") (right "=")) - '((assoc "when")) + '((assoc "when" "in")) '((assoc "elsif")) '((assoc "rescue" "ensure")) '((assoc ","))) @@ -499,7 +508,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." ((member tok '("unless" "if" "while" "until")) (if (save-excursion (forward-word-strictly -1) (ruby-smie--bosp)) tok "iuwu-mod")) - ((string-match-p "\\`|[*&]?\\'" tok) + ((string-match-p "\\`|[*&]*\\'" tok) (forward-char (- 1 (length tok))) (setq tok "|") (cond @@ -552,7 +561,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." ((ruby-smie--closing-pipe-p) "closing-|") (t tok))) ((string-match-p "\\`[^|]+|\\'" tok) "closing-|") - ((string-match-p "\\`|[*&]\\'" tok) + ((string-match-p "\\`|[*&]*\\'" tok) (forward-char 1) (substring tok 1)) ((and (equal tok "") (eq ?\\ (char-before)) (looking-at "\n")) @@ -588,7 +597,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (cond ((smie-rule-parent-p "def" "begin" "do" "class" "module" "for" "while" "until" "unless" - "if" "then" "elsif" "else" "when" + "if" "then" "elsif" "else" "when" "in" "rescue" "ensure" "{") (smie-rule-parent ruby-indent-level)) ;; For (invalid) code between switch and case. @@ -652,7 +661,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." ruby-indent-level)))) (`(:before . ,(or "else" "then" "elsif" "rescue" "ensure")) (smie-rule-parent)) - ('(:before . "when") + (`(:before . ,(or "when" "in")) ;; Align to the previous `when', but look up the virtual ;; indentation of `case'. (if (smie-rule-sibling-p) 0 (smie-rule-parent))) @@ -1722,13 +1731,14 @@ See `add-log-current-defun-function'." (insert "}") (goto-char orig) (delete-char 2) - ;; Maybe this should be customizable, let's see if anyone asks. - (insert "{ ") - (setq beg-marker (point-marker)) - (when (looking-at "\\s +|") - (delete-char (- (match-end 0) (match-beginning 0) 1)) - (forward-char) - (re-search-forward "|" (line-end-position) t)) + (insert "{") + (if (looking-at "\\s +|") + (progn + (just-one-space (if ruby-toggle-block-space-before-parameters 1 0)) + (setq beg-marker (point-marker)) + (forward-char) + (re-search-forward "|" (line-end-position) t)) + (setq beg-marker (point-marker))) (save-excursion (skip-chars-forward " \t\n\r") (setq beg-pos (point)) @@ -2447,6 +2457,13 @@ If there is no Rubocop config file, Rubocop will be passed a flag (setq-local beginning-of-defun-function #'ruby-beginning-of-defun) (setq-local end-of-defun-function #'ruby-end-of-defun) + ;; `outline-regexp' contains the first part of `ruby-indent-beg-re' + (setq-local outline-regexp (concat "^\\s *" + (regexp-opt '("class" "module" "def")) + "\\_>")) + (setq-local outline-level (lambda () (1+ (/ (current-indentation) + ruby-indent-level)))) + (add-hook 'after-save-hook #'ruby-mode-set-encoding nil 'local) (add-hook 'electric-indent-functions #'ruby--electric-indent-p nil 'local) (add-hook 'flymake-diagnostic-functions #'ruby-flymake-auto nil 'local) diff --git a/lisp/progmodes/scheme.el b/lisp/progmodes/scheme.el index a2689f17705..e0453c3b2f4 100644 --- a/lisp/progmodes/scheme.el +++ b/lisp/progmodes/scheme.el @@ -1,7 +1,6 @@ ;;; scheme.el --- Scheme (and DSSSL) editing mode -*- lexical-binding: t; -*- -;; Copyright (C) 1986-1988, 1997-1998, 2001-2022 Free Software -;; Foundation, Inc. +;; Copyright (C) 1986-2022 Free Software Foundation, Inc. ;; Author: Bill Rozas <jinx@martigny.ai.mit.edu> ;; Adapted-by: Dave Love <d.love@dl.ac.uk> @@ -115,12 +114,53 @@ (define-abbrev-table 'scheme-mode-abbrev-table ()) (defvar scheme-imenu-generic-expression - '((nil - "^(define\\(?:-\\(?:generic\\(?:-procedure\\)?\\|method\\)\\)?\\s-+(?\\(\\sw+\\)" 1) - ("Types" - "^(define-class\\s-+(?\\(\\sw+\\)" 1) - ("Macros" - "^(\\(defmacro\\|define-macro\\|define-syntax\\)\\s-+(?\\(\\sw+\\)" 2)) + `((nil + ,(rx bol "(define" + (zero-or-one "*") + (zero-or-one "-public") + (one-or-more space) + (zero-or-one "(") + (group (one-or-more (or word (syntax symbol))))) + 1) + ("Methods" + ,(rx bol "(define-" + (or "generic" "method" "accessor") + (one-or-more space) + (zero-or-one "(") + (group (one-or-more (or word (syntax symbol))))) + 1) + ("Classes" + ,(rx bol "(define-class" + (one-or-more space) + (zero-or-one "(") + (group (one-or-more (or word (syntax symbol))))) + 1) + ("Records" + ,(rx bol "(define-record-type" + (zero-or-one "*") + (one-or-more space) + (group (one-or-more (or word (syntax symbol))))) + 1) + ("Conditions" + ,(rx bol "(define-condition-type" + (one-or-more space) + (group (one-or-more (or word (syntax symbol))))) + 1) + ("Modules" + ,(rx bol "(define-module" + (one-or-more space) + (group "(" (one-or-more any) ")")) + 1) + ("Macros" + ,(rx bol "(" + (or (and "defmacro" + (zero-or-one "*") + (zero-or-one "-public")) + "define-macro" "define-syntax" "define-syntax-rule") + (one-or-more space) + (zero-or-one "(") + (group (one-or-more (or word (syntax symbol))))) + 1)) "Imenu generic expression for Scheme mode. See `imenu-generic-expression'.") (defun scheme-mode-variables () @@ -143,7 +183,6 @@ (setq-local comment-start-skip ";+[ \t]*") (setq-local comment-use-syntax t) (setq-local comment-column 40) - (setq-local parse-sexp-ignore-comments t) (setq-local lisp-indent-function 'scheme-indent-function) (setq mode-line-process '("" scheme-mode-line-process)) (setq-local imenu-case-fold-search t) @@ -161,12 +200,10 @@ (defvar scheme-mode-line-process "") -(defvar scheme-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map lisp-mode-shared-map) - map) - "Keymap for Scheme mode. -All commands in `lisp-mode-shared-map' are inherited by this map.") +(defvar-keymap scheme-mode-map + :doc "Keymap for Scheme mode. +All commands in `lisp-mode-shared-map' are inherited by this map." + :parent lisp-mode-shared-map) (easy-menu-define scheme-mode-menu scheme-mode-map "Menu for Scheme mode." @@ -351,12 +388,18 @@ See `run-hooks'." st)) (put 'lambda 'scheme-doc-string-elt 2) +(put 'lambda* 'scheme-doc-string-elt 2) ;; Docstring's pos in a `define' depends on whether it's a var or fun def. (put 'define 'scheme-doc-string-elt (lambda () ;; The function is called with point right after "define". (forward-comment (point-max)) (if (eq (char-after) ?\() 2 0))) +(put 'define* 'scheme-doc-string-elt 2) +(put 'case-lambda 'scheme-doc-string-elt 1) +(put 'case-lambda* 'scheme-doc-string-elt 1) +(put 'define-syntax-rule 'scheme-doc-string-elt 2) +(put 'syntax-rules 'scheme-doc-string-elt 2) (defun scheme-syntax-propertize (beg end) (goto-char beg) @@ -522,10 +565,20 @@ indentation." (lisp-indent-specform 2 state indent-point normal-indent) (lisp-indent-specform 1 state indent-point normal-indent))) -;; (put 'begin 'scheme-indent-function 0), say, causes begin to be indented -;; like defun if the first form is placed on the next line, otherwise -;; it is indented like any other form (i.e. forms line up under first). - +;; See `scheme-indent-function' (the function) for what these do. +;; In a nutshell: +;; . for forms with no `scheme-indent-function' property the 2nd +;; and subsequent lines will be indented with one space; +;; . if the value of the property is zero, then when the first form +;; is on a separate line, the next lines will be indented with 2 +;; spaces instead of the default one space; +;; . if the value is a positive integer N, the first N lines after +;; the first one will be indented with 4 spaces, and the rest +;; will be indented with 2 spaces; +;; . if the value is `defun', the indentation is like for `defun'; +;; . if the value is a function, it will be called to produce the +;; required indentation. +;; See also http://community.schemewiki.org/?emacs-indentation. (put 'begin 'scheme-indent-function 0) (put 'case 'scheme-indent-function 1) (put 'delay 'scheme-indent-function 0) @@ -536,12 +589,16 @@ indentation." (put 'letrec 'scheme-indent-function 1) (put 'let-values 'scheme-indent-function 1) ; SRFI 11 (put 'let*-values 'scheme-indent-function 1) ; SRFI 11 +(put 'and-let* 'scheme-indent-function 1) ; SRFI 2 (put 'sequence 'scheme-indent-function 0) ; SICP, not r4rs (put 'let-syntax 'scheme-indent-function 1) (put 'letrec-syntax 'scheme-indent-function 1) -(put 'syntax-rules 'scheme-indent-function 1) +(put 'syntax-rules 'scheme-indent-function 'defun) (put 'syntax-case 'scheme-indent-function 2) ; not r5rs +(put 'with-syntax 'scheme-indent-function 1) (put 'library 'scheme-indent-function 1) ; R6RS +;; Part of at least Guile, Chez Scheme, Chicken +(put 'eval-when 'scheme-indent-function 1) (put 'call-with-input-file 'scheme-indent-function 1) (put 'call-with-port 'scheme-indent-function 1) @@ -565,6 +622,14 @@ indentation." ;; SRFI-8 (put 'receive 'scheme-indent-function 2) +;; SRFI-204 (withdrawn, but provided in many implementations, see the SRFI text) +(put 'match 'scheme-indent-function 1) +(put 'match-lambda 'scheme-indent-function 0) +(put 'match-lambda* 'scheme-indent-function 0) +(put 'match-let 'scheme-indent-function 'scheme-let-indent) +(put 'match-let* 'scheme-indent-function 1) +(put 'match-letrec 'scheme-indent-function 1) + ;;;; MIT Scheme specific indentation. (if scheme-mit-dialect diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index 966357c0970..be9f325d93d 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el @@ -286,7 +286,7 @@ naming the shell." :group 'sh-script) (defcustom sh-imenu-generic-expression - '((sh + `((sh . ((nil ;; function FOO ;; function FOO() @@ -295,8 +295,21 @@ naming the shell." ;; FOO() (nil "^\\s-*\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*()" - 1) - ))) + 1))) + (mksh + . ((nil + ;; function FOO + ;; function FOO() + ,(rx bol (* (syntax whitespace)) "function" (+ (syntax whitespace)) + (group (1+ (not (any "\0\t\n \"$&'();<=>\\`|#*?[]/")))) + (* (syntax whitespace)) (? "()")) + 1) + (nil + ;; FOO() + ,(rx bol (* (syntax whitespace)) + (group (1+ (not (any "\0\t\n \"$&'();<=>\\`|#*?[]/")))) + (* (syntax whitespace)) "()") + 1)))) "Alist of regular expressions for recognizing shell function definitions. See `sh-feature' and `imenu-generic-expression'." :type '(alist :key-type (symbol :tag "Shell") @@ -306,7 +319,7 @@ See `sh-feature' and `imenu-generic-expression'." :value-type (repeat :tag "Regexp, index..." sexp))) :group 'sh-script - :version "20.4") + :version "29.1") (defun sh-current-defun-name () "Find the name of function or variable at point. @@ -402,45 +415,42 @@ This is buffer-local in every such buffer.") (rpm . (,sh-mode-syntax-table ?\' "."))) "Syntax-table used in Shell-Script mode. See `sh-feature'.") -(defvar sh-mode-map - (let ((map (make-sparse-keymap))) - (define-key map "\C-c(" 'sh-function) - (define-key map "\C-c\C-w" 'sh-while) - (define-key map "\C-c\C-u" 'sh-until) - (define-key map "\C-c\C-t" 'sh-tmp-file) - (define-key map "\C-c\C-s" 'sh-select) - (define-key map "\C-c\C-r" 'sh-repeat) - (define-key map "\C-c\C-o" 'sh-while-getopts) - (define-key map "\C-c\C-l" 'sh-indexed-loop) - (define-key map "\C-c\C-i" 'sh-if) - (define-key map "\C-c\C-f" 'sh-for) - (define-key map "\C-c\C-c" 'sh-case) - (define-key map "\C-c?" #'smie-config-show-indent) - (define-key map "\C-c=" #'smie-config-set-indent) - (define-key map "\C-c<" #'smie-config-set-indent) - (define-key map "\C-c>" #'smie-config-guess) - (define-key map "\C-c\C-\\" 'sh-backslash-region) - - (define-key map "\C-c+" 'sh-add) - (define-key map "\C-\M-x" 'sh-execute-region) - (define-key map "\C-c\C-x" 'executable-interpret) - (define-key map "\C-c\C-n" 'sh-send-line-or-region-and-step) - (define-key map "\C-c\C-d" 'sh-cd-here) - (define-key map "\C-c\C-z" 'sh-show-shell) - - (define-key map [remap delete-backward-char] - 'backward-delete-char-untabify) - (define-key map "\C-c:" 'sh-set-shell) - (define-key map [remap backward-sentence] 'sh-beginning-of-command) - (define-key map [remap forward-sentence] 'sh-end-of-command) - map) - "Keymap used in Shell-Script mode.") +(defvar-keymap sh-mode-map + :doc "Keymap used in Shell-Script mode." + "C-c (" #'sh-function + "C-c C-w" #'sh-while + "C-c C-u" #'sh-until + "C-c C-t" #'sh-tmp-file + "C-c C-s" #'sh-select + "C-c C-r" #'sh-repeat + "C-c C-o" #'sh-while-getopts + "C-c C-l" #'sh-indexed-loop + "C-c C-i" #'sh-if + "C-c C-f" #'sh-for + "C-c C-c" #'sh-case + "C-c ?" #'smie-config-show-indent + "C-c =" #'smie-config-set-indent + "C-c <" #'smie-config-set-indent + "C-c >" #'smie-config-guess + "C-c C-\\" #'sh-backslash-region + + "C-c +" #'sh-add + "C-M-x" #'sh-execute-region + "C-c C-x" #'executable-interpret + "C-c C-n" #'sh-send-line-or-region-and-step + "C-c C-d" #'sh-cd-here + "C-c C-z" #'sh-show-shell + "C-c :" #'sh-set-shell + + "<remap> <delete-backward-char>" #'backward-delete-char-untabify + "<remap> <backward-sentence>" #'sh-beginning-of-command + "<remap> <forward-sentence>" #'sh-end-of-command) (easy-menu-define sh-mode-menu sh-mode-map "Menu for Shell-Script mode." '("Sh-Script" ["Backslash region" sh-backslash-region - :help "Insert, align, or delete end-of-line backslashes on the lines in the region."] + :help "Insert, align, or delete end-of-line backslashes on the lines in the region"] ["Set shell type..." sh-set-shell :help "Set this buffer's shell to SHELL (a string)"] ["Execute script..." executable-interpret @@ -458,7 +468,7 @@ This is buffer-local in every such buffer.") ["Select Statement" sh-select :help "Insert a select statement "] ["Indexed Loop" sh-indexed-loop - :help "Insert an indexed loop from 1 to n."] + :help "Insert an indexed loop from 1 to n"] ["Options Loop" sh-while-getopts :help "Insert a while getopts loop."] ["While Loop" sh-while @@ -482,7 +492,7 @@ This is buffer-local in every such buffer.") ["Show indentation" smie-config-show-indent :help "Show the how the current line would be indented"] ["Learn buffer indentation" smie-config-guess - :help "Learn how to indent the buffer the way it currently is."])) + :help "Learn how to indent the buffer the way it currently is"])) (defvar sh-skeleton-pair-default-alist '((?\( _ ?\)) (?\)) (?\[ ?\s _ ?\s ?\]) (?\]) @@ -628,7 +638,8 @@ removed when closing the here document." (wksh sh-append ksh88) (zsh sh-append ksh88 - "autoload" "bindkey" "builtin" "chdir" "compctl" "declare" "dirs" + "autoload" "always" + "bindkey" "builtin" "chdir" "compctl" "declare" "dirs" "disable" "disown" "echotc" "enable" "functions" "getln" "hash" "history" "integer" "limit" "local" "log" "popd" "pushd" "r" "readonly" "rehash" "sched" "setopt" "source" "suspend" "true" @@ -643,7 +654,12 @@ implemented as aliases. See `sh-feature'." :version "24.4" ; bash4 additions :group 'sh-script) - +(defcustom sh-indent-statement-after-and t + "How to indent statements following && in Shell-Script mode. +If t, indent to align with &&. +If nil, indent to align with the previous line's indentation." + :type 'boolean + :version "29.1") (defcustom sh-leading-keywords '((bash sh-append sh @@ -866,7 +882,7 @@ See `sh-feature'.") "\\(?:\\(?:.*[^\\\n]\\)?\\(?:\\\\\\\\\\)*\\\\\n\\)*.*") (defconst sh-here-doc-open-re - (concat "[^<]<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\|[-/~._]\\)+\\)" + (concat "[^<]<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\|[-/~._@]\\)+\\)" sh-escaped-line-re "\\(\n\\)"))) (defun sh--inside-noncommand-expression (pos) @@ -1140,8 +1156,8 @@ Can be set to a number, or to nil which means leave it as is." "The default indentation increment. This value is used for the `+' and `-' symbols in an indentation variable." :type 'integer + :safe #'integerp :group 'sh-indentation) -(put 'sh-basic-offset 'safe-local-variable 'integerp) (defcustom sh-indent-comment t "How a comment line is to be indented. @@ -1409,7 +1425,7 @@ If FORCE is non-nil and no process found, create one." (defun sh-show-shell () "Pop the shell interaction buffer." (interactive) - (pop-to-buffer (process-buffer (sh-shell-process t)))) + (pop-to-buffer (process-buffer (sh-shell-process t)) display-comint-buffer-action)) (defun sh-send-text (text) "Send TEXT to `sh-shell-process'." @@ -1540,6 +1556,11 @@ with your script for an edit-interpret-debug cycle." (add-hook 'completion-at-point-functions #'sh-completion-at-point-function nil t) (setq-local outline-regexp "###") + (setq-local escaped-string-quote + (lambda (terminator) + (if (eq terminator ?') + "'\\'" + "\\"))) ;; Parse or insert magic number for exec, and set all variables depending ;; on the shell thus determined. (sh-set-shell @@ -1551,7 +1572,7 @@ with your script for an edit-interpret-debug cycle." ;; Checks that use `buffer-file-name' follow. ((string-match "\\.m?spec\\'" buffer-file-name) "rpm") ((string-match "[.]sh\\>" buffer-file-name) "sh") - ((string-match "[.]bash\\>" buffer-file-name) "bash") + ((string-match "[.]bash\\(rc\\)?\\>" buffer-file-name) "bash") ((string-match "[.]ksh\\>" buffer-file-name) "ksh") ((string-match "[.]mkshrc\\>" buffer-file-name) "mksh") ((string-match "[.]t?csh\\(rc\\)?\\>" buffer-file-name) "csh") @@ -1604,7 +1625,7 @@ This adds rules for comments and assignments." ;;; Completion -(defvar sh--completion-keywords '("if" "while" "until" "for")) +(defvar sh--completion-keywords '("if" "while" "until" "for" "then")) (defun sh--vars-before-point () (save-excursion @@ -1776,21 +1797,27 @@ Does not preserve point." (n (skip-syntax-backward "."))) (if (or (zerop n) (and (eq n -1) + ;; Skip past quoted white space. (let ((p (point))) (if (eq -1 (% (skip-syntax-backward "\\") 2)) t (goto-char p) nil)))) (while - (progn (skip-syntax-backward ".w_'") - (or (not (zerop (skip-syntax-backward "\\"))) - (when (eq ?\\ (char-before (1- (point)))) - (let ((p (point))) - (forward-char -1) - (if (eq -1 (% (skip-syntax-backward "\\") 2)) - t - (goto-char p) - nil)))))) + (progn + ;; Skip past words, but stop at semicolons. + (while (and (not (zerop (skip-syntax-backward "w_'"))) + (not (eq (char-before (point)) ?\;)) + (skip-syntax-backward "."))) + (or (not (zerop (skip-syntax-backward "\\"))) + ;; Skip past quoted white space. + (when (eq ?\\ (char-before (1- (point)))) + (let ((p (point))) + (forward-char -1) + (if (eq -1 (% (skip-syntax-backward "\\") 2)) + t + (goto-char p) + nil)))))) (goto-char (- (point) (% (skip-syntax-backward "\\") 2)))) (buffer-substring-no-properties (point) pos))) @@ -1899,9 +1926,9 @@ With t, you get the latter as long as that would indent the continuation line deeper than the initial line." :version "25.1" :type '(choice - (const nil :tag "Never") - (const t :tag "Only if needed to make it deeper") - (const always :tag "Always")) + (const :value nil :tag "Never") + (const :value t :tag "Only if needed to make it deeper") + (const :value always :tag "Always")) :group 'sh-indentation) (defun sh-smie--continuation-start-indent () @@ -1975,7 +2002,7 @@ May return nil if the line should not be treated as continued." (cons 'column (smie-indent-keyword ";")) (smie-rule-separator kind))) (`(:after . ,(or ";;" ";&" ";;&")) - (with-demoted-errors + (with-demoted-errors "SMIE rule error: %S" (smie-backward-sexp token) (cons 'column (if (or (smie-rule-bolp) @@ -1986,7 +2013,9 @@ May return nil if the line should not be treated as continued." (current-column) (smie-indent-calculate))))) (`(:before . ,(or "|" "&&" "||")) - (unless (smie-rule-parent-p token) + (when (and (not (smie-rule-parent-p token)) + (or (not (equal token "&&")) + sh-indent-statement-after-and)) (smie-backward-sexp token) `(column . ,(+ (funcall smie-rules-function :elem 'basic) (smie-indent-virtual))))) @@ -2381,6 +2410,8 @@ Lines containing only comments are considered empty." The working directory is that of the buffer, and only environment variables are already set which is why you can mark a header within the script. +The executed subshell is `sh-shell-file'. + With a positive prefix ARG, instead of sending region, define header from beginning of buffer to point. With a negative prefix ARG, instead of sending region, clear header." @@ -2388,17 +2419,18 @@ region, clear header." (if flag (setq sh-header-marker (if (> (prefix-numeric-value flag) 0) (point-marker))) - (if sh-header-marker - (save-excursion - (let (buffer-undo-list) - (goto-char sh-header-marker) - (append-to-buffer (current-buffer) start end) - (shell-command-on-region (point-min) - (setq end (+ sh-header-marker - (- end start))) - sh-shell-file) - (delete-region sh-header-marker end))) - (shell-command-on-region start end (concat sh-shell-file " -"))))) + (let ((shell-file-name sh-shell-file)) + (if sh-header-marker + (save-excursion + (let (buffer-undo-list) + (goto-char sh-header-marker) + (append-to-buffer (current-buffer) start end) + (shell-command-on-region (point-min) + (setq end (+ sh-header-marker + (- end start))) + sh-shell-file) + (delete-region sh-header-marker end))) + (shell-command-on-region start end (concat sh-shell-file " -")))))) (defun sh-remember-variable (var) diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el index 6183aee20e3..b950f93f2a0 100644 --- a/lisp/progmodes/sql.el +++ b/lisp/progmodes/sql.el @@ -274,8 +274,8 @@ file. Since that is a plaintext file, this could be dangerous." (defcustom sql-port 0 "Default port for connecting to a MySQL or Postgres server." :version "24.1" - :type 'number - :safe 'numberp) + :type 'natnum + :safe 'natnump) (defcustom sql-default-directory nil "Default directory for SQL processes." @@ -481,9 +481,9 @@ file. Since that is a plaintext file, this could be dangerous." :list-all ("\\d+" . "\\dS+") :list-table ("\\d+ %s" . "\\dS+ %s") :completion-object sql-postgres-completion-object - :prompt-regexp "^[[:alnum:]_]*=[#>] " + :prompt-regexp "^[-[:alnum:]_]*[-=][#>] " :prompt-length 5 - :prompt-cont-regexp "^[[:alnum:]_]*[-(][#>] " + :prompt-cont-regexp "^[-[:alnum:]_]*[-'(][#>] " :statement sql-postgres-statement-starters :input-filter sql-remove-tabs-filter :terminator ("\\(^\\s-*\\\\g\\|;\\)" . "\\g")) @@ -700,8 +700,17 @@ making new SQLi sessions." (sexp :tag "Value Expression"))))) :version "24.1") -(defvaralias 'sql-dialect 'sql-product) +(defun sql-add-connection (connection params) + "Add a new connection to `sql-connection-alist'. +If CONNECTION already exists, it is replaced with PARAMS." + (setq sql-connection-alist + (assoc-delete-all connection sql-connection-alist)) + (push + (cons connection params) + sql-connection-alist)) + +(defvaralias 'sql-dialect 'sql-product) (defcustom sql-product 'ansi "Select the SQL database product used. This allows highlighting buffers properly when you open them." @@ -963,12 +972,7 @@ If set to \"\\n\", each line in the history file will be interpreted as one command. Multi-line commands are split into several commands when the input ring is initialized from a history file. -This variable used to initialize `comint-input-ring-separator'. -`comint-input-ring-separator' is part of Emacs 21; if your Emacs -does not have it, setting `sql-input-ring-separator' will have no -effect. In that case multiline commands will be split into several -commands when the input history is read, as if you had set -`sql-input-ring-separator' to \"\\n\"." +This variable used to initialize `comint-input-ring-separator'." :type 'string) ;; The usual hooks @@ -1357,8 +1361,6 @@ specified, it's `sql-product' or `sql-connection' must match." (defvar sql-interactive-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map comint-mode-map) - (if (fboundp 'set-keymap-name) - (set-keymap-name map 'sql-interactive-mode-map)); XEmacs (define-key map (kbd "C-j") 'sql-accumulate-and-indent) (define-key map (kbd "C-c C-w") 'sql-copy-column) (define-key map (kbd "O") 'sql-magic-go) @@ -2832,16 +2834,6 @@ configured." (font-lock-mode-internal nil) (font-lock-mode-internal t)) - (add-hook 'font-lock-mode-hook - (lambda () - ;; Provide defaults for new font-lock faces. - (defvar font-lock-builtin-face - (if (boundp 'font-lock-preprocessor-face) - font-lock-preprocessor-face - font-lock-keyword-face)) - (defvar font-lock-doc-face font-lock-string-face)) - nil t) - ;; Setup imenu; it needs the same syntax-alist. (when imenu (setq imenu-syntax-alist syntax-alist)))) @@ -3219,19 +3211,12 @@ For both `:file' and `:completion', there can also be a symbol (let* ((default (plist-get plist :default)) (last-value (sql-default-value symbol)) - (prompt-def - (if default - (if (string-match "\\(\\):[ \t]*\\'" prompt) - (replace-match (format " (default \"%s\")" default) t t prompt 1) - (replace-regexp-in-string "[ \t]*\\'" - (format " (default \"%s\") " default) - prompt t t)) - prompt)) + (prompt-def (format-prompt prompt default)) (use-dialog-box nil)) (cond ((plist-member plist :file) (let ((file-name - (read-file-name prompt + (read-file-name prompt-def (file-name-directory last-value) default (if (plist-member plist :must-match) @@ -3261,7 +3246,7 @@ For both `:file' and `:completion', there can also be a default)) ((plist-get plist :number) - (read-number prompt (or default last-value 0))) + (read-number (concat prompt ": ") (or default last-value 0))) (t (read-string prompt-def last-value history-var default)))))) @@ -3311,7 +3296,7 @@ function like this: (sql-get-login \\='user \\='password \\='database)." (let ((plist (cdr-safe w))) (pcase (or (car-safe w) w) ('user - (sql-get-login-ext 'sql-user "User: " 'sql-user-history plist)) + (sql-get-login-ext 'sql-user "User" 'sql-user-history plist)) ('password (setq-default sql-password @@ -3330,14 +3315,14 @@ function like this: (sql-get-login \\='user \\='password \\='database)." (read-passwd "Password: " nil (sql-default-value 'sql-password))))) ('server - (sql-get-login-ext 'sql-server "Server: " 'sql-server-history plist)) + (sql-get-login-ext 'sql-server "Server" 'sql-server-history plist)) ('database - (sql-get-login-ext 'sql-database "Database: " + (sql-get-login-ext 'sql-database "Database" 'sql-database-history plist)) ('port - (sql-get-login-ext 'sql-port "Port: " + (sql-get-login-ext 'sql-port "Port" nil (append '(:number t) plist))))))) (defun sql-find-sqli-buffer (&optional product connection) @@ -3663,94 +3648,69 @@ Allows the suppression of continuation prompts.") (defvar sql-preoutput-hold nil) -(defun sql-starts-with-prompt-re () - "Anchor the prompt expression at the beginning of the output line. -Remove the start of line regexp." - (concat "\\`" comint-prompt-regexp)) - -(defun sql-ends-with-prompt-re () - "Anchor the prompt expression at the end of the output line. -Match a SQL prompt or a password prompt." - (concat "\\(?:\\(?:" sql-prompt-regexp "\\)\\|" - "\\(?:" comint-password-prompt-regexp "\\)\\)\\'")) - (defun sql-interactive-remove-continuation-prompt (oline) "Strip out continuation prompts out of the OLINE. Added to the `comint-preoutput-filter-functions' hook in a SQL -interactive buffer. If `sql-output-newline-count' is greater than -zero, then an output line matching the continuation prompt is filtered -out. If the count is zero, then a newline is inserted into the output -to force the output from the query to appear on a new line. - -The complication to this filter is that the continuation prompts -may arrive in multiple chunks. If they do, then the function -saves any unfiltered output in a buffer and prepends that buffer -to the next chunk to properly match the broken-up prompt. - -If the filter gets confused, it should reset and stop filtering -to avoid deleting non-prompt output." - - ;; continue gathering lines of text iff - ;; + we know what a prompt looks like, and - ;; + there is held text, or - ;; + there are continuation prompt yet to come, or - ;; + not just a prompt string +interactive buffer. The complication to this filter is that the +continuation prompts may arrive in multiple chunks. If they do, +then the function saves any unfiltered output in a buffer and +prepends that buffer to the next chunk to properly match the +broken-up prompt. + +The filter goes into play only if something is already +accumulated, or we're waiting for continuation +prompts (`sql-output-newline-count' is positive). In this case: +- Accumulate process output into `sql-preoutput-hold'. +- Remove any complete prompts / continuation prompts that we're waiting + for. +- In case we're expecting more prompts - return all currently + accumulated _complete_ lines, leaving the rest for the next + invocation. They will appear in the output immediately. This way we + don't accumulate large chunks of data for no reason. +- If we found all expected prompts - just return all current accumulated + data." (when (and comint-prompt-regexp - (or (> (length (or sql-preoutput-hold "")) 0) - (> (or sql-output-newline-count 0) 0) - (not (or (string-match sql-prompt-regexp oline) - (and sql-prompt-cont-regexp - (string-match sql-prompt-cont-regexp oline)))))) - + ;; We either already have something held, or expect + ;; prompts + (or sql-preoutput-hold + (and sql-output-newline-count + (> sql-output-newline-count 0)))) (save-match-data - (let (prompt-found last-nl) - - ;; Add this text to what's left from the last pass - (setq oline (concat sql-preoutput-hold oline) - sql-preoutput-hold "") - - ;; If we are looking for multiple prompts - (when (and (integerp sql-output-newline-count) - (>= sql-output-newline-count 1)) - ;; Loop thru each starting prompt and remove it - (let ((start-re (sql-starts-with-prompt-re))) - (while (and (not (string= oline "")) - (> sql-output-newline-count 0) - (string-match start-re oline)) - (setq oline (replace-match "" nil nil oline) - sql-output-newline-count (1- sql-output-newline-count) - prompt-found t))) - - ;; If we've found all the expected prompts, stop looking - (if (= sql-output-newline-count 0) - (setq sql-output-newline-count nil) - - ;; Still more possible prompts, leave them for the next pass - (setq sql-preoutput-hold oline - oline ""))) - - ;; If no prompts were found, stop looking - (unless prompt-found - (setq sql-output-newline-count nil - oline (concat oline sql-preoutput-hold) - sql-preoutput-hold "")) - - ;; Break up output by physical lines if we haven't hit the final prompt - (let ((end-re (sql-ends-with-prompt-re))) - (unless (and (not (string= oline "")) - (string-match end-re oline) - (>= (match-end 0) (length oline))) - ;; Find everything upto the last nl - (setq last-nl 0) - (while (string-match "\n" oline last-nl) - (setq last-nl (match-end 0))) - ;; Hold after the last nl, return upto last nl - (setq sql-preoutput-hold (concat (substring oline last-nl) - sql-preoutput-hold) - oline (substring oline 0 last-nl))))))) + ;; Add this text to what's left from the last pass + (setq oline (concat sql-preoutput-hold oline) + sql-preoutput-hold nil) + + ;; If we are looking for prompts + (when (and sql-output-newline-count + (> sql-output-newline-count 0)) + ;; Loop thru each starting prompt and remove it + (while (and (not (string-empty-p oline)) + (> sql-output-newline-count 0) + (string-match comint-prompt-regexp oline)) + (setq oline (replace-match "" nil nil oline) + sql-output-newline-count (1- sql-output-newline-count))) + + ;; If we've found all the expected prompts, stop looking + (if (= sql-output-newline-count 0) + (setq sql-output-newline-count nil) + ;; Still more possible prompts, leave them for the next pass + (setq sql-preoutput-hold oline + oline ""))) + + ;; Lines that are now complete may be passed further + (when sql-preoutput-hold + (let ((last-nl 0)) + (while (string-match "\n" sql-preoutput-hold last-nl) + (setq last-nl (match-end 0))) + ;; Return up to last nl, hold after the last nl + (setq oline (substring sql-preoutput-hold 0 last-nl) + sql-preoutput-hold (substring sql-preoutput-hold last-nl)) + (when (string-empty-p sql-preoutput-hold) + (setq sql-preoutput-hold nil)))))) oline) + ;;; Sending the region to the SQLi buffer. (defvar sql-debug-send nil "Display text sent to SQL process pragmatically.") @@ -4182,10 +4142,6 @@ must tell Emacs. Here's how to do that in your init file: (modify-syntax-entry ?\\\\ \"\\\\\" sql-mode-syntax-table)))" :abbrev-table sql-mode-abbrev-table - (when (and (featurep 'xemacs) - sql-mode-menu) - (easy-menu-add sql-mode-menu)) - ;; (smie-setup sql-smie-grammar #'sql-smie-rules) (setq-local comment-start "--") ;; Make each buffer in sql-mode remember the "current" SQLi buffer. @@ -4203,18 +4159,35 @@ must tell Emacs. Here's how to do that in your init file: (setq-local abbrev-all-caps 1) ;; Contains the name of database objects (setq-local sql-contains-names t) + (setq-local escaped-string-quote "'") (setq-local syntax-propertize-function - (syntax-propertize-rules - ;; Handle escaped apostrophes within strings. - ("''" - (0 - (if (save-excursion (nth 3 (syntax-ppss (match-beginning 0)))) - (string-to-syntax ".") - (forward-char -1) - nil))) - ;; Propertize rules to not have /- and -* start comments. - ("\\(/-\\)" (1 ".")) - ("\\(-\\*\\)" (1 ".")))) + (eval + '(syntax-propertize-rules + ;; Handle escaped apostrophes within strings. + ((if (eq sql-product 'mysql) + "\\\\'" + "''") + (0 + (if (save-excursion + (nth 3 (syntax-ppss (match-beginning 0)))) + (string-to-syntax ".") + (forward-char -1) + nil))) + ;; Propertize rules to not have /- and -* start comments. + ("\\(/-\\)" (1 ".")) + ("\\(-\\*\\)" + (1 + (if (save-excursion + (not (ppss-comment-depth + (syntax-ppss (match-beginning 1))))) + ;; If we're outside a comment, we don't let -* + ;; start a comment. + (string-to-syntax ".") + ;; Inside a comment, ignore it to avoid -*/ not + ;; being interpreted as a comment end. + (forward-char -1) + nil)))) + t)) ;; Set syntax and font-face highlighting ;; Catch changes to sql-product and highlight accordingly (sql-set-product (or sql-product 'ansi)) ; Fixes bug#13591 @@ -4308,9 +4281,6 @@ you entered, right above the output it created. (setq mode-name (concat "SQLi[" (or (sql-get-product-feature sql-product :name) (symbol-name sql-product)) "]")) - (when (and (featurep 'xemacs) - sql-interactive-mode-menu) - (easy-menu-add sql-interactive-mode-menu)) ;; Note that making KEYWORDS-ONLY nil will cause havoc if you try ;; SELECT 'x' FROM DUAL with SQL*Plus, because the title of the column @@ -4655,6 +4625,9 @@ the call to \\[sql-product-interactive] with (setq sql-buffer (buffer-name new-sqli-buffer)) (run-hooks 'sql-set-sqli-hook))) + ;; Also set the global value. + (setq-default sql-buffer (buffer-name new-sqli-buffer)) + ;; Make sure the connection is complete ;; (Sometimes start up can be slow) ;; and call the login hook @@ -4681,6 +4654,14 @@ the call to \\[sql-product-interactive] with (get-buffer new-sqli-buffer))))) (user-error "No default SQL product defined: set `sql-product'"))) +(defun sql-comint-automatic-password (_) + "Intercept password prompts when we know the password. +This must also do the job of detecting password prompts." + (when (and + sql-password + (not (string= "" sql-password))) + sql-password)) + (defun sql-comint (product params &optional buf-name) "Set up a comint buffer to run the SQL processor. @@ -4705,6 +4686,13 @@ buffer. If nil, a name is chosen for it." (setq buf-name (sql-generate-unique-sqli-buffer-name product nil))) (set-text-properties 0 (length buf-name) nil buf-name) + ;; Create the buffer first, because we want to set it up before + ;; comint starts to run. + (set-buffer (get-buffer-create buf-name)) + ;; Set up the automatic population of passwords, if supported. + (when (sql-get-product-feature product :password-in-comint) + (setq comint-password-function #'sql-comint-automatic-password)) + ;; Start the command interpreter in the buffer ;; PROC-NAME is BUF-NAME without enclosing asterisks (let ((proc-name (replace-regexp-in-string "\\`[*]\\(.*\\)[*]\\'" "\\1" buf-name))) diff --git a/lisp/progmodes/tcl.el b/lisp/progmodes/tcl.el index ed6dce02c03..7dae14f9e02 100644 --- a/lisp/progmodes/tcl.el +++ b/lisp/progmodes/tcl.el @@ -120,13 +120,13 @@ (defcustom tcl-indent-level 4 "Indentation of Tcl statements with respect to containing block." - :type 'integer) -(put 'tcl-indent-level 'safe-local-variable #'integerp) + :type 'integer + :safe #'integerp) (defcustom tcl-continued-indent-level 4 "Indentation of continuation line relative to first line of command." - :type 'integer) -(put 'tcl-continued-indent-level 'safe-local-variable #'integerp) + :type 'integer + :safe #'integerp) (defcustom tcl-auto-newline nil "Non-nil means automatically newline before and after braces you insert." @@ -344,7 +344,7 @@ information): Add functions to the hook with `add-hook': - (add-hook 'tcl-mode-hook #'tcl-guess-application)") + (add-hook \\='tcl-mode-hook #\\='tcl-guess-application)") (defvar tcl-proc-list diff --git a/lisp/progmodes/verilog-mode.el b/lisp/progmodes/verilog-mode.el index b2ce9140573..31d50a1882e 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.09.23.089128420 +;; Version: 2021.10.14.127365406 ;; Yoni Rabkin <yoni@rabkins.net> 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-09-23-54ffde4-vpo-GNU" +(defconst verilog-mode-version "2021-10-14-797711e-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.") @@ -1264,7 +1264,9 @@ See `verilog-auto-inst-param-value'." Also affects AUTOINSTPARAM. Declaration order is the default for backward compatibility, and as some teams prefer signals that are declared together to remain together. Sorted order reduces -changes when declarations are moved around in a file. +changes when declarations are moved around in a file. Sorting is +within input/output/inout groupings, there is intentionally no +option to intermix between input/output/inouts. See also `verilog-auto-arg-sort'." :version "24.1" ; rev688 @@ -3620,10 +3622,10 @@ is 0. Meaning of *single* declaration: E.g. In a module's port-list - module test(input clk, rst, x, output [1:0] y); - Here 'input clk, rst, x' is 1 *single* declaration statement, -and 'output [1:0] y' is the other single declaration. In the 1st single -declaration, POINT is moved to start of 'clk'. And in the 2nd declaration, -POINT is moved to 'y'." + Here `input clk, rst, x' is 1 *single* declaration statement, +and `output [1:0] y' is the other single declaration. In the 1st single +declaration, POINT is moved to start of `clk'. And in the 2nd declaration, +POINT is moved to `y'." (let (maxpoint old-point) @@ -5478,8 +5480,11 @@ becomes: (let* ((pop-up-windows t)) (let ((name (expand-file-name (read-file-name - (format "Find this error in: (default %s) " - file) + ;; `format-prompt' is new in Emacs 28.1. + (if (fboundp 'format-prompt) + (format-prompt "Find this error in" file) + (format "Find this error in (default %s): " + file)) nil ;; dir file t)))) (setq buffer @@ -6598,7 +6603,8 @@ Also move point to constraint." (equal (char-before) ?\;) (equal (char-before) ?\})) ;; skip what looks like bus repetition operator {#{ - (not (string-match "^{\\s-*[\\(\\)0-9a-zA-Z_]*\\s-*{" (buffer-substring p (point))))))))) + (not (string-match "^{\\s-*[()0-9a-zA-Z_\\]*\\s-*{" + (buffer-substring p (point))))))))) (progn (let ( (pt (point)) (pass 0)) (verilog-backward-ws&directives) @@ -7863,14 +7869,14 @@ If search fails, other files are checked based on (let* ((default (verilog-get-default-symbol)) ;; The following variable is used in verilog-comp-function (verilog-buffer-to-use (current-buffer)) - (label (if (not (string= default "")) - ;; Do completion with default - (completing-read (concat "Goto-Label: (default " - default ") ") - #'verilog-comp-defun nil nil "") - ;; There is no default value. Complete without it - (completing-read "Goto-Label: " - #'verilog-comp-defun nil nil ""))) + (label + (completing-read (cond ((fboundp 'format-prompt) + ;; `format-prompt' is new in Emacs 28.1. + (format-prompt "Goto-Label" default)) + ((not (string= default "")) + (concat "Goto-Label (default " default "): ")) + (t "Goto-Label: ")) + #'verilog-comp-defun nil nil "")) pt) ;; Make sure library paths are correct, in case need to resolve module (verilog-auto-reeval-locals) diff --git a/lisp/progmodes/vhdl-mode.el b/lisp/progmodes/vhdl-mode.el index 4e5f5df8142..39c5eb453b1 100644 --- a/lisp/progmodes/vhdl-mode.el +++ b/lisp/progmodes/vhdl-mode.el @@ -8789,7 +8789,10 @@ project is defined." (defun vhdl-electric-period (count) "`..' --> ` => '" (interactive "p") (if (and vhdl-stutter-mode (= count 1) (not (vhdl-in-literal))) - (cond ((= (preceding-char) vhdl-last-input-event) + ;; We use this-command-keys below to account for translation of + ;; kp-decimal into '.'; vhdl-last-input-event doesn't catch + ;; that. + (cond ((eq (preceding-char) (aref (this-command-keys) 0)) (progn (delete-char -1) (unless (eq (preceding-char) ? ) (insert " ")) (insert "=> "))) @@ -10687,8 +10690,9 @@ Include a library specification, if not already there." (replace-match "" t t) (vhdl-template-insert-date)) (goto-char beg) - (while (search-forward "<year>" end t) - (replace-match (format-time-string "%Y" nil) t t)) + (let ((year (format-time-string "%Y"))) + (while (search-forward "<year>" end t) + (replace-match year t t))) (goto-char beg) (when file-title (while (search-forward "<title string>" end t) diff --git a/lisp/progmodes/which-func.el b/lisp/progmodes/which-func.el index abe25f2c633..2e8e8d23192 100644 --- a/lisp/progmodes/which-func.el +++ b/lisp/progmodes/which-func.el @@ -64,7 +64,7 @@ ;; Variables for customization ;; --------------------------- ;; -(defvar which-func-unknown "???" +(defvar which-func-unknown "n/a" "String to display in the mode line when current function is unknown.") (defgroup which-func nil @@ -234,9 +234,6 @@ It creates the Imenu index for the buffer, if necessary." (setq which-func-mode nil) (error "Error in which-func-update: %S" info)))))) -;;;###autoload -(define-obsolete-function-alias 'which-func-mode 'which-function-mode "24.1") - (defvar which-func-update-timer nil) (unless (or (assq 'which-func-mode mode-line-misc-info) diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index c4b439f587c..0213ab3cc58 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -1,7 +1,7 @@ ;;; xref.el --- Cross-referencing commands -*-lexical-binding:t-*- ;; Copyright (C) 2014-2022 Free Software Foundation, Inc. -;; Version: 1.3.0 +;; Version: 1.4.1 ;; Package-Requires: ((emacs "26.1")) ;; This is a GNU ELPA :core package. Avoid functionality that is not @@ -75,7 +75,7 @@ (require 'project) (eval-and-compile - (when (version< emacs-version "28") + (when (version< emacs-version "28.0.60") ;; etags.el in Emacs 26 and 27 uses EIEIO, and its location type ;; inherits from `xref-location'. (require 'eieio) @@ -195,9 +195,16 @@ is not known." ;;; Cross-reference -(cl-defstruct (xref-item - (:constructor xref-make (summary location)) - (:noinline t)) +(defmacro xref--defstruct (name &rest fields) + (declare (indent 1)) + `(cl-defstruct ,(if (>= emacs-major-version 27) + name + (remq (assq :noinline name) name)) + ,@fields)) + +(xref--defstruct (xref-item + (:constructor xref-make (summary location)) + (:noinline t)) "An xref item describes a reference to a location somewhere." (summary nil :documentation "String which describes the location. @@ -213,14 +220,14 @@ locations point to the same line. This behavior is new in Emacs 28.") location) -(cl-defstruct (xref-match-item - (:include xref-item) - (:constructor xref-make-match (summary location length)) - (:noinline t)) +(xref--defstruct (xref-match-item + (:include xref-item) + (:constructor xref-make-match (summary location length)) + (:noinline t)) "A match xref item describes a search result." length) -(cl-defgeneric xref-match-length ((item xref-match-item)) +(cl-defmethod xref-match-length ((item xref-match-item)) "Return the length of the match." (xref-match-item-length item)) @@ -346,15 +353,9 @@ backward." (t (goto-char start) nil)))) -;;; Marker stack (M-. pushes, M-, pops) - -(defcustom xref-marker-ring-length 16 - "Length of the xref marker ring. -If this variable is not set through Customize, you must call -`xref-set-marker-ring-length' for changes to take effect." - :type 'integer - :initialize #'custom-initialize-default - :set #'xref-set-marker-ring-length) +;; Dummy variable retained for compatibility. +(defvar xref-marker-ring-length 16) +(make-obsolete-variable 'xref-marker-ring-length nil "29.1") (defcustom xref-prompt-for-identifier '(not xref-find-definitions xref-find-definitions-other-window @@ -380,7 +381,8 @@ elements is negated: these commands will NOT prompt." (defcustom xref-after-jump-hook '(recenter xref-pulse-momentarily) - "Functions called after jumping to an xref." + "Functions called after jumping to an xref. +Also see `xref-current-item'." :type 'hook) (defcustom xref-after-return-hook '(xref-pulse-momentarily) @@ -425,42 +427,79 @@ or earlier: it can break `dired-do-find-regexp-and-replace'." :version "28.1" :package-version '(xref . "1.2.0")) -(defvar xref--marker-ring (make-ring xref-marker-ring-length) - "Ring of markers to implement the marker stack.") +(make-obsolete-variable 'xref--marker-ring 'xref--history "29.1") + +(defun xref-set-marker-ring-length (_var _val) + (declare (obsolete nil "29.1")) + nil) -(defun xref-set-marker-ring-length (var val) - "Set `xref-marker-ring-length'. -VAR is the symbol `xref-marker-ring-length' and VAL is the new -value." - (set-default var val) - (if (ring-p xref--marker-ring) - (ring-resize xref--marker-ring val))) +(defvar xref--history (cons nil nil) + "(BACKWARD-STACK . FORWARD-STACK) of markers to visited Xref locations.") + +(defun xref--push-backward (m) + "Push marker M onto the backward history stack." + (unless (equal m (caar xref--history)) + (push m (car xref--history)))) + +(defun xref--push-forward (m) + "Push marker M onto the forward history stack." + (unless (equal m (cadr xref--history)) + (push m (cdr xref--history)))) (defun xref-push-marker-stack (&optional m) - "Add point M (defaults to `point-marker') to the marker stack." - (ring-insert xref--marker-ring (or m (point-marker)))) + "Add point M (defaults to `point-marker') to the marker stack. +The future stack is erased." + (xref--push-backward (or m (point-marker))) + (dolist (mk (cdr xref--history)) + (set-marker mk nil nil)) + (setcdr xref--history nil)) + +;;;###autoload +(define-obsolete-function-alias 'xref-pop-marker-stack #'xref-go-back "29.1") + +;;;###autoload +(defun xref-go-back () + "Go back to the previous position in xref history. +To undo, use \\[xref-go-forward]." + (interactive) + (if (null (car xref--history)) + (user-error "At start of xref history") + (let ((marker (pop (car xref--history)))) + (xref--push-forward (point-marker)) + (switch-to-buffer (or (marker-buffer marker) + (user-error "The marked buffer has been deleted"))) + (goto-char (marker-position marker)) + (set-marker marker nil nil) + (run-hooks 'xref-after-return-hook)))) ;;;###autoload -(defun xref-pop-marker-stack () - "Pop back to where \\[xref-find-definitions] was last invoked." +(defun xref-go-forward () + "Got to the point where a previous \\[xref-go-back] was invoked." (interactive) - (let ((ring xref--marker-ring)) - (when (ring-empty-p ring) - (user-error "Marker stack is empty")) - (let ((marker (ring-remove ring 0))) + (if (null (cdr xref--history)) + (user-error "At end of xref history") + (let ((marker (pop (cdr xref--history)))) + (xref--push-backward (point-marker)) (switch-to-buffer (or (marker-buffer marker) (user-error "The marked buffer has been deleted"))) (goto-char (marker-position marker)) (set-marker marker nil nil) (run-hooks 'xref-after-return-hook)))) -(defvar xref--current-item nil) +(define-obsolete-variable-alias + 'xref--current-item + 'xref-current-item + "29.1") + +(defvar xref-current-item nil + "Dynamically bound to the current item being processed. +This can be used from `xref-after-jump-hook', for instance.") (defun xref-pulse-momentarily () (pcase-let ((`(,beg . ,end) (save-excursion (or - (let ((length (xref-match-length xref--current-item))) + (let ((length (xref-match-length xref-current-item))) (and length (cons (point) (+ (point) length)))) (back-to-indentation) (if (eolp) @@ -470,17 +509,23 @@ value." ;; etags.el needs this (defun xref-clear-marker-stack () - "Discard all markers from the marker stack." - (let ((ring xref--marker-ring)) - (while (not (ring-empty-p ring)) - (let ((marker (ring-remove ring))) - (set-marker marker nil nil))))) + "Discard all markers from the xref history." + (dolist (l (list (car xref--history) (cdr xref--history))) + (dolist (m l) + (set-marker m nil nil))) + (setq xref--history (cons nil nil)) + nil) ;;;###autoload (defun xref-marker-stack-empty-p () - "Return t if the marker stack is empty; nil otherwise." - (ring-empty-p xref--marker-ring)) + "Whether the xref back-history is empty." + (null (car xref--history))) +;; FIXME: rename this to `xref-back-history-empty-p'. +;;;###autoload +(defun xref-forward-history-empty-p () + "Whether the xref forward-history is empty." + (null (cdr xref--history))) (defun xref--goto-char (pos) @@ -511,7 +556,7 @@ If SELECT is non-nil, select the target window." (window (pop-to-buffer buf t)) (frame (let ((pop-up-frames t)) (pop-to-buffer buf t)))) (xref--goto-char marker)) - (let ((xref--current-item item)) + (let ((xref-current-item item)) (run-hooks 'xref-after-jump-hook))) @@ -600,9 +645,15 @@ SELECT is `quit', also quit the *xref* window." (xref-buffer (current-buffer))) (cond (select (if (eq select 'quit) (quit-window nil nil)) - (select-window - (with-current-buffer xref-buffer - (xref--show-pos-in-buf marker buf)))) + (let* ((old-frame (selected-frame)) + (window (with-current-buffer xref-buffer + (xref--show-pos-in-buf marker buf))) + (frame (window-frame window))) + ;; If we chose another frame, make sure it gets input + ;; focus. + (unless (eq frame old-frame) + (select-frame-set-input-focus frame)) + (select-window window))) (t (save-selected-window (xref--with-dedicated-window @@ -619,7 +670,7 @@ SELECT is `quit', also quit the *xref* window." "Display the source of xref at point in the appropriate window, if any." (interactive) (let* ((xref (xref--item-at-point)) - (xref--current-item xref)) + (xref-current-item xref)) (when xref (xref--set-arrow) (xref--show-location (xref-item-location xref))))) @@ -678,7 +729,7 @@ quit the *xref* buffer." (let* ((buffer (current-buffer)) (xref (or (xref--item-at-point) (user-error "Choose a reference to visit"))) - (xref--current-item xref)) + (xref-current-item xref)) (xref--set-arrow) (xref--show-location (xref-item-location xref) (if quit 'quit t)) (if (fboundp 'next-error-found) @@ -695,7 +746,7 @@ quit the *xref* buffer." "Quit *xref* buffer, then pop the xref marker stack." (interactive) (quit-window) - (xref-pop-marker-stack)) + (xref-go-back)) (defun xref-query-replace-in-results (from to) "Perform interactive replacement of FROM with TO in all displayed xrefs. @@ -703,15 +754,23 @@ quit the *xref* buffer." This command interactively replaces FROM with TO in the names of the references displayed in the current *xref* buffer. +When called interactively, it uses '.*' as FROM, which means +replace the whole name. Unless called with prefix argument, in +which case the user is prompted for both FROM and TO. + As each match is found, the user must type a character saying what to do with it. Type SPC or `y' to replace the match, DEL or `n' to skip and go to the next match. For more directions, -type \\[help-command] at that time. -" +type \\[help-command] at that time." (interactive - (let ((fr (read-regexp "Xref query-replace (regexp)" ".*"))) - (list fr - (read-regexp (format "Xref query-replace (regexp) %s with: " fr))))) + (let* ((fr + (if current-prefix-arg + (read-regexp "Query-replace (regexp)" ".*") + ".*")) + (prompt (if current-prefix-arg + (format "Query-replace (regexp) %s with: " fr) + "Query-replace all matches with: "))) + (list fr (read-regexp prompt)))) (let* (item xrefs iter) (save-excursion (while (setq item (xref--search-property 'xref-item)) @@ -905,15 +964,15 @@ beginning of the line." (let ((win (get-buffer-window (current-buffer)))) (and win (set-window-point win (point)))) (xref--set-arrow) - (let ((xref--current-item xref)) + (let ((xref-current-item xref)) (xref--show-location (xref-item-location xref) t))) (t (error "No %s xref" (if backward "previous" "next")))))) (defvar xref--button-map (let ((map (make-sparse-keymap))) - (define-key map [mouse-1] #'xref-goto-xref) - (define-key map [mouse-2] #'xref-select-and-show-xref) + (define-key map [follow-link] 'mouse-face) + (define-key map [mouse-2] #'xref-goto-xref) map)) (defun xref-select-and-show-xref (event) @@ -1062,6 +1121,13 @@ Return an alist of the form ((GROUP . (XREF ...)) ...)." (cdr pair))) alist))) +(defun xref--ensure-default-directory (dd buffer) + ;; We might be in a let-binding which will restore the current value + ;; to a previous one (bug#53626). So do this later. + (run-with-timer + 0 nil + (lambda () (with-current-buffer buffer (setq default-directory dd))))) + (defun xref--show-xref-buffer (fetcher alist) (cl-assert (functionp fetcher)) (let* ((xrefs @@ -1072,7 +1138,7 @@ Return an alist of the form ((GROUP . (XREF ...)) ...)." (dd default-directory) buf) (with-current-buffer (get-buffer-create xref-buffer-name) - (setq default-directory dd) + (xref--ensure-default-directory dd (current-buffer)) (xref--xref-buffer-mode) (xref--show-common-initialize xref-alist fetcher alist) (pop-to-buffer (current-buffer)) @@ -1171,7 +1237,7 @@ local keymap that binds `RET' to `xref-quit-and-goto-xref'." (assoc-default 'display-action alist))) (t (with-current-buffer (get-buffer-create xref-buffer-name) - (setq default-directory dd) + (xref--ensure-default-directory dd (current-buffer)) (xref--transient-buffer-mode) (xref--show-common-initialize (xref--analyze xrefs) fetcher alist) (pop-to-buffer (current-buffer) @@ -1295,6 +1361,13 @@ definitions." (defvar xref--read-pattern-history nil) +;;;###autoload +(defun xref-show-xrefs (fetcher display-action) + "Display some Xref values produced by FETCHER using DISPLAY-ACTION. +The meanings of both arguments are the same as documented in +`xref-show-xrefs-function'." + (xref--show-xrefs fetcher display-action)) + (defun xref--show-xrefs (fetcher display-action &optional _always-show-list) (xref--push-markers) (unless (functionp fetcher) @@ -1340,12 +1413,17 @@ definitions." (xref--prompt-p this-command)) (let ((id (completing-read - (if def - (format "%s (default %s): " - (substring prompt 0 (string-match - "[ :]+\\'" prompt)) - def) - prompt) + ;; `format-prompt' is new in Emacs 28.1 + (if (fboundp 'format-prompt) + (format-prompt (substring prompt 0 (string-match + "[ :]+\\'" prompt)) + def) + (if def + (format "%s (default %s): " + (substring prompt 0 (string-match + "[ :]+\\'" prompt)) + def) + prompt)) (xref-backend-identifier-completion-table backend) nil nil nil 'xref--read-identifier-history def))) @@ -1406,7 +1484,7 @@ definition for IDENTIFIER, display it in the selected window. Otherwise, display the list of the possible definitions in a buffer where the user can select from the list. -Use \\[xref-pop-marker-stack] to return back to where you invoked this command." +Use \\[xref-go-back] to return back to where you invoked this command." (interactive (list (xref--read-identifier "Find definitions of: "))) (xref--find-definitions identifier nil)) @@ -1433,6 +1511,23 @@ is nil, prompt only if there's no usable symbol at point." (interactive (list (xref--read-identifier "Find references of: "))) (xref--find-xrefs identifier 'references identifier nil)) +(defun xref-find-references-and-replace (from to) + "Replace all references to identifier FROM with TO." + (interactive + (let* ((query-replace-read-from-default 'find-tag-default) + (common + (query-replace-read-args "Query replace identifier" nil))) + (list (nth 0 common) (nth 1 common)))) + (require 'xref) + (with-current-buffer + (let ((xref-show-xrefs-function + ;; Some future-proofing (bug#44905). + (custom--standard-value 'xref-show-xrefs-function)) + ;; Disable auto-jumping, it will mess up replacement logic. + xref-auto-jump-to-first-xref) + (xref-find-references from)) + (xref-query-replace-in-results ".*" to))) + ;;;###autoload (defun xref-find-definitions-at-mouse (event) "Find the definition of identifier at or around mouse click. @@ -1460,7 +1555,7 @@ This command is intended to be bound to a mouse event." (xref-find-references identifier)) (user-error "No identifier here")))) -(declare-function apropos-parse-pattern "apropos" (pattern)) +(declare-function apropos-parse-pattern "apropos" (pattern &optional do-all)) ;;;###autoload (defun xref-find-apropos (pattern) @@ -1497,7 +1592,8 @@ output of this command when the backend is etags." ;;; Key bindings ;;;###autoload (define-key esc-map "." #'xref-find-definitions) -;;;###autoload (define-key esc-map "," #'xref-pop-marker-stack) +;;;###autoload (define-key esc-map "," #'xref-go-back) +;;;###autoload (define-key esc-map [?\C-,] #'xref-go-forward) ;;;###autoload (define-key esc-map "?" #'xref-find-references) ;;;###autoload (define-key esc-map [?\C-.] #'xref-find-apropos) ;;;###autoload (define-key ctl-x-4-map "." #'xref-find-definitions-other-window) @@ -1633,7 +1729,8 @@ IGNORES is a list of glob patterns for files to ignore." . ;; '!*/' is there to filter out dirs (e.g. submodules). "xargs -0 rg <C> --null -nH --no-heading --no-messages -g '!*/' -e <R>" - )) + ) + (ugrep . "xargs -0 ugrep <C> --null -ns -e <R>")) "Associative list mapping program identifiers to command templates. Program identifier should be a symbol, named after the search program. @@ -1662,6 +1759,7 @@ utility function used by commands like `dired-do-find-regexp' and :type '(choice (const :tag "Use Grep" grep) (const :tag "Use ripgrep" ripgrep) + (const :tag "Use ugrep" ugrep) (symbol :tag "User defined")) :version "28.1" :package-version '(xref . "1.0.4")) @@ -1781,7 +1879,7 @@ to control which program to use when looking for matches." (xref--find-ignores-arguments ignores dir))) (defun xref--find-ignores-arguments (ignores dir) - "Convert IGNORES and DIR to a list of arguments for 'find'. + "Convert IGNORES and DIR to a list of arguments for `find'. IGNORES is a list of glob patterns. DIR is an absolute directory, used as the root of the ignore globs." (cl-assert (not (string-match-p "\\`~" dir))) @@ -1841,21 +1939,22 @@ Such as the current syntax table and the applied syntax properties." (defvar xref--last-file-buffer nil) (defvar xref--temp-buffer-file-name nil) +(defvar xref--hits-remote-id nil) (defun xref--convert-hits (hits regexp) (let (xref--last-file-buffer (tmp-buffer (generate-new-buffer " *xref-temp*")) - (remote-id (file-remote-p default-directory)) + (xref--hits-remote-id (file-remote-p default-directory)) (syntax-needed (xref--regexp-syntax-dependent-p regexp))) (unwind-protect (mapcan (lambda (hit) - (xref--collect-matches hit regexp tmp-buffer remote-id syntax-needed)) + (xref--collect-matches hit regexp tmp-buffer syntax-needed)) hits) (kill-buffer tmp-buffer)))) -(defun xref--collect-matches (hit regexp tmp-buffer remote-id syntax-needed) +(defun xref--collect-matches (hit regexp tmp-buffer syntax-needed) (pcase-let* ((`(,line ,file ,text) hit) - (file (and file (concat remote-id file))) + (file (and file (concat xref--hits-remote-id file))) (buf (xref--find-file-buffer file)) (inhibit-modification-hooks t)) (if buf @@ -1928,10 +2027,17 @@ Such as the current syntax table and the applied syntax properties." (defun xref--find-file-buffer (file) (unless (equal (car xref--last-file-buffer) file) - (setq xref--last-file-buffer - ;; `find-buffer-visiting' is considerably slower, - ;; especially on remote files. - (cons file (get-file-buffer file)))) + ;; `find-buffer-visiting' is considerably slower, + ;; especially on remote files. + (let ((buf (get-file-buffer file))) + (when (and buf + (or + (buffer-modified-p buf) + (unless xref--hits-remote-id + (not (verify-visited-file-modtime (current-buffer)))))) + ;; We can't use buffers whose contents diverge from disk (bug#54025). + (setq buf nil)) + (setq xref--last-file-buffer (cons file buf)))) (cdr xref--last-file-buffer)) (provide 'xref) diff --git a/lisp/progmodes/xscheme.el b/lisp/progmodes/xscheme.el index e6db65aced2..6e21131e4aa 100644 --- a/lisp/progmodes/xscheme.el +++ b/lisp/progmodes/xscheme.el @@ -574,9 +574,8 @@ See also the commands \\[xscheme-yank-pop] and \\[xscheme-yank-push]." (if (consp arg) (exchange-point-and-mark))) -;; Old name, to avoid errors in users' init files. -(fset 'xscheme-yank-previous-send - 'xscheme-yank) +(define-obsolete-function-alias 'xscheme-yank-previous-send + #'xscheme-yank "29.1") (defun xscheme-yank-pop (arg) "Insert or replace a just-yanked expression with an older expression. |