Я согласен с Аароном в отношении желательности вашего стилистического выбора, но, поскольку я также согласен с ним в том, что 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'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 , обработка ошибок , блоков и выходов .Наслаждайтесь!