Elisp: find-lisp.el

By Xah Lee. Date: .

Skipping Subdir

In other programing languages (perl python golang), usually the dir walker lets you skip some specified directory. The walker calls a doFile function you write, and pass current dir or file, so you can skip it to prevent going into.

In elisp, there's a package find-lisp.el that lets you use a regex to filter dir, but is very slow. I do not recommend it.

To skip some subdir, it's faster if you just use directory-files-recursively then filter result.

(setq
 skipDirs
 [
  "ergoemacs_org/emacs_manual/"
  "xahlee_info/REC-SVG11-20110816/"
  "xahlee_info/clojure-doc-1.8/"
  ])

(seq-filter
 (lambda (path)
   (not (seq-some
         (lambda (x) (string-match x path))
         skipDirs )))
 (directory-files-recursively "/Users/xah/web/xahlee_info/" "\\.svg$" ))

to filter a list, see Elisp: Sequence Functions

The package “find-lisp.el” lets you list all files in a dir and subdir with a regex to filter dir.

However, it's super slow. I do not recommend using it.

For how to walk a dir, see Elisp: Walk Directory, List Files

But if you really want to use find-lisp.el , here's some tips.

(require 'find-lisp)

The package provides the following functions:

(find-lisp-find-files dirpath regex) → recursive, filter by regex.

(find-lisp-find-files-internal dirpath 'file-predicate-p 'dir-predicate-p) → recursive, filter by predicate functions.

They return a list of all files in the directory dirpath.

Here's a simple example:

;; traverse a dir

(require 'find-lisp)

;; insert file path of all html files in the directory, recursive all subdirectory
(mapc

 (lambda (x)
   (insert x)
   (insert "\n"))

 (find-lisp-find-files
  "/home/john/web/"
  "\\.html$"
  ))

Filter by a General Predicate Function

Sometimes you need to use a general function that answer yes or no for a file/directory, not just by regex. For example, if the file size is larger than 1 megabytes, skip.

In that case, you can use the function find-lisp-find-files-internal.

(find-lisp-find-files-internal DIRECTORY FILE-PREDICATE
DIRECTORY-PREDICATE)

Find files under DIRECTORY which satisfy FILE-PREDICATE.
FILE-PREDICATE is a function which takes two arguments: the file and its
directory.

DIRECTORY-PREDICATE is used to decide whether to descend into directories.
It is a function which takes two arguments, the directory and its parent.

Here's example:

;; traverse a dir

(require 'find-lisp)

(find-lisp-find-files-internal
 dirpath
 'xah-find-files-file-predicate-p
 'xah-find-files-dir-predicate-p)

where xah-find-files-file-predicate-p and xah-find-files-dir-predicate-p are filter functions. They should return t if the file/directory is to be processed.

Each will receive 2 args, fname (short name, no path) and its parent directory parent-dir.

If the function returns true, then that file/directory will be visited.

Here's a example of file name filter:

(defun xah-find-files-file-predicate-p (fname parentdir)
  "return true if fname ends in .html and doesn't begin with xx."
  (and
   (string-match "\\.html$" fname)
   (not (string-match "^xx" fname))
   ))

〔see Elisp: Regex Tutorial

WARNING: the DIRECTORY-PREDICATE and Dot Directory

🛑 WARNING: the DIRECTORY-PREDICATE function will also receive itself (the dot directory), and the parent directory (the dot dot directory).

If you are not careful, it'll be infinite recursion.

So, to solve that problem, you add the default filter find-lisp-default-directory-predicate to yours. Like this:

(defun xah-find-files-dir-predicate-p (fname parentdir)
  "File name predicate. Returns true or false.
Return true if FNAME is not one of the list item (see code) and doesn't begin with xx, and `find-lisp-default-directory-predicate' returns true."
  (and
   (not
    (or
     (string-equal "clojure-doc-1.8" fname)
     (string-equal "javascript_es6" fname)
     (string-equal "jquery_doc" fname)
     (string-equal "node_api" fname)
     (string-match "^xx" fname)))
   (find-lisp-default-directory-predicate fname parentdir)))