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

в 10:12, , рубрики: python

По результатам исследованиям работы программистов около 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 проектом, например такая фича как работа с системой управления версиями — она заслуживает отдельной статьи, что я и собираюсь сделать в [возможно ближайшем] будущем.

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

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


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

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

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

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

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

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

Чего я ожидаю от комментариев читателей:

  • Укажите мне что я забыл (или не знал), я добавлю.
  • Кто не ленивый сделать подобный обзор по 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 и
на микрофон на ноуте. Во первых громко слышны нажатия клавиш, во вторых звук тихий и иногда слышны посторонние шумы. Увы… Возможно я пересниму видео, использовав
гарнитуру, но пока что есть — то есть. Лучше так чем вообще без них, правда?

Автор: avkoval

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js