solovyov.net

Управление проектами в Емаксе

4 min read · emacs, software

EDIT: на дворе 2019 год и я давным-давно пользуюсь projectile, чего и вам советую. :)

У Емакса какое-то подобие проектов из коробки появилось только в 23 версии, и то очень простое - только переменные директорий (directory variables, что-то типа file variables, только хранящиеся в отдельном файле - .dir-locals.el). По большей части потому, что über-решения нету, и разным людям нравятся разные решения. Вот я и решил вкратце рассказать о том, что нравится мне.

Пару лет назад я прочитал статью в каком-то блоге об одном из подобных решений и попробовал его использовать. Как-то это решение (это был eproject, что ли) не прижилось и я пошëл искать альтернативы. Перепробовав какое-то количество, я взял project-root, прикрутив к нему несколько понравившихся фич от других проектов. Теперь можно сказать, что мне действительно удобно - использую эту штуку уже больше года и доволен. :)

Вся идея заключается в том, что вручную указывать путь к проекту (как это сделано в большинстве - если не всех - IDE) - не очень удобно. У большинства проектов есть какой-то характерный файл (или набор файлов) в корне. К примеру, manage.py у джанговых проектов, или директория .hg у проекта, который лежит в репозитории меркуриала. В результате можно определять проект по наличию таких файлов; моя текущая конфигурация выглядит примерно таким образом:

(setq project-roots
      `(("Django project"
         :root-contains-files ("manage.py")
         :filename-regex ,(regexify-ext-list '(py html css js sh))
         :exclude-paths '("contrib"))
        ("Sphinx documentation"
         :root-contains-files ("Makefile" "conf.py")
         :filename-regex ,(regexify-ext-list '(py rst))
         :exclude-paths '("_build"))
        ("Python project with buildout"
         :root-contains-files ("../../buildout.cfg")
         :filename-regex ,(regexify-ext-list '(py)))
        ("Generic Python project"
         :root-contains-files ("setup.py")
         :filename-regex ,(regexify-ext-list '(py)))
        ("Generic Mercurial project"
         :root-contains-files (".hg"))
        ("Generic git project"
         :root-contains-files (".git"))))

Интересно посмотреть на определение проекта с билдаутом - из-за того, что обычно расположение директорий там выглядит как

proj/
    buildout.cfg
    src/
        proj/

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

Что это даëт? Это даëт возможность project-root‘у определить, что он находится в проекте, и включает работу клëвого макроса with-project-root. В принципе он практически ничем не занимается, кроме как изменением текущей директории на корень проекта - но этого хватает, чтоб заработали предопределëнные команды, которые я повесил на кнопки вот так:

(global-set-key (kbd "C-c p f") 'project-root-find-file)
(global-set-key (kbd "C-c p g") 'project-root-grep)
(global-set-key (kbd "C-c p a") 'project-root-ack)
(global-set-key (kbd "C-c p d") 'project-root-goto-root)
(global-set-key (kbd "C-c p l") 'project-root-browse-seen-projects)

Наиболее часто используемая из них - find-file, которая показывает в минибуфере список файлов (в виде file\parentdir\parentdir - обратный путь к корню проекта), давая выбрать один из них с помощью ido-mode - т.е. вся вкуснотища типа flex matching’а работает1. :)

Отступлю от темы и вкратце расскажу, что такое ido. Это такой клëвый пакет в емаксе, который позволяет сильно облегчить работу в минибуфере своим автодополнением и удобным переходом между соседними значениями (он показывает сразу все возможные значения одновременно - и это удобно). Я его использую вместо обычного открытия файла, для перехода между буферами, и вот с той же целью он используется в project-root.

Не думаю, что нужно описывать grep или ack, которые просто запускают соответствующую программу от корня проекта - результаты запуска, как обычно, показываются в буфере со ссылками на найденные значения (так что можно легко перейти к нужному файлу/строке). goto-root тоже не делает ничего особенного, просто открывает dired в корне проекта.

А вот browse-seen-projects - очень интересная штука. Дело в том, что project-root-find-file при первом открытия проекта добавляет его в список проектов в файле ~/.emacs.d/.project-roots и сохраняет этот файл. А browse-seen-projects создаëт новый буфер в режиме org-mode и добавляет туда все проекты - в виде ссылок на их директории, конечно. ;) Так что можно довольно быстро и удобно прыгнуть к нужному проекту.

В основном функциональность этого кусочка кода я описал, еще информацию можно почерпнуть на страничке project-root’a и в его исходниках. Естественно, что до идеального состояния его можно точить и точить, так что патчи - велкам. :)


  1. надо только не забыть установить ido-enable-flex-matching в t↩︎

If you like what you read — subscribe to my Twitter, I always post links to new posts there. Or, in case you're an old school person longing for an ancient technology, put a link to my RSS feed in your feed reader (it's actually Atom feed, but who cares).

Other recent posts

Server-Sent Events (SSE), but with POST
ngrok for the wicked, or expose your ports comfortably
PostgreSQL collation
History Snapshotting in TwinSpark
Code streaming: hundred ounces of nuances