Emacs Settings for PHP Development

PHP file in the main pane, with a unit test/compilation result pane at the bottom, and the neotree directory view to the right

The project that I've been working on over the weekend, I worked on in emacs. This is part of my re-learning the basics of text editing and programming in emacs to slowly move away from TextMate when it comes to scripts. I want to move away from TextMate because I eventually want to transition to be productive on a Linux machine – that means, to create stuff in an otherwise foreign operating system. Emacs is portable, so that's a good start.

Projectile and Neo Tree project settings

In general, I am using Neo Tree to display the project directory in a window on the right, and I use projectile to quickly find files inside the project context (C-c p f), switch between tests and implementations (C-c p t), and run the tests with a shortcut (C-c p P, note it's an uppercase "P", so you need to press shift).

It didn't take long to commit C-c p P to muscle memory to execute the tests; and the C-c p t accord works really well to switch back and forth between implementation and tests. When I read about Projectile's default shortcuts, I frowned at the prospect of memorizing all that stuff, but it wasn't that bad at all.

In my emacs init.el, I have these projectile settings:

(use-package projectile
  :ensure t
  :config 
    ;; Global configuration
    (setq projectile-switch-project-action 'neotree-projectile-action
          projectile-enable-caching t
          projectile-create-missing-test-files t
          projectile-switch-project-action #'projectile-commander
          projectile-ignored-project-function 'file-remote-p)
    (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
    ;; Register custom PHP project type
    (projectile-register-project-type 'php '("composer.json")
                                      :src-dir "src"
                                      :test "composer test"
                                      :run "composer serve"
                                      :test-suffix "Test"
                                      :test-dir "tests"))
(use-package counsel-projectile 
  :ensure t)

Apart from some configuration options, the most interesting part is the projectile-register-project-type command. PHP is not shipped by default, so I set it to discover the project type by looking for a composer.json file in the project root, then I configured the source and test directory according to the new-found conventions.

When I'm in a source file, I can quickly jump to the tests and back again with C-c p t. In my book, that's better than having to have both files open at all times and switch back and forth most of the time.

And here are the Neo Tree settings:

(use-package neotree
  :ensure t
  :config (global-set-key [f8] 'neotree-project-dir)
          ; Every time when the neotree window is opened, let it find current file and jump to node.
          (setq neo-smart-open t)
          ; Do not autorefresh directory to show current file
          (setq neo-autorefresh nil))

I found the default behavior to be odd. When I switch files inside of a project, Neo Tree by default highlights the file in the directory listing to the right, but also switches focus to the file's directory. When I would open a test file, only the test/ directory remained visible. Hiding all the sibling directories, including src/, wasn't what I wanted – I wanted to see an overview of the whole project. With Projectile's file finding being limited to the current project directory by default, the visual overview became less and less useful. Maybe I'll ditch Neo Tree in the future. I do like Neo Tree when I get back to old projects for which I don't remember what I put where, heh.

Code Completion

I also use the Comp[lete]any[thing] mode to have code-completion – to my amazement this apparently even works across files in the same project!

; Install company
(use-package company
  :ensure t
  :config (add-hook 'prog-mode-hook 'company-mode)
          (global-set-key (kbd "M-i") 'company-complete))
(use-package company-anaconda 
  :defer
  :after company
  :config (add-to-list 'company-backends 'company-anaconda))

; Company language package for PHP
(use-package company-php
  :defer
  :after company)

; Just as an example, aso Ruby:
(use-package robe ;; company-robe is a Ruby mode
  :ensure t
  :after company
  :config (add-to-list 'company-backends 'company-robe)
          (add-hook 'ruby-mode-hook 'robe-mode))

General programming mode settings

Scroll to the bottom of the compilation results window. I discovered two settings and show both here. I picked the one where the window scrolls to the first error of the compilation or test results; but scrolling to the bottom was just as nice.

;; Scroll to the bottom of the compilation buffer:
; (setq compilation-scroll-output t)

;; Scroll to the bottom OR the first error in the compilation buffer
(setq compilation-scroll-output 'first-error)

I also limit the bottom compilation window to a height of 10 lines – otherwise it would pop up at 50% height:

(setq compilation-window-height 10)

(defun ct/compilation-hook ()
  (when (not (get-buffer-window "*compilation*"))
    (save-selected-window
      (save-excursion
        (let* ((w (split-window-vertically))
               (h (window-height w)))
          (select-window w)
          (switch-to-buffer "*compilation*")
          (shrink-window (- h compilation-window-height)))))))
(add-hook 'compilation-mode-hook 'ct/compilation-hook)

I also have the following minor modes enabled by default:

  1. Display line numbers,
  2. break lines visually, e.g. at the edge of the window, instead of cutting them off,
  3. highlight paired parens as I open or close them, or hover over them with the cursor, and
  4. hide/show code blocks aka folding methods or class definitions.
(add-hook 'prog-mode-hook 'linum-mode)
(add-hook 'prog-mode-hook 'visual-line-mode)
(add-hook 'prog-mode-hook 'show-paren-mode)
(add-hook 'prog-mode-hook 'hs-minor-mode)

Browse the blog archive