Як я успадковую прогрес-режим, підтримуючи старший emacsen?


10

Я пишу основний режим для мови програмування, але хочу підтримувати старіші версії Emacs. prog-modeє відносно новим. Я хочу успадкувати, prog-modeякщо це визначено, але все-таки зробити щось розумне інакше.

Який найкращий підхід? Чи варто мені defalias prog-modeна старшому Emacsen, чи це буде заважати іншим режимам, якщо вони будуть робити те саме?


Я б порадив просто відмовитися від підтримки Emacs <24. На мою думку, більше не варто докладати зусиль, і вам доведеться відмовитися від важливіших можливостей, ніж prog-mode. Помітно, ви страждаєте від нестачі лексичного зв’язку.
місячник

Я беру участь у julia-mode, і частина основної команди використовує старший Emacsen і вважає за краще, щоб ми його підтримували.
Вільфред Х'юз

1
@lunaryorn Emacs 24 все ще досить новий. Emacs 23 - це поточна версія для багатьох ОС. Emacs 22 поки що є актуальним для кількох. Не кожен вдосконалює своє програмне забезпечення, як божевільний. Відмова від підтримки Emacs 23 обмежує вас кількох користувачів, які жадають кровоточити.
Жил "ТАК - перестань бути злим"

1
Існує багато причин використовувати старіші версії Emacs. Наприклад, у Windows Emacs 23 став дуже млявим, тому я вирішив дотримуватися Emacs 22 там.
Lindydancer

@Gilles я сумніваюся, що це просто "кілька користувачів". Flycheck ніколи не підтримував Emacs 23 в першу чергу і став одним з найпопулярніших пакетів на MELPA, тим не менше…
lunaryorn

Відповіді:


11

Ціною додаткового прив’язки символів верхнього рівня є дуже акуратне рішення, яке дозволяє уникнути повторення define-derived-modeформи:

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

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

Працює чудово в будь-якому Emacs> = 23. Я придумав це протягом haml-modeдекількох років тому IIRC, і, здається, він поширився звідти на кілька інших основних режимів. Головне, що define-derived-modeмакрос робить із символом батьківського режиму, - це генерувати код, який викликає його функцію: у цьому сенсі defaliasнова змінна робить рівнозначною ззаданою функцією.

Одне застереження полягає в тому, що це може заплутатись derived-mode-p, тому код, який перевіряє, чи не працює ваш режим, prog-modeможе працювати неправильно. На практиці я не зіткнувся з жодними проблемами: звичніший приєднання такого коду prog-mode-hook, який все-таки запускається.

(Як Йорген зазначає у коментарях, define-derived-modeтакож використовує mode-classвластивість з символу батьківського режиму, і defaliasне копіює його. На момент написання цього ресурсу, здається, він використовується лише дляspecial-mode .)

Оновлення: в ці дні я просто пропоную вимагати принаймні Emacs 24, оскільки старіші версії давно застаріли.


2
Приємне рішення! Просто застереження. Це працює для prog-mode, але не працює для кожного режиму. define-derived-modeкопіює mode-classвластивість символу в дочірній режим. defaliasБуде НЕ передати це майно. Якщо mode-classце стосується Вашого випадку використання, його потрібно скопіювати / встановити вручну.
Йорген Шефер

Дякую, що це зрозумів, Йорген - мені доведеться копатись та дізнаватися більше про те, що mode-classозначає власність.
sanityinc

3

tl; dr: Використовуйте ifта власну функцію init:

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

Потім виконайте всі ініціалізації режиму в your-cool-init.

Більш тривале пояснення:

Проблема полягає в тому, що офіційним способом написання похідного основного режиму є використання define-derived-modeмакросу:

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

Що стосується старшого Emacsen (до 24 років), це перерва, коли prog-mode. І ви не можете використовувати (if (fboundp 'prog-mode) ...)там, оскільки макрос очікує буквального символу, і він буде цитувати його для вас у розширенні.

define-derived-modeвикористовує батьківство безліччю способів. Вам потрібно буде скопіювати всі ці дані у власному режимі визначення, щоб скористатися ними, і це є втомливим і схильним до помилок.

Тож єдиний спосіб - використовувати два різних define-derived-modeтвердження, залежно від того, prog-modeіснує чи ні. Це ставить перед вами проблему написання коду ініціалізації двічі. Звичайно, це погано, тому ви отримуєте це у своїй функції, як описано вище.

(Найкраще рішення - це, звичайно, відмовитися від підтримки 23.x і використовувати лексичне обстеження. Але я думаю, ви вже розглядали та відмовилися від цього варіанту. :-))


Що є найближчим еквівалентом prog-modeу старшому Emacsen? Чи було б сенсом виходити з цього пункту text-modeабо fundamental-modeякщо prog-modeйого немає?
Вільфред Х'юз

@Jorgen Або ми можемо отримати проміжний режим, використовуючи fboundpспочатку, лише за допомогою define-derived-modeзаяви? Тоді власне режим з повним визначенням може бути отриманий з цього проміжного режиму? Таким чином, весь режим не потрібно визначати двічі.
Каушал Моді

1
@WilfredHughes, його немає. Виведення з fundamental-modeеквівалентно похідному від nil(і справді, define-derived-modeзамінює fundamental-modeна nil), хоча text-modeце не доречно, оскільки програмний код не є текстом. Більшість налаштувань за замовчуванням text-modeне мають сенсу в режимах програмування поза коментарями. Ось чому він prog-modeбув представлений в Emacs 24.
Йорген Шефер

@kaushalmodi, ви можете отримати проміжний режим, але це все одно потребує двох define-derived-modeвизначень у ifформі, лише для проміжного режиму замість остаточного. Ви б замінили defunфункцію init функцією a define-derived-modeдля остаточного режиму. Я не вважаю це особливо бажаним. Ви також можете визначити prog-modeсебе, як підказує оригінальне запитання, але це може легко заплутати інші режими, які покладаються на fboundpперевірку наявності цього режиму.
Йорген Шефер

Я не вірю, що необхідні дві різні define-derived-modeзаяви. Пару років тому я придумав рішення, яке я опублікував як окрему відповідь, і, здається, він працює добре в обох Emacs 23 і 24. Код, як він використовується у ряді популярних основних режимів.
sanityinc


0

Ви можете визначити макрос обгортки, define-derived-modeякий оцінює його аргументи.

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

(Попередження: перевірено лише мінімально.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.