¿Cómo mostrar el contenido del minibúfer en el medio del marco de emacs?

20

Cuando se trabaja en un marco Emacs maximizado, puede ser difícil ver el contenido del área de minibúfer / eco en la parte inferior de la pantalla, pero también hace que pierda el foco en el texto que se edita al escribir un comando, lo cual es bastante problema en términos de usabilidad.

¿Hay alguna forma de mover el área del minibúfer / eco en el centro del búfer emacs actual, sobre el texto que se edita cuando el minibúfer está activo o, mejor, capturar el contenido del minibúfer y mostrarlo también en el centro del búfer actual mientras escribe el comando?

Sébastien Le Callonnec
fuente
Para completar, debo agregar que mi solución actual para este problema de usabilidad es aumentar el tamaño de fuente del minibúfer, pero parece tonto en marcos no maximizados.
Sébastien Le Callonnec

Respuestas:

10

Minibúfer emergente en el centro

Aquí hay una manera de hacer exactamente lo que pidió: mostrar el minibúfer en el centro de la pantalla .

  1. Tener un marco separado para el minibúfer
  2. Posicionarlo en el centro
  3. Eleve este marco cada vez que el minibúfer se enfoque.

Eso es relativamente fácil de lograr y es lo que obtendrá con el oneonone.elpaquete (como señala @Drew). El problema con este enfoque es que el minibúfer también se usa para mensajes, por lo que mantenerlo oculto no es algo muy conveniente. ¡No es para preocuparse! Se me ocurrió otra solución.

  1. Haga que se muestre un segundo minibúfer en un marco separado.
  2. Colóquelo en el centro.
  3. Use el segundo marco para comandos interactivos, mientras que el minibúfer original (primero) se usa para hacer eco de mensajes.

Implementación

Para implementar esto, necesitamos crear el marco de minibúfer al inicio de emacs.

(defvar endless/popup-frame-parameters
  '((name . "MINIBUFFER")
    (minibuffer . only)
    (height . 1)
    ;; Ajust this one to your preference.
    (top . 200))
  "Parameters for the minibuffer popup frame.")

(defvar endless/minibuffer-frame
  (let ((mf (make-frame endless/popup-frame-parameters)))
    (iconify-frame mf) mf)
  "Frame holding the extra minibuffer.")

(defvar endless/minibuffer-window
  (car (window-list endless/minibuffer-frame t))
  "")

Luego aplicamos parches read-from-minibufferpara usar este segundo minibúfer, en lugar del minibúfer del marco original.

(defmacro with-popup-minibuffer (&rest body)
  "Execute BODY using a popup minibuffer."
  (let ((frame-symbol (make-symbol "selected-frame")))
    `(let* ((,frame-symbol (selected-frame)))
       (unwind-protect 
           (progn 
             (make-frame-visible endless/minibuffer-frame)
             (when (fboundp 'point-screen-height)
               (set-frame-parameter
                endless/minibuffer-frame 
                'top (point-screen-height)))
             (select-frame-set-input-focus endless/minibuffer-frame 'norecord)
             ,@body)
         (select-frame-set-input-focus ,frame-symbol)))))

(defun use-popup-minibuffer (function)
  "Rebind FUNCTION so that it uses a popup minibuffer."
  (let* ((back-symb (intern (format "endless/backup-%s" function)))
         (func-symb (intern (format "endless/%s-with-popup-minibuffer"
                                    function)))
         (defs `(progn
                  (defvar ,back-symb (symbol-function ',function))
                  (defun ,func-symb (&rest rest)
                    ,(format "Call `%s' with a poupup minibuffer." function)
                    ,@(list (interactive-form function))
                    (with-popup-minibuffer 
                     (apply ,back-symb rest))))))
    (message "%s" defs)
    (when (and (boundp back-symb) (eval back-symb))
      (error "`%s' already defined! Can't override twice" back-symb))
    (eval defs)
    (setf (symbol-function function) func-symb)))

;;; Try at own risk.
(use-popup-minibuffer 'read-from-minibuffer)
;;; This will revert the effect.
;; (setf (symbol-function #'read-from-minibuffer) endless/backup-read-from-minibuffer)
;; (setq endless/backup-read-from-minibuffer nil)

Esto podría no funcionar con absolutamente todo, pero trabajado en todo lo que probé hasta ahora --- find-file, ido-switch-buffer, eval-expression. Si haces encontrar alguna excepción, se puede parchear estas funciones sobre una base de caso por caso llamando use-popup-minibufferen ellos.

Posición cerca del punto

Para colocar este marco de minibúfer cerca de la altura del punto, simplemente defina algo como la siguiente función. No es perfecto (de hecho, es horrible en muchos casos), pero hace un trabajo decente al estimar la altura del punto.

(defun point-screen-height ()
  (* (/ (face-attribute 'default :height) 10) 2
     (- (line-number-at-pos (point))
        (line-number-at-pos (window-start)))))
Malabarba
fuente
En lugar de (setf (symbol-function function) ...)perder el tiempo, sería mejor usarlo advice-add(o el anterior ad-add-advice).
Stefan
6

Minibúfer en la parte superior

Mostrar el minibúfer en el centro de la pantalla es complicado. Una alternativa razonable (que puede o no encontrar más legible) es moverlo a la parte superior.

Puede configurar la ubicación (o existencia) del minibúfer de un cuadro con el minibufferparámetro del cuadro en la initial-frame-alist variable.

Marco separado

Establecer el parámetro en nilconfigurará el marco para que no tenga minibúfer, y Emacs creará automáticamente un marco de minibúfer separado para usted. Puede colocar estos dos marcos utilizando el administrador de ventanas de su sistema operativo, o puede hacerlo desde Emacs.

El siguiente fragmento ejemplifica cómo colocar el marco del minibúfer en la parte superior, con el marco regular justo debajo, pero definitivamente tendrá que cambiar los números para tener en cuenta el tamaño de su monitor.

(setq initial-frame-alist
      '((left . 1) (minibuffer . nil)
        ;; You'll need to adjust the following 3 numbers.
        (top . 75) ; In pixels
        (width . 127) ; In chars
        (height . 31)))

(setq minibuffer-frame-alist
      '((top . 1) (left . 1) (height . 2)
        ;; You'll need to adjust the following number.
        (width . 127)))

La forma más rápida de ajustar esto es cambiar el tamaño de los marcos en el administrador de ventanas y luego evaluar (frame-parameters)para ver los valores de los parámetros que tiene.

Mismo marco

Otra posibilidad es establecer el minibufferparámetro en una ventana. Desafortunadamente, no pude hacer que esto funcione para una ventana en el mismo marco.

Malabarba
fuente
5

Library oneonone.elte ofrece esto de forma inmediata.

Simplemente personalice la opción 1on1-minibuffer-frame-top/bottomen la posición que desee para el marco de minibúfer independiente. El valor es el número de píxeles desde la parte superior (si no es negativo) o desde la parte inferior (si es negativo) de su pantalla. Por ejemplo, establezca el valor en la altura de su pantalla dividida por 2, para centrarla verticalmente.

Si, por el contrario, desea que se posicione dinámicamente, digamos en el centro vertical del marco actual, puede hacerlo (a) dejando 1on1-minibuffer-frame-top/bottom= nily (b) aconsejando a la función 1on1-set-minibuffer-frame-top/bottom que vincule esa variable dinámicamente a la posición adecuada. Por ejemplo:

(defadvice 1on1-set-minibuffer-frame-top/bottom (around center activate)
  (let ((1on1-minibuffer-frame-top/bottom  (my-dynamic-position ...)))
    ad-do-it))

(Defina la función my-dynamic-positionpara calcular la posición que desea, en función de los criterios que desee (ventana actual / tamaño y posición del marco, fase de la luna, ...).

Dibujó
fuente