Pull to refresh

Настройка Emacs для последователей Dlang

Продвинутому в мире программирования человеку, коим скорее всего являешься ты, хабравчанин, пояснять, что такое язык D, нет смысла — о нем, хоть краем уха, но слышать был должен.

Dlang — это настоящее благословение для бородатых мальчиков-программистов C++. И эта статья предназначена как раз им, программистам, открывшим для себя этот язык, но вставших на перепутье тернистого выбора той самой IDE, посредством которой они будут познавать как «ходить» без костылей.

Я предпринимал несколько попыток пересесть на Emacs и, наконец, это случилось.

Ни для кого не секрет, что с IDE для Dlang всё очень туго.

  • MonoD
  • VisualD
  • Плагин для Eclipse
  • CodeBlocks
  • были плагины для QtCreator (вроде они перестали работать с последним QtCreator)
  • Coedit
  • плагин для SublimeText
  • DlangIde
  • ...

Из перечисленного в списке я пробовал всё, кроме VisualD, но каждый из них _какбе_намекает_: «Используй emacs». Отмечу, что DlangIde радует тем, что она написана на языке D, и можно участвовать в разработке, используя родной Dlang.

Так как я слишком привык к автодополнению кода, то этот пункт для меня основополагающий. Многие, казалось бы, умеют реализовывать его, но как-то не убедительно.

Итак, рецепт init.el


Первым делом хорошо было бы подключить пакетный менеджер. Он сам установит все необходимые плагины и т.п., если в cfg-var:packages добавить необходимый пакет.

;; ========== Автоустановка пакетов
(require 'cl) ;; common lisp
(require 'package) ;; пакетный менеджер

(defvar cfg-var:packages '(
    d-mode ;; подсветка Dlang
	ac-dcd ;; автодополнение Dlang
    nav    ;; навигация по файловой системе
    auto-complete ;; общее автодополнение
    flycheck ;; проверка синтаксиса
    autopair ;; авто скобки
    ))

(defun cfg:install-packages ()
    (let ((pkgs (remove-if #'package-installed-p cfg-var:packages)))
        (when pkgs
            (message "%s" "Emacs refresh packages database...")
            (package-refresh-contents)
            (message "%s" " done.")
            (dolist (p cfg-var:packages)
                (package-install p)))))

(add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/") t)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)
(add-to-list 'package-archives '("melpa-stable" . "http://stable.melpa.org/packages/") t)
(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/") t)
(package-initialize)
(cfg:install-packages)

Настройка пакета d-mode.


Основное — это конечно подсветка синтаксиса. Для удобной компиляции я написал функции rdmd() и dub(). Их можно привязать к клавишам f5, f6 только для режима d-mode.

(require 'd-mode)
(add-to-list 'auto-mode-alist '("\\.d\\'" . d-mode)) ; автозапуск d-mode для файлов .d

(defun rdmd() ;; запуск rdmd для текущего файла
  "Run rdmd for current file.d with unittests"
  (interactive)
  
  (setq rdmd-cmd (concat "rdmd -unittest " buffer-file-name))
  (save-buffer)
  (async-shell-command rdmd-cmd)
  )

(defun dub() ;; запуск dub для текущего проекта
  "Run dub for current project" ;; от ткущего файла спускается в низ по директориям 
  (interactive)                 ;; пока не найдёт dub.json,
  (save-buffer)                 ;; но не дальше 6ти директорий
  (setq str "./")
  (while (eq (file-exists-p (concat str "dub.json")) (eq str "./../../../../../../"))
    (setq str(concat str "../")))
  (setq str (concat "cd " str " && dub"))
  (async-shell-command str)
)

(define-key d-mode-map (kbd "<f6>") 'rdmd) ;; привязка клавиш к функциям
(define-key d-mode-map (kbd "<f5>") 'dub)  ;; rdmd и dub

;; отключаем вопросы при выходе по поводу запущенного dcd-server
(defadvice save-buffers-kill-emacs (around no-query-kill-emacs activate)
  "Prevent annoying \"Active processes exist\" query when you quit Emacs."
  (cl-letf (((symbol-function #'process-list) (lambda ())))
    ad-do-it))

То самое автодополнение.


За автодополнение отвечает плагин ac-dcd. Для работы плагина необходим сервер DCD в видимости системной переменной PATH. DCD можно собрать из исходников или установить из репозитория вашей системы.

;; ========== ac-dcd
(require 'ac-dcd)
(add-hook 'd-mode-hook
  (lambda ()
      (auto-complete-mode t)
      (when (featurep 'yasnippet) (yas-minor-mode-on))
      (ac-dcd-maybe-start-server)
      (ac-dcd-add-imports)
      (autopair-mode t)
      (ac-dcd--find-all-project-imports)
      (add-to-list 'ac-sources 'ac-source-dcd)
      (define-key d-mode-map (kbd "C-c ?") 'ac-dcd-show-ddoc-with-buffer)
      (define-key d-mode-map (kbd "C-c .") 'ac-dcd-goto-definition)
      (define-key d-mode-map (kbd "C-c ,") 'ac-dcd-goto-def-pop-marker)
      (define-key d-mode-map (kbd "C-c s") 'ac-dcd-search-symbol)          
	  
      (when (featurep 'popwin)
          (add-to-list 'popwin:special-display-config
                     `(,ac-dcd-error-buffer-name :noselect t))
          (add-to-list 'popwin:special-display-config
                     `(,ac-dcd-document-buffer-name :position right :width 80))
          (add-to-list 'popwin:special-display-config
                     `(,ac-dcd-search-symbol-buffer-name :position bottom :width 5)))))

Это минимум для работы.

Для наглядности покажу свой файл init.el полностью


;; ========== Цветовая схема
(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(ansi-color-faces-vector
   [default default default italic underline success warning error])
 '(custom-enabled-themes (quote (wombat))))
(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 )


;; ========== Автоустановка пакетов
(require 'cl) ;; common lisp
(require 'package) ;; пакетный менеджер

(defvar cfg-var:packages '(
    d-mode ;; подсветка Dlang
	ac-dcd ;; автодополнение Dlang
    nav    ;; навигация по файловой системе
    auto-complete ;; общее автодополнение
    flycheck ;; проверка синтаксиса
    autopair ;; авто скобки
    ))

(defun cfg:install-packages ()
    (let ((pkgs (remove-if #'package-installed-p cfg-var:packages)))
        (when pkgs
            (message "%s" "Emacs refresh packages database...")
            (package-refresh-contents)
            (message "%s" " done.")
            (dolist (p cfg-var:packages)
                (package-install p)))))

(add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/") t)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)
(add-to-list 'package-archives '("melpa-stable" . "http://stable.melpa.org/packages/") t)
(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/") t)
(package-initialize)
(cfg:install-packages)

;; ========== Хоткеи на русской раскладке
    ;; должна быть еще строчка в конце файла
(defun cfg:reverse-input-method (input-method)
  "Build the reverse mapping of single letters from INPUT-METHOD."
  (interactive
   (list (read-input-method-name "Use input method (default current): ")))
  (if (and input-method (symbolp input-method))
      (setq input-method (symbol-name input-method)))
  (let ((current current-input-method)
        (modifiers '(nil (control) (meta) (control meta))))
    (when input-method
      (activate-input-method input-method))
    (when (and current-input-method quail-keyboard-layout)
      (dolist (map (cdr (quail-map)))
        (let* ((to (car map))
               (from (quail-get-translation
                      (cadr map) (char-to-string to) 1)))
          (when (and (characterp from) (characterp to))
            (dolist (mod modifiers)
              (define-key local-function-key-map
                (vector (append mod (list from)))
                (vector (append mod (list to)))))))))
    (when input-method
      (activate-input-method current))))

 ;; ========== Общие нестройки
(server-start) ;; запуск в режиме сервера
(cua-mode t) ;; работа с буфером обмена по-человечески
(defalias 'yes-or-no-p 'y-or-n-p) ;; укорачиваем вопросы

;(set user-full-name "Vasya-Pupkin") ;; привязываем личность
;(set user-mail-address "pupkincore@mail.ru")

(setq inhibit-splash-screen t) ;; прячем экран приветсвия
(setq inhibit-startup-message t) ;; его можно вызвать C-h C-a

(show-paren-mode t) ;; выделяем выражения в скобках
(setq show-paren-style 'expression) ;; выделяем цветом

(delete-selection-mode t) ;; удаляем выделенный текст, когда пишем поверх

(setq make-backup-files nil) ;; отключаем автоматические сохранения
(setq auto-save-default nil)
(setq auto-save-list-file-name nil)

(require 'linum) ;; модуль нумерации строк
(line-number-mode t) ;; показывает номер строки в mode-line
(global-linum-mode t) ;; номера строк во всех буферах
(column-number-mode t) ;; показывать номер столбца в mode-line
(setq linum-format "%d") ;; задаём формат нумерации строк

;(fringe-mode '(8.0)) ;; ограничитель текста только слева
(setq-default indicate-empty-lines t) ;; отсутсвие строки выделить глифами рядом с полоской с номером строки
(setq-default indicate-buffer-boundaries 'left) ;; индикация только слева

(setq visible-bell t) ;; отключаем писк при ошибках. Вместо этого мигает в строке статуса

;(set-face-attribute 'default nil :font "Terminus-12")

(tool-bar-mode -1) ;; отключаем лишние панели
(menu-bar-mode -1)
(scroll-bar-mode -1)

(iswitchb-mode 1) ;; интерактивный режим переключения буферов 

(setq word-wrap t) ;; автоперенос по словам
(global-visual-line-mode t)

(require 'ido) ;; интерактивный режим открытия файлов
(ido-mode t)
(icomplete-mode t)
(ido-everywhere t)
(setq ido-virtual-buffers t)
(setq ido-enable-flex-matching t)

(require 'bs) ;; быстрая навигация между буферами
(require 'ibuffer)
(defalias 'list-buffers 'ibuffers) ;; отдельный список буферов C-x C-b
(global-set-key (kbd "<f2>") 'bs-show) ;; запуск buffer-selection по F2

(setq scroll-step 1) ;; скролинг по 1 строке
(setq scroll-margin 10) ;; сдвигать вверх вниз когда курсор в 10 строках
;(setq scroll-conservatively 10000) ;; ???

(setq x-select-enable-clipboard t) ;; общий буфер обмена с ОС

(setq search-highlight t) ;; Выделять результаты поиска
(setq query-replace-highlight t)

(global-set-key (kbd "<C-Tab>") 'other-window) ;; переключение окон

;; запилить закладки

;; ========== NAV
(require 'nav)
(nav-disable-overeager-window-splitting)
(global-set-key (kbd "<f8>") 'nav-toggle)

(defun nav-mode-hl-hook ()
  (local-set-key (kbd "<right>") 'nav-open-file-under-cursor)
  (local-set-key (kbd "<left>")  'nav-go-up-one-dir))

(add-hook 'nav-mode-hook 'nav-mode-hl-hook)

;; ========== autopair-mode
(require 'autopair) ;; автовставка скобок
(autopair-mode t)

;; ========== D-MODE
(require 'd-mode)
(add-to-list 'auto-mode-alist '("\\.d\\'" . d-mode)) ; автозапуск d-mode для файлов .d

(defun rdmd() ;; запуск rdmd для текущего файла
  "Run rdmd for current file.d with unittests"
  (interactive)
  
  (setq rdmd-cmd (concat "rdmd -unittest " buffer-file-name))
  (save-buffer)
  (async-shell-command rdmd-cmd)
  )

(defun dub() ;; запуск dub для текущего проекта
  "Run dub for current project" ;; от текущего файла спускается в низ по директориям 
  (interactive)                 ;; пока не найдёт dub.json,
  (save-buffer)                 ;; но не дальше 6ти директорий
  (setq str "./")
  (while (eq (file-exists-p (concat str "dub.json")) (eq str "./../../../../../../"))
    (setq str(concat str "../")))
  (setq str (concat "cd " str " && dub"))
  (async-shell-command str)
)

(define-key d-mode-map (kbd "<f6>") 'rdmd) ;; привязка клавиш к функциям
(define-key d-mode-map (kbd "<f5>") 'dub)  ;; rdmd и dub

;; отключаем вопросы при выходе по поводу запущенного dcd-server
(defadvice save-buffers-kill-emacs (around no-query-kill-emacs activate)
  "Prevent annoying \"Active processes exist\" query when you quit Emacs."
  (cl-letf (((symbol-function #'process-list) (lambda ())))
    ad-do-it))

;; ========== ac-dcd
(require 'ac-dcd)
(add-hook 'd-mode-hook
  (lambda ()
      (auto-complete-mode t)
      (when (featurep 'yasnippet) (yas-minor-mode-on))
      (ac-dcd-maybe-start-server)
      (ac-dcd-add-imports)
      (autopair-mode t)
      (ac-dcd--find-all-project-imports)
      (add-to-list 'ac-sources 'ac-source-dcd)
      (define-key d-mode-map (kbd "C-c ?") 'ac-dcd-show-ddoc-with-buffer)
      (define-key d-mode-map (kbd "C-c .") 'ac-dcd-goto-definition)
      (define-key d-mode-map (kbd "C-c ,") 'ac-dcd-goto-def-pop-marker)
      (define-key d-mode-map (kbd "C-c s") 'ac-dcd-search-symbol)          
	  
      (when (featurep 'popwin)
          (add-to-list 'popwin:special-display-config
                     `(,ac-dcd-error-buffer-name :noselect t))
          (add-to-list 'popwin:special-display-config
                     `(,ac-dcd-document-buffer-name :position right :width 80))
          (add-to-list 'popwin:special-display-config
                     `(,ac-dcd-search-symbol-buffer-name :position bottom :width 5)))))

;; ========== Отступы
(setq-default indent-tabs-mode nil) ; не использовать символ Tab для отсутпа

(setq tab-width 4 ; ширина таба
      c-default-style "stroustrup" ; отступ в CC mode
      js-indent-level 4 ; indentation level in JS mode
      css-indent-offset 4) ; indentation level in CSS mode

(add-hook 'd-mode-hook
          (lambda ()
            (setq tab-wdth 4)
            ))

(add-hook 'text-mode-hook
          (lambda ()
            (setq tab-wdth 4)
            ))

(global-set-key (kbd "TAB") 'self-insert-command) ;; indent
(setq-default c-basic-offset 4) 
(setq-default tab-width 4)

;; ========== Хоткеи на русской раскладке
;; А вот эта строка должна быть в самом конце
(cfg:reverse-input-method 'russian-computer)


или на GitHub.

Для большего понимания рекомендую копировать построчно и разбираться отдельно, что из этого получилось.

Выслушаю любые вопросы и предложения.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.