0

I am getting an elisp error that fails at lisp/progmodes/compile.el within compilation-next-error function that reads "Not in a compilation buffer". The elisp function I am writing attempts to automatically jump to the first error within the compilation buffer, then copies the error and writes it to file. Here is the function. I've tried a few things so I've left the tried stuff commented while the function itself will produce the error. Perhaps the error is an obvious one for trained eyes.

(defun write-first-error-to-file()
  "function copies first error in compilation buffer and writes it to specified file"
  (interactive)
;;;  (select-window (previous-window))
;;;  (switch-to-buffer "*compilation*")
;;;  (goto-char (compilation-next-error))
  (let ((display-buffer-overriding-action '(display-buffer-same-window)))
   (compilation-next-error))
;;;  (set-mark-command nil)
;;;  (goto-char (compilation-next-error))
;;;  (setq deactivate-mark nil)
  (kill-ring-save (region-beginning) (region-end))
  (write-region (region-beginning) (region-end) "~/copybuffer.txt"))

The function attempts to do the following:

  1. Declares the function as an interactive form
  2. Display the compilation buffer and within it go to the next error within compilation buffer. Note, it should be the first error if this is the first command executed. This is where I am getting the aforementioned error.
  3. Commented, set mark to select region
  4. Commented, Advance cursor to next compilation-next-error
  5. Commented, deactivate mark. At this point, the region should have been selected programmatically perhaps??
  6. Save region to kill-ring
  7. Write region to file! :D

As you can see there are quite a few issues with this function, perhaps someone can see what I am missing. Thanks for any help anyone can provide.

For reference, the above function produces the following error with strange error output. You can see the "Not in compilation buffer" within the error output.

×H^N%W\204s^A^N&\203þ^A^M×
ÌH^K>\204ô^AÈÍÎÏ#\210
ÐH8\211^U=\204s^A^N'\203^Y^BÞ
ÌH^K>\204^R^BÈÍÎÏ#\210
ÐH\233\204s^A^N(\2034^B
ÌH^K>\204,^BÈÍÎÏ#\210
ÐH^L=\204s^A^N\"T\211^V\"\202u^A)       b\210
\206J^BÈá^N$\"+\207" [buffer pt msg cl-struct-compilation--message-tags loc last local-variable-p compilation-locs error "Not in a compila\
tion buffer" ...] 7 ("/usr/share/emacs/24.5/lisp/progmodes/compile.elc" . 69981) "p"], 0
  • C-hf set-mark-command: "Novice Emacs Lisp programmers often try to use the mark for the wrong purposes. See the documentation of `set-mark' for more information." – Drew Jul 11 at 1:03
  • You don't need to establish the region to use write-region-to-file. Just pass the function two buffer positions, to save the text between them to the file. – Drew Jul 11 at 1:04
  • There is something wrong about that elisp info page. It is far too verbose to simply say "Elisp is a dialect of the Lisp programming language, used by Emacs. Please use elisp tag for questions regarding the language as it compares to other languages ONLY." If the page is regarding the tags use, it should only try to limit itself to its proper usage. It is far less confusing and more productive to provide its scope of use, rather than the additional superfluous information including negatives. – LeanMan Jul 11 at 4:40
2

Try the following:

(defun write-first-error-to-file()
  "function copies first error in compilation buffer and writes it to specified file"
  (interactive)
  (let (beg)
    (set-buffer "*compilation*")
    (goto-char (point-min))
    (compilation-next-error 1)
    (setq beg (point))
    (compilation-next-error 1)

    (write-region beg (point) "~/copybuffer.txt")))

Execute it with M-x write-first-error-to-file.

In words:

  • switch to the compilation buffer
  • set point to the beginning of the buffer
  • go to the next error
  • remember the position of point in beg
  • go to the next error
  • write the region between beg and the current position of point to a file.

Note that you have to switch to the compilation buffer: otherwise compilation-next-error will fail with exactly the error message you got.

Note also that compilation-next-error takes a mandatory argument, a number telling it how many errors to move forwards (or backwards if negative). It also does not return anything useful: you seem to think that it returns the position of point in the buffer, but that's not true. Do C-h f compilation-next-error RET to get information about the function.

Finally, note that all you need to know to define a region are its beginning and its end. That means that you do not have to set a mark in your function, which is a Good Thing(TM): marks are generally used by users for navigation and having functions munge them can be very confusing. See the doc for the functions set-mark-command and set-mark (using C-h v <func> RET as above) for some additional guidance and warnings about setting marks.

| improve this answer | |
  • Hi thanks! This is an interesting solution - truly appreciate it. I have a few comment/questions about your solution. (1) What does let do in your function? (declare beg?) (2). I did consult the documentation but it was still a bit extraneous to shift through. What threw me off regarding compilation-next-error was that from M-x compilation-next-error didn't require a parameter. Why isn't it the same? (3) I didn't know that about write-region, so the use of mark came from the following solution: stackoverflow.com/a/11690350/10421103. Its still not clear to me, its use. – LeanMan Jul 11 at 4:08
  • 1
    1) let binds names to values locally - see An Introduction to Programming in Emacs Lisp which I think you should read in its entirety 2) This is a bit subtle: compilation-next-error behaves differently when it is called interactively than when it is called programatically: when you call it interactively with M-x compilation-next-error, the argument n1 is given the value 1 by default; but when you call it from a program you need to specify it explicitly ... – NickD Jul 11 at 4:26
  • 1
    ... - otherwise you get (wrong-number-of-arguments (lambda (n) (interactive "p")... 3) The guiding principle should be: if you do not have to set a mark, do not set a mark; and if you do have to set a mark, then follow the advice in the documentation of set-mark and use push-mark instead - but in the vast majority of cases, you do not need to write functions that set marks. E.g. all of Org mode, a fairly sizeable application (about 110K lines of code) uses set-mark three times. – NickD Jul 11 at 4:35
  • I included an edit to your answer such that the current window doesn't change buffers and it is programmatically set behind the scenes in order to be operated on. I think this helps with intuitive experience. – LeanMan Jul 11 at 5:13
  • 1
    That is indeed the right way to go: thanks for the fix! – NickD Jul 11 at 14:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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