Skip to content

Commit

Permalink
Add new function file-name-with-extension
Browse files Browse the repository at this point in the history
* doc/lispref/files.texi (File Name Components): Document it.
* lisp/emacs-lisp/shortdoc.el (file-name): Ditto.

* lisp/files.el (file-name-with-extension): New function.
  • Loading branch information
fosskers authored and larsmagne committed Jun 30, 2021
1 parent 1dba0ca commit 4f2765f
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 0 deletions.
19 changes: 19 additions & 0 deletions doc/lispref/files.texi
Expand Up @@ -2129,6 +2129,25 @@ the period that delimits the extension, and if @var{filename} has no
extension, the value is @code{""}.
@end defun

@defun file-name-with-extension filename extension
This function returns @var{filename} with its extension set to
@var{extension}. A single leading dot in the @var{extension} will be
stripped if there is one. For example:

@example
(file-name-with-extension "file" "el")
@result{} "file.el"
(file-name-with-extension "file" ".el")
@result{} "file.el"
(file-name-with-extension "file.c" "el")
@result{} "file.el"
@end example

Note that this function will error if @var{filename} or
@var{extension} are empty, or if the @var{filename} is shaped like a
directory (i.e. if @code{directory-name-p} returns non-@code{nil}).
@end defun

@defun file-name-sans-extension filename
This function returns @var{filename} minus its extension, if any. The
version/backup part, if present, is only removed if the file has an
Expand Down
5 changes: 5 additions & 0 deletions etc/NEWS
Expand Up @@ -3058,6 +3058,11 @@ been added, and takes a callback to handle the return status.
---
** 'ascii' is now a coding system alias for 'us-ascii'.

+++
** New function 'file-name-with-extension'.
This function allows a canonical way to set/replace the extension of a
filename string.

+++
** New function 'file-backup-file-names'.
This function returns the list of file names of all the backup files
Expand Down
3 changes: 3 additions & 0 deletions lisp/emacs-lisp/shortdoc.el
Expand Up @@ -268,6 +268,9 @@ There can be any number of :example/:result elements."
:eval (file-name-extension "/tmp/foo.txt"))
(file-name-sans-extension
:eval (file-name-sans-extension "/tmp/foo.txt"))
(file-name-with-extension
:eval (file-name-with-extension "foo.txt" "bin")
:eval (file-name-with-extension "foo" "bin"))
(file-name-base
:eval (file-name-base "/tmp/foo.txt"))
(file-relative-name
Expand Down
21 changes: 21 additions & 0 deletions lisp/files.el
Expand Up @@ -4894,6 +4894,27 @@ extension, the value is \"\"."
(if period
"")))))

(defun file-name-with-extension (filename extension)
"Set the EXTENSION of a FILENAME.
The extension (in a file name) is the part that begins with the last \".\".
Trims a leading dot from the EXTENSION so that either \"foo\" or
\".foo\" can be given.
Errors if the filename or extension are empty, or if the given
filename has the format of a directory.
See also `file-name-sans-extension'."
(let ((extn (string-trim-left extension "[.]")))
(cond ((string-empty-p filename)
(error "Empty filename: %s" filename))
((string-empty-p extn)
(error "Malformed extension: %s" extension))
((directory-name-p filename)
(error "Filename is a directory: %s" filename))
(t
(concat (file-name-sans-extension filename) "." extn)))))

(defun file-name-base (&optional filename)
"Return the base name of the FILENAME: no directory, no extension."
(declare (advertised-calling-convention (filename) "27.1"))
Expand Down
18 changes: 18 additions & 0 deletions test/lisp/files-tests.el
Expand Up @@ -1478,5 +1478,23 @@ The door of all subtleties!
(buffer-substring (point-min) (point-max))
nil nil)))))

(ert-deftest files-tests-file-name-with-extension-good ()
"Test that `file-name-with-extension' succeeds with reasonable input."
(should (string= (file-name-with-extension "Jack" "css") "Jack.css"))
(should (string= (file-name-with-extension "Jack" ".css") "Jack.css"))
(should (string= (file-name-with-extension "Jack.scss" "css") "Jack.css"))
(should (string= (file-name-with-extension "/path/to/Jack.md" "org") "/path/to/Jack.org")))

(ert-deftest files-tests-file-name-with-extension-bad ()
"Test that `file-name-with-extension' fails on malformed input."
(should-error (file-name-with-extension nil nil))
(should-error (file-name-with-extension "Jack" nil))
(should-error (file-name-with-extension nil "css"))
(should-error (file-name-with-extension "" ""))
(should-error (file-name-with-extension "" "css"))
(should-error (file-name-with-extension "Jack" ""))
(should-error (file-name-with-extension "Jack" "."))
(should-error (file-name-with-extension "/is/a/directory/" "css")))

(provide 'files-tests)
;;; files-tests.el ends here

0 comments on commit 4f2765f

Please sign in to comment.