Trying out Lisp today on my Mac, I found the following a bit disorienting:
$ sbcl
This is SBCL 1.4.14, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
* (directory "*")
NIL
* (directory "*.*")
(#P"/private/tmp/sbcl/quicklisp.lisp" #P"/private/tmp/sbcl/test1.lisp"
#P"/private/tmp/sbcl/test2.lisp")
On macOS, Linux, FreeBSD, probably most other UNIX-likes, * represents all files. I've only ever seen *.* used on DOS and Windows.
Per the directory description, wildcards are implementation dependent. So, then, what exactly is the convention used by SBCL on the above operating systems? How do I find this out? SBCL's manual doesn't describe this. Could it be different even between the three OS' above?
The SBCL sources have this comment:
;;; FIXME: the below shouldn't really be here, but in documentation
;;; (chapter 19 makes a lot of requirements for documenting
;;; implementation-dependent decisions), but anyway it's probably not
;;; what we currently do.
;;;
;;; Unix namestrings have the following format:
;;;
;;; namestring := [ directory ] [ file [ type [ version ]]]
;;; directory := [ "/" ] { file "/" }*
;;; file := [^/]*
;;; type := "." [^/.]*
;;; version := "." ([0-9]+ | "*")
;;;
;;; Note: this grammar is ambiguous. The string foo.bar.5 can be
;;; parsed as either just the file specified or as specifying the
;;; file, type, and version. Therefore, we use the following rules
;;; when confronted with an ambiguous file.type.version string:
;;;
;;; - If the first character is a dot, it's part of the file. It is not
;;; considered a dot in the following rules.
;;;
;;; - Otherwise, the last dot separates the file and the type.
;;;
;;; Wildcard characters:
;;;
;;; If the directory, file, type components contain any of the
;;; following characters, it is considered part of a wildcard pattern
;;; and has the following meaning.
;;;
;;; ? - matches any one character
;;; * - matches any zero or more characters.
;;; [abc] - matches any of a, b, or c.
;;; {str1,str2,...,strn} - matches any of str1, str2, ..., or strn.
;;; (FIXME: no it doesn't)
;;;
;;; Any of these special characters can be preceded by an escape
;;; character to cause it to be treated as a regular character.
It says "but anyway it's probably not what we currently do", but I'm not sure if that is meant to mean that this comment may be inaccurate for current versions, or something else. I'm assuming at least the wildcard part is correct enough.
The important part here being that the directory, file and type components are all considered separately, so you must have a separate wildcard for both the filename and the extension (aka. the type component) with a dot to separate them.
The comment calls these "Unix namestrings", but I assume this applies to all platforms.
The following works, and I think should work in most implementations (I have tried this on LW on MacOS, SBCL on Linux and Clojure on MacOS).
(defun directory-pathname (p)
;; Everything but the file part: this should be called
;; non-file-pathname or something
(make-pathname :name nil :type nil :version nil
:defaults (pathname p)))
(defconstant +wild-file+
;; a completely wildcarded filename with no other components
(make-pathname :name ':wild
:type ':wild
:version ':wild))
(defun wild-directory (d)
;; Take the directory parts of D and return a pathname which matches
;; all files in tat directory.
;;
;; Actually we could just use D for the defaults since it's only
;; used for defaults and that's less consy, but never mind
(merge-pathnames +wild-file+ (directory-pathname d)))
Then, for instance (directory (wild-directory "/tmp/")) will return everything in the /tmp/ directory, and (directory (wild-directory (user-homedir-pathname))) will return everything in your home directory.
However note that (this is clearly very implementation-dependent but it looks like all the implementations I use agree on this and I agree with their interpretation): (directory (wild-directory "/tmp")) will return everything in /, because "/tmp" as a pathname means 'the file called tmp in the directory called /'. As I said I think this is a reasonable interpretation: pathnames which refer to directories end in /.
Also note that +wild-file+ really is wild: it doesn't treat files whose names begin with . in any special way. I think there is fairly obvious ambiguity about how filenames like /foo/bar/.x should be parsed, although all three implementations I've tried take what I think is the right interpretation: the name is ".x" and the type is nil (/foo/bar/.x.y seems to parse with a type of "y" which I think is also reasonable).
In any case where you need to do any serious pathname manipulation the trick is to define a bunch of wrapper functions and constants which you then use, and behind which you can hide implementation-dependence (in this case the implementation-dependence is how to get all the files in a directory, everything else seems to be common between the implementations).
I suspect such an abstraction layer exists, but I don't know what it is unfortunately.
After writing this I tried CLISP, and it does something interestingly different: it looks like directory doesn't return subdirectory names, just filenames. That's a perfectly reasonable implementation I think, but it would require some more abstraction.
Related
Is it possible to use wildcards such as * in elisp to expand filenames?
For example, I saw that
(expand-file-name "~/.emacs.d/elpa/babel-repl-20160504.1501")
expands to /home/username/.emacs.d/elpa/babel-repl-20160504.1501.
Is there a function to also expand filenames containing wildcards so that we can do:
(expand-file-name "~/.emacs.d/elpa/babel-repl*")
(taking the first filename if multiple matches exist)?
I tried the above, but expand-file-name does not seem to recognize * (Emacs 24.5 from Ubuntu 16.04)
-- EDIT --
#Drew I tried (byte-recompile-directory (file-expand-wildcards "~/.emacs.d/elpa/babel-repl*/") 0) but got an error Wrong type argument: stringp, nil.
I am not very familiar with elisp and the stringp type (a list of strings, I guess). I tried to use a (car ) on the returned value of file-expand-wildcards in order to get the first matched filename. But Emacs still work start correctly.
Any pointers?
Try file-expand-wildcards:
file-expand-wildcards is a compiled Lisp function in files.el.
(file-expand-wildcards PATTERN &optional FULL)
Expand wildcard pattern PATTERN.
This returns a list of file names which match the pattern.
If PATTERN is written as an absolute file name,
the values are absolute also.
If PATTERN is written as a relative file name, it is interpreted
relative to the current default directory, default-directory.
The file names returned are normally also relative to the current
default directory. However, if FULL is non-nil, they are absolute.
That gives you a list of expansions. Pick the first one or any one you want.
I recently learned the basics of emacs' org-mode and couldn't help but imagine applying the collapse/expand concept to parts of a source file. I would like to be able to divide my .emacs file in subparts and only display headers on load, somewhat like the following:
; ERC config...
; DIRED config...
; MISC config...
Each of these would of course be headers to many lines of codes once expanded, like this:
; ERC config
(defun start-irc ()
(interactive)
(erc-tls :server "irc.freenode.net" :port 6697 :nick "foo"))
; DIRED config...
; MISC config...
So is this possible? How could I accomplish something like this with emacs 24.2?
Thanks!
As nice as org-mode is, it does require some structure, which I don't believe can be maintained the way you want in your .emacs file.
What does work well is folding-mode. Check out the information for it on the wiki page, but basically what you do is set up comments around the chunks of code you want to put in a fold, like so:
;;{{{ some folder of some kind
(a few lines)
(of lisp)
(this "code" is just filler)
;;}}}
;;{{{ a different folder
(some more elisp code)
;;}}}
And when it is folded, it will look like:
;;{{{ some folder of some kind...
;;{{{ a different folder...
Babel enables you to achieve exactly this (i.e. managing your init file in org-mode). Specifically, see: http://orgmode.org/worg/org-contrib/babel/intro.html#emacs-initialization
Myself, I make use of outline-minor-mode in my init file for vaguely similar purposes. Various things are treated as outline headings, but you can set outline-regexp as a file local variable to restrict that behaviour, and then you toggle things open and closed with outline-toggle-children (which you would bind to some convenient key). The toggle command works from anywhere in the section, not just on the heading.
I start the headings I want to be collapsed by default with ;;;; * and end my init file with:
;;; Local Variables:
;;; outline-regexp: ";;;; "
;;; eval:(progn (outline-minor-mode 1) (while (re-search-forward "^;;;; \\* " nil t) (outline-toggle-children)))
;;; End:
In your instance you could use:
;;; Local Variables:
;;; outline-regexp: "; "
;;; eval:(progn (outline-minor-mode 1) (hide-body))
;;; End:
Pretty similar in effect to Trey's suggestion, although I expect with folding you can trivially nest sections which I'm not accounting for (having no need to do so). I feel the outline approach leaves the file looking slightly cleaner, if that matters to you.
You can also take a look at the new Outshine package which works together with outline-minor-mode to make it feel more like org-mode. In (e)lisp files outshine interprets sequences of semicolons as headers so all existing code which follows standard conventions for comments becomes foldable without any changes. Many org-mode-like key bindings (like TAB to fold/unfold heading, etc) work too.
I am manually constructing path strings in Elisp by concatenating partial paths and directory names. Unfortunately sometimes the paths end with slash, sometimes not. Therefore, I need to insert slash before concatenating a directory name when necessary but not otherwise. What's a good way to do this?
(file-name-as-directory dir) will return directory path dir with a trailing slash, adding one if necessary, and not otherwise.
If you had your sequence of partial paths in a list, you could do something like:
(let ((directory-list '("/foo" "bar/" "p/q/" "x/y"))
(file-name "some_file.el"))
(concat
(mapconcat 'file-name-as-directory directory-list "")
file-name))
"/foo/bar/p/q/x/y/some_file.el"
or as an alternative, if you wanted to include the file name in the list, you could utilise directory-file-name which does the opposite of file-name-as-directory:
(let ((path-list '("/foo" "bar/" "p/q/" "x/y/some_file.el")))
(mapconcat 'directory-file-name path-list "/"))
"/foo/bar/p/q/x/y/some_file.el"
(Someone please correct me if using directory-file-name on a non-directory is not portable?)
The easiest way to assemble file names from parts of questionable content is with expand-file-name. For example:
(expand-file-name "foo.txt")
this common form will give you a full file name based on default-directory:
/home/me/foo.txt
but if you have a variable 'dir' whose content is "/home/them/subdir" and want to use that, do this:
(expand-file-name "foo.txt" dir)
it doesn't matter if dir ends in / or not. If you are on some other platform, and contains the other slash, it will do the right thing then too. Do you have a mix? Just stack them:
(expand-file-name "foo.txt" (expand-file-name "somesubdir" dir))
Something like this should work as a starting point, although you'd want to flesh it out a bit to make it platform independent, etc.
(defun append-path-component (path new-part)
(if (string-match ".*/$" path)
(concat path new-part)
(concat path "/" new-part)))
As per usual, there's probably some bit of elisp that already does this that I'm just not aware of.
Unless you really care about keeping relative file names as relative, then it's always much better to avoid concat and use expand-file-name instead.
(defun* tofilename (directorylist &optional (filename nil))
"concatenate directory names into a path, with an optional file name as last part"
(concat
(mapconcat 'directory-file-name directorylist "/")
"/"
filename))
(tofilename '("~/" "Temp/") "temp.txt")
;; => "~/Temp/temp.txt"
(tofilename '("~/" "Temp/"))
;; => "~/Temp/"
(tofilename '("~/" "Temp/" "test"))
;; => "~/Temp/temp/"
If you deal with file manipulation, joining and splitting filepaths, checking empty directories and such, I strongly recommend installing f.el, modern file manipulation library. You will have a huge set of file and filepath manipulation functions under one namespace and will never reinvent the wheel again.
The function you need is f-join, it concatenates parts of a path, adding slash only where needed.
Are there any library or function that performs a bash-like glob expansion for emacs lisp?
For example:
(directory-files-glob "~/Desktop/*")
> ("/home/user/Desktop/file1" "/home/user/Desktop/file2")
If there isn't such a function are there any hint/suggestion on how to implement it?
EDIT:
I've found in the docs also an useful function that does quite exactly this:
file-expand-wildcards:
This function expands the wildcard pattern pattern, returning a list of file names that match it.
Not sure why this was overlooked maybe it was not in emacs in 2010 but in a current emacs at least there is function file-expand-wildcards
(file-expand-wildcards "~/Desktop/*")
which does exactly what you want to do..
Check out the documentation for directory-files:
(directory-files "~/Desktop" nil ".")
Note: The third argument is a regular expression - not globbing.
It is straight forward to turn globbing patterns into regular expressions. eshell comes with a translation package which you can use:
(require 'em-glob)
(defun directory-files-glob (path)
(directory-files (file-name-directory path)
nil
(eshell-glob-regexp (file-name-nondirectory path))))
And, if you want full exposure to eshell's globbing (with directories), there's probably a way to get that. The above assumes that the globbing part is in the non-directory portion of the path.
Package f adds a huge amount of file and filepath manipulation functions under a consistent naming scheme. It has a function f-glob that does exactly that:
(f-glob "~/doc/*.org") ; returns a list of files ending with ".org"
(f-glob "*.org" "~/doc/") ; they all behave the same
(f-glob "*.org" "~/doc")
I am starting with emacs, and don't know much elisp. Nearly nothing, really.
I want to use ack as a replacement of grep.
These are the instructions I followed to use ack from within emacs:
http://www.rooijan.za.net/?q=ack_el
Now I don't like the output format that is used in this el file, I would like the output to be that of ack --group.
So I changed:
(read-string "Ack arguments: " "-i" nil "-i" nil)
to:
(read-string "Ack arguments: " "-i --group" nil "-i --group" nil)
So far so good.
But this made me lose the ability to click-press_enter on the rows of the output buffer. In the original behaviour, compile-mode was used to be able to jump to the selected line.
I figured I should add a regexp to the ack-mode. The ack-mode is defined like this:
(define-compilation-mode ack-mode "Ack"
"Specialization of compilation-mode for use with ack."
nil)
and I want to add the regexp [0-9]+: to be detected as an error too, since it is what every row of the output bugger includes (line number).
I've tried to modify the define-compilation-modeabove to add the regexp, but I failed miserably.
How can I make the output buffer of ack let me click on its rows?
--- EDIT, I tried also: ---
(defvar ack-regexp-alist
'(("[0-9]+:"
2 3))
"Alist that specifies how to match rows in ack output.")
(setq compilation-error-regexp-alist
(append compilation-error-regexp-alist
ack-regexp-alist))
I stole that somewhere and tried to adapt to my needs. No luck.
--- EDIT, result after Ivan's proposal ---
With ack.el updated to include:
(defvar ack-regexp-alist
'(("^[0-9]+:" ;; match the line number
nil ;; the file is not found on this line, so assume that it's the same as before
0 ;; The line is the 0'th subexpression (the whole thing)
)
("^[^: ]+$" ;; match a file -- this could be better
0 ;; The file is the 0'th subexpression
))
"Alist that specifies how to match rows in ack output.")
(setq compilation-error-regexp-alist
(append compilation-error-regexp-alist
ack-regexp-alist))
(define-compilation-mode ack-mode "Ack"
"Specialization of compilation-mode for use with ack."
nil)
Then checking the compilation-error-regext-alist variable, I get the value:
(absoft ada aix ant bash borland caml comma edg-1 edg-2 epc ftnchek iar ibm irix java jikes-file jikes-line gnu gcc-include lcc makepp mips-1 mips-2 msft oracle perl rxp sparc-pascal-file sparc-pascal-line sparc-pascal-example sun sun-ada 4bsd gcov-file gcov-header gcov-nomark gcov-called-line gcov-never-called
("^[0-9]+:" nil 0)
("^[^: ]+$" 0))
I find the format of the variable very strange, isn't it? I don't know elisp (yet), so maybe it's correct that way.
Still no links or color in the *ack* buffer.
There is another full-ack package up on ELPA which I have used before and handles --group output.
That said, reading the documentation for compilation-error-regexp-alist you see that it has the form:
(REGEXP FILE [LINE COLUMN TYPE HYPERLINK HIGHLIGHT...])
In the case of --group output, you have to match file and line separately, so I think you want something like (untested)
(defvar ack-regexp-alist
'(("^\\S +$" ;; match a file -- this could be better
0 ;; The file is the 1st subexpression
)
("^[0-9]+:" ;; match the line number
nil ;; the file is not found on this line, so assume that it's the same as before
0 ;; The line is the 0'th subexpression (the whole thing)
))
"Alist that specifies how to match rows in ack output.")
-- Updated --
The variable compilation-error-regext-alist is a list of symbols or elements like (REGEXP ...). Symbols are looked up in compilation-error-regexp-alist-alist to find the corresponding elements. So yes, it is a little weird, but it's easier to see what's turned on and off without having to look at ugly regexes and guess what they do. If you were going to distribute this I would suggest adding the regex to compilation-error-regexp-alist-alist and then turning it on in compilation-error-regext-alist, but that is somewhat moot until you get it to work correctly.
Looking more closely at ack.el, I notice that it uses
(let (compile-command
(compilation-error-regexp-alist grep-regexp-alist)
...)
...
)
In other words it locally overwrites compilation-error-regexp-alist with grep-regexp-alist, so you need to add the regexes there instead. Or even better might be to replace it with
(let (compile-command
(compilation-error-regexp-alist ack-regexp-alist)
...)
...
)
In the end I still recommend full-ack since the filename regex does not seem to be working correctly. It seems more complete (though more complicated), and I have been happy with it.