многострочный отступ на Python в emacs

1

Я новичок emacs, я хочу, чтобы emacs мог отступать от моего кода, подобного этому

egg = spam.foooooo('vivivivivivivivivi')\
          .foooooo('emacs', 'emacs', 'emacs', 'emacs')

Это невозможно сделать автоматически по умолчанию (без ручной вставки пробелов или C-c > ), так как emacs всегда отступывает 4 пробела (если Im не разбивает несколько аргументов на несколько строк).

Какой лучший подход для этого?

PS: Если это плохая идея (против PEP 8 или что-то еще), пожалуйста, скажите мне

  • 2
    Лучше всего помещать ваш spam.fooooo(…) в круглые скобки: egg = (spam.fooo…) . Таким образом, вам не нужен знак продолжения (среди прочих преимуществ).
  • 0
    Это плохой стиль. Как правило, второй метод foooooo не имеет ничего общего со спамом, а скорее с тем, что возвращает первый foooooo. Гораздо более идиоматично разрывать после ( или после одного из , (и в любом случае вам не придется использовать `` из-за автоматического продолжения скобок).
Теги:
emacs
indentation
elisp

2 ответа

24
Лучший ответ

Я согласен с Аароном в вопросе о желательности вашего стилистического выбора, но, поскольку я также согласен с ним в том, что Emacs Lisp - это весело, я опишу, как вы могли бы реализовать это.

Emacs python-mode вычисляет отступ строки в функции python-calculate-indentation, а соответствующий раздел для обработки строк продолжения находится глубоко внутри функцию, без простейшего способа ее настройки.

Итак, у нас есть два варианта:

  • Замените всю python-calculate-indentation нашей собственной версией (кошмар обслуживания, когда python-mode изменяется); или
  • " Консультировать" функцию python-calculate-indentation: то есть обернуть ее в нашу собственную функцию, которая обрабатывает интересующий нас случай, и в противном случае отказывается от оригинала.

Вариант (2) кажется в этом случае практически выполнимым. Так что отпустите! Первое, что нужно сделать, это прочитать руководство о совете, которое предполагает, что наши советы должны выглядеть так:

(defadvice python-calculate-indentation (around continuation-with-dot)
  "Handle continuation lines that start with a dot and try to
line them up with a dot in the line they continue from."
  (unless 
      (this-line-is-a-dotted-continuation-line) ; (TODO)
    ad-do-it))

Здесь ad-do-it - волшебный токен, который defadvice заменяет исходную функцию. Исходя из фона Python, вы можете спросить: "Почему бы не сделать этот стиль декоратора?" Механизм консультаций Emacs разработан (1) для того, чтобы рекомендации были хорошо отделены от оригинала; и (2) иметь несколько советов для одной функции, которая не нуждается в сотрудничестве; (3), чтобы вы могли индивидуально контролировать, какие советы включаются и выключаются. Вы могли бы, конечно, представить себе что-то подобное в Python.

Здесь, как определить, является ли текущая строка пунктирной линией продолжения:

(beginning-of-line)
(when (and (python-continuation-line-p)
           (looking-at "\\s-*\\."))
    ;; Yup, it a dotted continuation line. (TODO)
    ...)

Есть одна проблема с этим: вызов в beginning-of-line фактически перемещает точку в начало строки. К сожалению. Мы не хотим двигаться вокруг, просто вычисляя отступ. Поэтому мы лучше завершим это при вызове save-excursion, чтобы удостовериться, что точка не переходит в блуждание.

Мы можем найти точку, которую нам нужно выстроить, пропуская назад по токенам или выраженным в скобках выражениям (что Lisp вызывает "S-выражения" или "sexps" ), пока мы не найдем точку, иначе мы получим к началу заявления. Хорошая идиома Emacs для выполнения поиска в ограниченной части буфера - это narrow буфер, содержащий только ту часть, которую мы хотим:

(narrow-to-region (point)
                  (save-excursion
                    (end-of-line -1)
                    (python-beginning-of-statement)
                    (point)))

а затем продолжайте пропуски sexps назад до тех пор, пока мы не найдем точку, или пока backward-sexp не перестанет добиваться прогресса:

(let ((p -1))
  (while (/= p (point))
    (setq p (point))
    (when (looking-back "\\.")
      ;; Found the dot to line up with.
      (setq ad-return-value (1- (current-column)))
      ;; Stop searching backward and report success (TODO)
      ...)
    (backward-sexp)))

Здесь ad-return-value - волшебная переменная, которая defadvice использует для возвращаемого значения из рекомендуемой функции. Уродливый, но практичный.

Теперь есть две проблемы. Во-первых, backward-sexp может сигнализировать об ошибке при определенных обстоятельствах, поэтому лучше поймать эту ошибку:

(ignore-errors (backward-sexp))

Другая проблема заключается в выходе из цикла, а также в указании успеха. Мы можем сделать это сразу, объявив имя block, а затем вызвав return-from. Блоки и выходы являются общими Lisp функциями, поэтому нам нужно (require 'cl)

Пусть все вместе:

(require 'cl)

(defadvice python-calculate-indentation (around continuation-with-dot)
  "Handle continuation lines that start with a dot and try to
line them up with a dot in the line they continue from."
  (unless 
      (block 'found-dot
        (save-excursion
          (beginning-of-line)
          (when (and (python-continuation-line-p)
                     (looking-at "\\s-*\\."))
            (save-restriction
              ;; Handle dotted continuation line.
              (narrow-to-region (point)
                                (save-excursion
                                  (end-of-line -1)
                                  (python-beginning-of-statement)
                                  (point)))
              ;; Move backwards until we find a dot or can't move backwards
              ;; any more (e.g. because we hit a containing bracket)
              (let ((p -1))
                (while (/= p (point))
                  (setq p (point))
                  (when (looking-back "\\.")
                    (setq ad-return-value (1- (current-column)))
                    (return-from 'found-dot t))
                  (ignore-errors (backward-sexp))))))))
    ;; Use original indentation.
    ad-do-it))

(ad-activate 'python-calculate-indentation)

Я не буду утверждать, что это лучший способ сделать это, но он иллюстрирует кучу умеренно сложных программ Emacs и Lisp: advice, excursions, narrowing, перемещение по sexps, обработка ошибок, блоки и выходы. Наслаждайтесь!

  • 3
    +1. +10, если бы я мог, так как это определенно заслуживает значка «хороший ответ».
  • 0
    Благодарю. Боюсь, что Emacs немного вкус меньшинства. Теперь, если бы это было Затмение ...
Показать ещё 1 комментарий
2

Это довольно уродливо и потребует от вас написать emacs lisp. Мне нужно изучить emacs lisp, поэтому, если бы это было не так безобразно, я, вероятно, воспользовался бы этим. Но это так, а я нет. Похоже, вы узнаете emacs lisp:) (если вы действительно хотите это сделать). Я как бы ревнив. Во всяком случае, вы сказали, что информирование вас о том, что это плохая идея, было приемлемым ответом, так вот:

Это страшный стилистический выбор. Не

egg = spam.foo('viviviv')
egg = egg.foo('emacs', 'emacs', 'emacs')

легче читать?

В отличие от PEP 8 не упоминается, что использование символа продолжения строки должно быть сведено к минимуму. Кроме того, это наиболее окончательно и объективно противоречит духу PEP 8. Я просто не уверен, как;)

  • 0
    Я согласен, это ужасно стилистически :). Ваше предложение уже приходило мне в голову. Похоже, я должен обновить свой вопрос, чтобы узнать больше мнения о стиле
  • 0
    забудь это. Вещество над стилем. Я последую за твоим ответом, если захочу последовать ПКП 8
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню