Pull to refresh

Emacs и Python (статья 2 из цикла)

Reading time10 min
Views20K
По результатам исследованиям работы программистов около 20% времени тратиться на непосредственное написание кода и около 80% времени — на просмотр старого, его анализ. Исходя из данной предпосылки текстовый редактор в первую очередь должен предоставить удобные средства навигации по коду. Большая часть описанных фич как раз имеет дело с навигацией и анализом.

В данной статье я постараюсь продемонстрировать максимум удобных в ежедневном применении возможностей редактора Emacs для языка Python, и более конкретно для редактирования Django проектов.

Как говорится в пословице: «обещанного три года ждут». Хотя три года и не прошло, но уже изрядно много с моей предыдущей статьи «Emacs для начинающих: введение». Я не буду «растекаться мысью по древу» а постараюсь максимально кратко показать как работают различные фичи Emacs в применении к Python.



Формат статьи:
  • описание фичи
  • пример как её включить в Вашем конфиге
  • и сопутствующая видео демонстрация работы. Warning: качество звука на всех видео так себе. Исправлю, но позже...


Для тех же, кому интересно применить рецепты целиком — мой конфиг доступен (Mercurial) тут, а здесь: рецепт по его примененнию.

Содержание




1. Интегрированная документация




Довольно удобная фича. В любом месте если забылось какие то детали по документации можно вызвать один из вариантов показа документации:

  • pylookup — индексированный Sphinx вариант для стандартной библиотеки Python
  • pydoc — встроенная документация из объекта (посредством ropemacs)


Показываю данные фичи по отдельности так как они независимы друг от друга.

1.1. Rope pydoc




Рецепт включения: по рецепту включения rope отдельныйи разговор и описан в:


После включения rope начинают работать его keybindings и меню:
  • В меню данная функция доступна через: [Rope]->[Shown documentation]
  • Keybinding: C-c d




1.2. Pylookup — индекс документации по стандартной библиотеке Python




Пакет ставится отсюда: https://github.com/tsgates/pylookup. У него две активные составляющие: pylookup.el размещаем в директорую, читаемую emacs для загрузки Lisp, а pylookup.py — куда нибудь, где она будет доступна по пути для пользователя.

Индексируется так:
./pylookup.py -d /var/db/pylookup/pylookup.db -u /usr/share/doc/python-docs-2.*/html


В emacs настраиваем загрузку модуля и удобную клавишу для вызова. У меня настроено
по клавишам Control+Shift+Menu.

Включается так (см также: cfg_pylookup.el):

(eval-when-compile (require 'pylookup))
(setq pylookup-program "/usr/local/bin/pylookup.py")
(setq pylookup-db-file "/var/db/pylookup/pylookup.db")
(global-set-key [(control shift menu)] 'pylookup-lookup)




1.2.1. Для индексации также Django документации:




Устанавливаем django-docs:

cd ~/
svn co http://code.djangoproject.com/svn/django/trunk/docs/ django-docs
cd django-docs
make html
cd _build/html
ln -s genindex.html genindex-all.html


А команду сверху дополняем:

./pylookup.py -d /var/db/pylookup/pylookup.db -u /usr/share/doc/python-docs-2.*/html -u ~/django-docs/_build/html/




2. Python дебаггер: pdb




Есть замечательный модуль pdbtrack который «следит» за передвижениями по коду программы во время отладки по клавишам n,s и другим. Да и кстати, умеючи работать с pdb можно достичь большего чем в аналогичных визуальных средствах отладки.

pdbtrack включается автоматически с включением главного режима python-mode.

Для того чтобы он вызвался в emacs, надо поставить строку типа:
import pdb; pdb.set_trace() в Вашем коде. Выполняемая программа должна обязательно выполняться в emacs/shell.



3. Rgrep




rgrep — быстрый поиск строки по всем файлам с определённым расширением. Тут стоит заметить что начиная с версии emacs 23.x разработчики что то поменяли в интерфейсе вызова и теперь разделить расширения пробелом, как например [*.html *.py] стало невозможно, если не знать конечно обходного пути:
чтобы ввести пробел в rgrep — экранируйте оный клавишей C-q

rgrep удивительно быстро работает, и даже в нашем самом большом проекте с десятками тысяч файлов он находит искомое за доли секунды, что немаловажно для часто выполняемых поисков.

У меня rgrep привязан на клавишу: C-f7



4. Occur




occur показывает вхождение данной строки в текущем файле. Для того, чтобы его было удобно вызывать я сделал макрос, который автоматически выделяет текущее слово и далее вызывает occor по нему. У меня данная функция привязана на клавишу: C-z o



5. Блоки в Emacs




Блоки бывают:
  • обычные
  • квадратные


Также блоки можно запоминить в разные регистры, соответственно можно работать с разными блоками одновременно.



5.1. Множественные блоки



Да. Применяется не очень часто, но когда применяется, экономит Вам массу времени, которое было бы потрачено на гуляние вверх-вних по klipper/parcellite или и того хуже (если у вас один буфер обмена).

Клавиши:

  • C-x r s char — запомнить блок в именованный буфер symbol, например C-x rs1 — запомнить в блок по имени 1.
  • C-x r i char — вставить из именованного блока


5.1. Квадратные блоки



5.1.1. Визуальные квадратные блоки

Визуальный режим включается кнопкой C-enter. Появляется прямоугольая область в которой можно заполнять текст, удалять,
или копировать весь прямоугольник. В данном режиме работают все станратные кнопки работы с блоком — Alt+Y чтобы запомнить
блок и Ctrl+Y чтобы вставить.

5.1.2. Невизуальные квадратные блоки


Есть второй вариант квадратных блоков, несколько урезанный, но работающий для терминального режима. Работает так: начитаете
выделять блок как обычно (C-space) но при этом работа с блоком по клавишам:

  • C-x r r char — запомнить прямоугольный блок в именованный регистр
  • C-x r i char — вставить из регистра


5.2. Просмотр kill ring


Некоторой удобной заменой именованного блока является встроенный в emacs аналог klipper, под названиам killring:

(require 'browse-kill-ring)
(global-set-key (kbd "C-c k") 'browse-kill-ring)


5.3. Запутанная ситуация с блоками и клавишами копирования



Стоит заметить что ситуацию с блокам и привязками клавиш к операциям запутана по нескольким объективным, историческим
причинам:

  • Знакомые всем Windows кнопки для работы с блоками C-c C-v не работают по умолчанию
  • В xorg существует два буфера обмена: главный(primary) и второстепенный(secondary)
  • Klipper, Parcellite также занимаясь управлением выделениями часто меняют последовательность, добавляя бардака ко всему
  • Emacs поменял процедуры работы с xorg primary/secondary selection в своей текущей версии 0.24


В результате часто даже такая банальная вещь как копирование блоками становиться сложным для освоения этапом в изучении
emacs.

Подливает воды в огонь также то, что в xorg True way для копирования блоков: Control Insert чтобы скопировать и Shift Insert
чтобы встатить блок. При этом мышь копирует в один буфер обмена а клавиатурные выделения попадают в другой.

Для начинающих я часто советую осваивать сразу «True way» и не пытаться включить так называемый cua-mode, призванный освободить C-c C-v от
стандартный emacs key bindinds, лишая конечного пользвателя массы удобного функционала. Если уж изучать emacs, то выучить
две дополнительные клавиши:
  • Alt-w чтобы запомнить блок
  • C-w чтобы его вставить


не является супер проблемой. Для тех, для кого является — включайте смело Cua-mode и работайте дальше, но скорее всего Вы всё равно
вернётесь к теме и вернёте стандартные клавиши, когда почувствуете что не хватает удобных клавиатурных комбинаций клавиш.

6. Yasnippet — автоматизация ввода с помошью snippets




Yasnippet — это удобный способ автоматизировать ввод частых, но сложных для запоминания блоков текста.



Ставится как независимый emacs пакет, обычно доступен в репозитории вашей системы.

Конфигурируется как:

(add-to-list 'load-path "/usr/share/emacs/site-lisp/yasnippet")
(autoload 'yas/initialize "yasnippet" "Do necessary initialization.")
(autoload 'yas/load-directory "yasnippet"
  "Load snippet definition from a directory hierarchy." t)
(require 'yasnippet) ;; not yasnippet-bundle
(yas/initialize)
(yas/load-directory "/usr/share/emacs/etc/yasnippet/snippets")
(yas/load-directory "~/.emacs.d/yasnippets/")
(setq hippie-expand-try-functions-list
      (cons 'yas/hippie-try-expand hippie-expand-try-functions-list))
 
(global-set-key [(\t)] 'indent-for-tab-command)
(setq yas/trigger-key (kbd "M-n"))


7. Навигация по коду python




7.1. Вверх-вниз по функциям


Клавиши:
  • Alt+Down — на функцию(класс) вниз
  • Alt+Up — на функцию(класс) вверх
  • C-c a — в начало класса
  • C-c e — в конец класса




Конфигурация:
(defun py-to-start-of-class()
  (interactive)
  (py-beginning-of-def-or-class 'class)
)
 
(defun py-to-end-of-class()
  (interactive)
  (py-end-of-def-or-class 'class)
)
 
(add-hook 'python-mode-hook
               '(lambda ()
  (local-set-key [(s menu)] 'rope-code-assist)
  (local-set-key [(s up)] 'python-move-to-start-of-class)
  (local-set-key [(s down)] 'python-move-to-end-of-class)
  (local-set-key [(meta down)] 'py-end-of-def-or-class)
  (local-set-key [(meta up)] 'py-beginning-of-def-or-class)
  (local-set-key (kbd "C-c C-a") 'py-to-start-of-class)
  (local-set-key (kbd "C-c C-e") 'py-to-end-of-class)
  (local-set-key (kbd "s-q") 'py-shift-region-left)
  (local-set-key (kbd "s-w") 'py-shift-region-right)
  )
       )


7.2. По функциям и классам текущего файла через IM-python




Клавиши (работает не только в pyhon, а вообще много где):
  • C-c v — показать список определений в текущем файле


Определение смотреть тут: idomenu.el



7.3. По функциям и классам текущего файла через Speedbar




настроено у меня по клавише Scroll_Lock:
(global-set-key [Scroll_Lock] 'speedbar)




7.4. Переход в место определения переменной[класса, метода]




Настроено через Rope и bookmark на клавишах:
  • Alt+Enter — ерейти в место определения
  • Alt+Shift+Enter — ернуться назад




Недостатком настройки является то, запись истории переходов глобальна и каждый переход оставляет мусор в bookmarks,
надо будет переписать этот момент на Lisp с массивом. Но пока работает и так.

(defun rope-goto-definition-save-place ()
   """ save current place as 'save-place' bookmark and rope-goto-definition """
   (interactive)
   (bookmark-set "save-place" 1)
   (rope-goto-definition)
)
 
(defun rope-return ()
   """ save current place as 'save-place' bookmark and rope-goto-definition """
   (interactive)
   (bookmark-jump "save-place")
)
 
(global-set-key [(M return)] 'rope-goto-definition-save-place)
(global-set-key [(M shift return)] 'rope-return)


7.5. Bookmarks



  • C-z b — поставить ссылку
  • C-z Up — перейти на ссылку выше (в текущем файле)
  • C-z Down — перейти на ссылку ниже (в текущем файле)
  • C-z space — перейти на глобальный список ссылок




(require 'bm)

(global-set-key (kbd "C-z b") 'bm-toggle)
(global-set-key (kbd "C-z <up>") 'bm-previous)
(global-set-key (kbd "C-z C-p") 'bm-previous)

;(global-set-key [(control shift down)] 'bm-next)
;(global-set-key [(control shift n)] 'bm-next)
(global-set-key (kbd "C-z <down>") 'bm-next)
(global-set-key (kbd "C-z C-n") 'bm-next)
(global-set-key (kbd "C-z <SPC>") 'bm-show-all)


7.6. Поиск файла по шаблону имени



Работает в любом режиме, не относиться напрямую к Python, просто удобная функция. Вызывается по:
  • C-S-f — поиск файла по имени


(global-set-key [(control shift f)] 'find-name-dired)




7.7. Открыть файл проекта: rope-file-find




Очень удобно, особенно для того чтобы открыть связанный файл, например template файл для view в Django.
  • C-x p f — ope-find-file — найти файл посредством пакета Rope




8. Работа с текстом python


8.1. Python Ident влево/вправо




  • s-q, C-c <, C-c C-l — двинуть блок влево
  • s-w, C-c >, C-c C-r — двинуть блок вправо




8.2. Борьба с пустым текстом



  • C-z w d — брать пустой текст




(define-key global-map  "C-zws"         'show-trailing-whitespace)
(define-key global-map  "C-zwh"         'hide-trailing-whitespace)
(define-key global-map  "C-zwd"         'delete-trailing-whitespace)


8.3. Визуализация превышения длинны строки




Устанавливается:
(make-face 'mode-line-80col-face)




8.4. Автодополнения




8.4.1. Автодополнения Rope




Начинает работать с момента установки Rope.



8.4.2. Автодополнения hippie-expand




<span style="color: #66cc66;">(</span>global-set-key <span style="color: #ff0000;">"M- "</span> 'hippie-expand<span style="color: #66cc66;">)</span>
 




8.5. Комментарии в тексте




  • C-S-z, C-# — омментировать или раскомментировать выделенный блок кода




(global-set-key [(control \#)] 'comment-or-uncomment-region)
(global-set-key [(control shift z)] 'comment-or-uncomment-region)


; hippie expand
(global-set-key "\M- " 'hippie-expand)


9. Тестирование качества кода




9.1. интеграция с Flymake: pyflymake




Для поддержки интеграции с flymake необходимо будет установить pyflakes, pylint пакеты и настроить их расположение в вашей
копии файла:

pyflymake.py
А также установить вот этот Lisp код:
cfg_flymake.el



К сожалению, даже после массы переработок данное решение не является идеальным. Иногда flymake пишет _flymake файлы не к месту на
сетевых сервисах, иногда просто некорректно отрабатывает. Но по сути в 98% случаев он очень полезен так как позволяет
раньше обнаружить сделанную ошибку.

9.2. вызов pep8




Модуль устанавливается в систему: pep8.



; pep8
(require 'python-pep8)
(global-set-key (kbd "C-c p 8") 'pep8)


9.3. вызов pylint




Аналогично. Вначале устанавливаем системный пакет pylint а далее уже:

; pylint
(require 'python-pylint)
(global-set-key (kbd "C-c p l") 'pylint)




10. Django специфика




1) Запускать проект необходимо из django-shell — чтобы был доступен pdb.
2) Есть несколько разных модулей показа синтаксиса django templates.



11. Режим компиляции для python и быстрый запуск



В любом месте программу на python, если это отдельно стоящий скрипт можно запустить через клавишу C-c C-c.
При генерации ошибки в таком случае курсор станет на место ошибки.

Вариант номер два: запускать программу из опции compile. В моём конфиге она настроена как:

(global-set-key [C-f9] 'compile)


В случае использования функции compile можно будет использовать переход по ошибкам по F8/Shift-F8.

В обоих вариантах вызов pdb работать не будет т к программы запускаются без связки с терминалом.



12. В завершение




Что не вошло в эту статью



Я постарался описать наиболее связанные с редактированием Python файлов фичи. Многие не вошли в этот обзор, хотя относяться напрямую к работе с Python проектом, например такая фича как работа с системой управления версиями — она заслуживает отдельной статьи, что я и собираюсь сделать в [возможно ближайшем] будущем.

Статья не закончена, по мере поступления комментариев я обязательно внесу изменения и дополнения.

Чего я ожидаю от комментариев читателей:
  • Укажите мне что я забыл (или не знал), я добавлю.
  • Кто не ленивый сделать подобный обзор по vim, Sublime, и тп? В первую очередь интересуют фичи, описанные тут, и те [полезные] фичи, которые отсутствуют в Emacs но присутствуют где то ещё.


P.S. Качество звука подкачало, писал с помощью ffmpeg -vf crop=970:505:7:15 -f alsa -i hw:0 -f x11grab -r 25 -s 1680x1050 -i :0.0 -s 1280x720 -vcodec libx264 -vpre lossless_ultrafast filename.avi и
на микрофон на ноуте. Во первых громко слышны нажатия клавиш, во вторых звук тихий и иногда слышны посторонние шумы. Увы… Возможно я пересниму видео, использовав
гарнитуру, но пока что есть — то есть. Лучше так чем вообще без них, правда?
Tags:
Hubs:
Total votes 27: ↑26 and ↓1+25
Comments19

Articles