0

I am looking for a way to view multiple org files that are siblings in a directory, one after another, similar to the way you might scroll a feed of blog posts. For simplicity let's say I have an org file for every day:

20240822.org
20240821.org
...

And I just want to see them all concatenated without having to open each one directly (within reason, presumably at some point I would have to tell the package, like a browser, to load more).

I could achieve something like this with org-transclusion if I maintained a file of file links to transclude. Wondering if there's an easier/existing way.

2
  • It's not clear to me what the problem is: is it the creation of the concatenated contents, given a list of files? Or is it the "convenient" specification of that list of files? I think you need to describe what you really want to do in much more detail, otherwise we'll end up proposing solutions to non-problems. E.g. I could write a function that takes a list of files as argument and does what you describe, but how would you want to specify that list? Simple enumeration would mean that you have to type all the names - is that acceptable? If not, how do you want to specify the names?
    – NickD
    Commented Aug 23 at 22:40
  • The question is about showing the contents concatenated. You can assume that I have the list or directory of files I want to show. Commented Aug 25 at 0:12

1 Answer 1

0

A basic function can be written fairly simply: create a buffer, and append the contents of each file in sequence to the buffer. Here's an implementation of that in three simple functions: my/find-concat-files creates the buffer, populates it by mapping my/find-append-file over the given list of files, starts View-mode on the resulting buffer to make it read-only and sets an exit action so that when you exit View-mode with q, the buffer is killed.

In turn, my/find-append-file passes the file to my/find-file-contents which returns the contents of the file as a string; my/find-append-file then inserts those contents at the end of the buffer.

Here's the implementation:

#+begin_src elisp
  (defun my/find-file-contents (file)
    "Contents of FILE as a string."
    (with-current-buffer (find-file-noselect file)
      (let ((s (buffer-string)))
        (kill-buffer-if-not-modified (current-buffer))
        s)))

  (defun my/find-append-file (file)
    "Append the contents of FILE to the end of the current buffer."
    (goto-char (point-max))
    (insert (my/find-file-contents file)))

  (defun my/find-concat-files (files)
    "Create a new buffer and append to it the contents of each file in FILES.
  Then go to the beginning of the buffer, enable `view-mode' on it (to
  make it read-only and enable all the motion commands that `view-mode'
  provides) and set up the `view-exit-action' so that when `view-mode' is
  exited (with `q'), the buffer is killed. Finally, pop the buffer up in
  some window and make it current, so that the user can interact with it."

    (with-current-buffer (generate-new-buffer " *Concat*")
      (mapc #'my/find-append-file files)
      (goto-char (point-min))
      (view-mode-enter nil (lambda (buf) (kill-buffer buf))))
    (pop-to-buffer " *Concat*"))
#+end_src

Some comments:

  • my/find-concat-files is not interactive, so you cannot bind it to a key. You have to call it with M-: (my/find-concat-files my/files) where my/files is a list of the files you want to concatenate.
  • If you want to make it interactive (i.e. a command that you can bind to a key), then the user-interface aspects of specifying the files are going to come to the fore, but following your comment, I did not worry about that.
  • I tested it by creating 100 files, fooNN.txt, in a test subdir and setting the list with (setq my/files (directory-files "test" 'full "^foo[0-9][0-9]\\.txt")).
  • It is fairly slow: it takes about 2 secs on my machine (and it required three garbage collections - about 0.5 secs). By far, the most expensive part of the code is the find-file-noselect: maybe that can be replaced by a lower level function to speed it up.
  • my/find-file-contents opens the file, saves and returns its contents and then kills the file buffer (if not modified). If you had the file open already, then that buffer will be gone after this. I don't see an easy way to deal with this, although I suspect that it's not going to be much of a problem, but keep this limitation in mind. OTOH, creating and killing the buffer, multiplied by 100, is probably what caused the garbage collections.
1
  • Thanks! This makes sense Commented Aug 25 at 16:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.