Wie zeige ich den Inhalt des Minibuffers in der Mitte des Emacs-Frames an?

20

Wenn Sie an einem maximierten Emacs-Frame arbeiten, ist der Inhalt des Minibuffer / Echo-Bereichs am unteren Rand des Bildschirms möglicherweise nur schwer zu erkennen, verliert jedoch beim Eingeben eines Befehls den Fokus auf den zu bearbeitenden Text Problem in Bezug auf die Benutzerfreundlichkeit.

Gibt es eine Möglichkeit, den Minibuffer / Echo-Bereich in der Mitte des aktuellen Emacs-Puffers über den zu bearbeitenden Text zu verschieben, wenn der Minibuffer aktiv ist, oder besser den Inhalt des Minibuffers zu erfassen und ihn auch in der Mitte anzuzeigen? des aktuellen Puffers während der Eingabe des Befehls?

Sébastien Le Callonnec
quelle
Der Vollständigkeit halber möchte ich hinzufügen, dass meine derzeitige Lösung für dieses Usability-Problem darin besteht, die Schriftgröße des Minipuffers zu erhöhen, aber bei nicht maximierten Frames sieht es albern aus.
Sébastien Le Callonnec

Antworten:

10

Popup Minibuffer in der Mitte

So können Sie genau das tun, was Sie gefragt haben: Zeigen Sie den Minipuffer in der Mitte des Bildschirms an .

  1. Einen separaten Rahmen für den Minipuffer haben
  2. Positionieren Sie es in der Mitte
  3. Erhöhen Sie diesen Frame, wenn der Minipuffer den Fokus erhält.

Das ist relativ einfach zu erreichen und wird mit dem oneonone.elPaket geliefert (wie @Drew hervorhebt). Das Problem bei diesem Ansatz ist, dass der Minipuffer auch für Nachrichten verwendet wird, so dass es nicht sehr praktisch ist, ihn versteckt zu halten. Keine Sorgen machen! Ich habe eine andere Lösung gefunden.

  1. Lassen Sie einen zweiten Minipuffer in einem separaten Frame anzeigen.
  2. Positionieren Sie es in der Mitte.
  3. Verwenden Sie den zweiten Frame für interaktive Befehle, während der ursprüngliche (erste) Minipuffer zum Echo von Nachrichten verwendet wird.

Implementierung

Um dies zu implementieren, müssen wir den Minibuffer-Frame beim Start von emacs erstellen.

(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))
  "")

Dann patchen wir read-from-minibuffer, um diesen zweiten Minibuffer anstelle des Minibuffers des ursprünglichen Frames zu verwenden.

(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)

Dies könnte nicht mit absolut alles funktioniert, aber es funktionierte auf alles , was ich bisher versucht --- find-file, ido-switch-buffer, eval-expression. Wenn Sie tun , alle Ausnahmen finden, können Sie diese Funktionen auf einer Fall-zu-Fall - Basis Patch durch den Aufruf use-popup-minibufferauf sich.

Position in der Nähe des Punktes

Um diesen Minipufferrahmen in der Nähe der Höhe des Punktes zu positionieren, definieren Sie einfach so etwas wie die folgende Funktion. Es ist nicht perfekt (in der Tat ist es in vielen Fällen schrecklich), aber es leistet gute Arbeit bei der Schätzung der Punkthöhe.

(defun point-screen-height ()
  (* (/ (face-attribute 'default :height) 10) 2
     (- (line-number-at-pos (point))
        (line-number-at-pos (window-start)))))
Malabarba
quelle
Anstatt herumzuspielen, ist (setf (symbol-function function) ...)es besser, advice-add(oder die ältere ad-add-advice) zu verwenden.
Stefan
6

Minipuffer oben

Die Anzeige des Minipuffers in der Mitte des Bildschirms ist kompliziert. Eine sinnvolle Alternative (die Sie vielleicht besser lesen können oder auch nicht) besteht darin, sie nach oben zu verschieben.

Sie können die Position (oder das Vorhandensein) des Minipuffers eines Frames mit dem minibufferFrame-Parameter in der initial-frame-alist Variablen konfigurieren .

Separater Rahmen

Wenn Sie den Parameter so nileinstellen, dass der Frame keinen Minibuffer enthält, erstellt Emacs automatisch einen separaten Minibuffer-Frame für Sie. Sie können diese beiden Frames mit dem Fenstermanager Ihres Betriebssystems oder über Emacs positionieren.

Das folgende Snippet zeigt, wie Sie den Minibuffer-Frame oben platzieren. Der reguläre Frame befindet sich direkt darunter. Sie müssen die Zahlen jedoch auf jeden Fall ändern, um die Monitorgröße zu berücksichtigen.

(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)))

Am schnellsten können Sie dies anpassen, indem Sie die Größe der Frames in Ihrem Fenstermanager ändern und dann auswerten (frame-parameters), um die von Ihnen eingegebenen Parameterwerte anzuzeigen.

Gleicher Frame

Eine andere Möglichkeit besteht darin, den minibufferParameter auf ein Fenster zu setzen. Leider konnte ich dies nicht für ein Fenster im selben Frame zum Laufen bringen.

Malabarba
quelle
5

Die Bibliothek oneonone.elbietet Ihnen dies sofort.

1on1-minibuffer-frame-top/bottomPassen Sie die Option einfach an die Position an, die Sie für den Standalone-Minipufferrahmen wünschen. Der Wert ist die Anzahl der Pixel von der Oberseite (falls nicht negativ) oder der Unterseite (falls negativ) Ihrer Anzeige. Stellen Sie den Wert beispielsweise auf Ihre Bildschirmhöhe geteilt durch 2 ein, um ihn vertikal zu zentrieren.

Wenn Sie stattdessen möchten, dass es dynamisch positioniert wird, beispielsweise in der vertikalen Mitte des aktuellen Frames, können Sie dies tun, indem Sie (a) 1on1-minibuffer-frame-top/bottom= verlassen nilund (b) die Funktion 1on1-set-minibuffer-frame-top/bottom anweisen, diese Variable dynamisch an die entsprechende Position zu binden. Beispielsweise:

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

(Definieren Sie die Funktion my-dynamic-positionzur Berechnung der gewünschten Position anhand der gewünschten Kriterien (aktuelle Fenster- / Rahmengröße und -position, Mondphase, ...).

Drew
quelle