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

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

    Подробнее
    Реклама
    Комментарии 19
    • 0
      блин, круто, а я что-то на Sublime Text 2 подсел, со всеми наворотами для питона
      надо будет ещё и emacs опять установить
      • 0
        та да, мне тоже очень нравиться Sublime. денег на лицензию не жалко, жалко будет если что нибудь случиться с автором либо процессом разработки… если бы у него была хотя бы double license, пользовался бы с удовольствием, и допиливал бы на Python plugins тот функционал, к которому привык в emacs. Но, учитывая полное отсутствие сорцов в Sublime меня напрягает тратить свое время на него — я уже несколько раз в жизни обжигался, инвестировав своё время в чужой продукт. По функционалу Sublime мне наверное ближе всего к тому, что здесь описано. Vim правда тоже умеет много чего и местами больше. Остальные редакторы недотягивают. По средам лучшая коммерческая альтернатива emacs/python: PyCharm.
        • 0
          согласен, но тут такое дело — не будет Sublime, будет emacs :-)
          emacs собственно всё то же самое умеет

          пожалуй, единственной killer-фичей для меня в эклипсе был отладчик, но с тех пор как я предпочёл использовать логгирование, необходимость в нём отпала — так что IDE мне не нужна, только быстрый и фичастый редактор
      • +1
        Ёп… онский городовой… счастья то сколько. Круто.
        Нет, правда — очень круто, спасибо! :)
        • 0
          Спасибо за огромнейший труд. Ряд моментов не знал, очень познавательно.

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


          Я правильно понимаю, что сейчас Emacs остался единственным текстовым редактором, который не может просто рисовать вертикальную черту после 80-го символа и вынужден расскрашивать «хвосты» вываливающегося текста?
          • +1
            Да. Нет в той модели UI, что применена в emacs возможности нарисовать вертикальную черту.

            Те поделки на Lisp что есть существенно тормозят, сам же engine не предоставляет такого интерфейса. Впрочем я не сильно переживаю, это из разряда фич «было бы неплохо». Сейчас всё равно идёт «общая» проверка синтаксиса в т ч и на long lines так что функционально всё есть, просто по другому, менее визуально.
            • +1
              Вообще — column-marker.el

              Автор предлагает другой подход, имхо, потому что его способ позволяет сразу вычленить строки с превышением в уже готовом коде.
              • +1
                (require 'fill-column-indicator)
                (add-hook 'python-mode-hook 'fci-mode)
                

                www.emacswiki.org/FillColumnIndicator
                Рисует вполне себе вертикальную черту. Говорят что даже в консольном режиме работает.
                • 0
                  Не рисует. Она рисует черту только там, где текста нет :). А где текст пересекает указанную отметку она никак не рисут — поэтому пользоваться очень неудобно — черта частично есть, а частично нет :(.
                  • 0
                    Даже не представлял что это может быть проблемой)
                    Но вообще я очень активно использую flymake, так что 80+ строки у меня так и так подсвечиваются фоном.
                • 0
                  Vim тоже не умеет. По крайней мере, я не нашёл, как это можно сделать, приходится хвосты красить.
              • 0
                >8.3. Визуализация превышения длинны строки
                whitespace-mode же (входит в поставку emacs'а)
                (setq whitespace-style '(face empty tabs lines-tail trailing))
                (setq whitespace-line-column 80)
                
                (add-hook 'prog-mode-hook
                  (lambda ()
                    (whitespace-mode)))
                


                Для линуксоидов, использующих гном3 советую поставить Emacs Manager, отлично решает проблемы с виртуальными окружениями.
                • 0
                  В целом — подборка интересная.

                  Несколько замечаний:
                  Alt+Y чтобы запомнить блок и Ctrl+Y чтобы вставить
                  M-y «прокручивает» элементы kill-ring'а на месте только что сделанной вставки, а не «запоминает блок».

                  C-w чтобы его вставить
                  Вставка — C-y. C-w — аналог cut, тогда как M-w — аналог copy.

                  Для начинающих я часто советую осваивать сразу «True way» и не пытаться включить так называемый cua-mode
                  и при этом
                  Визуальный режим включается кнопкой C-enter.
                  C-return включается как раз-таки в cua-mode. А про cua-enable-cua-keys в статье нет, чтобы включить C-return и выключить подмену C-x, C-c.

                  Вообще странно, что работа с окошками описана в предыдущей статье «для начинающих», а совершенно необходимый copy/paste почему-то в специализированной «для Python». Мне кажется, что целевая аудитория статьи с regions работать уже умеет, а значит, этот раздел вообще в статье лишний. Особенно про rectangles. Я, может, как-то неправильно редактирую тексты программ, но они (rectangles) мне за 15 лет ни разу в этой области не пригодились. Даже не то, что я легко заменял их привычными regions, а даже мысли не возникало: «а вот это с rectangles получилось бы проще».
                  • 0
                    К сожалению, даже после массы переработок данное решение не является идеальным. Иногда flymake пишет _flymake файлы не к месту на сетевых сервисах, иногда просто некорректно отрабатывает.
                    Тоже с этим мучался.
                    Вылечил установкой новой версии 0.4.12 и следующим кодом в конфиге:

                    ;; python-flymake
                    (require 'flymake)
                    (add-hook 'find-file-hook 'flymake-find-file-hook)
                    (setq flymake-run-in-place nil)
                    (setq flymake-number-of-errors-to-display nil)
                    
                    (defun flymake-python-init ()
                    	(let* ((temp-file (flymake-init-create-temp-buffer-copy
                    					   'flymake-create-temp-copy))
                    		   (local-file (file-relative-name
                    						temp-file
                    						(file-name-directory buffer-file-name))))
                    	  (list "~/.emacs.d/bin/pycheckers"  (list local-file))))
                    

                    Оно создает копию файла не рядом, а во временной директории, даже при редактировании через tramp

                    Скажите, а вы package используете для установки плагинов или вручную все выкачиваете?
                    • 0
                      на emacs 24 только недавно перешёл, не переходил на package, но возможно попробую на новом компе.
                      В Gentoo очень хорошая система управления пакетами, там 95% emacs-packages есть, ставяться легко, потому и особой необходимости не назрело.
                    • +1
                      Еще бы такую статью о emacs-c++, и больше ничего не надо. :)
                      • 0
                        Хочу заметить, проверка pep8 подключается через flymake, что позволяет показывать ошибки в процессе редактирования. Так же, откопал возможность нормальной интеграции emacs с unittest'ами, помахал ручкой еклипсу и перешел на емакс.
                        Как все это интегрируется можно посмотреть тут. Или использовать целиком, мне подошло не все.

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