15

In a recent answer by lunaryorn, he stated:

However, I'd recommend against most other parts of Org, for reasons already stated in comments: It's old, and full of legacy and harmful practices (e.g. find-file-noselect to read files non-interactively).

Can anyone explain why is find-file-noselect a bad idea to read files in Elisp programs? Is there a better way? I'm asking because I was thinking of using it in one of my projects.

mbork
  • 1,647
  • 1
  • 13
  • 23
  • Seemingly, there was no `good-practices` tag before; is it a good idea to use it? – mbork Oct 30 '14 at 21:02
  • I think that `good-practices` would fall under the category of "meta tag," which is [frowned upon](http://blog.stackoverflow.com/2010/08/the-death-of-meta-tags/) by SE. – nispio Oct 30 '14 at 22:08
  • 1
    @nispio I think it's a valid tag, but we can take this to the meta of course. – Malabarba Oct 30 '14 at 23:17
  • 1
    @nsipio: I've skimmed through that article, and I don't agree. But it's not me who decides. ;-) – mbork Oct 31 '14 at 14:23

2 Answers2

21

TL;DR: With find-file-noselect you have no control about what actually happens, and you may end up with arbitrary minor modes enabling in the buffer, depending on what the user enabled in their init.el. Also, cleanup is hard.

Use with-temp-buffer and insert-file-contents instead. If you need specific major or minor modes in the buffer, enable them explicitly. To write files, use with-temp-file instead, which—despite its name—lets you write to arbitrary files.

Side effects

find-file-noselect has a lot of side-effects, including

  • interactively asking questions (that alone is a no-go in non-interactive use),
  • automatically enabling view mode for readonly files,
  • entering normal mode otherwise,
  • and running find-file-hook.

Normal Mode itself

  • automatically selects a proper major mode for the current buffer,
  • runs all corresponding major and minor mode hooks,
  • and reads all local variables for the current buffer, i.e. file variables and directory variables, which again may ask interactive questions about unsafe local variables.

Since all hooks are run, you get all minor modes and hook functions the user enabled in their init.el, which can cause everything, from minor inconveniences (if undesirable minor modes are enabled) to major havoc (if the user added a hook function that expects to be called from an interactive context).

See https://github.com/flycheck/flycheck/issues/366 for an example. The use of find-file-noselect caused a data file to be syntax-checked by Flycheck, and since it was happening at Emacs shut-down, there was no time to properly clean up again, leaving a temporary file behind.

Cleanup

With find-file-noselect you need to be extra careful to kill the buffer again. find-file-noselect does not do that for you.

You need to remember the buffer at some place, and carefully use unwind-protect to make sure that the buffer gets killed even in case of non-local exits.

Alternatives

To read files, use with-temp-buffer and insert-file-contents, which only does the most basic things, e.g. coding system conversion, but does not ask questions, enable hooks, or setup local variables:

(with-temp-buffer
  (insert-file-contents (locate-user-emacs-file "foo.el"))
  ;; Enter the major mode explicitly
  (emacs-lisp-mode)
  ;; …
  )

with-temp-buffer takes care to properly kill the temporary buffer at the end of its body.

To write files, use with-temp-file, which creates a temporary buffer and writes the contents to the given file name at the end of its body:

(with-temp-file  (locate-user-emacs-file "foo.el")
  (prin1 (list 'my 'data) (current-buffer)))
10

From section 24.3 in the Elisp manual:

To copy the contents of a file into a buffer, use the function insert-file-contents. (Don't use the command insert-file in a Lisp program, as that sets the mark.)

Searching the Elisp documentation for find-file-noselect it is obvious that it does much more than just reading a file into a buffer. Perhaps people who think using this function is a bad idea are thinking about the, possibly unwanted, side-effects? I guess it depends on what you want to achieve. If you want to have as clean/untouched buffer content as possible, it might be a good idea to use the old and trusty with-temp-buffer + insert-file-contentscombination. If you want the buffer contents to be as close to what find-file produce, perhaps you do want to use find-file-noselect? Or perhaps he was thinking about find-file ;)

Mathias Dahl
  • 554
  • 2
  • 7
  • 3
    If something is being done non-interactively I can't see any scenario in which you'd want *" the buffer contents to be close to what find-file would produce"*. find-file is slow because it does a ton of unnecessary stuff, including all sorts of hooks. The only "feature" of find-file which you might want is the major-mode, but then you should just activate it yourself (you can't even guarantee find-file would turn on the mode that you want anyway). – Malabarba Oct 30 '14 at 23:15
  • Malabarba: If you intend for the buffer to remain available for editing by the user, then you may very well want to mimic the `find-file` process. – phils Oct 31 '14 at 23:48