diff options
| author | Liu Hui | 2025-07-07 17:45:18 +0800 |
|---|---|---|
| committer | Eli Zaretskii | 2025-07-12 11:38:27 +0300 |
| commit | ae46edff68e8d5729207ed849df83ecb039e11bb (patch) | |
| tree | fe5cc59038ab5ad916e17c9bb4c192fb38097371 | |
| parent | f746762e74adeac8beaa73abcf20ee5e74298597 (diff) | |
| download | emacs-ae46edff68e8d5729207ed849df83ecb039e11bb.tar.gz emacs-ae46edff68e8d5729207ed849df83ecb039e11bb.zip | |
Add option 'ffap-prefer-remote-file' (bug#78925)
This option only affects absolute filenames that are found by
ffap-file-at-point in buffers with remote default directory.
The handling of relative filenames in above buffers remains
unchanged: ffap-file-at-point returns the relative filename,
which can be converted to a remote absolute filename by
subsequent callers (e.g. ffap) using expand-file-name.
* lisp/ffap.el (ffap-prefer-remote-file): New user option.
(ffap-file-exists-string): Add an optional argument to allow the
check of existence of absolute filename on the remote host.
(ffap-file-at-point): Always find remote files in remote context
if the new option is non-nil.
* test/lisp/ffap-tests.el (ffap-test-remote): Add a test.
* etc/NEWS: Announce the change.
| -rw-r--r-- | etc/NEWS | 6 | ||||
| -rw-r--r-- | lisp/ffap.el | 74 | ||||
| -rw-r--r-- | test/lisp/ffap-tests.el | 22 |
3 files changed, 77 insertions, 25 deletions
| @@ -2249,6 +2249,12 @@ useful when you want the face attributes to be absolute and not | |||
| 2249 | 'unspecified'. | 2249 | 'unspecified'. |
| 2250 | 2250 | ||
| 2251 | --- | 2251 | --- |
| 2252 | *** New user option 'ffap-prefer-remote-file'. | ||
| 2253 | If non-nil, ffap always finds remote files in buffers with remote | ||
| 2254 | 'default-directory'. If nil, ffap finds local files first for absolute | ||
| 2255 | filenames in above buffers. The default is nil. | ||
| 2256 | |||
| 2257 | --- | ||
| 2252 | ** Flymake | 2258 | ** Flymake |
| 2253 | 2259 | ||
| 2254 | *** Windows without fringes now automatically use margin indicators. | 2260 | *** Windows without fringes now automatically use margin indicators. |
diff --git a/lisp/ffap.el b/lisp/ffap.el index 10afcd9514a..64c2c780672 100644 --- a/lisp/ffap.el +++ b/lisp/ffap.el | |||
| @@ -199,6 +199,16 @@ Sensible values are nil, \"news\", or \"mailto\"." | |||
| 199 | ) | 199 | ) |
| 200 | :group 'ffap) | 200 | :group 'ffap) |
| 201 | 201 | ||
| 202 | (defcustom ffap-prefer-remote-file nil | ||
| 203 | "Whether to prefer remote files in remote context. | ||
| 204 | If non-nil, ffap always finds remote files in buffers with remote | ||
| 205 | `default-directory'. If nil, ffap finds local files first for absolute | ||
| 206 | filenames in above buffers. Relative filenames are not affected by this | ||
| 207 | option." | ||
| 208 | :type 'boolean | ||
| 209 | :group 'ffap | ||
| 210 | :version "31.1") | ||
| 211 | |||
| 202 | (defvar ffap-max-region-length 1024 | 212 | (defvar ffap-max-region-length 1024 |
| 203 | "Maximum active region length. | 213 | "Maximum active region length. |
| 204 | When the region is active and larger than this value, | 214 | When the region is active and larger than this value, |
| @@ -488,7 +498,7 @@ Returned values: | |||
| 488 | (defvar ffap-compression-suffixes '(".gz" ".Z") ; .z is mostly dead | 498 | (defvar ffap-compression-suffixes '(".gz" ".Z") ; .z is mostly dead |
| 489 | "List of suffixes tried by `ffap-file-exists-string'.") | 499 | "List of suffixes tried by `ffap-file-exists-string'.") |
| 490 | 500 | ||
| 491 | (defun ffap-file-exists-string (file &optional nomodify) | 501 | (defun ffap-file-exists-string (file &optional nomodify remote-host) |
| 492 | ;; Early jka-compr versions modified file-exists-p to return the | 502 | ;; Early jka-compr versions modified file-exists-p to return the |
| 493 | ;; filename, maybe modified by adding a suffix like ".gz". That | 503 | ;; filename, maybe modified by adding a suffix like ".gz". That |
| 494 | ;; broke the interface of file-exists-p, so it was later dropped. | 504 | ;; broke the interface of file-exists-p, so it was later dropped. |
| @@ -496,23 +506,33 @@ Returned values: | |||
| 496 | "Return FILE (maybe modified) if the file exists, else nil. | 506 | "Return FILE (maybe modified) if the file exists, else nil. |
| 497 | When using jka-compr (a.k.a. `auto-compression-mode'), the returned | 507 | When using jka-compr (a.k.a. `auto-compression-mode'), the returned |
| 498 | name may have a suffix added from `ffap-compression-suffixes'. | 508 | name may have a suffix added from `ffap-compression-suffixes'. |
| 499 | The optional NOMODIFY argument suppresses the extra search." | 509 | The optional NOMODIFY argument suppresses the extra search. |
| 500 | (cond | 510 | |
| 501 | ((or (not file) ; quietly reject nil | 511 | The optional argument REMOTE-HOST, if non-nil, should be a string |
| 502 | (zerop (length file))) ; and also "" | 512 | returned by `file-remote-p'. If it is non-nil and FILE is absolute, |
| 503 | nil) | 513 | check whether FILE exists on REMOTE-HOST. The returned name uses |
| 504 | ((file-exists-p file) file) ; try unmodified first | 514 | REMOTE-HOST as the prefix if the file exists." |
| 505 | ;; three reasons to suppress search: | 515 | (let ((non-essential t)) |
| 506 | (nomodify nil) | 516 | (cond |
| 507 | ((not (rassq 'jka-compr-handler file-name-handler-alist)) nil) | 517 | ((or (not file) ; quietly reject nil |
| 508 | ((member (file-name-extension file t) ffap-compression-suffixes) nil) | 518 | (zerop (length file))) ; and also "" |
| 509 | (t ; ok, do the search | 519 | nil) |
| 510 | (let ((list ffap-compression-suffixes) try ret) | 520 | ((and remote-host ; prepend remote host to file |
| 511 | (while list | 521 | (file-name-absolute-p file) |
| 512 | (if (file-exists-p (setq try (concat file (car list)))) | 522 | (setq file (concat remote-host file)) |
| 513 | (setq ret try list nil) | 523 | nil)) |
| 514 | (setq list (cdr list)))) | 524 | ((file-exists-p file) file) |
| 515 | ret)))) | 525 | ;; three reasons to suppress search: |
| 526 | (nomodify nil) | ||
| 527 | ((not (rassq 'jka-compr-handler file-name-handler-alist)) nil) | ||
| 528 | ((member (file-name-extension file t) ffap-compression-suffixes) nil) | ||
| 529 | (t ; ok, do the search | ||
| 530 | (let ((list ffap-compression-suffixes) try ret) | ||
| 531 | (while list | ||
| 532 | (if (file-exists-p (setq try (concat file (car list)))) | ||
| 533 | (setq ret try list nil) | ||
| 534 | (setq list (cdr list)))) | ||
| 535 | ret))))) | ||
| 516 | 536 | ||
| 517 | (defun ffap-file-remote-p (filename) | 537 | (defun ffap-file-remote-p (filename) |
| 518 | "If FILENAME looks remote, return it (maybe slightly improved)." | 538 | "If FILENAME looks remote, return it (maybe slightly improved)." |
| @@ -1465,6 +1485,8 @@ which may actually result in an URL rather than a filename." | |||
| 1465 | string)) | 1485 | string)) |
| 1466 | (abs (file-name-absolute-p name)) | 1486 | (abs (file-name-absolute-p name)) |
| 1467 | (default-directory default-directory) | 1487 | (default-directory default-directory) |
| 1488 | (remote-p (and ffap-prefer-remote-file | ||
| 1489 | (file-remote-p default-directory))) | ||
| 1468 | (oname name)) | 1490 | (oname name)) |
| 1469 | (unwind-protect | 1491 | (unwind-protect |
| 1470 | (cond | 1492 | (cond |
| @@ -1484,10 +1506,11 @@ which may actually result in an URL rather than a filename." | |||
| 1484 | ;; Accept remote names without actual checking (too slow): | 1506 | ;; Accept remote names without actual checking (too slow): |
| 1485 | ((and abs (ffap-file-remote-p name))) | 1507 | ((and abs (ffap-file-remote-p name))) |
| 1486 | ;; Ok, not remote, try the existence test even if it is absolute: | 1508 | ;; Ok, not remote, try the existence test even if it is absolute: |
| 1487 | ((and abs (ffap-file-exists-string name))) | 1509 | ((and abs (ffap-file-exists-string name nil remote-p))) |
| 1488 | ;; Try stripping off line numbers. | 1510 | ;; Try stripping off line numbers. |
| 1489 | ((and abs (string-match ":[0-9]" name) | 1511 | ((and abs (string-match ":[0-9]" name) |
| 1490 | (ffap-file-exists-string (substring name 0 (match-beginning 0))))) | 1512 | (ffap-file-exists-string (substring name 0 (match-beginning 0)) |
| 1513 | nil remote-p))) | ||
| 1491 | ;; If it contains a colon, get rid of it (and return if exists) | 1514 | ;; If it contains a colon, get rid of it (and return if exists) |
| 1492 | ((and (string-match path-separator name) | 1515 | ((and (string-match path-separator name) |
| 1493 | (let ((this-name (ffap-string-at-point 'nocolon))) | 1516 | (let ((this-name (ffap-string-at-point 'nocolon))) |
| @@ -1495,7 +1518,7 @@ which may actually result in an URL rather than a filename." | |||
| 1495 | ;; the empty string. | 1518 | ;; the empty string. |
| 1496 | (when (> (length this-name) 0) | 1519 | (when (> (length this-name) 0) |
| 1497 | (setq name this-name) | 1520 | (setq name this-name) |
| 1498 | (ffap-file-exists-string name))))) | 1521 | (ffap-file-exists-string name nil remote-p))))) |
| 1499 | ;; File does not exist, try the alist: | 1522 | ;; File does not exist, try the alist: |
| 1500 | ((let ((alist ffap-alist) tem try case-fold-search) | 1523 | ((let ((alist ffap-alist) tem try case-fold-search) |
| 1501 | (while (and alist (not try)) | 1524 | (while (and alist (not try)) |
| @@ -1510,7 +1533,7 @@ which may actually result in an URL rather than a filename." | |||
| 1510 | (setq try (or | 1533 | (setq try (or |
| 1511 | (ffap-url-p try) ; not a file! | 1534 | (ffap-url-p try) ; not a file! |
| 1512 | (ffap-file-remote-p try) | 1535 | (ffap-file-remote-p try) |
| 1513 | (ffap-file-exists-string try)))))) | 1536 | (ffap-file-exists-string try nil remote-p)))))) |
| 1514 | try)) | 1537 | try)) |
| 1515 | ;; Try adding a leading "/" (common omission in ftp file names). | 1538 | ;; Try adding a leading "/" (common omission in ftp file names). |
| 1516 | ;; Note that this uses oname, which still has any colon part. | 1539 | ;; Note that this uses oname, which still has any colon part. |
| @@ -1543,17 +1566,18 @@ which may actually result in an URL rather than a filename." | |||
| 1543 | (string-match ffap-dired-wildcards name) | 1566 | (string-match ffap-dired-wildcards name) |
| 1544 | abs | 1567 | abs |
| 1545 | (ffap-file-exists-string (file-name-directory | 1568 | (ffap-file-exists-string (file-name-directory |
| 1546 | (directory-file-name name))) | 1569 | (directory-file-name name)) |
| 1570 | nil remote-p) | ||
| 1547 | name)) | 1571 | name)) |
| 1548 | ;; Try all parent directories by deleting the trailing directory | 1572 | ;; Try all parent directories by deleting the trailing directory |
| 1549 | ;; name until existing directory is found or name stops changing | 1573 | ;; name until existing directory is found or name stops changing |
| 1550 | ((let ((dir name)) | 1574 | ((let ((dir name)) |
| 1551 | (while (and dir | 1575 | (while (and dir |
| 1552 | (not (ffap-file-exists-string dir)) | 1576 | (not (ffap-file-exists-string dir nil remote-p)) |
| 1553 | (not (equal dir (setq dir (file-name-directory | 1577 | (not (equal dir (setq dir (file-name-directory |
| 1554 | (directory-file-name dir))))))) | 1578 | (directory-file-name dir))))))) |
| 1555 | (and (not (string= dir "/")) | 1579 | (and (not (string= dir "/")) |
| 1556 | (ffap-file-exists-string dir)))) | 1580 | (ffap-file-exists-string dir nil remote-p)))) |
| 1557 | ) | 1581 | ) |
| 1558 | (set-match-data data)))) | 1582 | (set-match-data data)))) |
| 1559 | 1583 | ||
diff --git a/test/lisp/ffap-tests.el b/test/lisp/ffap-tests.el index ea5e745bfaf..2bf5d8c79cd 100644 --- a/test/lisp/ffap-tests.el +++ b/test/lisp/ffap-tests.el | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | 25 | ||
| 26 | (require 'cl-lib) | 26 | (require 'cl-lib) |
| 27 | (require 'ert) | 27 | (require 'ert) |
| 28 | (require 'tramp) | ||
| 28 | (require 'ert-x) | 29 | (require 'ert-x) |
| 29 | (require 'ffap) | 30 | (require 'ffap) |
| 30 | 31 | ||
| @@ -289,6 +290,27 @@ End of search list. | |||
| 289 | (should (member (expand-file-name "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/15.0.0/include") | 290 | (should (member (expand-file-name "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/15.0.0/include") |
| 290 | (ffap--c-path))))) | 291 | (ffap--c-path))))) |
| 291 | 292 | ||
| 293 | (ert-deftest ffap-test-remote () | ||
| 294 | (skip-unless | ||
| 295 | (ignore-errors | ||
| 296 | (and | ||
| 297 | (file-remote-p ert-remote-temporary-file-directory) | ||
| 298 | (file-directory-p ert-remote-temporary-file-directory) | ||
| 299 | (file-writable-p ert-remote-temporary-file-directory)))) | ||
| 300 | (let* ((ffap-prefer-remote-file t) | ||
| 301 | (default-directory | ||
| 302 | (expand-file-name ert-remote-temporary-file-directory)) | ||
| 303 | (test-file (expand-file-name "ffap-test" default-directory))) | ||
| 304 | (with-temp-buffer | ||
| 305 | (ignore-errors (make-empty-file test-file)) | ||
| 306 | (insert (file-local-name test-file)) | ||
| 307 | (should (equal (ffap-file-at-point) test-file)) | ||
| 308 | (erase-buffer) | ||
| 309 | (insert (concat "/usr/bin:" (file-local-name test-file))) | ||
| 310 | (should (equal (ffap-file-at-point) test-file)) | ||
| 311 | (delete-file test-file) | ||
| 312 | (should (equal (ffap-file-at-point) default-directory))))) | ||
| 313 | |||
| 292 | (provide 'ffap-tests) | 314 | (provide 'ffap-tests) |
| 293 | 315 | ||
| 294 | ;;; ffap-tests.el ends here | 316 | ;;; ffap-tests.el ends here |