1

The context

I'm storing all my custom defined yasnippet snippets in an Org file (see below)

$ cat ~/.emacs.d/snippets/snippets.org
#+PROPERTY: header-args:yasnippet :mkdirp yes

#+begin_src yasnippet :tangle ~/.emacs.d/snippets/org-mode/a
# key: a
# --

a foo bar
#+end_src

#+begin_src yasnippet :tangle ~/.emacs.d/snippets/org-mode/b
# key: b
# --

b foo bar
#+end_src

The problem

When tangling any code block present in the Org file shown above (i.e. snippet definition) a newline is inserted at the end of the last line which is causing yasnippet insert a newline whenever a snippet is triggered. I don't want a newline to be inserted whenever an snippet is triggered since newlines are unnecessarily added to my files and I manually have to delete them.

The behavior of yasnippet of adding newlines after an snippet expansion is mentioned in the FAQ file located in the yasnippet master branch repository

If there is a newline at the end of a snippet definition file, YASnippet will add a newline when expanding that snippet.

Additional context

It is worth mentioning that this problem is not present when creating a new snippet with yas-new-snippet since the variable require-final-newline is set to nil in the opened buffer.

The behavior present in the buffers which are opened after yas-new-snippet has been executed is set by yasnippet itself.

$ grep -R 'require-final-newline' ~/repos/yasnippet
...
~/repos/yasnippet/yasnippet.el:        (set (make-local-variable 'require-final-newline) nil)
~/repos/yasnippet/yasnippet.el:      (set (make-local-variable 'require-final-newline) nil)
...

The question

  • Is there a way to change the behavior of tangling so that a newline is not inserted at the end of the file?
  • What other workarounds could I try to overcome this issue? I was thinking in executing a function that iterates over all files in my custom snippets directory and delete the last character if and only if it is a newline.
doltes
  • 567
  • 2
  • 11
  • So IIUC now, what you want is *no* newline at the end of the file at all: the last character of the file should be the `r` of `bar` - correct? – NickD Oct 10 '20 at 20:59
  • Yes. Currently, the last character of each tangled file is a newline character. – doltes Oct 11 '20 at 00:38
  • That's because the source block contents includes that newline. You need to postprocess the files to delete it, possibly with `org-babel-post-tangle-hook`. – NickD Oct 11 '20 at 01:25

1 Answers1

3

Here's an implementation using org-babel-post-tangle-hook as I hinted in my comment:

#+PROPERTY: header-args:yasnippet :mkdirp yes :padline no

* foo


#+begin_src yasnippet :tangle ./snippets/a
# key: a
# --

a foo bar
#+end_src

#+begin_src yasnippet :tangle ./snippets/b
# key: b
# --

b foo bar
#+end_src


* Code                                                          :noexport:

#+begin_src emacs-lisp
  (defun ndk/zap-newline-at-eob ()
     (goto-char (point-max))
     (when (equal (char-before) ?\n)
       (delete-char -1)
       (save-buffer)))

  (add-hook 'org-babel-post-tangle-hook #'ndk/zap-newline-at-eob)
#+end_src

When the hook runs, each function in the hook is called on each tangled file.

The function zaps one newline at the end of the file and saves the buffer. That creates backup files, so as the OP points out in a comment, one might want to prevent that by locally binding make-backup-files to nil:

(defun ndk/zap-newline-at-eob ()
  (let ((make-backup-files nil)) 
     (goto-char (point-max))
     (when (equal (char-before) ?\n)
       (delete-char -1)
       (save-buffer))))

You probably want to remove the function from the hook after you are done here (unfortunately, setting the hook locally in this buffer does not work: the tangling happens in a different buffer and only the global value of the hook is in effect there). I doubt that you want the newline zapping in anything other than yasnippet blocks, so adding a code block like this will help:

#+begin_src emacs-lisp
(remove-hook 'org-babel-post-tangle-hook #'ndk/zap-newline-at-eob)
#+end_src
NickD
  • 27,023
  • 3
  • 23
  • 42
  • Thanks. I've tried this but it doesn't solve this issue. As stated in the documentation, `:padline` inserts a newline between source code blocks. For example, let's suppose you have two source code blocks `a` and `b` which are tangled to the same file `c`; and you execute `org-babel-tangle`. If `:padline` is set to `yes`, then a newline is inserted between the content tangled of `a` and `b`. If `:padline` is set to `no`, than a newline is not inserted between the content tangled of `a` and `b`. – doltes Oct 10 '20 at 18:57
  • OK - I think this is better. Let me know if there are problems. – NickD Oct 16 '20 at 13:48
  • 1
    It works like a charm. I just inserted the body of the function in `(let ((make-backup-files nil)) ...)` in order for backups of the snippets not to be created. Otherwise, yasnippet would recognize two snippets (the original one and its corresponding backup `~`) for each snippet. – doltes Oct 20 '20 at 23:45