0

I currently have my *Org Agenda* opening on startup using the following in my init.el:

(setq inhibit-splash-screen t) ;; no welcome screen

(defun agendafocus()
  "Focus on agenda at Startup"
  (org-agenda-list)
  (delete-other-windows))

(add-hook 'after-init-hook #'agendafocus) ;; agenda on startup

However, I would like this to occur only if no file buffers exist, i.e., if buffer-file-name is nil. How would one get this behaviour?

[EDIT]: The solution suggested by Tobias below almost works: When evaluated manually (M-x (agendamode), it works as expected (i.e. it only opens *Org Agenda* if no other file buffer is present), but does not work as a hook, either as an after-init-hook nor as a emacs-startup-hook.

sacuL
  • 127
  • 6

2 Answers2

1

You can use (cl-remove-if-not #'buffer-file-name (buffer-list)) as a predicate for detecting whether there are live file buffers:

(require 'cl-lib)
(setq inhibit-splash-screen t) ;; no welcome screen

(defun agendafocus()
  "Focus on agenda at Startup"
  (unless (cl-remove-if-not #'buffer-file-name (buffer-list))
    (org-agenda-list)
    (delete-other-windows)))

(add-hook 'emacs-startup-hook #'agendafocus t)

Clearly, one can provoke situations where this approach is incomplete.

For instance, if one neglects the APPEND argument t of add-hook in the above code it might be that there is another function hooked into emacs-startup-hook that opens a file.

Even with APPEND argument of add-hook there might be other hook functions added later on that open file buffers.

Other possible pitfalls would be files opened by org-adenda-list or by a timer.

Tobias
  • 33,167
  • 1
  • 37
  • 77
  • @sacul My bad. Please try again. Have swapped when with unless. – Tobias Jan 12 '18 at 15:39
  • That seems to reproduce the behaviour from my original function, agendafocus runs regardless of whether there are other buffers or not... Basically what I'm looking for is something along the lines of (pardon my pseudocode) "if buffer-file-name is nil: run agendafocus, else: do nothing", but I'm not familiar enough with elisp to get that working :$ – sacuL Jan 12 '18 at 15:55
  • @sacul Maybe, you should really use emacs-startup-hook as I suggested in my comment to your question. But, that depends when you generate the other buffers. Your question is not very clear in respect to the additional buffers you expect. Are those file buffers? In that case my solution should be perfect in conjunction with emacs-startup-hook. – Tobias Jan 12 '18 at 15:57
  • yes, those are file buffers (I believe, i.e. buffer-file-name is non-nil). However, even with emacs-startup-hook rather than after-init-hook (which makes much more sense, by the way, thanks for that suggestion, as I didn't know about that hook), it still produces the same behaviour – sacuL Jan 12 '18 at 16:05
  • @sacul FWIW, this approach works for me. – Basil Jan 12 '18 at 16:16
  • I'm not sure why this isn't working for me; if I evaluate that function manually, rather than as a hook, it works: it only opens my org agenda if there are no file buffers open. However, when included as a hook in my init.el, it doesn't work. This suggests that at the time it is evaluated as a hook, there are other buffers loaded for some reason, but I can't find seem to figure out where or why that is happening... – sacuL Jan 12 '18 at 20:49
  • @sacul You can print the buffer list in the message buffer for debugging. Just replace the stuff in agendafocus with (message "Live file buffers: %s" (cl-remove-if-not #'buffer-file-name (buffer-list))). – Tobias Jan 12 '18 at 20:52
  • Doing this returns nil... so it should really work, but it still doesn't. – sacuL Jan 12 '18 at 21:34
  • @sacul Could it be that it is working, i.e. org-agenda-list results in an Agenda buffer, but that its window is not selected, so delete-other-windows deletes the Agenda window? – Basil Jan 12 '18 at 23:09
  • @sacul Note that you still have to set inhibit-splash-screen to t in order for Tobias' approach to work. – Basil Jan 12 '18 at 23:15
  • @Basil Thanks for the hint with inhibit-splash-screen. Only now I note the significance of that action for this question and added it in the answer. – Tobias Jan 12 '18 at 23:17
  • @Basil, what is happening is that even when another file buffer exists, it is still calling agendafocus and creating my org agenda, but I would like this to only be called when no other file buffer is present. In the opposite scenario, if my org agenda were not being created, that would possibly be an explanation. And I still have inhibit-splash-screen set to t. I'm probably missing something obvious, and silly here... But I appreciate all the helpful comments nonetheless – sacuL Jan 12 '18 at 23:18
  • @sacul That sounds like buffer-file-name is not the right predicate for detecting these "file buffers". Can you give an example of such a buffer? How are they being opened? – Basil Jan 12 '18 at 23:24
  • @Basil I'm talking about files I open for instance from my desktop, ~/Desktop/example.py or something like that. When I M-x eval-expression <RET> buffer-file-name when one of these is open, the result is non-nil (it prints the path name, as I would expect). – sacuL Jan 12 '18 at 23:27
  • @sacul So you're starting Emacs from the command-line via something like emacs ~/Desktop/example.py? Or is this file being opened by alternative means, such as some persistent buffer list? What does M-x version RET report? – Basil Jan 12 '18 at 23:33
  • @sacul Could it be that you actually are running an emacs server and open files with emacsclient? – Tobias Jan 12 '18 at 23:35
  • @Basil GNU emacs 25.3.1 (x86_64-apple-darwin17.2.0, NS appkit-1561.10 Version 10.13.1 (Build 17B1003)) of 2017-12-12, but no, in these cases I'm starting emacs by opening it directly from the desktop (clicking on it) – sacuL Jan 12 '18 at 23:37
  • @Tobias (daemonp) is nil, I believe this means it's not run via server, correct? – sacuL Jan 12 '18 at 23:39
  • @sacul Sorry, just to be clear - you click on the python file, and you've configured your environment to open it in Emacs? I'm afraid I don't own or know much about MacOS, so I can't help debug this much further. – Basil Jan 12 '18 at 23:41
  • @Basil yes that is correct. I just tried opening it from terminal (emacs -nw ~/Desktop/example.py) and it works! Which is nice, but also strange to me why that is fine but not via the gui. – sacuL Jan 12 '18 at 23:44
  • @sacul If you're unable to find something in your configuration/environment that could be causing this, I suggest reporting it via M-x report-emacs-bug RET. – Basil Jan 12 '18 at 23:47
  • @sacul You don't even need -nw. If you really start emacs it should also work with the gui-version. The buffer-local variable server-buffer-clients lists the clients that are associated with the buffer. (daemonp) only indicates that emacs is started in daemon-mode. But, that is not necessary for using it as a server. Note also that on some systems emacs is an alias for something like emacsclient --alternate-editor /usr/bin/emacs. – Tobias Jan 12 '18 at 23:47
  • @Tobias There could conceivably be a user-visible discrepancy between GUI and TTY versions of the startup code. See, for example, lisp/term/ns-win.el. – Basil Jan 12 '18 at 23:56
  • Thanks for all your help Tobias and @Basil. I learned a lot from your suggestions! – sacuL Jan 13 '18 at 02:16
0

See Tobias' answer for the simplest and, in the face of user customisations, more robust solution.

Just for fun, I'd like to share an alternative approach based on an answer of mine to a similar question.

The idea is that, during startup, the only buffers that are usually open, other than standard splash/scratch/etc. buffers, are ones due to unrecognised command-line arguments. For example, emacs -q foo results in a file-visiting buffer named foo being displayed.

This allows us to set the user option initial-buffer-choice to a function which focusses the Org Agenda for the case when there are no unrecognised CLI arguments, and then revert the variable to its original value when unrecognised CLI arguments are detected. See (elisp) Command-Line Arguments for more information on this API.

Without further ado, the required incantations:

(defun my-org-agenda-focus ()
  "Display nothing but the Org Agenda in the selected frame.
Return `current-buffer' for `initial-buffer-choice'."
  (org-agenda-list)
  (delete-other-windows)
  (current-buffer))

(defun my-detect-cli-args ()
  "Inhibit `my-org-agenda-focus' on unrecognised CLI args.
Return nil, i.e. leave arguments unprocessed."
  (ignore (setq initial-buffer-choice nil)))

(add-hook 'command-line-functions #'my-detect-cli-args)

(setq initial-buffer-choice  #'my-org-agenda-focus)
(setq inhibit-startup-screen t)

Note that, starting with Org 9, you can also write

(defun my-org-agenda-focus ()
  "Display nothing but the Org Agenda in the selected frame.
Return `current-buffer' for `initial-buffer-choice'."
  (let ((org-agenda-window-setup 'only-window))
    (org-agenda-list))
  (current-buffer))

The approach of OP and Tobias, when hooked into emacs-startup-hook or window-setup-hook, has the advantage of being simpler and running after most, if not all, buffers, windows and frames have been created.

The CLI arg approach is more beneficial for fine-grained inspection of each argument. For example, you could take different actions depending on whether Emacs was invoked with a special keyword, a directory, a file or a non-existent file argument.

Ultimately you can combine both approaches for the superlative Emacs customisation experience.

Basil
  • 12,383
  • 43
  • 69