Menulis Elisp portabel

8

Idealnya, saya ingin dapat menyimpan seluruh isi .emacs.ddirektori saya dan memilikinya "hanya berfungsi" pada Emacs mana pun saya memuatnya, tetapi masih memanfaatkan setiap fitur dari lingkungan spesifik, seperti sistem windowing GUI.

Saya tidak mencari ensiklopedia fitur yang tidak kompatibel. Saya hanya ingin tahu cara memeriksa fitur, OS, versi, grafik, dll, dan cara memanfaatkannya tanpa melanggar kode untuk konfigurasi lain.

Apa teknik dasar yang dapat saya gunakan untuk menulis Elisp yang bekerja di beberapa versi Emacs yang umum di alam liar (mis. 22.x +) dan pada beberapa platform yang mendasarinya (misalnya OSX, Linux, Windows, dan * nix lainnya), sambil mengambil keuntungan dari platform dan fitur khusus versi mana yang berlaku?

Paul Miller
sumber
1
@Malabarba Jika pertanyaan ditafsirkan sebagai "daftar semua perbedaan antara berbagai versi dan platform", ya, itu terlalu luas. Tetapi teknik dasar - pengujian jika pengidentifikasi terikat, pulih dari kesalahan, pengujian window-system, dll dapat dijawab secara wajar di sini.
Gilles 'SANGAT berhenti menjadi jahat'
1
Emacs 22 bukan "baru" lagi. Bahkan Emacs 23 adalah tanggal.
lunaryorn
1
Saya memasukkan emacs 22 karena itu umum di alam liar. Misalnya, ini dibundel dengan OSX 10.9.
Paul Miller
Setiap pengguna Emacs yang serius di Mac akan mengunduh versi terbaru. Emacs 22 hanya termasuk dalam OS X karena ini adalah versi terakhir yang dilisensikan di bawah GPLv2 (dan IMO mereka hanya harus membuangnya seluruhnya). Saya pikir 23+ jauh lebih berguna (IIRC Debian stable masih menggunakan 23).
shosti
2
Sheesh. Orang yang mengajukan pertanyaan bertanya tentang Emacs 22+ dan Anda ingin "memperbaiki" permintaan itu untuk bersikeras pada versi yang lebih baru? Apa selanjutnya - seseorang bertanya forward-chardan Anda ingin pertanyaan itu diubah untuk ditanyakan scroll-up? Jika OP menginginkan kompatibilitas Emacs 22+, biarkan saja. Dan tidak, pertanyaan yang diajukan bukan hanya tentang OS X. Dan ya, masih banyak orang yang menggunakan versi Emacs yang lebih lama (beberapa bahkan lebih tua dari 22, FWIW).
Drew

Jawaban:

10

Elisp adalah bahasa yang ditafsirkan. Anda dapat memasukkan kode khusus versi ke dalam kode Anda .emacs, tetapi melindunginya dengan menguji pada waktu buka yang beroperasi pada versi yang benar.

(if (is-new-feature-available)
    (shiny-new-feature)
  (old-less-nifty-feature))

Kode ini akan berfungsi di semua versi karena (shiny-new-feature)hanya dievaluasi ketika (is-new-feature-available)mengembalikan true. Sebagian besar jawaban ini ditujukan untuk bagaimana menerapkan (is-new-feature-available).

Mengatasi berbagai set fitur

Lebih baik menguji apakah fitur tersedia daripada menguji versi Emacs. Terkadang fitur tersebut tersedia sebagai paket opsional. Jika Anda ingin menjalankan kode dalam XEmacs atau varian Emacs lainnya, mungkin ia memperoleh fitur yang sama di versi yang berbeda. Gunakan fungsi boundpuntuk menguji apakah suatu variabel tersedia, dan fboundpuntuk menguji apakah suatu fungsi tersedia.

Misalnya, cuplikan berikut ini mengikat kunci untuk beralih visual-line-modejika tersedia, dan longlines-modesebaliknya.

(global-set-key "\eml" (if (fboundp 'visual-line-mode)
                           'visual-line-mode
                         'longlines-mode))

Terkadang, daripada menguji fitur, lebih mudah untuk menjalankan sepotong kode kecil dan mengabaikan kesalahan apa pun karena fungsi yang tidak terdefinisi, argumen yang tidak valid, dll. Jangan lakukan ini untuk sejumlah besar kode, karena ini akan membuat kode Anda sangat sulit di-debug.

Misalnya, saya tidak ingin melihat bilah alat. Versi Emacs yang lebih lama tidak memilikinya sama sekali. GNU Emacs dan XEmacs menambahkan fitur itu dengan cara yang berbeda dan menjadikannya default. Begini cara saya mematikannya. The set-specifierfungsi khusus untuk XEmacs, dan default-toolbar-visible-pkhusus untuk versi yang cukup baru-baru Emacs; menggunakan condition-casemengurus kedua persyaratan. GNU Emacs menyediakan fungsi khusus jadi saya hanya menguji apakah fungsi itu tersedia.

;; For XEmacs
(condition-case nil
    (set-specifier default-toolbar-visible-p nil)
  (error nil))
;; For GNU Emacs
(if (fboundp 'tool-bar-mode)
    (tool-bar-mode 0))

Beberapa nama wajah berubah karena versi. Gunakan facepuntuk menguji ketersediaan nama wajah.

(let ((face (if (facep 'mode-line) 'mode-line 'modeline)))
  (set-face-background face …))

Terkadang Anda mungkin ingin memuat paket yang bagus jika ada, dan tidak melakukan apa pun jika paket tidak tersedia. requirememiliki argumen opsional untuk itu.

(require 'tex-site nil t) ;; Load AUCTeX if available

Argumen ini diperkenalkan di GNU Emacs 20.4 dan tidak tersedia di XEmacs, jadi jika Anda ingin melangkah sejauh itu, Anda harus membungkusnya condition-caseatau menggunakannya load(yang tidak memeriksa perpustakaan yang sudah dimuat) .

Batasi ketergantungan versi ke fitur tingkat pengguna. Jangan gunakan fitur pemrograman yang lebih baru yang tidak tersedia di semua versi yang ingin Anda dukung: Anda harus menyediakan versi kompatibilitas untuk versi yang lebih lama, dan lebih mudah mempertahankan satu versi.

Terkadang Anda memang membutuhkan fitur di banyak tempat, dan itu tersedia di semua implementasi yang Anda pedulikan, tetapi dengan cara yang berbeda. Ini sebagian besar terjadi jika Anda ingin mendukung XEmacs dan GNU Emacs: mereka memiliki kecenderungan frustasi untuk menyalin fitur masing-masing tetapi tidak pada antarmuka mereka. Dalam hal ini, mendefinisikan fungsi kompatibilitas lebih mudah daripada pengujian pada saat digunakan.

Misalnya, kode berikut mendefinisikan fungsi yang mengembalikan sistem jendela dari frame saat ini, cara GNU modern, cara XEmacs modern, dan cara gaya lama ketika Anda tidak bisa menggabungkan frame terminal dan GUI dalam contoh yang sama.

(defalias 'compat-window-system
  (cond
   ((fboundp 'window-system) #'window-system)
   ((fboundp 'device-type)
    (lambda (&optional frame)
      (device-type (frame-device frame))))
   (t
    (lambda (&optional frame) window-system))))

Ketergantungan lingkungan

Tidak banyak kode yang harus bergantung pada platform. Variabel system-typemenunjukkan sistem operasi. Saya menggunakannya secara eksklusif untuk mengaktifkan beberapa peretasan ms-dos(ya, file saya setua itu) dan windows-nt.

Anda mungkin ingin menambahkan direktori ke jalur pencarian yang dapat dieksekusi ( PATH), tetapi itu biasanya paling baik dilakukan di luar Emacs, di .profilesistem Anda untuk Unix-like dan melalui panel kontrol di Windows. Untuk menguji apakah program eksternal tersedia, hubungi executable-find.

Untuk kode yang perlu bertindak berbeda tergantung pada jenis GUI jika ada, periksa window-typeatau penggantinya (lihat di atas).

File inisialisasi

Untuk kompatibilitas maksimum, masukkan kode Anda ~/.emacs. GNU Emacs mulai mencari di ~/emacs.dversi 22. XEmacs mulai mencari di ~/.xemacsdalam versi 21.4. Pendekatan alternatif adalah memasukkan kode kompatibilitas ~/.emacsdan menyelesaikannya dengan memuat file utama Anda. Letakkan di (setq load-home-init-file t)suatu tempat untuk menghindari versi terbaru dari XEmacs yang menanyakan apakah Anda ingin memindahkan Anda .emacske lokasi khusus XEmacs.

Versi Emacs yang berbeda mungkin memiliki ekspansi yang berbeda dan tidak kompatibel untuk beberapa makro. Jadi jangan berbagi file byte-yang dikompilasi antar versi, kompilasi file pada setiap mesin.

Terkadang fitur sudah usang, tetapi Anda masih ingin menggunakannya karena hanya itu yang ada di beberapa versi lain yang ingin Anda dukung. Peringatan byte compiler berasal dari byte-obsolete-variableproperti.

(cond
 ((not (boundp 'desktop-enable))
  (defvaralias 'desktop-enable 'desktop-save-mode))
 ((get 'desktop-enable 'byte-obsolete-variable)
  (put 'desktop-enable 'byte-obsolete-variable nil)))

¹ Secara relatif, dibandingkan dengan XEmac yang lebih lama.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
6

(Komunitas wiki. Silakan tambahkan milik Anda!)

  • Jika ada fungsi, ditambahkan dalam versi Emacs yang lebih baru, yang ingin Anda gunakan, periksa apakah itu didefinisikan dengan fboundp, dan tentukan fungsi kompatibilitas jika tidak ditentukan.

    Ini dianggap ide yang buruk untuk memberikan fungsi kompatibilitas nama yang sama dengan fungsi sebenarnya, karena kode Elisp lainnya mungkin menggunakan fboundptrik yang sama . Dengan demikian, gunakan awalan untuk fungsi kompatibilitas, dan gunakan defaliasuntuk nama yang sama jika didefinisikan. Misalnya:

    (if (fboundp 'propertize)
        (defalias 'my-propertize 'propertize)
      (defun my-propertize (string &rest properties)
        "Return a copy of STRING with text properties added.
    
     [Note: this docstring has been copied from the Emacs 21 version]
    
    First argument is the string to copy.
    Remaining arguments form a sequence of PROPERTY VALUE pairs for text
    properties to add to the result."
        (let ((str (copy-sequence string)))
          (add-text-properties 0 (length str)
                               properties
                               str)
          str)))
    
  • Jika beberapa konfigurasi hanya berlaku untuk OS tertentu, ada beberapa kemungkinan yang berbeda. Anda dapat memeriksa system-typevariabel, yang kembali gnu/linux, darwin, windows-ntdan beberapa orang lainnya (lihat docstring).

    Anda mungkin tergoda untuk menggunakan window-system, meskipun doktringnya menyatakan bahwa "Penggunaan variabel ini sebagai boolean sudah usang", dan merekomendasikan untuk menggunakannya display-graphic-p. Perhatikan bahwa Emacs dapat menggunakan berbagai jenis tampilan untuk frame yang berbeda saat ini (misalnya satu frame di terminal dan lainnya di jendela "tepat"), jadi ini dapat menyebabkan kejutan. Gunakan current-frame-configurationatau get-buffer-window-listdi elisp Anda untuk membuat pilihan yang tepat.

  • Anda mungkin ingin memeriksa apakah Anda menjalankan di bawah rasa emacs yang tepat. Gunakan featureep untuk memeriksa varian. Misalnya:

    (when (featurep 'xemacs)
       (require 'fsf-compat))
    

    Anda juga dapat menggunakannya untuk memeriksa modul tertentu yang dimuat. Sebagai contoh, jika Anda hanya menggunakan defun kecil dan mudah untuk mendefinisikan Common-lisp Anda mungkin memilih untuk mendefinisikan daripada membutuhkan. Misalnya:

    (unless (featurep 'cl)
       (defun caaar (x)
          "Return the `car' of the `car' of the `car' of X."
          (car (car (car x)))))
    
  • Hindari menyimpan file .elc. Ini tidak kompatibel maju atau mundur dengan antara beberapa versi Emacs.

legoscia
sumber
0

Jika Anda menggunakan fungsi dari cl-libtetapi tidak ingin menggunakan versi non-namespace yang sudah usang, jadikan pustaka kompatibilitas cl-lib sebagai ketergantungan proyek Anda. Itu akan memungkinkan Anda untuk menggunakan cl-fungsi namespace tetapi mempertahankan kompatibilitas ke belakang.

shosti
sumber
load-library: Tidak dapat membuka memuat file: cl-lib - Mengapa saya cl-libtetap ingin ? Apa kelebihannya cl, yang sudah ada sejak setidaknya 20 tahun yang lalu?
Gilles 'SANGAT berhenti menjadi jahat'
1
clsudah usang dan mungkin akan dihapus pada akhirnya. Menggunakannya dalam kode telah menyebabkan peringatan byte-compiler untuk waktu yang cukup lama. Saya pikir alasannya adalah bahwa mereka ingin menggunakan kembali beberapa clnama (seperti dolist) dengan semantik yang berbeda.
shosti