Currently I am working on a elisp major mode that makes uses of hashtables across sessions. So every time the major mode is initialized, the tables are loaded into memory. During and at the end of the session they are written to a file. My current implementation writes the data in the following way:
(with-temp-buffer
(prin1 hash-table (current-buffer))
(write-file ("path/to/file.el"))))
Loading the data at the beginning of the session is done via read and is something like this:
(setq name-of-table (car
(read-from-string
(with-temp-buffer
(insert-file-contents path-of-file)
(buffer-substring-no-properties
(point-min)
(point-max))))))))
It works but I have the feeling that this is not the most beautiful way to do it. My aim is: I want this major mode to turn into a nice clean package that stores it's own data in the folder where the other data of the package is stored.
This is how I implement
Write to file:
(defun my-write (file data)
(with-temp-file file
(prin1 data (current-buffer))))
Read from file:
(defun my-read (file symbol)
(when (boundp symbol)
(with-temp-buffer
(insert-file-contents file)
(goto-char (point-min))
(set symbol (read (current-buffer))))))
Call to write:
(my-write "~/test.txt" emacs-version)
Call to read
(my-read "~/test.txt" 'my-emacs-version)
I came up with the following solution, inspired by the first answer here:
(with-temp-buffer
(insert "(setq hash-table ")
(prin1 hash-table (current-buffer)
(insert ")")
(write-file (locate-library "my-data-lib"))
And during the init of the major mode, I just do:
(load "my-data-lib")
No need for and read-operation and the plus is that I also don't need to give any filepath, just the fact that there is such a file somewhere on the load-path is enough. Emacs will find it.
elisp rocks. :)
The package desktop can do it for you:
(require 'desktop)
(unless (memq 'hash-table desktop-globals-to-save)
(nconc desktop-globals-to-save (list 'hash-table)))`
Related
I want to kill all buffers visiting a file in a given directory, keeping all others. However, my function bombs out when it reaches the #<buffer *Minibuf-1*>; the minibuffer isn't visiting a file.
(defun my-kill-all-visiting-buffers (dir)
"Kill all buffers visiting DIR."
(interactive)
(mapcar
(lambda (buf)
(and (string-match-p
(regexp-quote dir)
(file-name-directory (buffer-file-name buf)))
(kill-buffer buf)))
(buffer-list)))
In Python-land, I would ask for forgiveness and wrap it in a try-except. How would I handle this in the land of lisp?
You could handle the exception with something like condition-case which elisp has. Or you could just do something like this (avoiding the regexp-quotery as well):
(defun my-kill-all-visiting-buffers (dir)
"Kill all buffers visiting DIR or any subdirectory of DIR"
(interactive "Ddirectory: ")
(mapc
(lambda (buf)
(let ((bfn (buffer-file-name buf)))
(when (and (not (null bfn))
(file-in-directory-p bfn dir))
(kill-buffer buf))))
(buffer-list)))
There are probably better approaches.
Notes
(not (null x)) is the same as x but I use it intentionally so when I read it I know I am checking for x not being a conceptually void value, rather than it just being false.
This avoids regexp operations (originally string= now string-prefix-p), because, famously:
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
Instead it uses file-in-directory-p which asks exactly the question it wants the answer to: is file in a directory or any subdirectory of dir? (Thanks to Stefan for this, which I didn't know about.)
All of the above is legal in Emacs 26.2: I don't keep track of which things in elisp changed & arrived when any more.
Elisp is not that close to many other dialects of Lisp in use other than rather superficially: techniques which are used in elisp may or may not apply to other languages in the family but often don't.
I found that it wasn't necessary to catch all possible errors. Handling the case where a buffer is not associated with a file is sufficient.
The use of regexp-quote is used to handle closing files in subdirectories.
(defun my-kill-all-visiting-buffers (dir)
"Kill all buffers visiting DIR."
(interactive)
(mapcar
(lambda (buf)
(let ((bfn (buffer-file-name buf)))
(and (not (null bfn))
(string-match-p (regexp-quote dir) (file-name-directory bfn))
(kill-buffer buf))))
(buffer-list)))
I want to read config.json file inside .emacs, how can I do this?
(require 'json)
(setq config (json-read-from-string (read-file "config.json")))
You can simplify your code to:
(defun my-file-contents (filename)
"Return the contents of FILENAME."
(with-temp-buffer
(insert-file-contents filename)
(buffer-string)))
edit: Although in this particular instance, I see that json-read-file is already defined, which cuts out the middle man.
Emacs 24.5 defines it like so:
(defun json-read-file (file)
"Read the first JSON object contained in FILE and return it."
(with-temp-buffer
(insert-file-contents file)
(goto-char (point-min))
(json-read)))
For JSON just use json-read-file. For general files f library provides f-read-bytes and f-read-text:
(f-read-text "file.txt" 'utf-8)
I ended up with this code:
(defun read-file (filename)
(save-excursion
(let ((new (get-buffer-create filename)) (current (current-buffer)))
(switch-to-buffer new)
(insert-file-contents filename)
(mark-whole-buffer)
(let ((contents (buffer-substring (mark) (point))))
(kill-buffer new)
(switch-to-buffer current)
contents))))
For those you dislike writing quite so much code you can use f-read from the f library (not part of the standard library). This comes from this question: elisp: read file into list of lists
I'm new to emacs and lisp.
I wanted to auto-load the dot file when it was saved. Meaning, when I save my .emacs file, it would automatically call load-file on it (thus letting me know right away if I messed up).
But I can't seen to be able to find a comprehensive tutorial on hooks in emacs.
This is what I've come up with:
(defun load-init-after-save ()
"After saving this file, load it"
(if (eq bname this) ('load-file this) (nil))
)
(add-hook 'after-save-hook 'load-init-after-save)
Of course, this is incorrect: bname and this are just placeholders. And I don't want this function to run on all saves, just when the .emacs file is saved.
Does anyone know how to do this? Is there a better, easier way?
The following code loads your .emacs or ~/.emacs.d/init.el file after save:
(defun my-load-user-init-file-after-save ()
(when (string= (file-truename user-init-file)
(file-truename (buffer-file-name)))
(let ((debug-on-error t))
(load (buffer-file-name)))))
(add-hook 'after-save-hook #'my-load-user-init-file-after-save)
Since your intended use is error-checking, the code also enables the debugger while loading the init file, so that you get a nice backtrace in case of errors.
For error checking of your init file you may also find Flycheck useful. It checks your init file on the fly with the byte compiler, highlights any errors and warnings in the buffer, and—optionally—gives you a list of all errors and warnings.
Disclaimer: I'm the maintainer of this library.
Way 1
One way of doing is to install auto-compile-mode from MELPA, and enable it:
(defun my-emacs-lisp-hook ()
(auto-compile-mode 1))
(add-hook 'emacs-lisp-mode-hook 'my-emacs-lisp-hook)
Now each time you save an Elisp file that's byte-compiled, it will be
re-compiled. Compilation will usually catch some bad errors.
To compile any Elisp file, select it in dired (C-x d)
and press B (dired-do-byte-compile).
Way 2
Use this custom code I wrote.
(defun test-emacs ()
(interactive)
(require 'async)
(async-start
(lambda () (shell-command-to-string "emacs --batch --eval \"(condition-case e (progn (load \\\"~/.emacs\\\") (message \\\"-OK-\\\")) (error (message \\\"ERROR!\\\") (signal (car e) (cdr e))))\""))
`(lambda (output)
(if (string-match "-OK-" output)
(when ,(called-interactively-p 'any)
(message "All is well"))
(switch-to-buffer-other-window "*startup error*")
(delete-region (point-min) (point-max))
(insert output)
(search-backward "ERROR!")))))
(defun auto-test-emacs ()
(when (eq major-mode 'emacs-lisp-mode))
(test-emacs))
(add-hook 'after-save-hook 'auto-test-emacs)
This will start a new Emacs instance in the background each time you
save a file. If something goes wrong, it will complain.
The second approach uses async.
If you really want to do this just for .emacs user this:
(defun auto-test-emacs ()
(when (and (eq major-mode 'emacs-lisp-mode)
(equal (file-truename user-init-file)
(expand-file-name
buffer-file-truename)))
(test-emacs)))
Is there any way to have EMACS save your undo history between sessions?
I'm aware of the savehist lib, the saveplace lib, the desktop lib, and the windows lib, these all provide some session control but none seem to save the undo history.
From version 0.4 onwards, undo-tree supports persistent storage of undo-tree data between sessions "out of the box". (Note that there are significant bug-fixes related to this feature in more recent versions; the latest version at the time of writing is 0.6.3.)
Simply enable the undo-tree-auto-save-history customization option to automatically save and load undo history in undo-tree buffers. Or use the undo-tree-save/load-history commands to save and load undo history manually.
You need at least Emacs version 24.3 for this to work reliably, but with a recent enough Emacs it works very well.
Add the following to your .emacs file :
(global-undo-tree-mode)
(setq undo-tree-auto-save-history t)
(setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))
Explanation
(global-undo-tree-mode) enables undo tree.
(setq undo-tree-auto-save-history t) enables auto save of undo history.
(setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))) so that your project does not get littered with undo-history savefiles.
Here's some code I wrote which seems to do the trick. It isn't bullet-proof, as in, it doesn't handle all the file handling intricacies that Emacs does (e.g. overriding where auto-save files are put, symlink handling, etc.). But, it seemed to do the trick for some simple text files I manipulated.
(defun save-undo-filename (orig-name)
"given a filename return the file name in which to save the undo list"
(concat (file-name-directory orig-name)
"."
(file-name-nondirectory orig-name)
".undo"))
(defun save-undo-list ()
"Save the undo list to a file"
(save-excursion
(ignore-errors
(let ((undo-to-save `(setq buffer-undo-list ',buffer-undo-list))
(undo-file-name (save-undo-filename (buffer-file-name))))
(find-file undo-file-name)
(erase-buffer)
(let (print-level
print-length)
(print undo-to-save (current-buffer)))
(let ((write-file-hooks (remove 'save-undo-list write-file-hooks)))
(save-buffer))
(kill-buffer))))
nil)
(defvar handling-undo-saving nil)
(defun load-undo-list ()
"load the undo list if appropriate"
(ignore-errors
(when (and
(not handling-undo-saving)
(null buffer-undo-list)
(file-exists-p (save-undo-filename (buffer-file-name))))
(let* ((handling-undo-saving t)
(undo-buffer-to-eval (find-file-noselect (save-undo-filename (buffer-file-name)))))
(eval (read undo-buffer-to-eval))))))
(add-hook 'write-file-hooks 'save-undo-list)
(add-hook 'find-file-hook 'load-undo-list)
desktop-save-mode does not save buffer-undo-list by default. You just have to tell him!
(add-to-list 'desktop-locals-to-save 'buffer-undo-list)
Emacs Session appears to support this:
(add-to-list 'session-locals-include 'buffer-undo-list)
I have managed to get the undo history working by using the information provided here: http://emacs.stackexchange.com/q/3725/2287
Instead of patching the original file desktop.el.gz I created an advice that temporarily overrides (buffer-local-variables) then I use it together with the function that gathers information about the buffer.
(defun +append-buffer-undo-list-to-buffer-local-variables-advice (orig-fn &rest args)
"Override `buffer-local-variables' and call ORIG-FN with ARGS.
There is a bug in Emacs where the `buffer-undo-list' data is
missing from the output of `buffer-local-variables'. This
advice temporarily overrides the function and appends the
missing data."
(let ((orig-buffer-local-variables-fn (symbol-function 'buffer-local-variables)))
(cl-letf (((symbol-function 'buffer-local-variables)
#'(lambda () (append (funcall orig-buffer-local-variables-fn)
`(,(cons 'buffer-undo-list buffer-undo-list))))))
(apply orig-fn args))))
(advice-add #'desktop-buffer-info :around #'+append-buffer-undo-list-to-buffer-local-variables-advice)
(push 'buffer-undo-list desktop-locals-to-save)
(desktop-save-mode 1)
I hope this helps someone else.
There are plenty of ways to fold code in Emacs and I've settled in on using the outline minor mode... it works great!
However, I really want my folding to be persisted when I close and re-open files. It is quite frustrating to have folding set up in a file the way I like it, only to have that lost when I restart Emacs.
Has anyone found a way to keep the folding state of a file persistent?
Edit: Now that I understand the question...
How about something like the following nippet of code. It seems to work for me, though I haven't figured out how to avoid being prompted for the file local variable every time.
(defvar omm-state nil
"file local variable storing outline overlays")
(defun omm-state-mode (&optional arg)
"poor man's minor mode to re-apply the outline overlays "
(interactive)
(omm-re-enable-outline-state)
(add-hook 'before-save-hook 'omm-state-save))
(defun omm-get-all-overlays ()
"return a list of outline information for all the current buffer"
(save-excursion
(let ((all-overlays (overlays-in (point-min) (point-max))))
(mapcar (lambda (o)
(list (overlay-start o) (overlay-end o) (overlay-get o 'invisible)))
(reverse all-overlays)))))
(defun omm-re-enable-outline-state (&optional arg)
"turn on outline-minor-mode and re-apply the outline information"
(outline-minor-mode 1)
(when (listp omm-state)
(mapcar (lambda (p)
(apply 'outline-flag-region p))
omm-state)))
(defun omm-state-save ()
"save the outline state in a file local variable
Note: this just replaces the existing value, you need to start
it off by adding something like this to your file:
# Local Variables:
# omm-state:()
# mode:omm-state
# End:
"
(ignore-errors
(save-excursion
(goto-char (point-max))
(when (search-backward "omm-state:" nil t)
(goto-char (match-end 0))
(kill-sexp)
(princ (omm-get-all-overlays) (current-buffer)))))
nil)
This solution requires you "seeding" your file with something like:
# Local Variables:
# omm-state:()
# mode:omm-state
# End:
I realize this is an old post but FWIW I created a minor mode that complements hs-minor-mode, outline-mode etc. I also "really want my folding to be persisted when I close and re-open files". :)
The package is in MELPA as of today and called persistent-overlays.
It is also available directly on github: https://github.com/mneilly/Emacs-Persistent-Overlays