+(defun window--major-non-side-window (&optional frame)
+ "Return the major non-side window of frame FRAME.
+The optional argument FRAME must be a live frame and defaults to
+the selected one.
+
+If FRAME has at least one side window, the major non-side window
+is either an internal non-side window such that all other
+non-side windows on FRAME descend from it, or the single live
+non-side window of FRAME. If FRAME has no side windows, return
+its root window."
+ (let ((frame (window-normalize-frame frame))
+ major sibling)
+ ;; Set major to the _last_ window found by `walk-window-tree' that
+ ;; is not a side window but has a side window as its sibling.
+ (walk-window-tree
+ (lambda (window)
+ (and (not (window-parameter window 'window-side))
+ (or (and (setq sibling (window-prev-sibling window))
+ (window-parameter sibling 'window-side))
+ (and (setq sibling (window-next-sibling window))
+ (window-parameter sibling 'window-side)))
+ (setq major window)))
+ frame t)
+ (or major (frame-root-window frame))))
+
+(defun window--major-side-window (side)
+ "Return major side window on SIDE.
+SIDE must be one of the symbols `left', `top', `right' or
+`bottom'. Return nil if no such window exists."
+ (let ((root (frame-root-window))
+ window)
+ ;; (1) If a window on the opposite side exists, return that window's
+ ;; sibling.
+ ;; (2) If the new window shall span the entire side, return the
+ ;; frame's root window.
+ ;; (3) If a window on an orthogonal side exists, return that
+ ;; window's sibling.
+ ;; (4) Otherwise return the frame's root window.
+ (cond
+ ((or (and (eq side 'left)
+ (setq window (window-with-parameter 'window-side 'right nil t)))
+ (and (eq side 'top)
+ (setq window (window-with-parameter 'window-side 'bottom nil t))))
+ (window-prev-sibling window))
+ ((or (and (eq side 'right)
+ (setq window (window-with-parameter 'window-side 'left nil t)))
+ (and (eq side 'bottom)
+ (setq window (window-with-parameter 'window-side 'top nil t))))
+ (window-next-sibling window))
+ ((memq side '(left right))
+ (cond
+ (window-sides-vertical
+ root)
+ ((setq window (window-with-parameter 'window-side 'top nil t))
+ (window-next-sibling window))
+ ((setq window (window-with-parameter 'window-side 'bottom nil t))
+ (window-prev-sibling window))
+ (t root)))
+ ((memq side '(top bottom))
+ (cond
+ ((not window-sides-vertical)
+ root)
+ ((setq window (window-with-parameter 'window-side 'left nil t))
+ (window-next-sibling window))
+ ((setq window (window-with-parameter 'window-side 'right nil t))
+ (window-prev-sibling window))
+ (t root))))))
+
+(defun display-buffer-in-major-side-window (buffer side slot &optional alist)
+ "Display BUFFER in a new window on SIDE of the selected frame.
+SIDE must be one of `left', `top', `right' or `bottom'. SLOT
+specifies the slot to use. ALIST is an association list of
+symbols and values as passed to `display-buffer-in-side-window'.
+This function may be called only if no window on SIDE exists yet.
+The new window automatically becomes the \"major\" side window on
+SIDE. Return the new window, nil if its creation window failed."
+ (let* ((root (frame-root-window))
+ (left-or-right (memq side '(left right)))
+ (major (window--major-side-window side))
+ (selected-window (selected-window))
+ (on-side (cond
+ ((eq side 'top) 'above)
+ ((eq side 'bottom) 'below)
+ (t side)))
+ ;; The following two bindings will tell `split-window' to take
+ ;; the space for the new window from `major' and not make a new
+ ;; parent window unless needed.
+ (window-combination-resize 'side)
+ (window-combination-limit nil)
+ (new (split-window major nil on-side))
+ fun)
+ (when new
+ ;; Initialize `window-side' parameter of new window to SIDE.
+ (set-window-parameter new 'window-side side)
+ ;; Install `window-slot' parameter of new window.
+ (set-window-parameter new 'window-slot slot)
+ ;; Install `delete-window' parameter thus making sure that when
+ ;; the new window is deleted, a side window on the opposite side
+ ;; does not get resized.
+ (set-window-parameter new 'delete-window 'delete-side-window)
+ ;; Auto-adjust height/width of new window unless a size has been
+ ;; explicitly requested.
+ (unless (if left-or-right
+ (cdr (assq 'window-width alist))
+ (cdr (assq 'window-height alist)))
+ (setq alist
+ (cons
+ (cons
+ (if left-or-right 'window-width 'window-height)
+ (/ (window-total-size (frame-root-window) left-or-right)
+ ;; By default use a fourth of the size of the
+ ;; frame's root window.
+ 4))
+ alist)))
+ ;; Install BUFFER in new window and return NEW.
+ (window--display-buffer buffer new 'window alist 'side))))
+
+(defun delete-side-window (window)
+ "Delete side window WINDOW."
+ (let ((window-combination-resize
+ (window-parameter (window-parent window) 'window-side))
+ (ignore-window-parameters t))
+ (delete-window window)))
+
+(defun display-buffer-in-side-window (buffer alist)
+ "Display BUFFER in a window on side SIDE of the selected frame.
+ALIST is an association list of symbols and values. The
+following symbols can be used:
+
+`side' denotes the side of the existing window where the new
+ window shall be located. Valid values are `bottom', `right',
+ `top' and `left'. The default is `bottom'.
+
+`slot' if non-nil, specifies the window slot where to display
+ BUFFER. A value of zero or nil means use the middle slot on
+ the specified side. A negative value means use a slot
+ preceding (that is, above or on the left of) the middle slot.
+ A positive value means use a slot following (that is, below or
+ on the right of) the middle slot. The default is zero."
+ (let ((side (or (cdr (assq 'side alist)) 'bottom))
+ (slot (or (cdr (assq 'slot alist)) 0))
+ new)
+ (cond
+ ((not (memq side '(top bottom left right)))
+ (error "Invalid side %s specified" side))
+ ((not (numberp slot))
+ (error "Invalid slot %s specified" slot)))
+
+ (let* ((major (window-with-parameter 'window-side side nil t))
+ ;; `major' is the major window on SIDE, `windows' the list of
+ ;; life windows on SIDE.
+ (windows
+ (when major
+ (let (windows)
+ (walk-window-tree
+ (lambda (window)
+ (when (eq (window-parameter window 'window-side) side)
+ (setq windows (cons window windows)))))
+ (nreverse windows))))
+ (slots (when major (max 1 (window-child-count major))))
+ (max-slots
+ (nth (cond
+ ((eq side 'left) 0)
+ ((eq side 'top) 1)
+ ((eq side 'right) 2)
+ ((eq side 'bottom) 3))
+ window-sides-slots))
+ (selected-window (selected-window))
+ window this-window this-slot prev-window next-window
+ best-window best-slot abs-slot new-window)
+
+ (cond
+ ((and (numberp max-slots) (<= max-slots 0))
+ ;; No side-slots available on this side. Don't create an error,
+ ;; just return nil.
+ nil)
+ ((not windows)
+ ;; No major window exists on this side, make one.
+ (display-buffer-in-major-side-window buffer side slot alist))
+ (t
+ ;; Scan windows on SIDE.
+ (catch 'found
+ (dolist (window windows)
+ (setq this-slot (window-parameter window 'window-slot))
+ (cond
+ ;; The following should not happen and probably be checked
+ ;; by window--side-check.
+ ((not (numberp this-slot)))
+ ((= this-slot slot)
+ ;; A window with a matching slot has been found.
+ (setq this-window window)
+ (throw 'found t))
+ (t
+ ;; Check if this window has a better slot value wrt the
+ ;; slot of the window we want.
+ (setq abs-slot
+ (if (or (and (> this-slot 0) (> slot 0))
+ (and (< this-slot 0) (< slot 0)))
+ (abs (- slot this-slot))
+ (+ (abs slot) (abs this-slot))))
+ (unless (and best-slot (<= best-slot abs-slot))
+ (setq best-window window)
+ (setq best-slot abs-slot))
+ (cond
+ ((<= this-slot slot)
+ (setq prev-window window))
+ ((not next-window)
+ (setq next-window window)))))))
+
+ ;; `this-window' is the first window with the same SLOT.
+ ;; `prev-window' is the window with the largest slot < SLOT. A new
+ ;; window will be created after it.
+ ;; `next-window' is the window with the smallest slot > SLOT. A new
+ ;; window will be created before it.
+ ;; `best-window' is the window with the smallest absolute difference
+ ;; of its slot and SLOT.
+
+ ;; Note: We dedicate the window used softly to its buffer to
+ ;; avoid that "other" (non-side) buffer display functions steal
+ ;; it from us. This must eventually become customizable via
+ ;; ALIST (or, better, avoided in the "other" functions).
+ (or (and this-window
+ ;; Reuse `this-window'.
+ (window--display-buffer buffer this-window 'reuse alist 'side))
+ (and (or (not max-slots) (< slots max-slots))
+ (or (and next-window
+ ;; Make new window before `next-window'.
+ (let ((next-side
+ (if (memq side '(left right)) 'above 'left))
+ (window-combination-resize 'side))
+ (setq window (split-window next-window nil next-side))
+ ;; When the new window is deleted, its space
+ ;; is returned to other side windows.
+ (set-window-parameter
+ window 'delete-window 'delete-side-window)
+ window))
+ (and prev-window
+ ;; Make new window after `prev-window'.
+ (let ((prev-side
+ (if (memq side '(left right)) 'below 'right))
+ (window-combination-resize 'side))
+ (setq window (split-window prev-window nil prev-side))
+ ;; When the new window is deleted, its space
+ ;; is returned to other side windows.
+ (set-window-parameter
+ window 'delete-window 'delete-side-window)
+ window)))
+ (set-window-parameter window 'window-slot slot)
+ (window--display-buffer buffer window 'window alist 'side))
+ (and best-window
+ ;; Reuse `best-window'.
+ (progn
+ ;; Give best-window the new slot value.
+ (set-window-parameter best-window 'window-slot slot)
+ (window--display-buffer
+ buffer best-window 'reuse alist 'side)))))))))
+