9

I've run into a problem viewing pdf documentation files with AucTex. I use pdf-tools to view PDF files from within Emacs, and I've set emacsclient -n as my default pdf-viewer (via xdg-mime on Debian Linux). This works fine in most circumstances, but it breaks the (Tex-documentation-texdoc ...) function of Auctex (C-c ?).

I've narrowed the problem down to a single line of code. When I try to view the documentation for the listings package, TeX-documentation-texdoc turns this into the following sexp:

(shell-command-to-string "texdoc --view  listings")

texdoc in turn calls emacsclient to actually open the file (based on how I've configured my desktop via xdg). However, at this point, the Emacs hangs and I need to quit (C-g) to get control back. After that, no new pdf is opened. The same thing happens if I try to call emacsclient directly:

(shell-command-to-string "emacsclient -n tmp.pdf")

Both commands work at the command line (i.e., emacsclient -n tmp.pdf and texdoc --view listings.

My question is, in an instance like this, how do I call emacsclient from within Emacs? (and I know I could just open the pdf file with find-file; that's not an option here as I need to call an external process (texdoc) to find the file, and that process then invokes emacsclient).

Tyler
  • 21,719
  • 1
  • 52
  • 92
  • Why not just use `texdoc -M --list listings` to find the file, and then use `find-file`? – suvayu Dec 22 '16 at 18:25
  • @suvayu Just convenience. Another alternative is switching to a terminal to call `texdoc --view` and then switching back to Emacs when it opens the file. But I think there should be a way to do this in a single step from Emacs? – Tyler Dec 22 '16 at 18:31
  • 1
    Can `(async-shell-command "emacsclient -n tmp.pdf")` solve the problem? – Name Dec 22 '16 at 20:49
  • 1
    @Name interesting - `(async-shell-command "emacsclient -n tmp.pdf")` works, but not `(async-shell-command "texdoc --view listings")` doesn't. So that's a useful clue. – Tyler Dec 22 '16 at 21:13
  • I don't have access to a linux machine to test. What error or message (async-shell-command "texdoc --view listings") produces? – Name Dec 23 '16 at 07:37
  • Might be similar to [#25234](https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25234 "call-process vs. start-process and xdg-open"), i.e., `texdoc -M --list listings` exits too early? – npostavs Dec 23 '16 at 13:08
  • @npostavs that does sounds very similar, thanks – Tyler Dec 23 '16 at 14:28
  • @Name no error, it just reports: `texdoc --view listings: finished.`, without actually displaying the doc in question. An empty `*Async Shell Command*` buffer opens up, but with no contents. – Tyler Dec 23 '16 at 14:30
  • 1
    Does `C-u C-c ?` work? It first shows the list of docs related to the package, then opens the viewer with `(call-process "texdoc" nil 0 nil "--just-view" doc)`. – giordano Dec 23 '16 at 16:19
  • @giordano yes it does! – Tyler Dec 23 '16 at 16:24
  • Good. I needed to use `shell-command-to-string` in order to show to users possible error messages in case no documentation is found (as explained in comments). The problem of `call-process` is that it doesn't tell anything to Emacs about the exit status of the program when `destination` is 0. I couldn't find a solution for this case, that's why I resorted to `shell-command-to-string`. – giordano Dec 23 '16 at 16:44
  • @giordano 1. do you want to submit using `call-process` instead of `shell-command` as an answer to this question? 2. Is this something that can be incorporated into Auctex, or should I write my own function for calling tex-doc to use locally? – Tyler Dec 23 '16 at 17:05
  • @giordano sounds like you should use `start-process` and with a [sentinel](https://www.gnu.org/software/emacs/manual/html_node/elisp/Sentinels.html) to catch error exit statuses. – npostavs Dec 23 '16 at 21:35
  • @Tyler Stefan answered the question (the trick is to call an asynchronous process). Regarding 2) I'd accept in AUCTeX a simple solution that doesn't break handling of the case of a doc not available (it's annoying for the user to not see the documentation being opened without any explanation). As I said in the comment to Stefan's answer, using a sentinel looks to me a bit too complicated for a non-fundamental feature (or you can persuade me this is a really good idea to use a sentinel even here ;-). – giordano Dec 24 '16 at 01:27

2 Answers2

5

The solution is to run texdoc within an asynchronous process.

The best way to do that is probably to use start-file-process instead of shell-command-to-string (which is a handy function for quick&dirty code when it's more expedient to write a little shell script than the corresponding Elisp code, but is otherwise better avoided in my experience).

But it will require substantial changes to the surrounding code, since start-file-process does not return the process's output directly, instead it lets you indicate in which buffer to place the output and then you have to use set-process-sentinel to a callback function that fetches the output from that buffer and does "whatever needs to be done with it" when the command finishes.

Stefan
  • 26,154
  • 3
  • 46
  • 84
  • In the specific case of running `texdoc` in AUCTeX I find the use of a sentinel a bit of an overkill, since this isn't a fundamental feature (like it is the opening of the viewer for the output document, in which case we do use the sentinel). – giordano Dec 24 '16 at 01:18
  • I have no idea why the "-to-string" function was used, so I don't know what is done with the command's output. If this output is needed (as suggested by the use of `...-to-string`), then an async solution will either need a process-filter or a proces-sentinel. If not, then the code can maybe use something like `(shell-command "texdoc --view listings &")`. – Stefan Dec 24 '16 at 01:37
  • It's explained in the comments to `TeX-documentation-texdoc`: the `...-to-string` variant is used to show to users possible error messages (for example when no documentation is found). In addition, `texdoc nonexistingpackage` returns 0, but the sentinel may be used to parse the output. – giordano Dec 24 '16 at 01:42
  • Then a sentinel seems to be the best option. – Stefan Dec 24 '16 at 04:38
  • I can't find an invocation of `start-file-process` that actually works here. `(start-file-process "texdoc" "*texdoc*" "texdoc" "--view" "listings")` creates the buffer `*texdoc*`, to which is inserted "Process texdoc finished", and the pdf never opens. The same thing happens when I set the xdg-mime pdf viewer to evince as well. – Tyler Jan 02 '17 at 14:35
1

If you only need to feed back a request to Emacs, without waiting for an answer, then you can run emacsclient in the background. Under Unix-style OSes (Linux, macOS, Cygwin, …):

emacsclient … &

Under native Windows:

start emacsclient …
  • Sure, but in this particular case I need to call a program (texdoc) that then calls (emacsclient). The additional level of redirection is causing problems. – Tyler Jan 02 '17 at 14:05
  • @Tyler `texdoc` is asynchronous (i.e. you aren't waiting for it to complete), isn't it? So you could apply the same principle: run `texdoc … &` as the shell command. – Gilles 'SO- stop being evil' Jan 02 '17 at 14:38
  • We tried that up in the comments under my question; it works when calling `emacsclient` directly, but not when calling `texdoc`. – Tyler Jan 02 '17 at 14:48