Bagaimana cara menampilkan konten minibuffer di tengah bingkai emacs?

20

Saat bekerja pada bingkai Emacs yang dimaksimalkan, memiliki konten tampilan area minibuffer / gema di bagian bawah layar bisa sulit dilihat, tetapi juga menyebabkan kehilangan fokus pada teks yang sedang diedit saat mengetik perintah - yang cukup masalah dalam hal kegunaan.

Apakah ada cara untuk memindahkan minibuffer / area gema di tengah buffer emacs saat ini, di atas teks yang sedang diedit ketika minibuffer aktif, atau, lebih baik, untuk menangkap konten minibuffer dan menampilkannya juga di tengah buffer saat mengetik perintah ini?

Sébastien Le Callonnec
sumber
Untuk kelengkapan, saya harus menambahkan bahwa solusi saya saat ini untuk masalah kegunaan ini adalah dengan meningkatkan ukuran font minibuffer, tetapi terlihat konyol pada frame yang tidak dimaksimalkan.
Sébastien Le Callonnec

Jawaban:

10

Popup Minibuffer di Center

Inilah cara untuk melakukan persis apa yang Anda minta: tampilkan minibuffer di bagian tengah layar .

  1. Memiliki bingkai terpisah untuk minibuffer
  2. Posisikan di tengah
  3. Naikkan frame ini setiap kali minibuffer mendapatkan fokus.

Itu relatif mudah untuk dicapai, dan itulah yang akan Anda dapatkan dengan oneonone.elpaket (seperti yang ditunjukkan oleh @Drew). Masalah dengan pendekatan ini, adalah bahwa minibuffer juga digunakan untuk pesan, jadi menyembunyikannya bukanlah sesuatu yang sangat nyaman. Tidak perlu khawatir! Saya datang dengan solusi lain.

  1. Miliki minibuffer kedua yang ditampilkan pada bingkai terpisah.
  2. Posisikan di tengah.
  3. Gunakan frame kedua untuk perintah interaktif, sedangkan minibuffer asli (pertama) digunakan untuk menggemakan pesan.

Pelaksanaan

Untuk mengimplementasikan ini, kita perlu membuat frame minibuffer di startup 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))
  "")

Kemudian kami tambalan read-from-minibufferuntuk menggunakan minibuffer kedua ini, bukan minibuffer frame asli.

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

Ini mungkin tidak bekerja dengan benar-benar semuanya, tetapi bekerja pada segala sesuatu yang saya mencoba sejauh --- find-file, ido-switch-buffer, eval-expression. Jika Anda melakukan menemukan pengecualian apapun, Anda dapat menambal fungsi-fungsi ini atas dasar kasus per kasus dengan memanggil use-popup-minibuffermereka.

Posisi Near Point

Untuk menempatkan frame minibuffer ini di dekat ketinggian titik, cukup tentukan sesuatu seperti fungsi berikut. Itu tidak sempurna (pada kenyataannya, itu mengerikan pada banyak kasus), tetapi ia melakukan pekerjaan yang layak untuk memperkirakan ketinggian titik.

(defun point-screen-height ()
  (* (/ (face-attribute 'default :height) 10) 2
     (- (line-number-at-pos (point))
        (line-number-at-pos (window-start)))))
Malabarba
sumber
Daripada bermain-main dengan (setf (symbol-function function) ...)Anda lebih baik menggunakan advice-add(atau yang lebih tua ad-add-advice).
Stefan
6

Minibuffer di atas

Menampilkan minibuffer di tengah layar itu rumit. Alternatif yang masuk akal (yang Anda mungkin atau mungkin tidak menemukan lebih mudah dibaca) adalah memindahkannya ke atas.

Anda dapat mengkonfigurasi lokasi (atau keberadaan) minibuffer minibufferframe dengan parameter frame dalam initial-frame-alist variabel.

Bingkai terpisah

Mengatur parameter untuk nilakan mengonfigurasi frame untuk tidak memiliki minibuffer, dan Emacs akan secara otomatis membuat frame minibuffer terpisah untuk Anda. Anda dapat memposisikan dua frame ini menggunakan window manager OS Anda, atau Anda dapat melakukannya dari Emacs.

Cuplikan berikut menjelaskan cara menempatkan frame minibuffer di bagian atas, dengan frame reguler tepat di bawahnya, tetapi Anda harus mengubah angka untuk memperhitungkan ukuran monitor Anda.

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

Cara tercepat untuk menyesuaikan ini, adalah mengubah ukuran frame di window manager Anda, dan kemudian mengevaluasi (frame-parameters)untuk melihat nilai parameter yang Anda miliki.

Frame yang sama

Kemungkinan lain adalah mengatur minibufferparameter ke jendela. Sayangnya, saya tidak dapat menjalankannya untuk jendela dalam bingkai yang sama.

Malabarba
sumber
5

Perpustakaan oneonone.elmemberi Anda ini di luar kotak.

Cukup sesuaikan opsi 1on1-minibuffer-frame-top/bottomke posisi yang Anda inginkan untuk frame minibuffer mandiri. Nilainya adalah jumlah piksel dari atas (jika tidak negatif) atau bagian bawah (jika negatif) dari tampilan Anda. Misalnya, setel nilai ke tinggi tampilan Anda dibagi 2, untuk menempatkannya secara vertikal.

Jika Anda ingin memposisikannya secara dinamis, katakan di tengah vertikal dari frame saat ini, maka Anda dapat melakukannya dengan (a) meninggalkan 1on1-minibuffer-frame-top/bottom= nildan (b) menasihati fungsi 1on1-set-minibuffer-frame-top/bottom untuk mengikat variabel itu secara dinamis ke posisi yang sesuai. Sebagai contoh:

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

(Tentukan fungsi my-dynamic-positionuntuk menghitung posisi yang Anda inginkan, berdasarkan kriteria apa pun yang Anda inginkan (ukuran & posisi jendela / bingkai saat ini, fase bulan, ...).

Drew
sumber