Skip to main content
added 107 characters in body
Source Link
NickD
  • 33.6k
  • 3
  • 30
  • 47
  • 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.
  • 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.
  • 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.
added 341 characters in body
Source Link
NickD
  • 33.6k
  • 3
  • 30
  • 47
  • 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.
  • 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-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.
Source Link
NickD
  • 33.6k
  • 3
  • 30
  • 47

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.