aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/progmodes')
-rw-r--r--lisp/progmodes/eglot.el65
-rw-r--r--lisp/progmodes/elisp-mode.el4
-rw-r--r--lisp/progmodes/etags-regen.el2
-rw-r--r--lisp/progmodes/etags.el10
-rw-r--r--lisp/progmodes/make-mode.el18
-rw-r--r--lisp/progmodes/project.el147
-rw-r--r--lisp/progmodes/python.el40
-rw-r--r--lisp/progmodes/xref.el23
8 files changed, 200 insertions, 109 deletions
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 80099a26ee8..4752b0100d9 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -282,7 +282,8 @@ automatically)."
282 . ,(eglot-alternatives 282 . ,(eglot-alternatives
283 '(("solargraph" "socket" "--port" :autoport) "ruby-lsp"))) 283 '(("solargraph" "socket" "--port" :autoport) "ruby-lsp")))
284 (haskell-mode 284 (haskell-mode
285 . ("haskell-language-server-wrapper" "--lsp")) 285 . ,(eglot-alternatives
286 '(("haskell-language-server-wrapper" "--lsp") "static-ls")))
286 (elm-mode . ("elm-language-server")) 287 (elm-mode . ("elm-language-server"))
287 (mint-mode . ("mint" "ls")) 288 (mint-mode . ("mint" "ls"))
288 ((kotlin-mode kotlin-ts-mode) . ("kotlin-language-server")) 289 ((kotlin-mode kotlin-ts-mode) . ("kotlin-language-server"))
@@ -308,7 +309,7 @@ automatically)."
308 (racket-mode . ("racket" "-l" "racket-langserver")) 309 (racket-mode . ("racket" "-l" "racket-langserver"))
309 ((latex-mode plain-tex-mode context-mode texinfo-mode bibtex-mode tex-mode) 310 ((latex-mode plain-tex-mode context-mode texinfo-mode bibtex-mode tex-mode)
310 . ,(eglot-alternatives '("digestif" "texlab"))) 311 . ,(eglot-alternatives '("digestif" "texlab")))
311 (erlang-mode . ("erlang_ls" "--transport" "stdio")) 312 (erlang-mode . ("elp" "server"))
312 (wat-mode . ("wat_server")) 313 (wat-mode . ("wat_server"))
313 ((yaml-ts-mode yaml-mode) . ("yaml-language-server" "--stdio")) 314 ((yaml-ts-mode yaml-mode) . ("yaml-language-server" "--stdio"))
314 ((toml-ts-mode conf-toml-mode) . ("tombi" "lsp")) 315 ((toml-ts-mode conf-toml-mode) . ("tombi" "lsp"))
@@ -1438,6 +1439,12 @@ PRESERVE-BUFFERS as in `eglot-shutdown', which see."
1438 (maphash (lambda (f s) 1439 (maphash (lambda (f s)
1439 (when (eq s server) (remhash f eglot--servers-by-xrefed-file))) 1440 (when (eq s server) (remhash f eglot--servers-by-xrefed-file)))
1440 eglot--servers-by-xrefed-file) 1441 eglot--servers-by-xrefed-file)
1442 ;; Cleanup entries in 'flymake-list-only-diagnostics'
1443 (setq flymake-list-only-diagnostics
1444 (cl-delete-if
1445 (lambda (x) (eq server
1446 (get-text-property 0 'eglot--server (car x))))
1447 flymake-list-only-diagnostics))
1441 (cond ((eglot--shutdown-requested server) 1448 (cond ((eglot--shutdown-requested server)
1442 t) 1449 t)
1443 ((not (eglot--inhibit-autoreconnect server)) 1450 ((not (eglot--inhibit-autoreconnect server))
@@ -2024,21 +2031,25 @@ according to `eglot-advertise-cancellation'.")
2024 (timeout-fn nil timeout-fn-supplied-p) 2031 (timeout-fn nil timeout-fn-supplied-p)
2025 (timeout nil timeout-supplied-p) 2032 (timeout nil timeout-supplied-p)
2026 hint 2033 hint
2027 &aux moreargs 2034 &aux moreargs id
2028 id (buf (current-buffer))) 2035 (buf (current-buffer))
2036 (inflight eglot--inflight-async-requests))
2029 "Like `jsonrpc-async-request', but for Eglot LSP requests. 2037 "Like `jsonrpc-async-request', but for Eglot LSP requests.
2030SUCCESS-FN, ERROR-FN and TIMEOUT-FN run in buffer of call site. 2038SUCCESS-FN, ERROR-FN and TIMEOUT-FN run in buffer of call site.
2031HINT argument is a symbol passed as DEFERRED to `jsonrpc-async-request' 2039HINT argument is a symbol passed as DEFERRED to `jsonrpc-async-request'
2032and also used as a hint of the request cancellation mechanism (see 2040and also used as a hint of the request cancellation mechanism (see
2033`eglot-advertise-cancellation')." 2041`eglot-advertise-cancellation')."
2034 (cl-labels 2042 (cl-labels
2035 ((clearing-fn (fn) 2043 ((wrapfn (fn)
2036 (lambda (&rest args) 2044 (lambda (&rest args)
2037 (eglot--when-live-buffer buf 2045 (eglot--when-live-buffer buf
2038 (when (and 2046 (cond (eglot-advertise-cancellation
2039 fn (memq id (cl-getf eglot--inflight-async-requests hint))) 2047 (when-let* ((tail (and fn (plist-member inflight hint))))
2040 (apply fn args)) 2048 (when (memq id (cadr tail))
2041 (cl-remf eglot--inflight-async-requests hint))))) 2049 (apply fn args))
2050 (setf (cadr tail) (delete id (cadr tail)))))
2051 (t
2052 (apply fn args)))))))
2042 (eglot--cancel-inflight-async-requests (list hint)) 2053 (eglot--cancel-inflight-async-requests (list hint))
2043 (when timeout-supplied-p 2054 (when timeout-supplied-p
2044 (setq moreargs (nconc `(:timeout ,timeout) moreargs))) 2055 (setq moreargs (nconc `(:timeout ,timeout) moreargs)))
@@ -2047,13 +2058,12 @@ and also used as a hint of the request cancellation mechanism (see
2047 (setq id 2058 (setq id
2048 (car (apply #'jsonrpc-async-request 2059 (car (apply #'jsonrpc-async-request
2049 server method params 2060 server method params
2050 :success-fn (clearing-fn success-fn) 2061 :success-fn (wrapfn success-fn)
2051 :error-fn (clearing-fn error-fn) 2062 :error-fn (wrapfn error-fn)
2052 :timeout-fn (clearing-fn timeout-fn) 2063 :timeout-fn (wrapfn timeout-fn)
2053 moreargs))) 2064 moreargs)))
2054 (when (and hint eglot-advertise-cancellation) 2065 (when (and hint eglot-advertise-cancellation)
2055 (push id 2066 (push id (plist-get inflight hint)))
2056 (plist-get eglot--inflight-async-requests hint)))
2057 id)) 2067 id))
2058 2068
2059(cl-defun eglot--delete-overlays (&optional (prop 'eglot--overlays)) 2069(cl-defun eglot--delete-overlays (&optional (prop 'eglot--overlays))
@@ -3422,11 +3432,8 @@ object. The originator of this \"push\" is usually either regular
3422 (with-current-buffer buffer 3432 (with-current-buffer buffer
3423 (if (and version (/= version eglot--docver)) 3433 (if (and version (/= version eglot--docver))
3424 (cl-return-from eglot--flymake-handle-push)) 3434 (cl-return-from eglot--flymake-handle-push))
3425 (setq 3435 ;; if no explicit version received, assume it's current.
3426 ;; if no explicit version received, assume it's current. 3436 (setq version eglot--docver)
3427 version eglot--docver
3428 flymake-list-only-diagnostics
3429 (assoc-delete-all path flymake-list-only-diagnostics))
3430 (funcall then diagnostics)) 3437 (funcall then diagnostics))
3431 (cl-loop 3438 (cl-loop
3432 for diag-spec across diagnostics 3439 for diag-spec across diagnostics
@@ -3437,12 +3444,13 @@ object. The originator of this \"push\" is usually either regular
3437 (flymake-make-diagnostic 3444 (flymake-make-diagnostic
3438 path (cons line char) nil 3445 path (cons line char) nil
3439 (eglot--flymake-diag-type severity) 3446 (eglot--flymake-diag-type severity)
3440 (list source code message)))) 3447 (list source code message)
3448 `((eglot-lsp-diag . ,diag-spec)))))
3441 into diags 3449 into diags
3442 finally 3450 finally
3443 (setq flymake-list-only-diagnostics 3451 (setf (alist-get (propertize path 'eglot--server server)
3444 (assoc-delete-all path flymake-list-only-diagnostics)) 3452 flymake-list-only-diagnostics nil nil #'equal)
3445 (push (cons path diags) flymake-list-only-diagnostics)))) 3453 diags))))
3446 3454
3447(cl-defun eglot--flymake-pull (&aux (server (eglot--current-server-or-lose)) 3455(cl-defun eglot--flymake-pull (&aux (server (eglot--current-server-or-lose))
3448 (origin (current-buffer))) 3456 (origin (current-buffer)))
@@ -3506,6 +3514,17 @@ MODE is like `eglot--flymake-report-1'."
3506 (pushed-outdated-p (and pushed-docver (< pushed-docver eglot--docver)))) 3514 (pushed-outdated-p (and pushed-docver (< pushed-docver eglot--docver))))
3507 "Push previously collected diagnostics to `eglot--flymake-report-fn'. 3515 "Push previously collected diagnostics to `eglot--flymake-report-fn'.
3508If KEEP, knowingly push a dummy do-nothing update." 3516If KEEP, knowingly push a dummy do-nothing update."
3517 ;; Maybe hack in diagnostics we previously may have saved in
3518 ;; `flymake-list-only-diagnostics', pushed for this file before it was
3519 ;; visited (github#1531).
3520 (when-let* ((hack (and (<= eglot--docver 0)
3521 (null eglot--pushed-diagnostics)
3522 (cdr (assoc (buffer-file-name)
3523 flymake-list-only-diagnostics)))))
3524 (cl-loop
3525 for x in hack
3526 collect (alist-get 'eglot-lsp-diag (flymake-diagnostic-data x)) into res
3527 finally (setq eglot--pushed-diagnostics `(,(vconcat res) ,eglot--docver))))
3509 (eglot--widening 3528 (eglot--widening
3510 (if (and (null eglot--pulled-diagnostics) pushed-outdated-p) 3529 (if (and (null eglot--pulled-diagnostics) pushed-outdated-p)
3511 ;; Here, we don't have anything interesting to give to Flymake. 3530 ;; Here, we don't have anything interesting to give to Flymake.
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index c4fb6946aeb..f5c3dc3fbb2 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -1783,7 +1783,9 @@ and `eval-expression-print-level'.
1783 (funcall 1783 (funcall
1784 (syntax-propertize-rules 1784 (syntax-propertize-rules
1785 (emacs-lisp-byte-code-comment-re 1785 (emacs-lisp-byte-code-comment-re
1786 (1 (prog1 "< b" (elisp--byte-code-comment end (point)))))) 1786 (1 (prog1 "< b"
1787 (goto-char (match-end 2))
1788 (elisp--byte-code-comment end (point))))))
1787 start end)) 1789 start end))
1788 1790
1789;;;###autoload 1791;;;###autoload
diff --git a/lisp/progmodes/etags-regen.el b/lisp/progmodes/etags-regen.el
index b6adca8af7a..3d3ddc0521f 100644
--- a/lisp/progmodes/etags-regen.el
+++ b/lisp/progmodes/etags-regen.el
@@ -348,7 +348,7 @@ File extensions to generate the tags for."
348 348
349(defun etags-regen--build-program-options (ctags-p) 349(defun etags-regen--build-program-options (ctags-p)
350 (when (and etags-regen-regexp-alist ctags-p) 350 (when (and etags-regen-regexp-alist ctags-p)
351 (user-error "etags-regen-regexp-alist is not supported with Ctags")) 351 (user-error "etags-regen-regexp-alist not supported with Ctags; to use this option, customize `etags-regen-program'"))
352 (nconc 352 (nconc
353 (mapcan 353 (mapcan
354 (lambda (group) 354 (lambda (group)
diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el
index 79cfb91caa9..f7532fce6b1 100644
--- a/lisp/progmodes/etags.el
+++ b/lisp/progmodes/etags.el
@@ -2114,8 +2114,14 @@ file name, add `tag-partial-file-name-match-p' to the list value.")
2114 :type 'boolean 2114 :type 'boolean
2115 :version "28.1") 2115 :version "28.1")
2116 2116
2117;;;###autoload 2117;;;###autoload (defun etags--xref-backend ()
2118(defun etags--xref-backend () 'etags) 2118;;;###autoload (when (or tags-table-list tags-file-name)
2119;;;###autoload (load "etags")
2120;;;###autoload 'etags))
2121
2122(defun etags--xref-backend ()
2123 (when (or tags-table-list tags-file-name)
2124 'etags))
2119 2125
2120(cl-defmethod xref-backend-identifier-at-point ((_backend (eql 'etags))) 2126(cl-defmethod xref-backend-identifier-at-point ((_backend (eql 'etags)))
2121 (find-tag--default)) 2127 (find-tag--default))
diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el
index 8856856100e..e34eaba3150 100644
--- a/lisp/progmodes/make-mode.el
+++ b/lisp/progmodes/make-mode.el
@@ -331,7 +331,7 @@ not be enclosed in { } or ( )."
331 &rest fl-keywords) 331 &rest fl-keywords)
332 `(;; Do macro assignments. These get the "variable-name" face. 332 `(;; Do macro assignments. These get the "variable-name" face.
333 (,makefile-macroassign-regex 333 (,makefile-macroassign-regex
334 (1 font-lock-variable-name-face) 334 (1 'font-lock-variable-name-face)
335 ;; This is for after != 335 ;; This is for after !=
336 (2 'makefile-shell prepend t) 336 (2 'makefile-shell prepend t)
337 ;; This is for after normal assignment 337 ;; This is for after normal assignment
@@ -340,10 +340,10 @@ not be enclosed in { } or ( )."
340 ;; Rule actions. 340 ;; Rule actions.
341 ;; FIXME: When this spans multiple lines we need font-lock-multiline. 341 ;; FIXME: When this spans multiple lines we need font-lock-multiline.
342 (makefile-match-action 342 (makefile-match-action
343 (1 font-lock-type-face nil t) 343 (1 'font-lock-type-face nil t)
344 (2 'makefile-shell prepend) 344 (2 'makefile-shell prepend)
345 ;; Only makepp has builtin commands. 345 ;; Only makepp has builtin commands.
346 (3 font-lock-builtin-face prepend t)) 346 (3 'font-lock-builtin-face prepend t))
347 347
348 ;; Variable references even in targets/strings/comments. 348 ;; Variable references even in targets/strings/comments.
349 (,var 2 font-lock-variable-name-face prepend) 349 (,var 2 font-lock-variable-name-face prepend)
@@ -364,11 +364,11 @@ not be enclosed in { } or ( )."
364 (string-replace "-" "[_-]" (regexp-opt (cdr keywords) t)) 364 (string-replace "-" "[_-]" (regexp-opt (cdr keywords) t))
365 (regexp-opt keywords t))) 365 (regexp-opt keywords t)))
366 "\\>[ \t]*\\([^: \t\n#]*\\)") 366 "\\>[ \t]*\\([^: \t\n#]*\\)")
367 (1 font-lock-keyword-face) (2 font-lock-variable-name-face)))) 367 (1 'font-lock-keyword-face) (2 'font-lock-variable-name-face))))
368 368
369 ,@(if negation 369 ,@(if negation
370 `((,negation (1 font-lock-negation-char-face prepend) 370 `((,negation (1 'font-lock-negation-char-face prepend)
371 (2 font-lock-negation-char-face prepend t)))) 371 (2 'font-lock-negation-char-face prepend t))))
372 372
373 ,@(if space 373 ,@(if space
374 '(;; Highlight lines that contain just whitespace. 374 '(;; Highlight lines that contain just whitespace.
@@ -436,9 +436,9 @@ not be enclosed in { } or ( )."
436 436
437 ;; Colon modifier keywords. 437 ;; Colon modifier keywords.
438 '("\\(:\\s *\\)\\(build_c\\(?:ache\\|heck\\)\\|env\\(?:ironment\\)?\\|foreach\\|signature\\|scanner\\|quickscan\\|smartscan\\)\\>\\([^:\n]*\\)" 438 '("\\(:\\s *\\)\\(build_c\\(?:ache\\|heck\\)\\|env\\(?:ironment\\)?\\|foreach\\|signature\\|scanner\\|quickscan\\|smartscan\\)\\>\\([^:\n]*\\)"
439 (1 font-lock-type-face t) 439 (1 'font-lock-type-face t)
440 (2 font-lock-keyword-face t) 440 (2 'font-lock-keyword-face t)
441 (3 font-lock-variable-name-face t)) 441 (3 'font-lock-variable-name-face t))
442 442
443 ;; $(function ...) $((function ...)) ${...} ${{...}} $[...] $[[...]] 443 ;; $(function ...) $((function ...)) ${...} ${{...}} $[...] $[[...]]
444 '("[^$]\\$\\(?:((?\\|{{?\\|\\[\\[?\\)\\([-a-zA-Z0-9_.]+\\s \\)" 444 '("[^$]\\$\\(?:((?\\|{{?\\|\\[\\[?\\)\\([-a-zA-Z0-9_.]+\\s \\)"
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 997c876b1fa..9e5a8be5e13 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -84,6 +84,12 @@
84;; This project type can also be used for non-VCS controlled 84;; This project type can also be used for non-VCS controlled
85;; directories, see the variable `project-vc-extra-root-markers'. 85;; directories, see the variable `project-vc-extra-root-markers'.
86;; 86;;
87;; Some of the methods on this backend cache their computations for time
88;; determined either by variable `project-vc-cache-timeout' or
89;; `project-vc-non-essential-cache-timeout', depending on whether the
90;; MAYBE-PROMPT argument to `project-current' is non-nil, or the value
91;; of `non-essential' when project methods are called.
92;;
87;; Utils: 93;; Utils:
88;; 94;;
89;; `project-combine-directories' and `project-subtract-directories', 95;; `project-combine-directories' and `project-subtract-directories',
@@ -275,7 +281,8 @@ of the project instance object."
275 (if pr 281 (if pr
276 (project-remember-project pr) 282 (project-remember-project pr)
277 (project--remove-from-project-list 283 (project--remove-from-project-list
278 directory "Project `%s' not found; removed from list") 284 (abbreviate-file-name directory)
285 "Project `%s' not found; removed from list")
279 (setq pr (cons 'transient directory)))) 286 (setq pr (cons 'transient directory))))
280 pr)) 287 pr))
281 288
@@ -586,16 +593,74 @@ project backend implementation of `project-external-roots'.")
586 593
587See `project-vc-extra-root-markers' for the marker value format.") 594See `project-vc-extra-root-markers' for the marker value format.")
588 595
589;; FIXME: Should perhaps use `vc--repo-*prop' functions 596(defvar project-vc-cache-timeout '((file-remote-p . nil)
590;; (after promoting those to public). --spwhitton 597 (always . 2))
598 "Number of seconds to cache a value in VC-aware project methods.
599It can be nil, a number, or an alist where
600the key is a predicate, and the value is a number.
601A predicate function should take a directory string and if it returns
602non-nil, the corresponding value will be used as the timeout.
603Set to nil to disable time-based expiration.")
604
605(defvar project-vc-non-essential-cache-timeout '((file-remote-p . nil)
606 (always . 300))
607 "Number of seconds to cache non-essential information.
608The format of the value is same as `project-vc-cache-timeout', but while
609the former is intended for interactive commands, this variable uses
610higher numbers, intended for \"background\" things like
611`project-mode-line' indicators and `project-uniquify-dirname-transform'.
612It is used when `non-essential' is non-nil.")
613
614(defun project--get-cached (dir key)
615 (let ((cached (vc-file-getprop dir key))
616 (current-time (float-time)))
617 (when (and (numberp (cdr cached))
618 ;; Support package upgrade mid-session.
619 (let* ((project-vc-cache-timeout
620 (if non-essential
621 project-vc-non-essential-cache-timeout
622 project-vc-cache-timeout))
623 (timeout
624 (cond
625 ((numberp project-vc-cache-timeout)
626 project-vc-cache-timeout)
627 ((null project-vc-cache-timeout)
628 nil)
629 ((listp project-vc-cache-timeout)
630 (cdr
631 (seq-find (lambda (pair)
632 (and (functionp (car pair))
633 (funcall (car pair) dir)))
634 project-vc-cache-timeout)))
635 (t nil))))
636 (or (null timeout)
637 (< (- current-time (cdr cached)) timeout))))
638 (car cached))))
639
640(defun project--set-cached (dir key value)
641 (vc-file-setprop dir key (cons value (float-time))))
642
643;; TODO: We can have our own, separate obarray.
644(defun project--clear-cache ()
645 (obarray-map
646 (lambda (sym)
647 (if (get sym 'project-vc)
648 (put sym 'project-vc nil)))
649 vc-file-prop-obarray))
650
591(defun project-try-vc (dir) 651(defun project-try-vc (dir)
592 ;; FIXME: Learn to invalidate when the value changes: 652 "Returns a project value corresponding to DIR from the VC-aware backend.
593 ;; `project-vc-merge-submodules' or `project-vc-extra-root-markers'. 653
594 (or (vc-file-getprop dir 'project-vc) 654The value is cached, and depending on whether MAYBE-PROMPT was non-nil
595 ;; FIXME: Cache for a shorter time (bug#78545). 655in the `project-current' call, the timeout is determined by
596 (let ((res (project-try-vc--search dir))) 656`project-vc-cache-timeout' or `project-vc-non-essential-cache-timeout'."
597 (and res (vc-file-setprop dir 'project-vc res)) 657 (let ((cached (project--get-cached dir 'project-vc)))
598 res))) 658 (if (eq cached 'none)
659 nil
660 (or cached
661 (let ((res (project-try-vc--search dir)))
662 (project--set-cached dir 'project-vc (or res 'none))
663 res)))))
599 664
600(defun project-try-vc--search (dir) 665(defun project-try-vc--search (dir)
601 (let* ((backend-markers 666 (let* ((backend-markers
@@ -896,13 +961,24 @@ DIRS must contain directory names."
896 (cl-set-difference files dirs :test #'file-in-directory-p)) 961 (cl-set-difference files dirs :test #'file-in-directory-p))
897 962
898(defun project--value-in-dir (var dir) 963(defun project--value-in-dir (var dir)
964 (alist-get
965 var
966 (let ((cached (project--get-cached dir 'project-vc-dir-locals)))
967 (if (eq cached 'none)
968 nil
969 (or cached
970 (let ((res (project--read-dir-locals dir)))
971 (project--set-cached dir 'project-vc-dir-locals (or res 'none))
972 res))))
973 (symbol-value var)))
974
975(defun project--read-dir-locals (dir)
899 (with-temp-buffer 976 (with-temp-buffer
900 (setq default-directory (file-name-as-directory dir)) 977 (setq default-directory (file-name-as-directory dir))
978 ;; Don't use `hack-local-variables-apply' to avoid setting modes.
901 (let ((enable-local-variables :all)) 979 (let ((enable-local-variables :all))
902 (hack-dir-local-variables)) 980 (hack-dir-local-variables))
903 ;; Don't use `hack-local-variables-apply' to avoid setting modes. 981 file-local-variables-alist))
904 (alist-get var file-local-variables-alist
905 (symbol-value var))))
906 982
907(cl-defmethod project-buffers ((project (head vc))) 983(cl-defmethod project-buffers ((project (head vc)))
908 (let* ((root (expand-file-name (file-name-as-directory (project-root project)))) 984 (let* ((root (expand-file-name (file-name-as-directory (project-root project))))
@@ -924,6 +1000,11 @@ DIRS must contain directory names."
924 (nreverse bufs))) 1000 (nreverse bufs)))
925 1001
926(cl-defmethod project-name ((project (head vc))) 1002(cl-defmethod project-name ((project (head vc)))
1003 "Returns the name of this VC-aware type PROJECT.
1004
1005The value is cached, and depending on whether `non-essential' is nil,
1006the timeout is determined by `project-vc-cache-timeout' or
1007`project-vc-non-essential-cache-timeout'."
927 (or (project--value-in-dir 'project-vc-name (project-root project)) 1008 (or (project--value-in-dir 'project-vc-name (project-root project))
928 (cl-call-next-method))) 1009 (cl-call-next-method)))
929 1010
@@ -2206,7 +2287,7 @@ result in `project-list-file'. Announce the project's removal
2206from the list using REPORT-MESSAGE, which is a format string 2287from the list using REPORT-MESSAGE, which is a format string
2207passed to `message' as its first argument." 2288passed to `message' as its first argument."
2208 (project--ensure-read-project-list) 2289 (project--ensure-read-project-list)
2209 (when-let* ((ent (assoc (abbreviate-file-name project-root) project--list))) 2290 (when-let* ((ent (assoc project-root project--list)))
2210 (setq project--list (delq ent project--list)) 2291 (setq project--list (delq ent project--list))
2211 (message report-message project-root) 2292 (message report-message project-root)
2212 (project--write-project-list))) 2293 (project--write-project-list)))
@@ -2385,6 +2466,7 @@ projects.
2385Display a message at the end summarizing what was found. 2466Display a message at the end summarizing what was found.
2386Return the number of detected projects." 2467Return the number of detected projects."
2387 (interactive "DDirectory: \nP") 2468 (interactive "DDirectory: \nP")
2469 (project--clear-cache)
2388 (project--ensure-read-project-list) 2470 (project--ensure-read-project-list)
2389 (let ((dirs (if recursive 2471 (let ((dirs (if recursive
2390 (directory-files-recursively dir "" t) 2472 (directory-files-recursively dir "" t)
@@ -2417,12 +2499,18 @@ PREDICATE can be a function with 1 argument which determines which
2417projects should be deleted." 2499projects should be deleted."
2418 (dolist (proj (project-known-project-roots)) 2500 (dolist (proj (project-known-project-roots))
2419 (when (and (funcall (or predicate #'identity) proj) 2501 (when (and (funcall (or predicate #'identity) proj)
2420 (not (file-exists-p proj))) 2502 (condition-case-unless-debug nil
2503 (not (file-exists-p proj))
2504 (file-error
2505 (yes-or-no-p
2506 (format "Forget unreachable project `%s'? "
2507 proj)))))
2421 (project-forget-project proj)))) 2508 (project-forget-project proj))))
2422 2509
2423(defun project-forget-zombie-projects (&optional interactive) 2510(defun project-forget-zombie-projects (&optional interactive)
2424 "Forget all known projects that don't exist any more." 2511 "Forget all known projects that don't exist any more."
2425 (interactive (list t)) 2512 (interactive (list t))
2513 (project--clear-cache)
2426 (let ((pred (when interactive (alist-get 'interactively project-prune-zombie-projects)))) 2514 (let ((pred (when interactive (alist-get 'interactively project-prune-zombie-projects))))
2427 (project--delete-zombie-projects pred))) 2515 (project--delete-zombie-projects pred)))
2428 2516
@@ -2435,6 +2523,7 @@ to remove those projects from the index.
2435Display a message at the end summarizing what was forgotten. 2523Display a message at the end summarizing what was forgotten.
2436Return the number of forgotten projects." 2524Return the number of forgotten projects."
2437 (interactive "DDirectory: \nP") 2525 (interactive "DDirectory: \nP")
2526 (project--clear-cache)
2438 (let ((count 0)) 2527 (let ((count 0))
2439 (if recursive 2528 (if recursive
2440 (dolist (proj (project-known-project-roots)) 2529 (dolist (proj (project-known-project-roots))
@@ -2624,7 +2713,8 @@ slash-separated components from `project-name' will be appended to
2624the buffer's directory name when buffers from two different projects 2713the buffer's directory name when buffers from two different projects
2625would otherwise have the same name." 2714would otherwise have the same name."
2626 (if-let* ((proj (project-current nil dirname))) 2715 (if-let* ((proj (project-current nil dirname)))
2627 (let ((root (project-root proj))) 2716 (let ((root (project-root proj))
2717 (non-essential t))
2628 (expand-file-name 2718 (expand-file-name
2629 (file-name-concat 2719 (file-name-concat
2630 (file-name-directory root) 2720 (file-name-directory root)
@@ -2634,27 +2724,6 @@ would otherwise have the same name."
2634 2724
2635;;; Project mode-line 2725;;; Project mode-line
2636 2726
2637(defvar project-name-cache-timeout 300
2638 "Number of seconds to cache the project name.
2639Used by `project-name-cached'.")
2640
2641(defun project-name-cached (dir)
2642 "Return the cached project name for the directory DIR.
2643Until it's cached, retrieve the project name using `project-current'
2644and `project-name', then put the name to the cache for the time defined
2645by the variable `project-name-cache-timeout'. This function is useful
2646for project indicators such as on the mode line."
2647 (let ((cached (vc-file-getprop dir 'project-name))
2648 (current-time (float-time)))
2649 (if (and cached (< (- current-time (cdr cached))
2650 project-name-cache-timeout))
2651 (let ((value (car cached)))
2652 (if (eq value 'none) nil value))
2653 (let ((res (when-let* ((project (project-current nil dir)))
2654 (project-name project))))
2655 (vc-file-setprop dir 'project-name (cons (or res 'none) current-time))
2656 res))))
2657
2658;;;###autoload 2727;;;###autoload
2659(defcustom project-mode-line nil 2728(defcustom project-mode-line nil
2660 "Whether to show current project name and Project menu on the mode line. 2729 "Whether to show current project name and Project menu on the mode line.
@@ -2691,7 +2760,9 @@ value is `non-remote', show the project name only for local files."
2691 ;; 'last-coding-system-used' when reading the project name 2760 ;; 'last-coding-system-used' when reading the project name
2692 ;; from .dir-locals.el also enables flyspell-mode (bug#66825). 2761 ;; from .dir-locals.el also enables flyspell-mode (bug#66825).
2693 (when-let* ((last-coding-system-used last-coding-system-used) 2762 (when-let* ((last-coding-system-used last-coding-system-used)
2694 (project-name (project-name-cached default-directory))) 2763 (non-essential t)
2764 (project (project-current))
2765 (project-name (project-name project)))
2695 (concat 2766 (concat
2696 " " 2767 " "
2697 (propertize 2768 (propertize
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index b6981c9156c..2a3035c95c5 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -3366,6 +3366,16 @@ from `python-shell-prompt-regexp',
3366 python-shell--prompt-calculated-output-regexp 3366 python-shell--prompt-calculated-output-regexp
3367 (funcall build-regexp output-prompts))))) 3367 (funcall build-regexp output-prompts)))))
3368 3368
3369(defun python-shell-get-project-name ()
3370 "Return the project name for the current buffer.
3371Use `project-name-cached' if available."
3372 (when (featurep 'project)
3373 (if (fboundp 'project-name-cached)
3374 (project-name-cached default-directory)
3375 (when-let* ((proj (project-current)))
3376 (file-name-nondirectory
3377 (directory-file-name (project-root proj)))))))
3378
3369(defun python-shell-get-process-name (dedicated) 3379(defun python-shell-get-process-name (dedicated)
3370 "Calculate the appropriate process name for inferior Python process. 3380 "Calculate the appropriate process name for inferior Python process.
3371If DEDICATED is nil, this is simply `python-shell-buffer-name'. 3381If DEDICATED is nil, this is simply `python-shell-buffer-name'.
@@ -3374,11 +3384,8 @@ name respectively the current project name."
3374 (pcase dedicated 3384 (pcase dedicated
3375 ('nil python-shell-buffer-name) 3385 ('nil python-shell-buffer-name)
3376 ('project 3386 ('project
3377 (if-let* ((proj (and (featurep 'project) 3387 (if-let* ((proj-name (python-shell-get-project-name)))
3378 (project-current)))) 3388 (format "%s[%s]" python-shell-buffer-name proj-name)
3379 (format "%s[%s]" python-shell-buffer-name (file-name-nondirectory
3380 (directory-file-name
3381 (project-root proj))))
3382 python-shell-buffer-name)) 3389 python-shell-buffer-name))
3383 (_ (format "%s[%s]" python-shell-buffer-name (buffer-name))))) 3390 (_ (format "%s[%s]" python-shell-buffer-name (buffer-name)))))
3384 3391
@@ -3816,16 +3823,6 @@ variable.
3816 (compilation-shell-minor-mode 1) 3823 (compilation-shell-minor-mode 1)
3817 (python-pdbtrack-setup-tracking)) 3824 (python-pdbtrack-setup-tracking))
3818 3825
3819(defvar-local python-shell--process-cache)
3820(defvar-local python-shell--process-cache-valid)
3821
3822(defun python-shell--invalidate-process-cache ()
3823 "Invalidate process cache."
3824 (dolist (buffer (buffer-list))
3825 (with-current-buffer buffer
3826 (setq python-shell--process-cache nil
3827 python-shell--process-cache-valid nil))))
3828
3829(defun python-shell-make-comint (cmd proc-name &optional show internal) 3826(defun python-shell-make-comint (cmd proc-name &optional show internal)
3830 "Create a Python shell comint buffer. 3827 "Create a Python shell comint buffer.
3831CMD is the Python command to be executed and PROC-NAME is the 3828CMD is the Python command to be executed and PROC-NAME is the
@@ -3842,7 +3839,6 @@ killed."
3842 (let* ((proc-buffer-name 3839 (let* ((proc-buffer-name
3843 (format (if (not internal) "*%s*" " *%s*") proc-name))) 3840 (format (if (not internal) "*%s*" " *%s*") proc-name)))
3844 (when (not (comint-check-proc proc-buffer-name)) 3841 (when (not (comint-check-proc proc-buffer-name))
3845 (python-shell--invalidate-process-cache)
3846 (let* ((cmdlist (split-string-and-unquote cmd)) 3842 (let* ((cmdlist (split-string-and-unquote cmd))
3847 (interpreter (car cmdlist)) 3843 (interpreter (car cmdlist))
3848 (args (cdr cmdlist)) 3844 (args (cdr cmdlist))
@@ -3966,15 +3962,7 @@ If current buffer is in `inferior-python-mode', return it."
3966 3962
3967(defun python-shell-get-process () 3963(defun python-shell-get-process ()
3968 "Return inferior Python process for current buffer." 3964 "Return inferior Python process for current buffer."
3969 (unless (and python-shell--process-cache-valid 3965 (get-buffer-process (python-shell-get-buffer)))
3970 (or (not python-shell--process-cache)
3971 (and (process-live-p python-shell--process-cache)
3972 (buffer-live-p
3973 (process-buffer python-shell--process-cache)))))
3974 (setq python-shell--process-cache
3975 (get-buffer-process (python-shell-get-buffer))
3976 python-shell--process-cache-valid t))
3977 python-shell--process-cache)
3978 3966
3979(defun python-shell-get-process-or-error (&optional interactivep) 3967(defun python-shell-get-process-or-error (&optional interactivep)
3980 "Return inferior Python process for current buffer or signal error. 3968 "Return inferior Python process for current buffer or signal error.
@@ -5854,7 +5842,7 @@ Set to nil by `python-eldoc-function' if
5854 5842
5855(defcustom python-eldoc-function-timeout 1 5843(defcustom python-eldoc-function-timeout 1
5856 "Timeout for `python-eldoc-function' in seconds." 5844 "Timeout for `python-eldoc-function' in seconds."
5857 :type 'integer 5845 :type 'number
5858 :version "25.1") 5846 :version "25.1")
5859 5847
5860(defcustom python-eldoc-function-timeout-permanent t 5848(defcustom python-eldoc-function-timeout-permanent t
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index 84a3fa4dfba..1e51b23eaff 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -247,11 +247,9 @@ generic functions.")
247 247
248;;;###autoload 248;;;###autoload
249(defun xref-find-backend () 249(defun xref-find-backend ()
250 (or 250 (run-hook-with-args-until-success 'xref-backend-functions))
251 (run-hook-with-args-until-success 'xref-backend-functions)
252 (user-error "No Xref backend available")))
253 251
254(cl-defgeneric xref-backend-definitions (backend identifier) 252(cl-defgeneric xref-backend-definitions (_backend _identifier)
255 "Find definitions of IDENTIFIER. 253 "Find definitions of IDENTIFIER.
256 254
257The result must be a list of xref objects. If IDENTIFIER 255The result must be a list of xref objects. If IDENTIFIER
@@ -264,7 +262,8 @@ IDENTIFIER can be any string returned by
264`xref-backend-identifier-at-point', or from the table returned by 262`xref-backend-identifier-at-point', or from the table returned by
265`xref-backend-identifier-completion-table'. 263`xref-backend-identifier-completion-table'.
266 264
267To create an xref object, call `xref-make'.") 265To create an xref object, call `xref-make'."
266 (xref--no-backend-available))
268 267
269(cl-defgeneric xref-backend-references (_backend identifier) 268(cl-defgeneric xref-backend-references (_backend identifier)
270 "Find references of IDENTIFIER. 269 "Find references of IDENTIFIER.
@@ -285,12 +284,13 @@ The default implementation uses `xref-references-in-directory'."
285 (xref--project-root pr) 284 (xref--project-root pr)
286 (project-external-roots pr)))))) 285 (project-external-roots pr))))))
287 286
288(cl-defgeneric xref-backend-apropos (backend pattern) 287(cl-defgeneric xref-backend-apropos (_backend _pattern)
289 "Find all symbols that match PATTERN string. 288 "Find all symbols that match PATTERN string.
290The second argument has the same meaning as in `apropos'. 289The second argument has the same meaning as in `apropos'.
291 290
292If BACKEND is implemented in Lisp, it can use 291If BACKEND is implemented in Lisp, it can use
293`xref-apropos-regexp' to convert the pattern to regexp.") 292`xref-apropos-regexp' to convert the pattern to regexp."
293 (xref--no-backend-available))
294 294
295(cl-defgeneric xref-backend-identifier-at-point (_backend) 295(cl-defgeneric xref-backend-identifier-at-point (_backend)
296 "Return the relevant identifier at point. 296 "Return the relevant identifier at point.
@@ -306,8 +306,9 @@ recognize and then delegate the work to an external process."
306 (let ((thing (thing-at-point 'symbol))) 306 (let ((thing (thing-at-point 'symbol)))
307 (and thing (substring-no-properties thing)))) 307 (and thing (substring-no-properties thing))))
308 308
309(cl-defgeneric xref-backend-identifier-completion-table (backend) 309(cl-defgeneric xref-backend-identifier-completion-table (_backend)
310 "Return the completion table for identifiers.") 310 "Return the completion table for identifiers."
311 nil)
311 312
312(cl-defgeneric xref-backend-identifier-completion-ignore-case (_backend) 313(cl-defgeneric xref-backend-identifier-completion-ignore-case (_backend)
313 "Return t if case is not significant in identifier completion." 314 "Return t if case is not significant in identifier completion."
@@ -329,6 +330,10 @@ KEY extracts the key from an element."
329 (cl-loop for key being hash-keys of table using (hash-values value) 330 (cl-loop for key being hash-keys of table using (hash-values value)
330 collect (cons key (nreverse value))))) 331 collect (cons key (nreverse value)))))
331 332
333(defun xref--no-backend-available ()
334 (user-error
335 "No Xref backend. Try `M-x eglot', `M-x visit-tags-table', or `M-x etags-regen-mode'."))
336
332(defun xref--insert-propertized (props &rest strings) 337(defun xref--insert-propertized (props &rest strings)
333 "Insert STRINGS with text properties PROPS." 338 "Insert STRINGS with text properties PROPS."
334 (let ((start (point))) 339 (let ((start (point)))