0

This might require more tricks than just Emacs-fu, but since Emacs is at the heart of it I hope to get some ideas on how to approach my need here.

I use Emacs/Make/gcc as my development environment. I have a script that watches my source files so that whenever a change is saved it rebuilds, run all tests and updates coverage (thanks cov-mode).

I'm normally running this in a separate terminal window so any compilation errors will show up there, when what I really want is for those errors to end up in a compilation-mode-type buffer in Emacs so I can go to next-error with one key-stroke.

Here are my attempts so far:

  • run script in a term-window - failed to get next-error to work
  • run script in a shell-window- same problem
  • run script and collect output in a file which is visited in a buffer - couldn't get that window to auto-update (can I?)

I also want to ensure that next-error is from the last "round", but since the buffer is not cleared on a ^L/clear-command you would have to step through all the errors unknowing when you are actually on a current one.

Update:

I have been exploring writing the output from the script into a file and then forcing my running Emacs to re-read it using auto-revert-mode while still keeping the *compilation*-like behaviour in that buffer. This works, but the problem is that next-error can't be reset to start from the new errors after reverting. Using C-u C-x ` just errors with "Moved past last error". Not even using the after-revert-hook to do (next-error 4 t) seems to do the trick.

How can I reset the next-error "pointer" in this scenario?

Or are there any other approaches I should try?

12
  • emacs.stackexchange.com/tags/elisp/info
    – Drew
    Jun 7, 2020 at 20:41
  • "couldn't get that window to auto-update (can I?)" -- auto-revert-mode ?
    – phils
    Jun 7, 2020 at 22:34
  • You might also consider whether your compile step could actually run compile inside Emacs?
    – phils
    Jun 7, 2020 at 22:37
  • @phils what are you thinking with "actually run compile inside Emacs"? Externally making Emacs run the compile through some scripting or PPC? If so how could I do that?
    – thoni56
    Jun 8, 2020 at 14:49
  • @Drew sorry about the erroneous tag... Learning.
    – thoni56
    Jun 8, 2020 at 15:00

1 Answer 1

1

I have a similar script, and I just run it directly from M-x compile. In my case, the script runs continuously once started and triggers a recompilation on file change. Sounds like that should work for you? You'd run utman make debug unit test from compile, don't invoke emacsclient or anything.

Then, to get next-error working correctly on recompile, I do this.

  1. Add a function to compilation-filter-hook to watch the script output for a special string the script emits on recompilation (for example purposes let's say that string is "Change detected, recompiling ...")
  2. Call compilation-forget-errors when the string is found. (Note that the manual doesn't advertise this function, I found it by hunting around in the compile.el source code.)
  3. Not technically necessary, but for quality of life I after-advise compilation-forget-errors to reset the mode-line counts of errors / warnings / info lines. For some reason it doesn't do that by default (in Emacs 27.1)

All in, my code snippet looks like this:

;;; this runs with the *compilation* buffer current.  see the documentation
;;; for `compilation-filter-hook'
(defun my/forget-compilation-errors ()
  (let ((inserted-string (buffer-substring-no-properties
                          compilation-filter-start (point))))
    (when (string-match-p "Change detected, recompiling" inserted-string)
      (compilation-forget-errors))))

(add-hook 'compilation-filter-hook #'my/forget-compilation-errors)

(defadvice compilation-forget-errors (after reset-num-errors-found activate)
  (progn
    (setq-local compilation-num-errors-found 0)
    (setq-local compilation-num-warnings-found 0)
    (setq-local compilation-num-infos-found 0)))

Actually that snippet up there is a bit of a lie. It's what I had at first. These days I actually always give a prefix argument to compile so that I get comint-mode + compilation-shell-minor-mode. That way I can interact with my script if it prompts for input, which it does sometimes, and I can cleanly shut it down with C-c C-c. The same principles apply, it's just I have to use a different hook. So my actual code snippet now looks like:

(defun my/forget-compilation-errors (inserted-string)
  (prog1 inserted-string
    (when (string-match-p "Change detected, recompiling" inserted-string)
      (compilation-forget-errors))))

(add-hook 'comint-preoutput-filter-functions #'my/forget-compilation-errors)

(defadvice compilation-forget-errors (after reset-num-errors-found activate)
  (progn
    (setq-local compilation-num-errors-found 0)
    (setq-local compilation-num-warnings-found 0)
    (setq-local compilation-num-infos-found 0)))
1
  • This worked really well. Thank you. Just to clarify, the only difference between the two implementations is that you can interact with your program/script in the second version?
    – thoni56
    May 18, 2021 at 5:37

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.