Привет, хабражители!

Недавно, около года назад, я начал увлекаться емаксом. Спасибо за это товарищу по работе, который много чего рассказал и влюбил меня в емакс.

Но, не хватало мне очень хорошего и удобного менеджера буферов, я начал искать и нашел emacs-purpose.

Это очень удобная система построения своей конфигурации буферов и их расположения на странице. Что интересно, что она подразумевает что у каждого буфера есть предназначение и соответственно целевое место в твоем layoutе. На основании этого extensionа даже возможно сделать свой собственный ide в emacs очень легко. Итак, давайте рассмотрим несколько шагов по построению своей версии IDE используя этот движок.

Для затравки, вот то как выглядит мой интерфейс емакса.

image

Мы будем строить свою версию IDE в несколько шагов:

  1. построение своей структуры расположения буферов
  2. назначение каждому буферу списка режимов, которые будут отображаться в этом буфере.
  3. упрощение переключения между буферами.
  4. другие улучшения юзабилити для нашего IDE.

1. Построение своей структуры расположения буферов


Это очень просто!

В файле window-purpose-x.el нужно поменять определение переменной purpose-x-code1—window-layout.
В идеале это можно сделать также поместив layout отдельно в папку ~/.emacs.d/layouts/ и затем подгрузив layout следующей функцией.

(purpose-load-window-layout-file "~/.emacs.d/layouts/full-ide.window-layout")

но пока почему то это не работает, отправил Pull request с новым layout автору (разбираемся почему не работает).

Пока это не пофикшено в основном репозитории, то мне пришлось поправить window-purpose-x.el. Вообще судя по тому что я понял, там проблема с новыми типами purposeов, которые я ввожу в layout, проблема по всей видимости в функции:
pull request

Вот моя конфигурация, которая выглядит так (рисунок был сверху):

elisp код
(nil
    (0 0 152 35)
    (t
     (0 0 29 35)
     (:purpose dired :purpose-dedicated t :width 0.16 :height 0.5 :edges
               (0.0 0.0 0.19333333333333333 0.5))
     (:purpose buffers :purpose-dedicated t :width 0.16 :height 0.4722222222222222 :edges
               (0.0 0.5 0.19333333333333333 0.9722222222222222)))
    (t
     (29 0 125 35)
    (:purpose edit :purpose-dedicated t :width 0.6 :height 0.85 :edges
              (0.19333333333333333 0.0 0.8266666666666667 0.85))
    (:purpose misc :purpose-dedicated t :width 0.6 :height 0.1 :edges
              (0.19333333333333333 0.8722222222222222 0.8266666666666667 0.9722222222222222))
    )
    (t
     (125 0 152 35)
    (:purpose ilist :purpose-dedicated t :width 0.15333333333333332 :height 0.6 :edges
              (0.82666666666666667 0.0 0.9722222222222222 0.6))
    (:purpose todo :purpose-dedicated t :width 0.15333333333333332 :height 0.372222222 :edges
              (0.8266666666666667 0.6 0.9722222222222222 0.9722222222222222))

     )
    )

А вообще достаточно этот текст будет поместить в ~/.emacs.d/layouts/full-ide.window-layout и добавить такую вещь в наш init.el

(defun load-purpose-mode ()
  (interactive)
  (purpose-load-window-layout-file "~/.emacs.d/layouts/full-ide.window-layout")
  (purpose-x-code1-setup)
)
(global-set-key (kbd "M-L") 'load-purpose-mode)

по нажатию Alt+Shift+L у меня загружается эта конфигурация. Кстати, подсказка: можно сделать кучу конфигураций. Например, если вы переходите в режим анализа изменений в гите, то можно сделать конфигурацию специально для анализа состояния репозитория.

Постепенно мы будем дополнять нашу функцию load-purpose-mode другими вкусностями.

2. Назначение каждому буферу списка режимов, которые будут отображаться в этом буфере


Для своего IDE я определил следующим режимам отображаться в буферах с определенными purposами.
misc purpose todo purpose edit purpose
inferior-python-mode org-mode css-mode
python-inferior-mode yaml-mode
gdb-inferior-io-mode conf-unix-mode
fundamental-mode *magit*
compilation-mode
shell-mode
eshell-mode
term-mode
Сделать это очень просто (один из вариантов):

(add-to-list 'purpose-user-mode-purposes
             '(YOUR_MODE . PURPOSE))

Для magit все немного сложней (из документации emacs-purpose):

(defvar purpose-x-magit-single-conf
    (purpose-conf "magit-single"
      :regexp-purposes '(("^\\*magit" . misc)))
      "Configuration that gives each magit major mode the same purpose.")
(purpose-x-magit-single-on)

После всех ваших изменений не забудьте выполнить следующую команду в вашем init.el:

(purpose-compile-user-configuration)

Тогда ваши изменения применятся.

3. Упрощение переключения между буферами


Много буферов? Но вроде бы они разделены по назначениям, но как между ними переключаться? Благодаря этой библиотеке – это достаточно легко.

Вот мой список команд для переключения на буферы с разными назначениями:

elisp код
;; helper для получения первого буфера с определенным purpose. нужна для прямого переключения на нужный буфер. (ie, на специальный purpose буфер)
(defun get-only-one-buffer-with-purpose (purpose)
  "Get buffers wih purpose"
  (buffer-name (nth 0 (purpose-buffers-with-purpose purpose)))
  )
;; переключитья на dired buffer, к сожалению, на каждую открытую директорию создается буфер, это значит что по прошествии нескольких часов у вас может быть открыто очень много dired буферов. Здесь есть один недостаток. А может можно проще ? Я не нашел как переключить просто на активный буфер с конкретным purpose. Итак, ты сможешь выбрать на какой буфер переключиться
(define-key purpose-mode-map (kbd "C-c C-f")
  (lambda () (interactive) (purpose-switch-buffer-with-some-purpose 'dired))
  )
;; переключиться на буфер со списком открытых файлов. Здесь все просто супер. Он один. Поэтому нет проблемы.
(define-key purpose-mode-map (kbd "C-c C-l")
  (lambda () (interactive) (purpose-switch-buffer (get-only-one-buffer-with-purpose 'buffers)))
  )
;; переключиться на один из файлов для редактирования. Та же проблемы что и с dired
(define-key purpose-mode-map (kbd "C-c C-c")
  (lambda () (interactive) (purpose-switch-buffer-with-some-purpose 'edit))
  )
;; переключиться на буфер с списком определений (функций, классов и тд и тп) в открытом файле для редактирования. Здесь все просто супер. Он один. Поэтому нет проблемы.
(define-key purpose-mode-map (kbd "C-c C-d")
  (lambda () (interactive)  (purpose-switch-buffer (get-only-one-buffer-with-purpose 'ilist)))
  )
;; переключиться на буфер с списком todo вещей. Об этом расскажу чуть позже. Полезная вещь. И он тоже один
(define-key purpose-mode-map (kbd "C-c C-t")
  (lambda () (interactive)  (purpose-switch-buffer (get-only-one-buffer-with-purpose 'todo)))
  )

4. Другие улучшения юзабилити для нашего IDE


Лично для меня важно еще видеть список todo в проекте, напоминает о каких-то важных вещах.

Поэтому я сделал специально буфер с purpose todo. И открываю в нем файл написанный для org-mode, в котором содержится список todo конкретного проекта.
Генерируется этот файлик автоматически при активации window-purpose layoutа

В функцию load-purpose-mode (приводилась выше) в конец нужно добавить (todo-mode-get-buffer-create) и вот кусочек elispа отвечающего за этот процесс

elisp код
(defconst todo-mode-buffer-name "*CodeTodo*"
  "Name of the buffer that is used to display todo entries.")

;; когда процесс создания todo файла закончился, выполнится эта функция, фактически эта функция откроет файл в read only mode с списком @todo. Можно будет перейти к месту @todo и посмотреть что там за вопрос не решен.
(defun on-org-mode-todo-file-built (process event)
  (find-file (concat (getenv "PWD") "/todo.org"))
  (call-interactively 'read-only-mode)
  )

;; функция отвечающая за генерацию todo файла для org-mode
(defun build-org-mode-file-for-todo ()
  (start-process "Building todo things" "*CodeTodo*" "bash" "-ic" "source ~/.bashrc; collecttodotags")
  (set-process-sentinel (get-process "Building todo things") 'on-org-mode-todo-file-built)
  )

;; создание буффера если его еще нет, запуск функций приведенных выше
(defun todo-mode-get-buffer-create ()
    "Return the todo-mode buffer.
If it doesn't exist, create it."
    (or (get-buffer todo-mode-buffer-name)
        (let ((buffer (get-buffer-create todo-mode-buffer-name)))
          (with-current-buffer buffer
            (org-mode)
            (build-org-mode-file-for-todo)
            (pop-to-buffer todo-mode-buffer-name))
          buffer)))

Команда collectotodotags это алиас на башевскую команду:

alias collecttodotags="find `pwd` -type d \( -name .git -o -name myworkenv -o -name node_modules \) -prune -o -type f \( -name todo.org \) -prune -o -type f -print -exec grep -n '@todo' '{}' \; | create_org_mode_todo_file.py > ./todo.org"

Эта команда находит список всех файлов с упоминанием места, где есть todo в коде и посылает список этих файлов с todo на вход следующему питоновскому файлу:
github

Причем обратите внимание, как можно указать, что какое-то todo важнее другого
github

Чем @todooo имеет больше «o» в конце, тем оно важней. Тут не совсем уверен как лучше было бы сделать, может кто подскажет?

И да, последняя вкусность. Смысл misc purposа в том, чтобы отображать там все промежуточные вещи, поиски и тд и тп, но буфер маленький, поэтому я добавил следующие shortcuts:

(global-set-key "\C-c+" (lambda () (interactive) (enlarge-window +20)))
(global-set-key "\C-c_" (lambda () (interactive) (enlarge-window -20)))

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

И в целом как вам этот emacs extension?
Спасибо за внимание!

P.S. Если интересна тема, то могу продолжить и рассказать другие моменты касательно моего конфига emacsа
Поделиться с друзьями
-->

Комментарии (14)


  1. hamnsk
    18.07.2017 19:10
    +1

    Одно замечание: не кликабельный скриншот низкого разрешения, просто не читаем на ретине, и подобных дисплеях. Разглядеть на нем результат работы, или получить какую то затравку просто не реально. Исправьте пожалуйста. А лучше приложить ссылки на файлы в хорошем разрешении. Спасибо.


    1. sergeyglazyrin
      18.07.2017 21:24

      Добрый день!
      Спасибо за замечание.
      Добавил ссылку на скриншот. Сам скриншот нормального качества. Просто в публикации он сжимается в два раза. Поэтому не сильно разборчиво. Сделал скриншот кликабельным.
      Так лучше?


  1. Aries_ua
    18.07.2017 23:16

    Тема довольно интересна. Но есть несколько вопросов.

    1. Есть что либо что будет показывать не спикок файлов в директории, а дерево. Для мена оно реально наглядней.
    2. Есть что либо для управления проектами? Ну к примеру открыть список проектов, выбрать проект и далее с ним работать. Сейчас использую Атом и там есть плагин для списка проектов.

    Пишити еще, актуально.


    1. sergeyglazyrin
      19.07.2017 01:59

      Спасибо за мысли.
      1. Да, вы абсолютно правы насчет проектов, я тоже задумался об этом во время написания статьи.
      На сегодняшний момент я просто открываю в нужной папке свою консоль и запускаю оттуда емакс, что конечно же не комильфо. Так что планирую скоро заняться интеграцией туда чего то для поддержки проектов.

      2. По поводу списка файлов как дерева. Хм, мне с трудом представляется как это сделать. И тут проблема даже не техническая, а скорей проблема маленького размера буфера для dired мода. Я не работал к сожалению на емаксе на мониторе 21 дюйм, работаю на 17 дюймовом ноуте и мне как то кажется, что мое кол-во места, которое я выделил этому буферу ничтожно мало для отображения дерева, но попробую что то придумать, стало интересно.


      1. balamyt92
        19.07.2017 10:33

        По поводу проектов хочу посоветовать проджектайл. Я не эмаксер но когда я пробовал, мне понравилось. Вообще хочу посоветовать глянуть на http://spacemacs.org/ как кладезь хороших решений.


    1. Crandel
      19.07.2017 08:34

      Для дерева файлов отлично подойдет neotree
      По поводу проектов — Projectile просто незаменимая вещь, вот демка для наглядности.
      Ну если вас еще что-то заинтересует — вот мои конфиги


      1. emacsway
        22.07.2017 15:18

        Еще как вариант speedbar, он не только файлы отображает, но и дерево содержимого самого файла.


        1. Crandel
          24.07.2017 07:37

          Для меня не подошел, я в терминале работаю


          1. emacsway
            25.07.2017 03:35

            Он работает везде. Я тоже в терминале работаю…


            1. Crandel
              28.07.2017 13:24

              Мне он показался неудобным


  1. Sirikid
    19.07.2017 03:01

    Документацию к функциям лучше поместить в "документационную строку"; закрывающие скобки переносить не принято, особенно помогают забыть эту вредную привычку режимы для структурного редактирования, например paredit (какой-то пост с гифками, видео).


  1. x33x
    19.07.2017 11:05

    Отличное расширение! Возьму себе на вооружение. Спасибо автору!


  1. evgenWebm
    19.07.2017 16:45

    Интерфейс очень смахивает на PhpStorm.


  1. emacsway
    22.07.2017 11:39

    sergeyglazyrin, спасибо за статью, конечно, пиши еще, актуально.