Prefiere divisiones verticales sobre horizontales

9

Hay varias preguntas similares a esta. Los he leído todos y no proporcionan una respuesta para mi escenario. Quiero que emacs prefiera divisiones verticales (división de ventanas en partes izquierda y derecha) sobre horizontales, incluso si es posible la división horizontal y vertical . Esto es lo que dice el manual :

La división puede ser vertical u horizontal, dependiendo de las variables split-height-umbral y split-width-umbral. Estas variables deben tener valores enteros. Si el umbral de altura de división es menor que la altura de la ventana elegida, la división coloca la nueva ventana debajo. De lo contrario, si el umbral de ancho de división es menor que el ancho de la ventana, la división coloca la nueva ventana a la derecha.

Entonces, si ambos split-height-thresholdy split-width-thresholdson más pequeños que el ancho y la altura de la ventana, emacs realizará una división horizontal. Quiero lo contrario Si ambos umbrales son más pequeños, realice una división vertical.

Una forma de lograrlo es a conjunto split-height-thresholda nil, pero que no me gusta que debido a que desactiva la división horizontal por completo.

He examinado la split-window-sensiblyfunción, pero no soy lo suficientemente bueno en elisp para escribir mi propia my-split-window-sensiblyfunción que funciona como quiero.

Björn Lindqvist
fuente
Hay una variable llamada split-window-preferred-functionque se puede configurar para usar una función personalizada. Eche un buen vistazo a la función split-window-sensiblyy vea si puede satisfacer sus necesidades ajustando ciertas variables como mencionó en su pregunta, y también lea la cadena de documentos de esa función ... si no se puede hacer para satisfacer sus necesidades, entonces se puede escribir otra, u obtener ayuda para escribir otra función ...
lawlist

Respuestas:

5

En mi experiencia, este es un problema más difícil que uno podría pensar, porque la idea intuitiva de lo que es sensible no siempre es fácil de expresar en términos precisos. Solo describiré con qué terminé, pero es posible que tengas que jugar un poco.

Primero: la split-window-sensiblyfunción existente siempre prefiere terminar con una pila horizontal de ventanas (que, de manera bastante confusa, llama una "división" vertical, aunque la división es horizontal ...) sobre una disposición de lado a lado. Es bastante fácil crear una función que tenga la preferencia opuesta, que es esencialmente solo una copia de split-window-sensiblylas preferencias invertidas:

(defun split-window-sensibly-prefer-horizontal (&optional window)
"Based on split-window-sensibly, but designed to prefer a horizontal split,
i.e. windows tiled side-by-side."
  (let ((window (or window (selected-window))))
    (or (and (window-splittable-p window t)
         ;; Split window horizontally
         (with-selected-window window
           (split-window-right)))
    (and (window-splittable-p window)
         ;; Split window vertically
         (with-selected-window window
           (split-window-below)))
    (and
         ;; If WINDOW is the only usable window on its frame (it is
         ;; the only one or, not being the only one, all the other
         ;; ones are dedicated) and is not the minibuffer window, try
         ;; to split it horizontally disregarding the value of
         ;; `split-height-threshold'.
         (let ((frame (window-frame window)))
           (or
            (eq window (frame-root-window frame))
            (catch 'done
              (walk-window-tree (lambda (w)
                                  (unless (or (eq w window)
                                              (window-dedicated-p w))
                                    (throw 'done nil)))
                                frame)
              t)))
     (not (window-minibuffer-p window))
     (let ((split-width-threshold 0))
       (when (window-splittable-p window t)
         (with-selected-window window
               (split-window-right))))))))

Así que ahora tenemos dos funciones: la original que "prefiere" una pila vertical y la nueva que "prefiere" una pila horizontal.

Luego necesitamos una función que tiende a preferir la que preferimos usar.

(defun split-window-really-sensibly (&optional window)
  (let ((window (or window (selected-window))))
    (if (> (window-total-width window) (* 2 (window-total-height window)))
        (with-selected-window window (split-window-sensibly-prefer-horizontal window))
      (with-selected-window window (split-window-sensibly window)))))

Debe jugar con los valores aquí, pero la idea básica es que preferimos una disposición vertical siempre que haya al menos el doble de ancho que de altura. Puede pensar que lo quería donde la ventana existente es más ancha que alta, pero en mi experiencia eso no es correcto y le permite terminar con ventanas que son demasiado delgadas.

Finalmente, también necesitamos tener algunos mínimos sanos. Establezco un split-height-threshold4 (es decir, no quiero, a menos que sea inevitable, tener menos de 2 líneas en una ventana) y un split-width-threshold40 (es decir, no quiero, a menos que sea inevitable, tener menos de 20 caracteres de ancho en una ventana), al menos creo que eso es lo que significan.

Entonces uno solo se une split-window-preferred-functionasplit-window-really-sensibly

(setq
   split-height-threshold 4
   split-width-threshold 40 
   split-window-preferred-function 'split-window-really-sensibly)

Otra idea (que podría preferir) sería simplemente sustituir la disposición de "preferencia de lado a lado" y establecerla split-width-thresholden 80: entonces obtendría ventanas de lado a lado siempre que hubiera espacio para ellos, creo.

Paul Stanley
fuente
1
¡Funciona! Pero tenía que mantener los split-height/width-thresholdvalores por defecto o de lo contrario la pop-to-bufferfunción crearía nuevas divisiones en lugar de reutilizar las antiguas. Prefiero tener una sola división derecha / izquierda y no quiero que las funciones de emacs se metan con eso.
Björn Lindqvist
1

Puede usar mi paquete el-patchpara implementar la función split-window-sensibly-prefer-horizontalde manera que haga obvio lo que ha cambiado desde el original split-window-sensibly, y también le permite detectar si la definición original cambia en una futura versión de Emacs:

(el-patch-defun (el-patch-swap
                  split-window-sensibly
                  split-window-sensibly-prefer-horizontal)
  (&optional window)
  "Split WINDOW in a way suitable for `display-buffer'.
WINDOW defaults to the currently selected window.
If `split-height-threshold' specifies an integer, WINDOW is at
least `split-height-threshold' lines tall and can be split
vertically, split WINDOW into two windows one above the other and
return the lower window.  Otherwise, if `split-width-threshold'
specifies an integer, WINDOW is at least `split-width-threshold'
columns wide and can be split horizontally, split WINDOW into two
windows side by side and return the window on the right.  If this
can't be done either and WINDOW is the only window on its frame,
try to split WINDOW vertically disregarding any value specified
by `split-height-threshold'.  If that succeeds, return the lower
window.  Return nil otherwise.

By default `display-buffer' routines call this function to split
the largest or least recently used window.  To change the default
customize the option `split-window-preferred-function'.

You can enforce this function to not split WINDOW horizontally,
by setting (or binding) the variable `split-width-threshold' to
nil.  If, in addition, you set `split-height-threshold' to zero,
chances increase that this function does split WINDOW vertically.

In order to not split WINDOW vertically, set (or bind) the
variable `split-height-threshold' to nil.  Additionally, you can
set `split-width-threshold' to zero to make a horizontal split
more likely to occur.

Have a look at the function `window-splittable-p' if you want to
know how `split-window-sensibly' determines whether WINDOW can be
split."
  (let ((window (or window (selected-window))))
    (or (el-patch-let
            (($fst (and (window-splittable-p window)
                        ;; Split window vertically.
                        (with-selected-window window
                          (split-window-below))))
             ($snd (and (window-splittable-p window t)
                        ;; Split window horizontally.
                        (with-selected-window window
                          (split-window-right)))))
          (el-patch-swap $fst $snd)
          (el-patch-swap $snd $fst))
        (and
         ;; If WINDOW is the only usable window on its frame (it
         ;; is the only one or, not being the only one, all the
         ;; other ones are dedicated) and is not the minibuffer
         ;; window, try to split it s/vertically/horizontally
         ;; disregarding the value of `split-height-threshold'.
         (let ((frame (window-frame window)))
           (or
            (eq window (frame-root-window frame))
            (catch 'done
              (walk-window-tree (lambda (w)
                                  (unless (or (eq w window)
                                              (window-dedicated-p w))
                                    (throw 'done nil)))
                                frame)
              t)))
         (not (window-minibuffer-p window))
         (let (((el-patch-swap split-height-threshold
                               split-width-threshold)
                0))
           (when (window-splittable-p window)
             (with-selected-window window
               ((el-patch-swap split-window-below split-window-right)))))))))
Radon Rosborough
fuente
1

Encontré esta solución en la lista de correo de Emacs , y funciona de maravilla:

;; Fix annoying vertical window splitting.
;; https://lists.gnu.org/archive/html/help-gnu-emacs/2015-08/msg00339.html
(with-eval-after-load "window"
  (defcustom split-window-below nil
    "If non-nil, vertical splits produce new windows below."
    :group 'windows
    :type 'boolean)

  (defcustom split-window-right nil
    "If non-nil, horizontal splits produce new windows to the right."
    :group 'windows
    :type 'boolean)

  (fmakunbound #'split-window-sensibly)

  (defun split-window-sensibly
      (&optional window)
    (setq window (or window (selected-window)))
    (or (and (window-splittable-p window t)
             ;; Split window horizontally.
             (split-window window nil (if split-window-right 'left  'right)))
        (and (window-splittable-p window)
             ;; Split window vertically.
             (split-window window nil (if split-window-below 'above 'below)))
        (and (eq window (frame-root-window (window-frame window)))
             (not (window-minibuffer-p window))
             ;; If WINDOW is the only window on its frame and is not the
             ;; minibuffer window, try to split it horizontally disregarding the
             ;; value of `split-width-threshold'.
             (let ((split-width-threshold 0))
               (when (window-splittable-p window t)
                 (split-window window nil (if split-window-right
                                              'left
                                            'right))))))))

(setq-default split-height-threshold  4
              split-width-threshold   160) ; the reasonable limit for horizontal splits

Felicitaciones a Alexander, el autor original.

dangom
fuente