Remote Intelephense with Emacs LSP

I have come to rely on Intelephense for any kind of PHP hackery, and this includes when I’m quickly editing a file on my own remote server, or in a vagrant box or what have you. Remote editing is one of the big strengths for me when it comes to using Emacs, and TRAMP has been serving me very well for it, by default so LSP servers don’t “just work”(TM) when accessing a remote file as the local LSP server won’t have access to the files. Luckily lsp-mode already has provisions for it so and will recognize that a file is remote, and startup an LSP server on the target machine if available, but it requires some configuration. For Intelephense this means making sure it is installed on the remote, easily done via NPM, and setting up a remote client configuration somewhere, for me best done as part of initializing lsp-mode.

(use-package lsp-php
  ;; register remote intelephense
   (make-lsp-client :new-connection 
                    (lsp-tramp-connection lsp-intelephense-server-command)
                    :activation-fn (lsp-activate-on "php")
                    :priority -1
                    (ht ("indexingStarted" #'ignore)
                        ("indexingEnded" #'ignore))
                    (lambda ()
                      (list :storagePath lsp-intelephense-storage-path
                            :licenceKey lsp-intelephense-licence-key
                            :clearCache lsp-intelephense-clear-cache))
                    :multi-root lsp-intelephense-multi-root
                    :completion-in-comments? t
                    :remote? t
                    :server-id 'iph-remote
                    :synchronize-sections '("intelephense"))))

I am reusing the lsp-intelephense-server-command and most of the other lsp-intelephense variables as they contain the right configuration, and I haven’t had an issue, even so lsp-intelephense-storage-path might need configuration depending on the value and setup, but it has worked for me. Importantly server-id and remote? need to be set for lsp-mode to handle the remote server correctly, but this has worked like a charm for me. Having the same tooling both locally and remote sure is nice!

Emacs, echo mike alpha charlie sierra

Having moved to a different country spelling words over the phone is a real pain as its not easy for people to understand a foreign name, and because I don’t know the military alphabet in english. So even when I decide spell things out I get stuck on letters as I don’t know what the common word would be. After not remembering how to spell out my last name over the phone again today I decide to create a quick little helper, after all I almost always take notes on my laptop anyway when calling.

Here we go quick emacs string-to-military, with configurable alphabet.

(defun string-to-military (s)
  (interactive "sEnter string to transfor to military: ")
  (flet ((string-blank-p (s) (string-match-p "^\\s-*$" s))
         (military-get-string (s)
                              (if (string-blank-p s) " "
                                  (alist-get s military-alphabet "" nil #'equal))))
    (let* ((chars (mapcar 'string s))
           (normalized-chars (mapcar 'downcase chars)))
      (string-join (mapcar 'military-get-string normalized-chars) " "))))

(defun string-to-military-region (start end)
  (interactive "r")
  (let ((content (buffer-substring-no-properties start end)))
    (delete-region start end)
    (insert (string-to-military content))))

and for the curious

(defvar military-alphabet '(("a" . "alpha")
                            ("b" . "bravo")
                            ("c" . "charlie")
                            ("d" . "delta")
                            ("e" . "echo")
                            ("f" . "foxtrot")
                            ("g" . "golf")
                            ("h" . "hotel")
                            ("i" . "india")
                            ("j" . "juliet")
                            ("k" . "kilo")
                            ("l" . "lima")
                            ("m" . "mike")
                            ("n" . "november")
                            ("o" . "oscar")
                            ("p" . "papa")
                            ("q" . "quebec")
                            ("r" . "romeo")
                            ("s" . "sierra")
                            ("t" . "tango")
                            ("u" . "uniform")
                            ("v" . "victor")
                            ("w" . "whiskey")
                            ("x" . "x-Ray")
                            ("y" . "yankee")
                            ("z" . "zulu"))
  "Military alphabet alist (CHARACTER . WORD), all characters are
lowercase for lookup.")

now part of my coders-little-helper.el in my local emacs.

Setup use-package

I like my emacs configuration to be managable in git installing everything on first start, as I migrated away from my home grown install and package manage to use-package I wanted to keep this nicety, so I need to make sure use-package gets installed if it isn’t already present.

;; setup use-package, if we don't have it, install it
(require 'use-package nil t)

(unless (featurep 'use-package)
  (package-install 'use-package)
  (require 'use-package))

Projectile and TRAMP

Projectile requires a lot of file system interaction to stay up to date, this does not work well when accessing remote files and causes massive slowdowns when used over a less than ideal SSH connection. My solution based on a comment regarding this issue has been to deactivate projectile on remote files, sadly its not just files but also dired buffers which suffer so a small addition is needed to the solution in the comment adding dired buffers to the list of things dired should not be enabled for.

(defadvice projectile-on (around exlude-tramp activate)
  "This should disable projectile when visiting a remote file"
  (unless  (--any? (and it (file-remote-p it))

furthermore to avoid unneeded calculation of the current project the projectile modeline needs to be set static.

(setq projectile-mode-line "Projectile")

with this in place projectile works nicely locally and remote file interactions are as quick as they can be.

GoGuru scope in emacs

GoGuru is an amazing tool and can be really useful when exploring a project, setting up the scope can be a bit of a pain so as the default function doesn’t even support autocompleting. After some quick hacking here is the first attempt to make at least allow for some easier setup, taking the current file and assuming it is the main package, this little function will setup the scope relative to this file.

Hacking on rails using emacs

Rails contributor?

Apparently I committed to rails before, I’m pretty sure it was a doc update, but none the less this is something I actually like to do more. So first step get a working setup for running rails tests locally and getting a reasonably productive environment setup. Since I’m mostly using emacs these days it would be nice if everything would actually work out of this editor/operating system.

rake test

The easiest way to run the tests after cloning down rails and bundling is to use rake.

$ bundle exec rake test

in the root rails folder will run all the available tests, this is great but when working on something also not what I want most of the time. But running it in a subfolder like activesupport will actually do the right thing and run only this subset of tests

$ cd activesupport 
$ bundle exec rake test

To make this easier I’m using textmate.el to execute most commands while inside a project from the determined project-root. Meaning if I switch to a file I set the working directory to the current root automatically.

(defun coder/default-project-dir ()
  (if (textmate-project-root)
      (setq default-directory (textmate-project-root))))

(add-hook 'find-file-hook 'coder/default-project-dir)

Adding .emacs-project files to all the subdirectories will now make sure that when I run a rake command it will be run in the correct directory. Combining this with rake.el allows to run test via a simple M-x rake and selecting test, M-x rake-rerun will now rerun the given tests with a simple command.

This works great while working in quick running test suites but running the whole suite on every change can become inconvenient quickly, this is solved by 2 more enhancements to the flow

running a single test file

Rake allows for passing a file name to limit the run to a single test file

$ bundle exec rake test TEST="test/abstract/callbacks_test.rb"

for example will run only this file of the actionpack testsuite for example. Make sure to run with bundle exec, rake.el does not detect this when in a subfolder, but C-u M-x rake will let you modify the command as needed.

running a single test

Should a file have a lot of tests it can be a good idea to only run a single one, this is possible by passing an option to the minitest testrunner.

$ bundle exec rake test TEST="test/abstract/callbacks_test.rb" TESTOPTS="--name=test_around_action_works"

TESTOPTS will be passed through to minitest which will only run this one test.

integration in emacs

All this works on the command line, as well as using rake.el, personally I use rake.el and have rake rerun bound to C-c r to allow me to quickly run the tests I’m currently working on, this executes in compilation-mode, which allows emacs to parse out file urls and makes them clickable in case of errors.

Next up to make it even better would be to figure out the test name I’m currently in, this works quite simply for files which use the

class TestClass < Minitest::Testcase
  def test_foo
    assert_true doing_the_foo

syntax, but is much harder with files

class TestClass < Minitest::Testcase
  test "doing the foo" do
    assert_true doing_the_foo

So for now I stick with doing it by hand.

How to remember circuit breaker configuration with Emacs

Some things are just not easy to remember and looking them up, saving a bookmark to wherever, is just tedious. So how about saving a bookmark and at the same time automating the calculation? Well this is why I started a file which hopefully grows with all those little formulas, using the lisp docstrong to provide a note and a link to why this is relevant.

First thing, when configuring a circuit breaker we need to set a size for how many parallel requests we want to accept, luckily Netflix has a nice formula for this, and even more lucky it is documented on the yammer tenacity repository.

(defun hysterix/thread-pool-size (p99 reqs)
  "calculate and insert the thread pool size for a hysterix
circuit based on the p99 and the requests per secound see for
more details"
  (interactive "nP99 in ms:
nRequests per sec: ")
  (let ((thread-pool-size (ceiling (* (/ (float p99) 1000.0) reqs))))
    (insert (format "%d" thread-pool-size))))

All I need to do now when configuring a circuit, hit M-x hysterix/thread-pool-size and insert the 99th percentile for request latency I can easily get from the service monitoring, as well as the requests per second. If ever in doubt I can use C-h f hysterix/thread-pool-size to get the documentation.

Creating Pull Requests quickly from Emacs

A lot of code I work on lives in GitHub, both work and personal project normally make their way there quickly so maintaining it means I work with the GitHub interface quite a lot as well. To make my live a little easier I decided to create a quick Emacs function to drop me in the matching interface and create or visit the pull request I’m currently working on. Luckily most of the pieces where already out there. All that was needed was grabbing the correct URL and branch from the current repository.

Switching git users in Emacs based on repository

Using Github internally for work is really nice, but especially when working on multiple projects, some on public and some on internal Github it can also be quite hard to always remember to use the correct username and email for the commits. Because I tend to forget such things I decided to create a little elisp function to make sure the correct user is set when I start magit. Right now I only have to switch between 2 users so it is managable, but I think this is easily extended to more if the need arises (god I hope not).