LINUX.ORG.RU

emacs 29.2 use-package как работает секция :hook ?

 , ,


0

1

Привет, ЛОР.

Перешел на 29.2 с нативной компиляцией. До этого был 28.2. Переписываю понемногу свой конфиг на use-package (до этого не использовал его) и не понимаю, каким образом исполняется код в секции (макросе?) :hook. Например,

;; python setup
(use-package python
    :hook ((python-ts-mode . eglot-ensure)
           (python-ts-mode . company-mode))
    :mode (("\\.py\\'" . python-ts-mode)))

(use-package company
    :ensure t
    :config
    (setq company-idle-delay 0.1)
    (setq company-minimum-prefix-length 2))

Простой, казалось бы, сетап для питона. Только вот открытие файлов с расширением py включает eglot, но не включает company-mode. Ручная активация M-x company-mode срабатывает без проблем.

Почему? Как исправить? Как отдебажить? В лиспе не силен. Старт с debug-on-error никаких трейсов при открытии emacsa или открытии питоновских файлов не дает.


По синтаксису похоже, что в :hook должен лежать ассоциативный список (alist). Тогда можно предположить откуда проблемы растут. Хоть в alist-у и можно положить несколько значений с одним и тем же ключом, но по ключу будет получен только самый первый, что как будто в твоем случае и произошло. Но это не точно, у господина сверху вот работает и так.

Тебе могло бы помочь все в одну лямбду засунуть. Типа такого:

(use-package python
  :hook ((python-ts-mode . (lambda ()
                             (eglot-ensure)
                             (company-mode))))
  :mode (("\\.py\\'" . python-ts-mode)))
gejzenbug
()

Как отдебажить?

Можно развернуть макрос и посмотреть, что на самом деле будет происходить: поставить курсор перед открывающей скобкой формы и M-x emacs-lisp-macroexpand. Для первого вызова use-package получается что-то вроде

(progn
  (straight-use-package 'python)
  (defvar use-package--warning85
    #'(lambda
        (keyword err)
        (let
            ((msg
              (format "%s/%s: %s" 'python keyword
                      (error-message-string err))))
          (display-warning 'use-package msg :error))))
  (condition-case-unless-debug err
      (progn
        (unless
            (fboundp 'eglot-ensure)
          (autoload #'eglot-ensure "python" nil t))
        (unless
            (fboundp 'company-mode)
          (autoload #'company-mode "python" nil t))
        (unless
            (fboundp 'python-ts-mode)
          (autoload #'python-ts-mode "python" nil t))
        (add-hook 'python-ts-mode-hook #'eglot-ensure)
        (add-hook 'python-ts-mode-hook #'company-mode)
        (add-to-list 'auto-mode-alist
                     '("\\.py\\'" . python-ts-mode)))
    (error
     (funcall use-package--warning85 :catch err))))

Хуки добавляются оба, догадку с alist можно исключить.

(По M-. на символе функции/макроса можно провалиться в его определение, почитать докстринг. По M-, вернуться обратно. Конечно, документацию на всё, что угодно, можно получить по C-h, но это больше клавиш клацать.)

Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 3)

Как исправить?

Можно попробовать добавить :after:

(use-package python
  :after (company)
  :hook ((python-ts-mode . eglot-ensure)
         (python-ts-mode . company-mode))

Nervous ★★★★★
()

:hook, помимо собственно хука, манипулирует тем, в какой момент пакет подгружается в Emacs. В целом надо, чтобы из конфигурироемого пакета была команда, а не хук. Первый хук должен быть в конфиге eglot, а второй — в конфиге company.

Если хотите оставить здесь — попробуйте добавить :after (company eglot).

akho
()
Ответ на: комментарий от Nervous

И такой вариант, и вариант @gejzenbug не работают. Более того, в таких вариантах eglot не соединяет с lsp сервером. Если у вас работает, подозреваю, где-то у меня в конфиге еще что-то мешает…

curbar
() автор топика
Ответ на: комментарий от akho

Первый хук должен быть в конфиге eglot, а второй — в конфиге company.

Это было ближе. Так работает (без хуков в python)

;; python setup
(use-package company
    :ensure t
    :hook (python-mode . company-mode)
    :config
    (setq company-idle-delay 0.1)
    (setq company-minimum-prefix-length 2))

(use-package eglot
    :ensure t
    :hook (python-mode . eglot-ensure))

(use-package python
    :ensure t
    :mode (("\\.py\\'" . python-ts-mode)))

Спасибо!

curbar
() автор топика
Ответ на: комментарий от akho

надо, чтобы из конфигурируемого пакета была команда, а не хук

СЯУ: сегодня я узнал %)

Nervous ★★★★★
()
Ответ на: комментарий от curbar

Немного дополню вариант, т.к. я без tree-sitter сначала настроил. Теперь с ним

(use-package company
    :ensure t
    :hook (python-ts-mode . company-mode)
    :config
    (setq company-idle-delay 0.1)
    (setq company-minimum-prefix-length 2))

(use-package eglot
    :ensure t
    :hook (python-ts-mode . eglot-ensure))

(add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode))
(use-package python
    :ensure t
    :mode (("\\.py\\'" . python-ts-mode))
    :config
    (setq python-indent-offset 4))
curbar
() автор топика
Ответ на: комментарий от akho

Кстати, да. Убрал.

Авто tree-sitter мне не нужен. Ручная настройка удобнее, когда разобрался.

curbar
() автор топика

Привет!

Твой код нужно переписать примерно таким образом:

(use-package company
  :ensure t
  :custom
  (company-idle-delay 0.1)
  (company-minimum-prefix-length 2)
  :hook (python-ts-mode . company-mode))

(use-package eglot
  :ensure t
  :hook (python-ts-mode . eglot-ensure))

(use-package python
  :ensure t
  :after (company eglot)
  :mode (("\\.py\\'" . python-ts-mode)))

Теперь давай пройдёмся по ошибкам в твоём конфиге.

  1. Если значение переменной use-package-always-ensure не равно t, то секцию :ensure t нужно добавлять во все вызовы use-package. В противном случае нужные пакеты не будут установлены и загружены и работать не будут.

  2. В секции :hook нужно описывать пары следующего вида:

    (режим-хука . нужный-режим)
    

    Это превращается в код следующего вида:

    (add-hook 'режим-hook 'нужный-режим)
    

    Можно указать список таких пар, будет работать точно так же.

    А ещё можно так:

    ;; -> ELEC-PAIR MODE
    ;; Встроенный пакет.
    ;; Автоматически вставляет при вводе одной скобки или кавычки парную ей. Если
    ;; выделен регион, то в скобки обрамляется он.
    (use-package elec-pair
      :ensure nil
      :config
      (add-to-list 'electric-pair-pairs '(?\( . ?\)))
      (add-to-list 'electric-pair-pairs '(?\[ . ?\]))
      (add-to-list 'electric-pair-pairs '(?{ . ?}))
      (add-to-list 'electric-pair-pairs '(?« . ?»))
      (add-to-list 'electric-pair-pairs '(?‘ . ’?))
      (add-to-list 'electric-pair-pairs '(?‚ . ‘?))
      (add-to-list 'electric-pair-pairs '(?“ . ”?))
      :hook
      ((
        adoc-mode
        emacs-lisp-data-mode
        emacs-lisp-mode
        lisp-data-mode
        markdown-mode) . electric-pair-local-mode))
    

    Однако, в твоём случае неправильно определён порядок вызова функций. Например, функция eglot-ensure не существует в момент описания хука. Поэтому правильный её вызов делается в описании настроек для модуля eglot, а не другого режима, в котором ты хочешь его использовать.

    Ещё раз, простыми словами: пары в блоке hook состоят из двух частей:

    • Название основного режима, к которому добавляется слово -hook (на самом деле там вызов специальной функции, но это сейчас не важно).
    • Название функции, которая должна сработать. И эта функция должна существовать (быть загружена из пакета) на момент описания хука. А в твоём конфиге происходит так, что ты пытаешься несуществующую (загружаемую позже) функцию привязать к хуку.
  3. Можно использовать setq в блоке :config, но правильный путь – использование :custom. Пример я тебе добавил.

Палю свой init.el и Telegram-канал GNU Emacs для технических писателей.

dunmaksim
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.