Jak wyświetlić zawartość minibufora w środku ramki emacsa?

20

Podczas pracy na zmaksymalizowanej ramce Emacsa trudno jest zobaczyć zawartość obszaru minibufora / echa na dole ekranu, ale powoduje także utratę koncentracji na edytowanym tekście podczas wpisywania polecenia - co jest dość problem pod względem użyteczności.

Czy istnieje sposób na przesunięcie obszaru minibufora / echa w środku bieżącego bufora emacs, nad tekstem edytowanym, gdy minibufor jest aktywny, lub, lepiej, aby uchwycić zawartość minibufora i wyświetlić go również na środku bieżącego bufora podczas wpisywania polecenia?

Sébastien Le Callonnec
źródło
Dla kompletności powinienem dodać, że moim obecnym rozwiązaniem tego problemu z użytecznością jest zwiększenie rozmiaru czcionki minibufora, ale wygląda to głupio na nie zmaksymalizowanych ramkach.
Sébastien Le Callonnec

Odpowiedzi:

10

Wyskakujący minibufor w centrum

Oto sposób na wykonanie dokładnie tego, o co prosiłeś: wyświetl minibufor na środku ekranu .

  1. Mają osobną ramkę dla minibufora
  2. Ustaw go na środku
  3. Podnoś tę ramkę, gdy minibuffer zyska ostrość.

Jest to stosunkowo łatwe do osiągnięcia i właśnie to otrzymasz wraz z oneonone.elpakietem (jak wskazuje @Drew). Problem z tym podejściem polega na tym, że minibufor jest również używany do przesyłania wiadomości, więc ukrywanie go nie jest zbyt wygodne. Nie martwić się! Wymyśliłem inne rozwiązanie.

  1. Wyświetl drugi minibufor w osobnej ramce.
  2. Ustaw go na środku.
  3. Drugiej ramki używaj do komend interaktywnych, podczas gdy oryginalny (pierwszy) minibufor służy do echa wiadomości.

Realizacja

Aby to zaimplementować, musimy utworzyć ramkę minibufora podczas uruchamiania emacsa.

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

Następnie łatamy, read-from-minibufferaby używać tego drugiego minibufora zamiast oryginalnego minibufora ramki.

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

To może nie działać z absolutnie wszystko, ale to działało na wszystko próbowałem dotąd --- find-file, ido-switch-buffer, eval-expression. Jeśli zrobić znaleźliśmy żadnych wyjątków, można załatać te funkcje na zasadzie case-by-case wywołując use-popup-minibufferna nich.

Ustaw blisko punktu

Aby ustawić tę ramkę minibufora w pobliżu wysokości punktu, wystarczy zdefiniować coś w rodzaju następującej funkcji. To nie jest idealne (w rzeczywistości jest okropne w wielu przypadkach), ale porządnie szacuje wysokość punktu.

(defun point-screen-height ()
  (* (/ (face-attribute 'default :height) 10) 2
     (- (line-number-at-pos (point))
        (line-number-at-pos (window-start)))))
Malabarba
źródło
Zamiast się z tym bawić (setf (symbol-function function) ...), lepiej użyć advice-add(lub starszego ad-add-advice).
Stefan
6

Minibufor na górze

Wyświetlanie minibufora na środku ekranu jest skomplikowane. Rozsądną alternatywą (którą możesz lub nie możesz znaleźć bardziej czytelną) jest przeniesienie jej na górę.

Możesz skonfigurować lokalizację (lub istnienie) minibufora ramki za pomocą minibufferparametru ramki w initial-frame-alist zmiennej.

Oddzielna ramka

Ustawienie parametru na nilskonfiguruje ramkę tak, aby nie miała minibufora, a Emacs automatycznie utworzy dla ciebie osobną ramkę minibufora. Możesz ustawić te dwie ramki za pomocą menedżera okien swojego systemu operacyjnego lub możesz to zrobić za pomocą Emacsa.

Poniższy fragment kodu ilustruje sposób umieszczenia ramki minibufora u góry, ze zwykłą ramką tuż pod nią, ale na pewno będziesz musiał zmienić liczby, aby uwzględnić rozmiar monitora.

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

Najszybszym sposobem na dostosowanie tego jest zmiana rozmiaru ramek w menedżerze okien, a następnie ocena w (frame-parameters)celu sprawdzenia posiadanych wartości parametrów.

Ta sama rama

Inną możliwością jest ustawienie minibufferparametru na okno. Niestety nie udało mi się sprawić, aby działało to dla okna w tej samej ramce.

Malabarba
źródło
5

Biblioteka oneonone.eldaje to od razu po wyjęciu z pudełka.

Po prostu dostosuj opcję 1on1-minibuffer-frame-top/bottomdo żądanej pozycji dla samodzielnej ramy minibufora. Wartością jest liczba pikseli od góry (jeśli nieujemna) lub na dole (jeśli ujemna) wyświetlacza. Na przykład ustaw wartość wysokości wyświetlania podzieloną przez 2, aby wyśrodkować ją w pionie.

Jeśli zamiast tego chcesz, aby była dynamicznie pozycjonowana, powiedzmy w pionowym środku bieżącej klatki, możesz to zrobić poprzez (a) pozostawienie 1on1-minibuffer-frame-top/bottom= nili (b) funkcji doradczej, 1on1-set-minibuffer-frame-top/bottom aby dynamicznie powiązać tę zmienną z odpowiednią pozycją. Na przykład:

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

(Zdefiniuj funkcję my-dynamic-positiondo obliczania żądanej pozycji na podstawie dowolnych kryteriów (aktualny rozmiar i pozycja okna / ramy, faza księżyca, ...).

Rysował
źródło