Como exibir o conteúdo do minibuffer no meio do quadro emacs?

20

Ao trabalhar em um quadro Emacs maximizado, é difícil ver o conteúdo da área minibuffer / eco na parte inferior da tela, mas também causa perda de foco no texto que está sendo editado ao digitar um comando - o que é bastante questão em termos de usabilidade.

Existe uma maneira de mover a área do minibuffer / eco no centro do buffer emacs atual, sobre o texto que está sendo editado quando o minibuffer está ativo ou, melhor ainda, para capturar o conteúdo do minibuffer e exibi-lo também no centro do buffer atual enquanto digita o comando?

Sébastien Le Callonnec
fonte
Para completar, devo acrescentar que minha solução atual para esse problema de usabilidade é aumentar o tamanho da fonte do minibuffer, mas parece bobo em quadros não maximizados.
Sébastien Le Callonnec

Respostas:

10

Minibuffer pop-up no centro

Aqui está uma maneira de fazer exatamente o que você pediu: exiba o minibuffer no centro da tela .

  1. Tenha um quadro separado para o minibuffer
  2. Posicione-o no centro
  3. Aumente esse quadro sempre que o minibuffer ganhar foco.

Isso é relativamente fácil de conseguir e é o que você obterá com o oneonone.elpacote (como o @Drew aponta). O problema com essa abordagem é que o minibuffer também é usado para mensagens, portanto, mantê-lo oculto não é algo muito conveniente. Não se preocupe! Eu vim com outra solução.

  1. Tenha um segundo minibuffer exibido em um quadro separado.
  2. Posicione-o no centro.
  3. Use o segundo quadro para comandos interativos, enquanto o minibuffer original (primeiro) é usado para ecoar mensagens.

Implementação

Para implementar isso, precisamos criar o quadro minibuffer na inicialização do 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))
  "")

Em seguida, fazemos o patch read-from-minibufferpara usar esse segundo minibuffer, em vez do minibuffer do quadro 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)

Isso pode não funcionar com absolutamente tudo, mas funcionou em tudo o que eu tentei até agora --- find-file, ido-switch-buffer, eval-expression. Se você fazer encontrar exceções, você pode corrigir estas funções numa base caso-a-caso, chamando use-popup-minibuffersobre eles.

Posição Próximo Ponto

Para posicionar esse quadro do minibuffer próximo à altura do ponto, basta definir algo como a seguinte função. Não é perfeito (na verdade, é horrível em muitos casos), mas faz um trabalho decente ao estimar a altura do ponto.

(defun point-screen-height ()
  (* (/ (face-attribute 'default :height) 10) 2
     (- (line-number-at-pos (point))
        (line-number-at-pos (window-start)))))
Malabarba
fonte
Em vez de brincar com (setf (symbol-function function) ...)você, seria melhor usar advice-add(ou mais antigo ad-add-advice).
11288 Stefan
6

Minibuffer no topo

Exibir o minibuffer no meio da tela é complicado. Uma alternativa razoável (que você pode ou não achar mais legível) é movê-la para o topo.

Você pode configurar a localização (ou existência) do minibuffer de um quadro com o minibufferparâmetro de quadro na initial-frame-alist variável

Quadro separado

Definir o parâmetro para nilconfigurará o quadro para não possuir minibuffer e o Emacs criará automaticamente um quadro separado para ele. Você pode posicionar esses dois quadros usando o gerenciador de janelas do seu sistema operacional ou pode fazê-lo no Emacs.

O snippet a seguir explica como colocar o quadro do minibuffer no topo, com o quadro regular logo abaixo, mas você definitivamente precisará alterar os números para explicar o tamanho do 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)))

A maneira mais rápida de ajustar isso é redimensionar os quadros no seu gerenciador de janelas e depois avaliar (frame-parameters)para ver os valores dos parâmetros que você possui.

Mesmo quadro

Outra possibilidade é definir o minibufferparâmetro para uma janela. Infelizmente, não consegui fazer isso funcionar para uma janela no mesmo quadro.

Malabarba
fonte
5

A Biblioteca oneonone.elfornece isso imediatamente.

Basta personalizar a opção 1on1-minibuffer-frame-top/bottompara a posição desejada para o quadro de minibffer independente. O valor é o número de pixels da parte superior (se não negativa) ou da parte inferior (se negativa) da sua exibição. Por exemplo, defina o valor para sua altura de exibição dividido por 2, para centralizá-lo verticalmente.

Se você deseja que ele seja posicionado dinamicamente, digamos, no centro vertical do quadro atual, você pode fazer isso (a) deixando 1on1-minibuffer-frame-top/bottom= nile (b) aconselhando a função 1on1-set-minibuffer-frame-top/bottom a vincular dinamicamente essa variável à posição apropriada. Por exemplo:

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

(Defina a função my-dynamic-positionpara calcular a posição desejada, com base nos critérios desejados (tamanho e posição atual da janela / estrutura, fase da lua, ...).

Desenhou
fonte