Bagaimana saya mewarisi dari prog-mode, sementara masih mendukung emacsen yang lebih lama?

10

Saya sedang menulis mode utama untuk bahasa pemrograman, tetapi saya ingin mendukung versi Emacs yang lebih lama. prog-moderelatif baru. Saya ingin mewarisi dari prog-modejika sudah ditentukan, tetapi masih melakukan sesuatu yang masuk akal sebaliknya.

Apa pendekatan terbaik? Haruskah saya defalias prog-modemenggunakan Emacsen yang lebih lama, atau akankah itu mengganggu mode lain jika mereka melakukan hal yang sama?

Wilfred Hughes
sumber
Saya menyarankan untuk tidak memberikan dukungan untuk Emacs <24. Menurut pendapat saya itu sudah tidak layak lagi, dan Anda harus mengabaikan fitur yang lebih penting daripada prog-mode. Khususnya, Anda akan menderita karena kurangnya ikatan leksikal.
lunaryorn
Saya berkontribusi pada mode julia, dan beberapa tim inti menggunakan Emacsen yang lebih lama dan lebih suka kami mendukungnya.
Wilfred Hughes
1
@Lunaryorn Emacs 24 masih cukup baru. Emacs 23 adalah versi saat ini di banyak OS. Emacs 22 masih berlaku saat ini. Tidak semua orang memperbarui perangkat lunak mereka seperti orang gila. Menjatuhkan dukungan untuk Emacs 23 akan membatasi Anda untuk beberapa pengguna yang menginginkan tepi pendarahan.
Gilles 'SANGAT berhenti menjadi jahat'
1
Ada banyak alasan untuk menggunakan versi Emacs yang lebih lama. Sebagai contoh, pada Windows, Emacs 23 menjadi sangat lamban, jadi saya memilih untuk tetap menggunakan Emacs 22 di sana.
Lindydancer
@Gilles Saya ragu itu hanya "beberapa pengguna". Flycheck tidak pernah mendukung Emacs 23 sejak awal, dan menjadi salah satu paket paling populer di MELPA ...
lunaryorn

Jawaban:

11

Dengan biaya pengikatan simbol level atas tambahan, ada solusi yang sangat rapi yang menghindari pengulangan define-derived-modeformulir:

(defalias 'my-fancy-parent-mode
  (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))

(define-derived-mode my-fancy-mode my-fancy-parent-mode
   ...)

Berfungsi dengan baik di Emacs> = 23. Saya menemukan ini untuk haml-modebeberapa tahun yang lalu IIRC, dan tampaknya telah menyebar dari sana ke beberapa mode utama lainnya. Hal utama yang define-derived-modemakro lakukan dengan simbol mode induk adalah menghasilkan kode yang memanggil fungsinya: dalam hal ini, defaliasmembuat variabel baru persis sama dengan fungsi alias.

Satu peringatan adalah bahwa ini dapat membingungkan derived-mode-p, jadi kode yang memeriksa untuk melihat apakah mode Anda berasal prog-modemungkin tidak berfungsi dengan benar. Dalam praktiknya saya belum mengalami masalah: lebih sering untuk menghubungkan kode seperti itu prog-mode-hook, yang masih berjalan.

(Seperti yang ditunjukkan Jorgen dalam komentar, define-derived-modejuga menggunakan mode-classproperti dari simbol mode induk, dan defaliastidak akan menyalinnya. Pada saat penulisan, properti ini sepertinya hanya digunakan untukspecial-mode .)

Pembaruan: hari ini saya hanya menyarankan membutuhkan setidaknya Emacs 24, karena versi yang lebih lama sudah usang.

sanityinc
sumber
2
Solusi bagus! Hanya peringatan: Ini berfungsi untuk prog-mode, tetapi tidak akan bekerja untuk setiap mode. define-derived-modemenyalin mode-classproperti simbol ke mode anak. The defaliasakan tidak mentransfer properti ini. Jika mode-classrelevan dengan use case Anda, Anda perlu menyalin / mengaturnya secara manual.
Jorgen Schäfer
Terima kasih telah menangkapnya, Jorgen - Saya harus menggali dan mempelajari lebih lanjut tentang apa yang mode-classditunjukkan properti.
sanityinc
3

tl; dr: Gunakan ifdan fungsi init Anda sendiri:

(if (fboundp 'prog-mode)
    (define-derived-mode your-cool-mode prog-mode "Cool"
      "Docstring"
      (your-cool--init))
  (define-derived-mode your-cool-mode nil "Cool"
    "Docstring"
    (your-cool--init)))

Kemudian lakukan semua inisialisasi mode di your-cool-init.

Penjelasan yang lebih panjang:

Masalahnya adalah bahwa cara resmi menulis mode utama turunan adalah dengan menggunakan define-derived-modemakro:

(define-derived-mode your-cool-mode prog-mode ...)

Pada Emacsen yang lebih tua (pra-24), ini rusak ketika prog-mode. Dan Anda tidak dapat menggunakannya di (if (fboundp 'prog-mode) ...)sana karena makro mengharapkan simbol literal, dan akan mengutipnya untuk Anda dalam ekspansi.

define-derived-modemenggunakan orangtua dalam banyak cara. Anda harus menyalin semua itu dalam definisi mode Anda sendiri untuk memanfaatkannya, dan itu membosankan dan rentan kesalahan.

Jadi satu-satunya cara adalah dengan menggunakan dua define-derived-modepernyataan yang berbeda , tergantung pada apakah prog-modeada atau tidak. Itu membuat Anda kesulitan menulis kode inisialisasi Anda dua kali. Yang tentu saja buruk, jadi Anda mengekstraknya ke dalam fungsinya sendiri, seperti dijelaskan di atas.

(Solusi terbaik tentu saja untuk menjatuhkan dukungan untuk 23.x dan menggunakan scoping lexical. Tapi saya kira Anda sudah mempertimbangkan dan menjatuhkan opsi itu. :-))

Jorgen Schäfer
sumber
Apa yang paling mirip dengan prog-modeEmacsen yang lebih tua? Apakah masuk akal untuk berasal dari text-modeatau fundamental-modejika prog-modetidak tersedia?
Wilfred Hughes
@ Jorgen Atau bisakah kita menurunkan mode antara menggunakan fboundpterlebih dahulu, hanya dengan define-derived-modepernyataan? Lalu mode aktual dengan definisi penuh dapat diturunkan dari mode menengah itu? Dengan begitu seluruh mode tidak harus didefinisikan dua kali.
Kaushal Modi
1
@ WillfredHughes, tidak ada. Berasal dari fundamental-modesama dengan berasal dari nil(dan memang, define-derived-modemenggantikan fundamental-modedengan nil), sementara text-modetidak sesuai, karena kode program bukan teks. Sebagian besar pengaturan default di text-modetidak masuk akal dalam mode pemrograman di luar komentar. Inilah sebabnya mengapa prog-modediperkenalkan di Emacs 24.
Jorgen Schäfer
@kaushalmodi, Anda bisa mendapatkan mode menengah, tetapi itu masih membutuhkan dua define-derived-modedefinisi dalam ifformulir, hanya untuk mode menengah dan bukan mode final. Anda akan mengganti defunfungsi init dengan a define-derived-modeuntuk mode akhir. Saya tidak berpikir ini lebih disukai. Anda juga dapat menentukan prog-modesendiri, seperti yang disarankan pertanyaan awal, tetapi itu dapat dengan mudah membingungkan mode lain yang bergantung fboundpuntuk memeriksa keberadaan mode itu.
Jorgen Schäfer
Saya tidak percaya bahwa dua define-derived-modepernyataan berbeda diperlukan. Beberapa tahun yang lalu saya datang dengan solusi yang saya posting sebagai jawaban terpisah, dan tampaknya berfungsi dengan baik di kedua Emacs 23 & 24. Kode seperti itu digunakan dalam sejumlah mode utama populer.
sanityinc
0

Saya pikir pengujian menggunakan fboundplebih masuk akal.

(if (fboundp 'prog-mode)
    ...
   ...)
Alex Schröder
sumber
0

Anda bisa mendefinisikan makro pembungkus untuk define-derived-modeyang mengevaluasi argumennya.

(defmacro define-derived-mode* (child parent name &optional docstring &rest body)
  (macroexpand `(define-derived-mode ,(eval child) ,(eval parent) ,(eval name)
                                     ,(eval docstring) . ,body)))
(define-derived-mode* 'toy-mode
  (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode)
  "Toy"
  "Major mode for my favorite toy language"
  (toy-mode-setup))

(Peringatan: hanya diuji minimal.)

Gilles 'SANGAT berhenti menjadi jahat'
sumber