summaryrefslogtreecommitdiff
path: root/lisp/progmodes
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2017-11-08 19:22:26 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2017-11-08 19:22:26 -0800
commit6ee827059463e4b95c9251ef22a21f9c67cf668d (patch)
tree0612e1b84922cc43eeed5ea219104d4c5985cbdf /lisp/progmodes
parent83dc3f0e9b518e45bcfb0aaebcc706f79622f157 (diff)
parent255ba01148f69f452937e67feb7af5d4c1466fed (diff)
downloademacs-6ee827059463e4b95c9251ef22a21f9c67cf668d.tar.gz
Merge from origin/emacs-26
255ba01148 Fix handling of nil PRED2 arg for completion-table-with-pr... 949b70a7d8 ; Minor comment copyedit in window.c 20f9bf30f0 Describe Lao rendering problem 3d61657694 Fix docstring of arabic-shaper-ZWNJ-handling db949166ec Handle single-line comments correctly (Bug#26049) 795bb233a5 * test/lisp/net/tramp-tests.el (tramp-test16-file-expand-w... ba00ea7d0d * etc/PROBLEMS: Add URL to relevant issues for xterm+evil bug 4a6015811e ; In text.texi fix typo spotted by Štěpán Němec 93cd8415b2 Document new treatment of 'comment-auto-fill-only-comments' ca2d94ba61 Do not load Tramp unless `tramp-mode' is non-nil 709478eaa8 Prefer `customize-set-variable' in tramp.texi 5d36f2227f Fix last change in hscroll_window_tree 72d07d1950 Ediff: add some missing documentation 18af404ef3 Support python virtualenv on w32 (Bug#24464) efd0371c23 Improve dired deletion error handling (Bug#28797) 5d744e032f Don't replace user input when completion prefix is empty (... ec6cf35c5f ; Describe xt-mouse problems with Evil mode c572e1f329 Return non-nil from gnutls-available-p under GnuTLS 2.x a2cc6d74c5 Fix Flymake help-echo functions across windows (bug#29142) 58bb3462ee Add tests for Flymake backends for Ruby and Perl 58e742b21d Add a Flymake backend for Perl 9dee764165 Add a Flymake backend for Ruby 3ad712ebc9 Add a Flymake backend for Python (bug#28808) 8db2b3a79b Allow "%" in Tramp host names 8c50842790 ; Fix typo in test/file-organization.org 00fa4449cd ; Fix typo 918a2dda07 Use hybrid malloc for FreeBSD (Bug#28308) 725ab635d9 Add html-, mhtml- and python-mode support to semantic symref 369da28702 Improve documentation of 'window-scroll-functions' 93818eed8a Fix subtle problems in tabulated-list-mode with line numbers 1f1de8e872 Make gdb-non-stop-setting default to nil on MS-Windows 680e8e119b Fix gdb-mi prompt after "attach PID" command ff33074546 Fix doc string of 'window-configuration-change-hook' 787b75ad71 Improve documentation of window hooks d9be8704ae Fix hscroll suspension after cursor motion 6f43d29d29 ; * CONTRIBUTE, admin/make-tarball.txt: Doc tweaks re 'ema... a0d30d6369 Introduce a function to CC Mode which displays the current... 383abc8898 ; Fix some comment typos b02c2714c3 Fix typos in ediff-wind.el 4f38bdec74 Examine tex-chktex--process in the correct buffer 94b490529a * nt/INSTALL.W64: Update to current mingw64 install instru... c25113d4ac Don't resignal errors in flymake-diag-region e950f329c0 New xref-quit-and-goto-xref command bound to TAB (bug#28814) 5d34e1b288 Allow split-window-sensibly to split threshold in further ... 2a973edeac Honor window-switching intents in xref-find-definitions (b... 78e9065e9f * lisp/emacs-lisp/generator.el (iter-do): Add a debug decl... caa63cc96c * lisp/progmodes/flymake.el (flymake-start): Fix dead buff... 9f4f130b79 Fix buffer name comparison in async shell-command # Conflicts: # etc/NEWS # lisp/vc/ediff-wind.el
Diffstat (limited to 'lisp/progmodes')
-rw-r--r--lisp/progmodes/cc-cmds.el19
-rw-r--r--lisp/progmodes/cc-mode.el3
-rw-r--r--lisp/progmodes/cperl-mode.el4
-rw-r--r--lisp/progmodes/flymake-proc.el2
-rw-r--r--lisp/progmodes/flymake.el26
-rw-r--r--lisp/progmodes/gdb-mi.el19
-rw-r--r--lisp/progmodes/perl-mode.el71
-rw-r--r--lisp/progmodes/python.el151
-rw-r--r--lisp/progmodes/ruby-mode.el63
-rw-r--r--lisp/progmodes/xref.el91
10 files changed, 397 insertions, 52 deletions
diff --git a/lisp/progmodes/cc-cmds.el b/lisp/progmodes/cc-cmds.el
index ca64b544200..2b663135932 100644
--- a/lisp/progmodes/cc-cmds.el
+++ b/lisp/progmodes/cc-cmds.el
@@ -1821,7 +1821,6 @@ the open-parenthesis that starts a defun; see `beginning-of-defun'."
"Return the name of the current defun, or NIL if there isn't one.
\"Defun\" here means a function, or other top level construct
with a brace block."
- (interactive)
(c-save-buffer-state
(beginning-of-defun-function end-of-defun-function
where pos name-end case-fold-search)
@@ -2048,6 +2047,23 @@ with a brace block."
(eq (char-after) ?\{)
(cons (point-min) (point-max))))))))
+(defun c-display-defun-name (&optional arg)
+ "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 (c-defun-name))
+ (limits (c-declaration-limits t))
+ (point-bol (c-point 'bol)))
+ (when name
+ (message "%s. Line %s/%s." name
+ (1+ (count-lines (car limits) point-bol))
+ (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 ()
"Put mark at end of the current top-level declaration or macro, point at beginning.
If point is not inside any then the closest following one is
@@ -2092,7 +2108,6 @@ function does not require the declaration to contain a brace block."
(defun c-cpp-define-name ()
"Return the name of the current CPP macro, or NIL if we're not in one."
- (interactive)
(let (case-fold-search)
(save-excursion
(and c-opt-cpp-macro-define-start
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index b0e5fe47a7c..f74e931a8bb 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -389,7 +389,8 @@ control). See \"cc-mode.el\" for more info."
;;(define-key c-mode-base-map "\C-c\C-v" 'c-version)
;; (define-key c-mode-base-map "\C-c\C-y" 'c-toggle-hungry-state) Commented out by ACM, 2005-11-22.
(define-key c-mode-base-map "\C-c\C-w" 'c-subword-mode)
- (define-key c-mode-base-map "\C-c\C-k" 'c-toggle-comment-style))
+ (define-key c-mode-base-map "\C-c\C-k" 'c-toggle-comment-style)
+ (define-key c-mode-base-map "\C-c\C-z" 'c-display-defun-name))
;; We don't require the outline package, but we configure it a bit anyway.
(cc-bytecomp-defvar outline-level)
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index 1681922fa5a..e6ab8c4ea60 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -1896,7 +1896,9 @@ or as help on variables `cperl-tips', `cperl-problems',
(if cperl-pod-here-scan
(or cperl-syntaxify-by-font-lock
(progn (or cperl-faces-init (cperl-init-faces-weak))
- (cperl-find-pods-heres)))))
+ (cperl-find-pods-heres))))
+ ;; Setup Flymake
+ (add-hook 'flymake-diagnostic-functions 'perl-flymake nil t))
;; Fix for perldb - make default reasonable
(defun cperl-db ()
diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el
index f504a1c21a8..e28451d9417 100644
--- a/lisp/progmodes/flymake-proc.el
+++ b/lisp/progmodes/flymake-proc.el
@@ -82,7 +82,7 @@ Overrides `flymake-proc-allowed-file-name-masks'."
("\\.xml\\'" flymake-proc-xml-init)
("\\.html?\\'" flymake-proc-xml-init)
("\\.cs\\'" flymake-proc-simple-make-init)
- ("\\.p[ml]\\'" flymake-proc-perl-init)
+ ;; ("\\.p[ml]\\'" flymake-proc-perl-init)
("\\.php[345]?\\'" flymake-proc-php-init)
("\\.h\\'" flymake-proc-master-make-header-init flymake-proc-master-cleanup)
("\\.java\\'" flymake-proc-simple-make-java-init flymake-proc-simple-java-cleanup)
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el
index 19dac81fe6d..0d200f01b34 100644
--- a/lisp/progmodes/flymake.el
+++ b/lisp/progmodes/flymake.el
@@ -342,7 +342,7 @@ region is invalid."
(let* ((beg (fallback-bol))
(end (fallback-eol beg)))
(cons beg end)))))))
- (error (flymake-error "Invalid region line=%s col=%s" line col))))
+ (error (flymake-log :warning "Invalid region line=%s col=%s" line col))))
(defvar flymake-diagnostic-functions nil
"Special hook of Flymake backends that check a buffer.
@@ -522,11 +522,12 @@ associated `flymake-category' return DEFAULT."
(flymake--fringe-overlay-spec
(overlay-get ov 'bitmap)))
(default-maybe 'help-echo
- (lambda (_window _ov pos)
- (mapconcat
- #'flymake--diag-text
- (flymake-diagnostics pos)
- "\n")))
+ (lambda (window _ov pos)
+ (with-selected-window window
+ (mapconcat
+ #'flymake--diag-text
+ (flymake-diagnostics pos)
+ "\n"))))
(default-maybe 'severity (warning-numeric-level :error))
(default-maybe 'priority (+ 100 (overlay-get ov 'severity))))
;; Some properties can't be overridden.
@@ -603,8 +604,8 @@ not expected."
(null expected-token))
;; should never happen
(flymake-error "Unexpected report from stopped backend %s" backend))
- ((and (not (eq expected-token token))
- (not force))
+ ((not (or (eq expected-token token)
+ force))
(flymake-error "Obsolete report from backend %s with explanation %s"
backend explanation))
((eq :panic report-action)
@@ -744,8 +745,11 @@ Interactively, with a prefix arg, FORCE is t."
()
(remove-hook 'post-command-hook #'start-post-command
nil)
- (with-current-buffer buffer
- (flymake-start (remove 'post-command deferred) force)))
+ ;; The buffer may have disappeared already, e.g. because of
+ ;; code like `(with-temp-buffer (python-mode) ...)'.
+ (when (buffer-live-p buffer)
+ (with-current-buffer buffer
+ (flymake-start (remove 'post-command deferred) force))))
(start-on-display
()
(remove-hook 'window-configuration-change-hook #'start-on-display
@@ -948,7 +952,7 @@ applied."
(message
"%s"
(funcall (overlay-get target 'help-echo)
- nil nil (point)))))
+ (selected-window) target (point)))))
(interactive
(user-error "No more Flymake errors%s"
(if filter
diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index 28d1974893d..58552759b95 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -400,14 +400,22 @@ valid signal handlers.")
(const :tag "Unlimited" nil))
:version "22.1")
-(defcustom gdb-non-stop-setting t
- "When in non-stop mode, stopped threads can be examined while
+(defcustom gdb-non-stop-setting (not (eq system-type 'windows-nt))
+ "If non-nil, GDB sessions are expected to support the non-stop mode.
+When in the non-stop mode, stopped threads can be examined while
other threads continue to execute.
+If this is non-nil, GDB will be sent the \"set non-stop 1\" command,
+and if that results in an error, the non-stop setting will be
+turned off automatically.
+
+On MS-Windows, this is off by default, because MS-Windows targets
+don't support the non-stop mode.
+
GDB session needs to be restarted for this setting to take effect."
:type 'boolean
:group 'gdb-non-stop
- :version "23.2")
+ :version "26.1")
;; TODO Some commands can't be called with --all (give a notice about
;; it in setting doc)
@@ -2188,7 +2196,10 @@ a GDB/MI reply message."
(defun gdbmi-bnf-console-stream-output (c-string)
"Handler for the console-stream-output GDB/MI output grammar rule."
- (gdb-console c-string))
+ (gdb-console c-string)
+ ;; We've written to the GUD console, so we should print the prompt
+ ;; after the next result-class or async-class.
+ (setq gdb-first-done-or-error t))
(defun gdbmi-bnf-target-stream-output (_c-string)
"Handler for the target-stream-output GDB/MI output grammar rule."
diff --git a/lisp/progmodes/perl-mode.el b/lisp/progmodes/perl-mode.el
index 24b934ce6c2..8e7cd13088f 100644
--- a/lisp/progmodes/perl-mode.el
+++ b/lisp/progmodes/perl-mode.el
@@ -581,6 +581,73 @@ create a new comment."
(match-string-no-properties 1))))
+;;; Flymake support
+(defcustom perl-flymake-command '("perl" "-w" "-c")
+ "External tool used to check Perl source code.
+This is a non empty list of strings, the checker tool possibly
+followed by required arguments. Once launched it will receive
+the Perl source to be checked as its standard input."
+ :group 'perl
+ :type '(repeat string))
+
+(defvar-local perl--flymake-proc nil)
+
+;;;###autoload
+(defun perl-flymake (report-fn &rest _args)
+ "Perl backend for Flymake. Launches
+`perl-flymake-command' (which see) and passes to its standard
+input the contents of the current buffer. The output of this
+command is analysed for error and warning messages."
+ (unless (executable-find (car perl-flymake-command))
+ (error "Cannot find a suitable checker"))
+
+ (when (process-live-p perl--flymake-proc)
+ (kill-process perl--flymake-proc))
+
+ (let ((source (current-buffer)))
+ (save-restriction
+ (widen)
+ (setq
+ perl--flymake-proc
+ (make-process
+ :name "perl-flymake" :noquery t :connection-type 'pipe
+ :buffer (generate-new-buffer " *perl-flymake*")
+ :command perl-flymake-command
+ :sentinel
+ (lambda (proc _event)
+ (when (eq 'exit (process-status proc))
+ (unwind-protect
+ (if (with-current-buffer source (eq proc perl--flymake-proc))
+ (with-current-buffer (process-buffer proc)
+ (goto-char (point-min))
+ (cl-loop
+ while (search-forward-regexp
+ "^\\(.+\\) at - line \\([0-9]+\\)"
+ nil t)
+ for msg = (match-string 1)
+ for (beg . end) = (flymake-diag-region
+ source
+ (string-to-number (match-string 2)))
+ for type =
+ (if (string-match
+ "\\(Scalar value\\|Useless use\\|Unquoted string\\)"
+ msg)
+ :warning
+ :error)
+ collect (flymake-make-diagnostic source
+ beg
+ end
+ type
+ msg)
+ into diags
+ finally (funcall report-fn diags)))
+ (flymake-log :debug "Canceling obsolete check %s"
+ proc))
+ (kill-buffer (process-buffer proc)))))))
+ (process-send-region perl--flymake-proc (point-min) (point-max))
+ (process-send-eof perl--flymake-proc))))
+
+
(defvar perl-mode-hook nil
"Normal hook to run when entering Perl mode.")
@@ -665,7 +732,9 @@ Turning on Perl mode runs the normal hook `perl-mode-hook'."
;; Setup outline-minor-mode.
(setq-local outline-regexp perl-outline-regexp)
(setq-local outline-level 'perl-outline-level)
- (setq-local add-log-current-defun-function #'perl-current-defun-name))
+ (setq-local add-log-current-defun-function #'perl-current-defun-name)
+ ;; Setup Flymake
+ (add-hook 'flymake-diagnostic-functions #'perl-flymake nil t))
;; This is used by indent-for-comment
;; to decide how much to indent a comment in Perl code
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 895117b9ee3..d4226e5ce7b 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -2113,20 +2113,25 @@ remote host, the returned value is intended for
(defun python-shell-calculate-exec-path ()
"Calculate `exec-path'.
Prepends `python-shell-exec-path' and adds the binary directory
-for virtualenv if `python-shell-virtualenv-root' is set. If
-`default-directory' points to a remote host, the returned value
-appends `python-shell-remote-exec-path' instead of `exec-path'."
+for virtualenv if `python-shell-virtualenv-root' is set - this
+will use the python interpreter from inside the virtualenv when
+starting the shell. If `default-directory' points to a remote host,
+the returned value appends `python-shell-remote-exec-path' instead
+of `exec-path'."
(let ((new-path (copy-sequence
(if (file-remote-p default-directory)
python-shell-remote-exec-path
- exec-path))))
+ exec-path)))
+
+ ;; Windows and POSIX systems use different venv directory structures
+ (virtualenv-bin-dir (if (eq system-type 'windows-nt) "Scripts" "bin")))
(python-shell--add-to-path-with-priority
new-path python-shell-exec-path)
(if (not python-shell-virtualenv-root)
new-path
(python-shell--add-to-path-with-priority
new-path
- (list (expand-file-name "bin" python-shell-virtualenv-root)))
+ (list (expand-file-name virtualenv-bin-dir python-shell-virtualenv-root)))
new-path)))
(defun python-shell-tramp-refresh-remote-path (vec paths)
@@ -5142,6 +5147,138 @@ returned as is."
(ignore-errors (string-match regexp "") t))
+;;; Flymake integration
+
+(defgroup python-flymake nil
+ "Integration between Python and Flymake."
+ :group 'python
+ :link '(custom-group-link :tag "Flymake" flymake)
+ :version "26.1")
+
+(defcustom python-flymake-command '("pyflakes")
+ "The external tool that will be used to perform the syntax check.
+This is a non empty list of strings, the checker tool possibly followed by
+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\" \"-\")."
+ :group 'python-flymake
+ :type '(repeat string))
+
+;; The default regexp accomodates for older pyflakes, which did not
+;; report the column number, and at the same time it's compatible with
+;; flake8 output, although it may be redefined to explicitly match the
+;; TYPE
+(defcustom python-flymake-command-output-pattern
+ (list
+ "^\\(?:<?stdin>?\\):\\(?1:[0-9]+\\):\\(?:\\(?2:[0-9]+\\):\\)? \\(?3:.*\\)$"
+ 1 2 nil 3)
+ "Specify how to parse the output of `python-flymake-command'.
+The value has the form (REGEXP LINE COLUMN TYPE MESSAGE): if
+REGEXP matches, the LINE'th subexpression gives the line number,
+the COLUMN'th subexpression gives the column number on that line,
+the TYPE'th subexpression gives the type of the message and the
+MESSAGE'th gives the message text itself.
+
+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."
+ :group 'python-flymake
+ :type '(list regexp
+ (integer :tag "Line's index")
+ (choice
+ (const :tag "No column" nil)
+ (integer :tag "Column's index"))
+ (choice
+ (const :tag "No type" nil)
+ (integer :tag "Type's index"))
+ (integer :tag "Message's index")))
+
+(defcustom python-flymake-msg-alist
+ '(("\\(^redefinition\\|.*unused.*\\|used$\\)" . :warning))
+ "Alist used to associate messages to their types.
+Each element should be a cons-cell (REGEXP . TYPE), where TYPE must be
+one defined in the variable `flymake-diagnostic-types-alist'.
+For example, when using `flake8' a possible configuration could be:
+
+ ((\"\\(^redefinition\\|.*unused.*\\|used$\\)\" . :warning)
+ (\"^E999\" . :error)
+ (\"^[EW][0-9]+\" . :note))
+
+By default messages are considered errors."
+ :group 'python-flymake
+ :type `(alist :key-type (regexp)
+ :value-type (symbol)))
+
+(defvar-local python--flymake-proc nil)
+
+(defun python--flymake-parse-output (source proc report-fn)
+ "Collect diagnostics parsing checker tool's output line by line."
+ (let ((rx (nth 0 python-flymake-command-output-pattern))
+ (lineidx (nth 1 python-flymake-command-output-pattern))
+ (colidx (nth 2 python-flymake-command-output-pattern))
+ (typeidx (nth 3 python-flymake-command-output-pattern))
+ (msgidx (nth 4 python-flymake-command-output-pattern)))
+ (with-current-buffer (process-buffer proc)
+ (goto-char (point-min))
+ (cl-loop
+ while (search-forward-regexp rx nil t)
+ for msg = (match-string msgidx)
+ for (beg . end) = (flymake-diag-region
+ source
+ (string-to-number
+ (match-string lineidx))
+ (and colidx
+ (match-string colidx)
+ (string-to-number
+ (match-string colidx))))
+ for type = (or (and typeidx
+ (match-string typeidx)
+ (assoc-default
+ (match-string typeidx)
+ python-flymake-msg-alist
+ #'string-match))
+ (assoc-default msg
+ python-flymake-msg-alist
+ #'string-match)
+ :error)
+ collect (flymake-make-diagnostic
+ source beg end type msg)
+ into diags
+ finally (funcall report-fn diags)))))
+
+(defun python-flymake (report-fn &rest _args)
+ "Flymake backend for Python.
+This backend uses `python-flymake-command' (which see) to launch a process
+that is passed the current buffer's content via stdin.
+REPORT-FN is Flymake's callback function."
+ (unless (executable-find (car python-flymake-command))
+ (error "Cannot find a suitable checker"))
+
+ (when (process-live-p python--flymake-proc)
+ (kill-process python--flymake-proc))
+
+ (let ((source (current-buffer)))
+ (save-restriction
+ (widen)
+ (setq python--flymake-proc
+ (make-process
+ :name "python-flymake"
+ :noquery t
+ :connection-type 'pipe
+ :buffer (generate-new-buffer " *python-flymake*")
+ :command python-flymake-command
+ :sentinel
+ (lambda (proc _event)
+ (when (eq 'exit (process-status proc))
+ (unwind-protect
+ (when (with-current-buffer source
+ (eq proc python--flymake-proc))
+ (python--flymake-parse-output source proc report-fn))
+ (kill-buffer (process-buffer proc)))))))
+ (process-send-region python--flymake-proc (point-min) (point-max))
+ (process-send-eof python--flymake-proc))))
+
+
(defun python-electric-pair-string-delimiter ()
(when (and electric-pair-mode
(memq last-command-event '(?\" ?\'))
@@ -5255,7 +5392,9 @@ returned as is."
(make-local-variable 'python-shell-internal-buffer)
(when python-indent-guess-indent-offset
- (python-indent-guess-indent-offset)))
+ (python-indent-guess-indent-offset))
+
+ (add-hook 'flymake-diagnostic-functions #'python-flymake nil t))
(provide 'python)
diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el
index 0024957c39b..1f4aa6d9fbd 100644
--- a/lisp/progmodes/ruby-mode.el
+++ b/lisp/progmodes/ruby-mode.el
@@ -2253,6 +2253,68 @@ See `font-lock-syntax-table'.")
(progn (set-match-data value) t))
(ruby-match-expression-expansion limit)))))
+;;; Flymake support
+(defcustom ruby-flymake-command '("ruby" "-w" "-c")
+ "External tool used to check Ruby source code.
+This is a non empty list of strings, the checker tool possibly
+followed by required arguments. Once launched it will receive
+the Ruby source to be checked as its standard input."
+ :group 'ruby
+ :type '(repeat string))
+
+(defvar-local ruby--flymake-proc nil)
+
+(defun ruby-flymake (report-fn &rest _args)
+ "Ruby backend for Flymake. Launches
+`ruby-flymake-command' (which see) and passes to its standard
+input the contents of the current buffer. The output of this
+command is analysed for error and warning messages."
+ (unless (executable-find (car ruby-flymake-command))
+ (error "Cannot find a suitable checker"))
+
+ (when (process-live-p ruby--flymake-proc)
+ (kill-process ruby--flymake-proc))
+
+ (let ((source (current-buffer)))
+ (save-restriction
+ (widen)
+ (setq
+ ruby--flymake-proc
+ (make-process
+ :name "ruby-flymake" :noquery t :connection-type 'pipe
+ :buffer (generate-new-buffer " *ruby-flymake*")
+ :command ruby-flymake-command
+ :sentinel
+ (lambda (proc _event)
+ (when (eq 'exit (process-status proc))
+ (unwind-protect
+ (if (with-current-buffer source (eq proc ruby--flymake-proc))
+ (with-current-buffer (process-buffer proc)
+ (goto-char (point-min))
+ (cl-loop
+ while (search-forward-regexp
+ "^\\(?:.*.rb\\|-\\):\\([0-9]+\\): \\(.*\\)$"
+ nil t)
+ for msg = (match-string 2)
+ for (beg . end) = (flymake-diag-region
+ source
+ (string-to-number (match-string 1)))
+ for type = (if (string-match "^warning" msg)
+ :warning
+ :error)
+ collect (flymake-make-diagnostic source
+ beg
+ end
+ type
+ msg)
+ into diags
+ finally (funcall report-fn diags)))
+ (flymake-log :debug "Canceling obsolete check %s"
+ proc))
+ (kill-buffer (process-buffer proc)))))))
+ (process-send-region ruby--flymake-proc (point-min) (point-max))
+ (process-send-eof ruby--flymake-proc))))
+
;;;###autoload
(define-derived-mode ruby-mode prog-mode "Ruby"
"Major mode for editing Ruby code."
@@ -2265,6 +2327,7 @@ See `font-lock-syntax-table'.")
(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 nil 'local)
(setq-local font-lock-defaults '((ruby-font-lock-keywords) nil nil))
(setq-local font-lock-keywords ruby-font-lock-keywords)
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index 3dbf65ef6f5..db025d40aa3 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -448,43 +448,74 @@ If SELECT is non-nil, select the target window."
(when xref-w
(set-window-dedicated-p xref-w xref-w-dedicated)))))
-(defun xref--show-pos-in-buf (pos buf select)
- (let ((xref-buf (current-buffer))
- win)
+(defvar-local xref--original-window-intent nil
+ "Original window-switching intent before xref buffer creation.")
+
+(defvar-local xref--original-window nil
+ "The original window this xref buffer was created from.")
+
+(defun xref--show-pos-in-buf (pos buf)
+ "Goto and display position POS of buffer BUF in a window.
+Honor `xref--original-window-intent', run `xref-after-jump-hook'
+and finally return the window."
+ (let* ((xref-buf (current-buffer))
+ (pop-up-frames
+ (or (eq xref--original-window-intent 'frame)
+ pop-up-frames))
+ (action
+ (cond ((memq
+ xref--original-window-intent
+ '(window frame))
+ t)
+ ((and
+ (window-live-p xref--original-window)
+ (or (not (window-dedicated-p xref--original-window))
+ (eq (window-buffer xref--original-window) buf)))
+ `(,(lambda (buf _alist)
+ (set-window-buffer xref--original-window buf)
+ xref--original-window))))))
(with-selected-window
- (xref--with-dedicated-window
- (display-buffer buf))
+ (with-selected-window
+ ;; Just before `display-buffer', place ourselves in the
+ ;; original window to suggest preserving it. Of course, if
+ ;; user has deleted the original window, all bets are off,
+ ;; just use the selected one.
+ (or (and (window-live-p xref--original-window)
+ xref--original-window)
+ (selected-window))
+ (display-buffer buf action))
(xref--goto-char pos)
(run-hooks 'xref-after-jump-hook)
(let ((buf (current-buffer)))
- (setq win (selected-window))
(with-current-buffer xref-buf
- (setq-local other-window-scroll-buffer buf))))
- (when select
- (select-window win))))
+ (setq-local other-window-scroll-buffer buf)))
+ (selected-window))))
(defun xref--show-location (location &optional select)
+ "Help `xref-show-xref' and `xref-goto-xref' do their job.
+Go to LOCATION and if SELECT is non-nil select its window. If
+SELECT is `quit', also quit the *xref* window."
(condition-case err
(let* ((marker (xref-location-marker location))
- (buf (marker-buffer marker)))
- (xref--show-pos-in-buf marker buf select))
+ (buf (marker-buffer marker))
+ (xref-buffer (current-buffer)))
+ (cond (select
+ (if (eq select 'quit) (quit-window nil nil))
+ (with-current-buffer xref-buffer
+ (select-window (xref--show-pos-in-buf marker buf))))
+ (t
+ (save-selected-window
+ (xref--with-dedicated-window
+ (xref--show-pos-in-buf marker buf))))))
(user-error (message (error-message-string err)))))
-(defvar-local xref--window nil
- "The original window this xref buffer was created from.")
-
(defun xref-show-location-at-point ()
"Display the source of xref at point in the appropriate window, if any."
(interactive)
(let* ((xref (xref--item-at-point))
(xref--current-item xref))
(when xref
- ;; Try to avoid the window the current xref buffer was
- ;; originally created from.
- (if (window-live-p xref--window)
- (with-selected-window xref--window
- (xref--show-location (xref-item-location xref)))
- (xref--show-location (xref-item-location xref))))))
+ (xref--show-location (xref-item-location xref)))))
(defun xref-next-line ()
"Move to the next xref and display its source in the appropriate window."
@@ -503,12 +534,19 @@ If SELECT is non-nil, select the target window."
(back-to-indentation)
(get-text-property (point) 'xref-item)))
-(defun xref-goto-xref ()
- "Jump to the xref on the current line and select its window."
+(defun xref-goto-xref (&optional quit)
+ "Jump to the xref on the current line and select its window.
+Non-interactively, non-nil QUIT means to first quit the *xref*
+buffer."
(interactive)
(let ((xref (or (xref--item-at-point)
(user-error "No reference at point"))))
- (xref--show-location (xref-item-location xref) t)))
+ (xref--show-location (xref-item-location xref) (if quit 'quit t))))
+
+(defun xref-quit-and-goto-xref ()
+ "Quit *xref* buffer, then jump to xref on current line."
+ (interactive)
+ (xref-goto-xref t))
(defun xref-query-replace-in-results (from to)
"Perform interactive replacement of FROM with TO in all displayed xrefs.
@@ -632,6 +670,7 @@ references displayed in the current *xref* buffer."
(define-key map (kbd "p") #'xref-prev-line)
(define-key map (kbd "r") #'xref-query-replace-in-results)
(define-key map (kbd "RET") #'xref-goto-xref)
+ (define-key map (kbd "TAB") #'xref-quit-and-goto-xref)
(define-key map (kbd "C-o") #'xref-show-location-at-point)
;; suggested by Johan Claesson "to further reduce finger movement":
(define-key map (kbd ".") #'xref-next-line)
@@ -726,7 +765,8 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)."
(xref--xref-buffer-mode)
(pop-to-buffer (current-buffer))
(goto-char (point-min))
- (setq xref--window (assoc-default 'window alist))
+ (setq xref--original-window (assoc-default 'window alist)
+ xref--original-window-intent (assoc-default 'display-action alist))
(current-buffer)))))
@@ -753,7 +793,8 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)."
(t
(xref-push-marker-stack)
(funcall xref-show-xrefs-function xrefs
- `((window . ,(selected-window)))))))
+ `((window . ,(selected-window))
+ (display-action . ,display-action))))))
(defun xref--prompt-p (command)
(or (eq xref-prompt-for-identifier t)