Я новичок emacs, я хочу, чтобы emacs мог отступать от моего кода, подобного этому
egg = spam.foooooo('vivivivivivivivivi')\
.foooooo('emacs', 'emacs', 'emacs', 'emacs')
Это невозможно сделать автоматически по умолчанию (без ручной вставки пробелов или C-c > ), так как emacs всегда отступывает 4 пробела (если Im не разбивает несколько аргументов на несколько строк).
Какой лучший подход для этого?
PS: Если это плохая идея (против PEP 8 или что-то еще), пожалуйста, скажите мне
Я согласен с Аароном в вопросе о желательности вашего стилистического выбора, но, поскольку я также согласен с ним в том, что 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, обработка ошибок, блоки и выходы. Наслаждайтесь!
Это довольно уродливо и потребует от вас написать emacs lisp. Мне нужно изучить emacs lisp, поэтому, если бы это было не так безобразно, я, вероятно, воспользовался бы этим. Но это так, а я нет. Похоже, вы узнаете emacs lisp:) (если вы действительно хотите это сделать). Я как бы ревнив. Во всяком случае, вы сказали, что информирование вас о том, что это плохая идея, было приемлемым ответом, так вот:
Это страшный стилистический выбор. Не
egg = spam.foo('viviviv')
egg = egg.foo('emacs', 'emacs', 'emacs')
легче читать?
В отличие от PEP 8 не упоминается, что использование символа продолжения строки должно быть сведено к минимуму. Кроме того, это наиболее окончательно и объективно противоречит духу PEP 8. Я просто не уверен, как;)
spam.fooooo(…)
в круглые скобки:egg = (spam.fooo…)
. Таким образом, вам не нужен знак продолжения (среди прочих преимуществ).(
или после одного из,
(и в любом случае вам не придется использовать `` из-за автоматического продолжения скобок).