2

When I use tuareg I like to have the toplevel in a separate frame, but as soon as I evaluate some code in the original frame the toplevel window reopen in that frame and that's not what I wan't.

More generally, I'd like to always popup new windows into frames as I'm using emacs daemon and a tiling window manager, I would like to replace all the window part of emacs by the frame.

There's a function to pop a window into a frame but I'd like to change the default behaviour.

"Pop" a window into a frame

How tuareg works :

I can open ocaml interpreter with C-c C-s, if the interpreter isn't running it runs it in a new buffer called *ocaml toplevel* in a new window splitted horizontally. If it's already opened but the window at the bottom isn't, it open the window with the already opened *ocaml toplevel* in it. It is binded to the function :

(defun tuareg-run-ocaml ()
  "Run an OCaml toplevel process.  I/O via buffer `*ocaml-toplevel*'."
  (interactive)
  (tuareg-run-process-if-needed)
  (display-buffer tuareg-interactive-buffer-name))

I guess that it's display-buffer whih I wan't to hack...

There's also evaluate region and evaluate buffer that evaluate some code in the tuareg buffer by the opened interpreter, and if it's not opened it opens it in a new window at the bottom like previously. In fact, it is quite similar to elisp evaluation but the result is in the *\ocaml toplevel* buffer and the window is always reopened when needed to show the result.

I don't show these functions because they call other tuareg functions so there would be a lot of functions in the post and I don't want to bother you with all that code. Jus tell me if you want more.

Thanks

  • I use a custom solution -- see this related thread entitled **How to intercept a file before it opens and decide which frame**: http://stackoverflow.com/questions/18346785/how-to-intercept-a-file-before-it-opens-and-decide-which-frame Most people, however, look for something more simple with a little regexp and customizing the `display-buffer-alist` -- that will not cover all the situations, but it will as to many. – lawlist Dec 21 '15 at 17:51
  • There are quite a few examples already out there -- Google a bit for: **"display-buffer-alist" emacs** -- if you still need some help, provide an example of a specific set of circumstances with the buffer you are trying to display in the target frame -- e.g., major-mode, function being called (e.g., a compile command or something), exact buffer name, whether you need a specific frame, or just any old frame, a new frame, or an existing frame, etc. – lawlist Dec 21 '15 at 18:49
  • I'll give it a try but I think emacs daemon does part of the job of that function... I think what I need in first time is to replace `split-window-preferred-function` which I guess is what is called to popup things like tuareg toplevel or other sort of menus... I have tried to replace it with the same value as `pop-up-frame-function` i.e. `(lambda nil (make-frame pop-up-frame-alist))` but that one doesn't work as I expected, it open the buffer in the current frame. – Nicolas Scotto Di Perto Dec 21 '15 at 18:51
  • For example, I have tuareg top level in a frame and source code in another frame, when I ask tuareg to evaluate some source code in the source frame it split the window to show tuareg toplevel. I don't wan't him to split the window for the toplevel since there is a frame currently showing it. And if I run the toplevel for the first time, it would be cool to open it in another buffer. – Nicolas Scotto Di Perto Dec 21 '15 at 18:55
  • To properly assist you, we need to see the source-code of the function responsible for "**it split the window to show tuareg toplevel**". If you can track down that function, then the solution can be specifically tailored to suit your needs. I don't use that library and haven't the slightest idea where to even start the search. Start with the name of the function that you call which begins the process and work your way forwards. The most common approach is to use `M-x describe-key` & `M-x find-find-function`. There may of course be a reader of this thread intimately familiar with tuareg. – lawlist Dec 21 '15 at 21:39
  • It's a big package so theres a lot of functions involved, I describe a little bit how it is used. I describe it in the post. – Nicolas Scotto Di Perto Dec 22 '15 at 08:34
  • Your edit to the question is sufficient to now move forwards towards a solution -- i.e., the function begins at line 2531 of `tuareg.el`: https://github.com/ocaml/tuareg/blob/master/tuareg.el It is now bedtime (1:00 a.m.) and I will check back tomorrow to see if this question is still unresolved -- I don't want to rush and make a mistake, and then leave you hanging while I am soundly sleeping . . . :) – lawlist Dec 22 '15 at 08:54
  • 10 a.m. to me, it's funny how internet can connect people from all other world ! cheers – Nicolas Scotto Di Perto Dec 22 '15 at 09:01
  • I have found this https://github.com/vava/i3-emacs which at first seems to fit all my needs, I have used its `one-window-per-frame-mode` to prevents emacs from splitting windows (for a more unified world ;) ), and it does. But when I'm in tuareg and I run the toplevel, it switch the buffer when I want it to open a new frame and it always does that when I evaluate some code which is quite annoying... I would like to replace splitting to opening frame and, if possible, if the frame already exists, just switch to it. – Nicolas Scotto Di Perto Dec 22 '15 at 23:08
  • I will be cranking out some unrelated projects by the end of business today, but would be happy to take a stab at a solution in a few hours. My tentative plan is to use the same principle in the link of the first/initial comment to this question. The functions will either look for an existing frame named "TUAREG" or create a new one with that exact name if it doesn't already exist -- then the buffer `tuareg-interactive-buffer-name` will display in the largest window in that "TUAREG" frame. The plan is to modify the function named `tuareg-run-ocaml`; but, exceptions may need to be added. – lawlist Dec 22 '15 at 23:19
  • Naming frames is a good idea ! – Nicolas Scotto Di Perto Dec 22 '15 at 23:21
  • Okay, I know the time zone is different wherever you are -- go ahead and give the answer a whirl whenever you get a chance. If other functions use `tuareg-run-ocaml`, then we will need to add some exceptions and I will need your help to identify what those exceptions are . . . – lawlist Dec 23 '15 at 01:57

1 Answers1

1

The following example relies upon two functions from the frame-fns.el library written by Drew Adams -- http://www.emacswiki.org/emacs/frame-fns.el -- which has been consolidated into just one function for the purposes of this example. The original poster has indicated that the function tuareg-run-ocaml is the most likely suspect that causes behavior described by the original poster as: "it split the window to show tuareg toplevel". This example modifies tuareg-run-ocaml by adding an optional second argument of display-buffer for ACTION -- e.g., what do we want to do with the buffer. We use Drew Adams' function to test for whether there is an existing frame named TUAREG: If there is, then we use that. If there is not, then we create a new frame with that name. If the buffer is already being displayed in that frame, then use the same window -- if not, then the buffer is displayed in the largest visible window in said frame.

;; Original Author:  Drew Adams -- http://www.emacswiki.org/emacs/frame-fns.el
;; @lawlist combined the functions `get-frame-name` and `get-a-frame`.
(defun db-get-frame--drew-adams (frame)
  "Return a frame, if any, named FRAME (a frame or a string).
  If none, return nil. If FRAME is a frame, it is returned."
  (let ((get-frame-name--drew-adams
          (lambda (&optional frame)
            (unless frame (setq frame (selected-frame)))
            (if (framep frame)
                (cdr (assq 'name (frame-parameters frame)))
              (error "Argument not a frame: `%s'" frame)))))
    (cond ((framep frame) frame)
          ((stringp frame)
           (catch 'get-a-frame-found
             (dolist (fr (frame-list))
               (when (string= frame (funcall get-frame-name--drew-adams fr))
                 (throw 'get-a-frame-found fr)))
             nil))
          (t
           (error "Arg neither a string nor a frame: `%s'" frame)))))

(defun db-display-buffer-fn (buffer _alist)
  (let (frame)
    (if (db-get-frame--drew-adams "TUAREG")
      (setq frame (db-get-frame--drew-adams "TUAREG"))
      (setq frame (make-frame (list '(name . "TUAREG")))))
    (select-frame frame)
    (raise-frame frame)
    (unless (get-buffer-window buffer)
      (set-window-buffer (get-largest-window) buffer))
    (select-window (get-buffer-window buffer))))

;; In order to redefine `tuareg-run-ocaml', need to make sure the library is loaded.
(require 'tuareg)

(defun tuareg-run-ocaml ()
  "Run an OCaml toplevel process.  I/O via buffer `*ocaml-toplevel*'."
  (interactive)
  (tuareg-run-process-if-needed)
  (display-buffer tuareg-interactive-buffer-name '(db-display-buffer-fn)))
lawlist
  • 18,826
  • 5
  • 37
  • 118
  • It workes like a charm ! I just had to add `'(db-display-buffer-fn)` to `switch-buffer` for function `tureg-eval-region` and now it works EXACTLY how I want ! Your explanations have been really full of teaching and I am sincerely grateful to you, THANK'S A LOT. I have just a last question: this has been very specific to tuareg but there are also other modes that have a dedicated window, like a compile window or interpreter, can I generalize the process or should I do a hack for each one ? – Nicolas Scotto Di Perto Dec 23 '15 at 08:26
  • There will undoubtedly be a few or more exceptions to the general rule where where setting the `display-buffer-alist` will not suffice. I recently found an unrelated situation where a popular built-in minor-mode was merely splitting a window and selecting the new window instead of using `display-buffer`. So, if you want it to be perfect, use a scalpel instead of a machete. If you want to cover the majority of situations, then setting the `display-buffer-alist` will suffice for most. I like the scalpel approach, which is custom tailored -- but most people want an easier approach . . . . – lawlist Dec 23 '15 at 09:09
  • Its bedtime again (1:14 a.m.) -- I'll check back again tomorrow if you need additional follow-up. If it is resolved, then a check-mark would be appreciated. Either way, I'm pleased to help if you need further assistance. – lawlist Dec 23 '15 at 09:15
  • I'll try to do it with all the informations I have now, thank you ! – Nicolas Scotto Di Perto Dec 23 '15 at 18:57
  • The example in the link to the first comment of the original question uses the `display-buffer-alist` and tests for certain buffer names to determine what frame should be used. It is possible to use other/additional criteria, such as a major-mode -- e.g., `(eq major-mode 'text-mode)` -- however, some libraries display the buffer before a major-mode has been set (so the test would fail to the extent it relies on the major mode existing). I usually fix the source code so that the major-mode is set before the buffer is displayed, instead of after the fact. – lawlist Dec 23 '15 at 19:12
  • As you work more with frames, you will find (perhaps to your frustration) that Emacs doesn't do a really good job keeping buffers associated with a particular frame. Alp Aker has written a library called **Frame Bufs** which embeds a list of associated buffers into the frame-parameter of each frame -- permitting frame-local buffer association: https://github.com/alpaker/Frame-Bufs I took that one or two steps further and married it with tabbar (extracting the relevant code from Frame Bufs): http://emacs.stackexchange.com/a/10112/2287 And, I also married it with the `display-buffer-alist`. – lawlist Dec 23 '15 at 19:31