многострочный отступ на Python в emacs - PullRequest
2 голосов
/ 30 октября 2010

Я новичок в emacs, я хочу, чтобы emacs мог делать отступы моего кода следующим образом

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

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

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

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

Ответы [ 2 ]

25 голосов
/ 11 ноября 2010

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

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

Итак, мы имеемдва варианта:

  1. Заменить все python-calculate-indentation нашей собственной версией (кошмар обслуживания при изменении python-mode);или
  2. " Посоветуйте " функцию 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's a dotted continuation line. (TODO)
    ...)

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

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

(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. Блоки и выходы являются функциями Common 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 , экскурсии , сужение , перемещение поsexps , обработка ошибок , блоков и выходов .Наслаждайтесь!

3 голосов
/ 30 октября 2010

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

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

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

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

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

...