If I do this:
(message (format "===> %s loaded" (file-name-base load-file-name)))
I get just the basename of the file.
===> 0100-start loaded
If I do this:
(message (format "===> %s loaded" 'load-file-name))
I get:
===> load-file-name loaded
Not really what I want ...
If I do this:
(message (format "===> %s loaded" load-file-name))
I get:
===> nil loaded
No clue what is happening there. I though the variable load-file-name has the value of the module where it is used. And it somehow has it, otherwise I could not get the (file-name-base load-file-name), but used by itself does not work. I probably need to backquote-forward-quote-and-precede-with-a-comma the variable name, but no idea how to do this. Elisp witchery.
What I want to get is:
===> /home/user1/.emacs.d/conf/0100-start.el loaded
How to get the full, absolute path of the lisp module? I though this was the job of load-file-name. How can I display it?
How can I use a variable in elisp?
First of all, message can do formatting, so format is not needed.
Second, when you quote a variable, like 'foo, it is not evaluated, so you are not getting its value.
Third, C-h f file-name-base RET should explain your first output.
Finally, the correct forma inside the file being loaded is
(message "===> %s loaded" load-file-name)
and the reason you see nil there is that it was not evaluated during loading. Please try C-h v load-file-name RET.
Scoping Rules for Variable Binding1:
Local bindings in Emacs Lisp have indefinite scope and dynamic extent.
[...]
Indefinite scope means that any part of the program can potentially access the variable binding. Extent refers to when, as the program is executing, the binding exists.
Dynamic extent means that the binding lasts as long as the activation of the construct that established it.
(Emacs can also perform lexical binding)
When you load a file, something like this happens:
(defun load (file)
(let ((load-file-name file))
(do-load file)))
And so, load-file-name is only bound to file during the time the file is being loaded. When the code exists the scope of the let, the binding is no more effective.
Note that if you did not load but require a module, then upon loading Emacs will also register the module as being provided and will not reload it when calling require another time. This might be the reason you get nil the third time (see unload-feature).
In your module, you can define a global binding for your own symbol:
(defvar jeckyll2hide/module-path load-file-name)
Then, the first time you load (or require) your module, you will define a variable that will be globally bound to the value that is locally bound to load-file-name during loading.
1. formatting and emphasis mine
Related
I'm just learning emacs and came across a configuration that demonstrates quite a bit of the functionality I want in my own configuration. It uses init.el as an entry point to an org file that handles the bulk of the configuration with extensive documentation. I am confused by the following function:
(put 'after-save-hook 'safe-local-variable
(lambda (value) (equal value '(org-babel-tangle t))))
What I think I understand is that this puts the value of the lambda expression into the property list of after-save-hook under the property name safe-local-variable, and that a value is safe if, when passed to the safe-local-variable-p function it returns a non-nil value. This lambda then appears to do an equality comparison between value and the list (org-babel-tangle t), so presumably this means that value is safe only when it's equal to the list (org-babel-tangle t)?
What I am having trouble understanding is twofold. First, where is the lambda getting value from? Second, what is this all actually doing? Neither the documentation I could find on after-save-hook nor org-babel-tangle clarified this for me. The author's comments say "Mark safe variables early so that tangling won't break," but I still don't get it.
The anonymous function with argument value gets its arg from the value of after-save-hook.
after-save-hook is a variable. Its value is a list of hooks.
This code puts the anonymous function as the safe-local-variable property value of symbol after-save-hook, so that when after-save-hook is processed as to see if it is a safe local variable, that function is called. The function is called on the current value of the variable, after-save-hook.
See the Elisp manual, node File Local Variables.
The reason for this is that the file DESKTOP.org starts with this line:
# -*- after-save-hook: (org-babel-tangle t); -*-
That is, when you open that file in Emacs, the local value of after-save-hook becomes (org-babel-tangle t). So whenever you save that file, it's going to call the function org-babel-tangle without any arguments, in order to generate a few shell scripts, e.g. scripts/screenshot.region.sh. The value t in a hook variable means that after calling all the functions in the local value of the hook variable, run-hooks is going to look at the global value of after-save-hook and call any functions listed there as well.
Obviously, allowing any file to specify arbitrary Lisp code to be run would be a security hole equaled only by Microsoft Word macros, so by default file-local settings for risky variables are ignored. Since we know this particular value is safe,* we use the safe-local-variable trick you're asking about. As per the documentation:
You can specify safe values for a variable with a
‘safe-local-variable’ property. The property has to be a function of
one argument; any value is safe if the function returns non-‘nil’ given
that value.
So that's what we have here:
(lambda (value) (equal value '(org-babel-tangle t)))
It is a function that takes one argument, and checks that the argument is equal to the specific value we want to allow. safe-local-variable-p is going to call this function on the specified file-local value, and only allow it if the function returns non-nil. Thus, value is going to be the value that is about to be assigned to after-save-hook. We can see that in action in M-x ielm:
*** Welcome to IELM *** Type (describe-mode) for help.
ELISP> (setq my-function (lambda (value) (equal value '(org-babel-tangle t))))
(lambda
(value)
(equal value
'(org-babel-tangle t)))
ELISP> (funcall my-function '(org-babel-tangle t))
t
ELISP> (funcall my-function 'something-else)
nil
* Is this safe, though?... If an attacker can get you to download a specially crafted file and run org-babel-tangle on it, they can overwrite arbitrary files in the file system using the privileges of your user. It's not what's happening in this case, just something to be aware of.
I have a file functions called "myloaddefs.el". It has magic coments and forms under them just like the one below.
;;;###autoload
(defun an-awesome-function '()
(interactive)
"A descriptive comment." t)
;;; other descriptive comments and forms...
It's full path is ~/.emacs.d/core/myloaddefs.el.
I also have an autoloads file whose full path is ~/.emacs.d/.local/autoloads.el. I store its path in the variable my-autoload-file.
Before calling update-file-autoloads, my-autoload-file only has an empty comment ;; (making sure it's non-empty to avoid an error). Calling update-file-autoloads as I do below returns nil. And it when I check the my-autoload-file it was indeed updated with autoloads. Loading the 'my-autoload-filereturnst` and also seems successful.
(update-file-autoloads (concat my-core-dir "myloaddefs.el") t my-autoload-file) ; => nil
(load-file my-autoload-file) ; => t
However after calling an autoloaded interactive function with, M-x an-awesome-function I get "Cannot open load file: no such file or directory" "../core/myautoloads". This confuses me greatly because the directory and the file do exist. What could be wrong here?
The path to your autoload file needs to be on your load path since you are using relative paths to load the libraries (eg. ../core/autoloads). I would use expand-file-name anywhere you are creating a path instead of building them using concat.
Try (push (expand-file-name ".local" "~") load-path) prior to calling an-awesome-function (whose definition is incorrect).
I defined this macro:
(defmacro with-current-directory (directory &rest body)
"Set the working directory temporarily set to DIRECTORY and run BODY.
DIRECTORY is expanded"
`(let ((default-directory
,(file-name-as-directory
(expand-file-name (eval directory)))))
,#body))
which I use in some lisp functions that are loaded when emacs opens. I always get these warnings:
Eager macro-expansion failure: (void-variable repo-dir)
Eager macro-expansion failure: (wrong-type-argument stringp nil)
Which I understand is happening because those variables are not defined at load time, and emacs is trying to eval them. My question is, how do I avoid getting these warnings. Is there a way to define the macro so that doesn't happen? I cannot figure out how to use the value of the variable, and not the symbol for the variable itself.
Like this:
`(let ((default-directory
(file-name-as-directory (expand-file-name ,directory))))
Since directory is not the value, but the lisp expression that would evaluate to the value, you need to insert (using the comma operator) the expression into the expansion of the macro. If you put the comma before the call to file-name-as-directory, you would have to be able to compute the directory at macro expansion time based only on the tokens of the expression, which you can't do if directory refers to a variable name.
Looks like some beat me to it. Look at the other answer.
You should not be evaluating the file name expansion at expansion time. Also the eval call should not be there. The only thing that should happen during macro expansion is placing the value of directory inside the returned expression. Remove eval and place your comma in front of directory.
It's nice to know that anytime you're using eval, you're probably doing something wrong
Byte compiling emacs lisp is pretty useful, as it generates compiler warnings that, though sometimes cryptic, always point at an error or unfinished tasks, such as missing imports or unimplemented functions.
However, I cannot find a way to generate custom compiler-warnings that integrate well with the *Compile-Log* buffer, i.e. that show the position of the error like
mymodule.el:247:1:Warning: Unused lexical variable `file-name'
E.g. I'm using the subsequent code for placing todo items that raise compile-time messages:
(eval-when-compile
(defmacro TODO (string)
`(eval-when-compile
(message "TODO: %s" ,string))))
However, I cannot find a way to add information (at compile-time) on
file name
line-number
At load-time the variable load-file-name is available, but it is nil at compile-time. The variable default-directory is defined at compile-time but doesn't help in this case.
For the line-number I know no method at all.
When using (warn ...) instead, I get something like
Warning (emacs): TODO: Complete or remove
i.e. no position information at all. If I use (error ...), I get the line number etc displayed automatically, but compilation stops instead of showing all errors and warnings, so it is not a viable solution either.
Update
A partial solution seems to be
(funcall (if byte-compile-current-file 'byte-compile-warn 'warn) FORMAT [ARGS ...])
You need to use the internal variables byte-compile-current-file (the name of the file being compiled) and byte-compile-read-position (the character position at the start of the last read).
Alternatively, you can try the function byte-compile-warning-prefix which inserts the file:line prefix in the *Warnings* buffer.
Either way, you are on your own, messing with the Emacs internals; SO is your only friend. :-)
Indeed, that was a problem. And even byte-compile-read-position is fairly poor because it's not yet up-to-date when the macro is expanded. In Emacs's trunk there is macroexp--warn-and-return, tho as the -- implies, it's currently still considered internal. E.g.
(defmacro TODO (string)
(macroexp--warn-and-return
(format "TODO: %s" string)
nil))
To understand how to use it, you have to understand that it works by returning a special piece of code which makes the byte-compiler later on (when the line-info is available) emit the message.
I want to set the destination directory for emacs lisp byte compilation using relative path such as ../foo. I figured out I should use byte-compile-dest-file-function, but do not know how to set it. How can I set it?
To set the byte-compile-dest-function variable, you can use either customize-variable interactively, or setq in your init file. Since you'll have to write a function doing the job either way, I would recommand the latter, so that everything is in the same place in your init file.
For example:
(defun my-dest-function (filename)
(concat (file-name-directory filename)
"../"
(file-name-sans-extension (file-name-nondirectory filename))
".elc"))
(setq byte-compile-dest-file-function 'my-dest-function)
You can find it using C-h v followed by that variable name.
(defcustom byte-compile-dest-file-function nil
"Function for the function `byte-compile-dest-file' to call.
It should take one argument, the name of an Emacs Lisp source
file name, and return the name of the compiled file."
:group 'bytecomp
:type '(choice (const nil) function)
:version "23.2")
You can see that it is a customizable variable, so you can change it's value to "function".
EDIT: I am not so sure this is the variable you want to change. In fact, you can see that it deals with the variable directories often, I don't see how to set a certain directory where all the .elc's should go.