In this new GNU Emacs series, we will explore the various Emacs Lisp packages available and also discuss advanced use cases of GNU Emacs.
GNU Emacs is a popular, free/libre and open source software text editor, and is available on multiple platforms. It was written by Richard Stallman in 1985 and the latest stable version is 26.1, which was released in 2018. The source code is primarily written in C and Emacs Lisp, and is released under the GPLv3 licence. The editor is quite extensible and has a large number of packages available for its end users. The project home page is available at https://www.gnu.org/software/emacs/. We have already completed an introductory series on GNU Emacs in OSFY and the articles can be obtained from https://www.opensourceforu.com/?s=emacs.
An introduction to f.el
f.el is an API that can be used in GNU Emacs to work with files and directories. It has been written by Johan Andersson and released under the GPLv3 licence. The source code is available at https://github.com/rejeep/f.el. The latest tagged release is v0.20.0. It internally requires the use of the s.el string manipulation and dash.el Emacs list libraries.
The f.el package is available in Milkypostman’s Emacs Lisp Package Archive (MELPA) and you can install it using the following command inside GNU Emacs:
M-x package-install f
You can also copy the f.el source file to your Emacs load path to use it. If you are using Cask, then you can add the following command to your Cask file:
Let us go through a few examples to demonstrate the APIs available with the f.el library.
Path: The entire path to a file can be created using the f-join construct. For example:
(f-join “/” “home” “guest” “downloads”) “/home/guest/downloads”
You can use f-split to obtain the individual path elements as shown below:
(f-split “/home/guest/downloads”) (“/” “home” “guest” “downloads”)
The entire path of a directory present in the current folder can be fetched using f-expand:
(f-expand “tmp”) “/home/guest/tmp”
You can obtain the file name with its extension using the f-filename API. If you need to obtain the parent directory for the file, you can use the ‘f-dirname’ construct. The file name extension can be fetched using the f-ext API as illustrated below:
(f-filename “/etc/resolv.conf”) “resolv.conf” (f-dirname “/etc/resolv.conf”) “/etc” (f-ext “/etc/resolv.conf”) “conf”
If you would like to rename a file name extension, you can use the f-swap-ext function as follows:
(f-swap-ext “/etc/resolv.conf” “conf.bak”) “/etc/resolv.conf.bak”
The actual source code implementation in f.el is given below:
(defun f-swap-ext (path ext) “Return PATH but with EXT as the new extension. EXT must not be nil or empty.” (if (s-blank? ext) (error “Extension cannot be empty or nil”) (concat (f-no-ext path) “.” ext)))
The extension is checked to see if it is empty. If it is not blank, then the path without the present extension is concatenated with the new extension and returned.
The f-base API provides you just the file name without the extension.
(f-base “/etc/resolv.conf”) “resolv”
A file name path that contains the tilde (~) symbol will be expanded to return the long version using the f-long construct as shown below:
(f-long “~/downloads”) “/home/guest/downloads”
If you need a trailing “/” at the end of a file name path, you can apply the f-slash function. For example:
(f-slash “/home/guest/docs”) “/home/guest/docs/”
The f-full API will return the absolute PATH as well as the trailing slash, as indicated below:
(f-full “~/code”) “/home/guest/code/”
The f.el library also provides functions to perform read/write operations on files. You can read the file contents in UTF-8 format using the f-read-text API:
(f-read-text “/etc/resolv.conf” ‘utf-8) # Generated by NetworkManager nameserver 192.168.0.1
You can write text to a file using the f-write-text function, as shown below:
(f-write-text “Hello World!” ‘utf-8 “/tmp/hello.txt”) nil $ cat /tmp/hello.txt Hello World! $
Appending text to a file can be accomplished using the f-append-text function, as illustrated below:
(f-append-text “\n” ‘utf-8 “/tmp/hello.txt”) nil $ cat /tmp/hello.txt Hello World! $
A directory can be created using the f-mkdir API. On success, it returns the value nil. An example is shown below:
(f-mkdir “/tmp” “test”) nil
You can delete a directory with the f-delete function and nil is returned on success.
(f-delete “/tmp/test”) nil
A symbolic link file can be created using f-symlink with the argument target linkpath, where the linkpath is created for the target. Nil is returned on success.
(f-symlink “/etc/resolv.conf” “/tmp/resolv.conf”) nil
You can rename a file using the f-move API as shown below:
(f-move “/tmp/hello.txt” “/tmp/hello-world.txt”) nil
The f-copy function can be used to copy a file or directory from a source to a target location. For example:
(f-copy “/tmp/hello-world.txt” “/tmp/greetings.txt”) nil
You can copy an entire directory’s contents from a source to a target using the f-copy-contents construct as illustrated below:
(f-mkdir “/tmp” “nginx”) nil (f-copy-contents “/etc/nginx” “/tmp/nginx”) nil
A file’s last modification date can be updated using the f-touch API as follows:
$ ls -l /tmp/hello-world.txt -rw-r--r-- 1 guest guest 14 Feb 7 16:49 hello-world.txt (f-touch “/tmp/hello-world.txt”) t $ ls -l /tmp/hello-world.txt -rw-r--r-- 1 guest guest 14 Feb 7 17:00 hello-world.txt
The f.el library provides a number of predicate functions that you can use to make assertions on files and directories. The f-exists? function returns true if the PATH exists. For example:
(f-exists? “/tmp/hello-world.txt”) t
The actual function definition in the source code is as follows:
(defun f-exists? (path) “Return t if PATH exists, false otherwise.” (file-exists-p path))
It uses the GNU Emacs built-in file-exists-p function to return true if the PATH exists, or returns false otherwise.
You can check if a file or directory exists using the f-file? and f-directory? APIs, respectively.
(f-file? “/tmp/hello-world.txt”) t (f-file? “/tmp/test”) nil (f-directory? “/tmp/test”) t (f-directory? “/tmp/hello-world.txt”) nil
The f-symlink? function returns true if the given PATH is a symbolic link file.
(f-symlink? “/tmp/resolv.conf”) t
You can check if a file has read, write or executable permissions using the f-readable?, f-writable? and f-executable? functions, respectively, as illustrated below:
(f-readable? “/etc/resolv.conf”) t (f-writable? “/etc/resolv.conf”) nil (f-executable? “/etc/resolv.conf”) nil (f-executable? “/usr/bin/date”) t
You can obtain the size of a file using the f-size API as shown below:
(f-size “/tmp/resolv.conf”) 16
The f-depth API provides the depth of the PATH as illustrated in the following examples:
(f-depth “/”) 0 (f-depth “/etc”) 1 (f-depth “/etc/nginx”) 2
The list of files and directories in a PATH can be obtained using the f-entriess function.
(f-entries “/etc”) => (“/etc/.pwd.lock” “/etc/.updated” “/etc/GNUstep” “/etc/ImageMagick-6” “/etc/ImageMagick-7” “/etc/NetworkManager” “/etc/UPower” “/etc/X11” “/etc/adjtime” “/etc/ansible” “/etc/arch-release” “/etc/avahi” ...)
The ‘f-directories’ API provides the list of directories in a PATH as shown below:
(f-directories “/tmp”) (“/tmp/.ICE-unix” “/tmp/.Test-unix” “/tmp/.X11-unix” “/tmp/.XIM-unix” “/tmp/.font-unix” “/tmp/babel-awGAhc” “/tmp/emacs1000” “/tmp/nginx” “/tmp/test”)
You are encouraged to refer to the README file at https://github.com/rejeep/f.el/blob/master/README.md for more examples and usage.