Emacs Lisp - declare functions with a variable/varying name - emacs

I'm working on an Emacs Lisp package and one particular feature I would like to add is ability to define functions on the fly - they would follow the same naming convention, but it would help me not having to declare every single one of them manually.
To give an example, I have a basic function called exec, which takes an argument that is the name of executable to launch:
(def exec (cmd)
(async-shell-command cmd "buffer"))
At the same time, in this particular case, I know the list of the executables that I will want to use - or more precisely, I know how to get a list of them, as it can change over time. So what I would like to do, given the following list of executables:
("a" "b" "c")
is to iterate over them and for each one to create a function with a name exec-[executable] - exec-a, exec-b, exec-c.
Unfortunately, defun does not evaluate the NAME argument so I cannot create the function name dynamically.
PS. The exec command is good enough in itself - it uses completing-read with the list of executables supplied, but I thought the above would be nice addition.

How 'bout
(dolist (name name-list)
(defalias (intern (concat "exec-" name))
`(lambda () ,(format "Run %s via `exec'." name) (interactive) (exec ,name))))

Related

How to extend Neotree to open a file using hexl?

I'm trying to extend Neotree to open a file using hexl-mode with the shortcut C-c C-x. How would one do this?
I've tried to evaluate a key definition after the Neotree load where it uses my/neotree-hex to open a file path using neo-buffer--get-filename-current-line.
(defun my/neotree-hex
(hexl-find-file neo-buffer--get-filename-current-line))
(with-eval-after-load 'neotree
(define-key neotree-mode-map (kbd "C-c C-x")
'my/neotree-hex))
At the very least, you are missing the (empty) argument list in the function:
(defun my/neotree-hex ()
(hexl-find-file neo-buffer--get-filename-current-line))
I don't know what neo-buffer--get-filename-current-line is: if it is a function, then you are not calling it correctly - in lisp, you call a function by enclosing the (name of the) function and its arguments in parens: (func arg1 arg2 ...)[1]; so if it is a function and it takes no arguments, then your function should probably look like this:
(defun my/neotree-hex ()
(interactive)
(hexl-find-file (neo-buffer--get-filename-current-line)))
In order to be able to bind it to a key, you have to make your function a command, which means that you need to add the (interactive) form.
Disclaimer: I know nothing about neotree.
[1] You might want to read an introduction to lisp. One (specifically tailored to Emasc Lisp) is included with the emacs documentation, but is also available online. Eventually, you will want to read the Emacs Lisp Reference Manual. Calling a function is covered in the Introduction and is covered in detail in the Reference.

How can I build up the #:usage-help string dynamically in racket lang's (command-line ...) function?

I am trying to build a git-style CLI application, eg racket-app command command-params .... This is similar to what you can do with thor's sub-command feature.
I have this code:
(define commands
(hash
"noop" (list "Does nothing"
(lambda () (log-info "noop was called :)")))
"render" (list "Prepares and renders the static HTML (hugo)"
render-proc)))
(define s1 (build-possible-commands-from-the-keys-of-commands))
(define command
(command-line
#:usage-help s1
#:args (op) op))
(cond
[(dict-has-key? commands command) ((second (dict-ref commands command)))]
[else (log-error "Unknown command")])
It fails with command-line: #:usage-help clause contains non-string.
If I replace the reference to s1 with an actual string (eg "aoeu") then it works just fine. I would like to build up that string (s1 in this example) dynamically, but I can't figure out how to do that.
I believe the issue here is that the usage-help must be a literal string and not one computed as the program runs, because this string should be available from the command-line without running the program. That is, raco help should be able to provide the help string without running the program.
You can sort of work around this; you need to provide the string at expansion time. Here's the most straightforward way:
#lang racket
(define-syntax my-cmd
(syntax-rules ()
[(_ str)
(define command
(command-line
#:usage-help str
#:args (op) op))]))
(my-cmd "aoeu")
In this example, it's clear that I've separated the "command" call from the literal string, but it may not be so useful to you.
So, here's the question: what's the use case? How are you trying to abstract over the string?
EDIT
After reading over your use case, it totally looks to me like you want to use parse-command-line, as Jon Zeppieri suggested on the racket mailing list. This allows you to specify a procedure to be called when argument parsing fails, and you can emit any string you like.
The grammar for command-line says:
flag-clause = ...
#:usage-help string ...
This means that the command-line will not accept general expressions after
#:usage-help.
I can't think of a good reason for that restriction though. I too would expect an expression to be accepted.
I suggest asking at the Racket mailing list - maybe someone has an explanation - or if not maybe someone will change the behaviour of command-line.

compile large project within emacs

A large project may have directory with several level depth. Emacs's default compile command is "make -k", if I modified a certain source code, then typed "M-x compile RET RET", it will execute "make -k" under the directory which the source code lies.
I think I can write a function to determine if the Makefile exist under current directory, if yes, keep searching under the parent directory until find the top level directory, then execute the building command, it would be right like my expectation.
However, I'm not very clearly how to start, could anyone give me some hints to start? Like the function or variable I may encounter. Thanks.
You can try to use something like:
(setq compile-command
'(let ((mf (locate-dominating-file default-directory "Makefile")))
(if mf (setq mf (file-name-directory mf)))
(concat (if (and mf (not (equal mf default-directory)))
(format "cd %s; "
(shell-quote-argument
(file-relative-name
(directory-file-name mf)))))
"make -k ")))
There is a smarter-compile in marmalade.
From the documentation....When you require it, you can specify a list of cons cells, each one like (TEST . COMMAND).
COMMAND is used for the compile-command when
the TEST succeeds.
TEST can be:
a string. In this case it is used as a regex,
and matched against the filename associated to the
buffer. The TEST succeeds when the regex matches.
a symbol, representing the major-mode. In this case
if the buffer uses that major mode, the TEST
succeeds.
a symbol, representing any function with a name not
ending in \"-mode\". In this case, the function is
called and if it returns non-nil, the TEST
succeeds.
a list of forms. In this case the forms are eval'd,
and if the return value is non-nil, the TEST
succeeds.
So you could produce a function that does the scan for makefile in parent directories,
and use that as your TEST.
According to the documentation, if the COMMAND is nil, then the package uses the result of the TEST as the compile command. Which means you would need only one function, returning a make command referencing the makefile in the appropriate directory.
Take a look at: http://emacswiki.org/emacs/CompileCommand
"C-h v compile-command" directly from emacs.
Here's a solution for people who prefer bash scripting over Emacs Lisp. In my .emacs I define a command which saves all buffers and runs a bash script which compiles the project.
(defun save-all-and-compile () (interactive)
(save-some-buffers 1)
(shell-command "make-and-run.sh &"))
(add-hook 'c-mode-common-hook (lambda ()
(local-set-key (kbd "<f5>") 'save-all-and-compile)))
For small projects the script could be as simple as
#!/bin/bash
make -j && ./<main>
where 'main' is the name of your executable. For larger projects one would first need to locate the root directory. Similarly, you could have different scripts (bound for different keys) for building and running the program. And then some more scripts for testing different parts of the project. But these are just details one can figure out for themselves.
Make sure the script is run asynchronously using '&'. This way the Async Shell Command buffer will open with the output from make and your project and will stay open.
EDIT
Based on the discussion below it appears I have initially overthought it and the solution is quite simple. Instead of passing the usual 'make' or 'make -k' to compile-command one could use a shell script which first navigates to the project's root directory and the builds.
(setq compile-command "script.sh")

Emacs lisp - autocomplete bookmark names

I'm new to elisp. http://www.gnu.org/s/emacs/manual/html_node/elisp/Interactive-Codes.html#Interactive-Codes lists 'code characters' for interactive parameters, which AFAIK modifies the behaviour of the input mechanism when prompting the user for input (eg: if you specify that the input is a filename that exists, emacs' autocomplete functionality will look for file names that exists).
I'm trying to find a code for a bookmark name that already exists - ie: emacs will prompt the user for a bookmark name, and upon pressing tab emacs will show possible bookmark name completions.
Does such a code exist?
Use completing-read for that. You could write a function that prompts the user for a bookmark like so:
(defun my-function ()
(interactive)
(let ((bookmark (completing-read "Bookmark: " (bookmark-all-names))))
...))
If you prefer the prompting to be part of interactive (so that the result will be bound automatically to your function's arguments), you could use the following alternative:
(defun my-function (bookmark)
(interactive (list (completing-read "Bookmark: " (bookmark-all-names))))
...)
For Emacs to find the function bookmark-all-names you also have to add the following line to your .emacs file:
(require 'bookmark)
Function bookmark-completing-read is the standard way to complete a bookmark name. You do not need the lower-level function completing-read for this. Example:
(bookmark-completing-read "Bookmark" bookmark-current-bookmark)
If you use Bookmark+ then bookmark-completing-read accepts some optional arguments (similar to completing-read) that can help:
ALIST -- an alist of bookmarks to choose from (instead of all bookmarks: bookmark-alist)
PRED -- a predicate that filters the list of bookmark candidates
HIST -- an input history list
There is also a non-strict version of the function, bmkp-completing-read-lax, which is useful if you want to accept a new bookmark name or complete against existing names.

What does (interactive) mean in an Emacs Lisp function?

Emacs Lisp function often start like this:
(lambda () (interactive) ...
What does "(interactive)" do?
Just to clarify (it is in the quoted docs that Charlie cites) (interactive) is not just for key-bound functions, but for any function. Without (interactive), it can only be called programmatically, not from M-x (or via key-binding).
EDIT: Note that just adding "(interactive)" to a function won't necessarily make it work that way, either -- there could be many reasons functions are not interactive. Scoping, dependencies, parameters, etc.
I means that you're including some code for the things you need to make a function callable when bound to a key -- things like getting the argument from CTRL-u.
Have a look at CTRL-h f interactive for details:
interactive is a special form in `C source code'.
(interactive args)
Specify a way of parsing arguments for interactive use of a function.
For example, write
(defun foo (arg) "Doc string" (interactive "p") ...use arg...)
to make ARG be the prefix argument when `foo' is called as a command.
The "call" to `interactive' is actually a declaration rather than a function;
it tells `call-interactively' how to read arguments
to pass to the function.
When actually called, `interactive' just returns nil.
The argument of `interactive' is usually a string containing a code letter
followed by a prompt. (Some code letters do not use I/O to get
the argument and do not need prompts.) To prompt for multiple arguments,
give a code letter, its prompt, a newline, and another code letter, etc.
Prompts are passed to format, and may use % escapes to print the
arguments that have already been read.
Furthermore it’s worth mentioning that interactive's main purpose is, in an interactive context (e.g. when user calls function with key binding), let user specify function arguments that otherwise could be only given programmatically.
For instance, consider function sum returns sum of two numbers.
(defun sum (a b)
(+ a b))
You may call it by (sum 1 2) but you can do it only in a Lisp program (or in a REPL). If you use the interactive special form in your function, you can ask the user for the arguments.
(defun sum (a b)
(interactive
(list
(read-number "First num: ")
(read-number "Second num: ")))
(+ a b))
Now M-x sum will let you type two numbers in the minibuffer, and you can still do (sum 1 2) as well.
interactive should return a list that would be used as the argument list if function called interactively.
(interactive) is for functions meant to interact with the user, be it through M-x or through keybindings.
M-x describe-function RET interactive RET for detailed info on how to use it, including parameter to catch strings, integers, buffer names, etc.
One of the "gotchas" that this clarifies is that the argument to interactive is actually a kind of mini-formatting language (like for printf) that specifies the following (for the surrounding function's input):
schema (number of arguments and their type)
source (e.g., marked-region in buffer and/or user input, etc.)
For example,
'r'
Point and the mark, as two numeric arguments, smallest first.
means that the interactive-annotated function needs exactly two arguments.
e.g. this will work
(defun show-mark (start stop)
(interactive "r")
(print start)
(print stop))
This will break:
(defun show-mark (start)
(interactive "r")
(print start))
Wrong number of arguments: ((t) (start) (interactive "r") (print start)), 2