Как отобразить содержимое минибуфера в середине фрейма emacs?

20

При работе с максимизированным фреймом Emacs содержимое отображения области минибуфера / эха в нижней части экрана может быть трудно различимым, но также приводит к потере фокуса на текст, редактируемый при вводе команды, что довольно вопрос с точки зрения юзабилити.

Есть ли способ переместить область минибуфера / эха в центр текущего буфера Emacs, поверх текста, редактируемого, когда минибуфер активен, или, что лучше, захватить содержимое минибуфера и отобразить его также в центре текущего буфера при наборе команды?

Себастьян Ле Каллоннек
источник
Для полноты я должен добавить, что мое текущее решение этой проблемы юзабилити заключается в увеличении размера шрифта минибуфера, но это выглядит глупо на не максимизированных кадрах.
Себастьян Ле Каллоннек,

Ответы:

10

Всплывающий минибуфер в центре

Вот способ сделать именно то, что вы просили: отобразить минибуфер в центре экрана .

  1. Есть отдельная рама для минибуфера
  2. Расположите его в центре
  3. Поднимайте этот кадр всякий раз, когда минибуфер фокусируется.

Этого относительно легко достичь, и это то, что вы получите с oneonone.elпакетом (как указывает @Drew). Проблема этого подхода заключается в том, что минибуфер также используется для сообщений, поэтому скрывать его не очень удобно. Не беспокоиться! Я придумал другое решение.

  1. Пусть второй минибуфер отображается в отдельной рамке.
  2. Поместите это в центр.
  3. Используйте второй кадр для интерактивных команд, в то время как оригинальный (первый) минибуфер используется для отображения сообщений.

Реализация

Чтобы реализовать это, нам нужно создать фрейм минибуфера при запуске 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))
  "")

Затем мы исправляем read-from-minibufferиспользование второго минибуфера вместо минибуфера оригинального кадра.

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

Это не может работать абсолютно со всем, но он работал на все , что я пытался до сих пор --- find-file, ido-switch-buffer, eval-expression. Если вы делаете найти какое - либо исключение, вы можете исправить эти функции на индивидуальной основе случая, позвонив use-popup-minibufferна них.

Положение рядом с точкой

Чтобы расположить эту рамку минибуфера рядом с высотой точки, просто определите что-то вроде следующей функции. Он не идеален (на самом деле, он ужасен во многих случаях), но он отлично справляется с оценкой высоты точки.

(defun point-screen-height ()
  (* (/ (face-attribute 'default :height) 10) 2
     (- (line-number-at-pos (point))
        (line-number-at-pos (window-start)))))
Malabarba
источник
Вместо того, чтобы возиться с (setf (symbol-function function) ...)тобой, лучше использовать advice-add(или старшее ad-add-advice).
Стефан
6

Минибуфер наверху

Отображение минибуфера в середине экрана является сложным. Разумная альтернатива (которую вы можете или не можете найти более читабельной) - переместить ее наверх.

Вы можете настроить местоположение (или существование) минибуфера minibufferфрейма с параметром фрейма в initial-frame-alist переменной.

Отдельная рамка

Если nilзадать для этого параметра значение, у фрейма не будет минибуфера, а Emacs автоматически создаст для вас отдельный фрейм минибуфера. Вы можете расположить эти два фрейма, используя оконный менеджер вашей ОС, или вы можете сделать это из Emacs.

В следующем фрагменте приведен пример размещения рамки минибуфера сверху, с обычной рамкой чуть ниже нее, но вам определенно придется изменить числа, чтобы учесть размер монитора.

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

Самый быстрый способ изменить это - изменить размеры фреймов в вашем оконном менеджере, а затем (frame-parameters)выполнить оценку, чтобы увидеть значения параметров, которые у вас есть.

Та же рамка

Другая возможность - установить minibufferпараметр в окно. К сожалению, я не смог заставить это работать для окна в том же кадре.

Malabarba
источник
5

Библиотека oneonone.elдает вам это из коробки.

Просто настройте опцию 1on1-minibuffer-frame-top/bottomв положение, которое вы хотите для автономной рамы минибуфера. Значение - это количество пикселей сверху (если неотрицательно) или снизу (если отрицательно) вашего дисплея. Например, установите значение высоты дисплея, деленное на 2, чтобы расположить его вертикально по центру.

Если вместо этого вы хотите, чтобы он был расположен динамически, скажем, в вертикальном центре текущего кадра, то вы можете сделать это, (a) оставив 1on1-minibuffer-frame-top/bottom= nilи (b) посоветовав функции 1on1-set-minibuffer-frame-top/bottom динамически связать эту переменную с соответствующей позицией. Например:

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

(Определите функцию my-dynamic-positionдля расчета желаемой позиции на основе любых критериев, которые вы хотите (текущий размер окна / рамки и положение, фаза луны, ...).

Нарисовалась
источник