Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

Linking emacs SLIME and docker-compose with SWANK server -- modern Common Lisp development

Better translation here: https://kvantos.github.io/#articles_en/emacs-slime/

Emacs works quite well with Common Lisp.

We can use REPL and autocompletion feature connecting to new Common Lisp implementation or working implementation; SLIME server booted on implementation holds communicaiton between implementation and Emacs.

Though frequently we connect into working implementation on local environment with SLIME and that is well-documented on the Web, there is few documents that describes a way to connect Emacs into working implementation and enjoy productive development in modern environment -- Docker compose environment.

Thus I try to:

  • Connect emacs into living typical Common Lisp application (Caveman 2) booted on docker-compose.
  • Interact with living code and interfare with its behavior.

I assume you have already done with basic SLIME configuration on Emacs.

Motivation

My hobby program written in Common Lisp is dockerized and able to boot with docker-compose, except "editor and its environment". So situation is this: I connected Emacs into the local-booted implementation with SLIME for syntax highlight and autocompletion, not into the docker-compose-booted implementation. It raised many probrems such that malfunction of autocompletion and loading library, and burdened developer of maintaining develop environment.

Now I am going to harmonize coding environment and server environment to develop with genuine living code.

Environment

  • cat /proc/version
    • Linux version 4.4.104-39-default (geeko@buildhost) (gcc version 4.8.5 (SUSE Linux) ) #1 SMP Thu Jan 4 08:11:03 UTC 2018 (7db1912)
  • lsb_release -a
    • LSB Version: n/a
    • Distributor ID: openSUSE project
    • Description: openSUSE Leap 42.3
    • Release: 42.3
    • Codename: n/a

ros version

build with gcc (SUSE Linux) 4.8.5
libcurl=7.37.0
Quicklisp=2017-03-06
Dist=2017-08-30
lispdir='/usr/local/etc/roswell/'
homedir='/home/windymelt/.roswell/'
configdir='/home/windymelt/.roswell/'

ros run -- -version

SBCL 1.4.0

Sample application

I prepared the sample application. You can try to connect into the application with SWANK.

github.com

Let docker-compose expose SWANK port

SWANK server should be booted on an implementation to communicate with Emacs.

We expose port used for SWANK server through host to allow connect from Emacs.

Here we suppose that the implementation is working on app service built from following Dockerfile. Web application (caveman 2) and SWANK server use 5000 port and 6005 port respectively. You can choose arbitary port number for SWANK server because Emacs always asks for SWANK host and port number.

# docker-compose.yml
version: '3'
services:
  app:
    build: .
    ports:
      - "6005:6005" # For development; SWANK port. cf. Dockerfile
      - "5000:5000"

Dockerfile

In Dockerfile we command CMD to boot SWANK server first, preceding application server booting.

We look at bottom part of Dockerfile because many part of Dockerfile have no buissiness with SWANK:

CMD [ \
  "qlot", "exec", "ros", \
  "-e", "(ql:quickload :swank)", \
  "-e", "(setf swank::*loopback-interface* \"0.0.0.0\")", \
  "-e", "(swank:create-server :port 6005 :dont-close t :style :spawn)", \
  "-l", "bundle-libs/bundle.lisp", \
  "-S", ".", "~/.roswell/bin/clackup", "--server", ":woo", "--address", "0.0.0.0", "--port", "5000", "app.lisp" \
]

The container boots qlot, library localiser. It makes loading libraries under quicklisp/.

qlot has responsibility to pass configuration about project-local libraries to implementation and the implementation is booted by ros. ros processes dangling options in written order:

First swank is loaded by -e (ql:quickload :swank) to prepare booting SWANK server. You may declare dependency into qlfile and write -e (asdf:load-system :swank) here.

Then we overwrite the address which SWANK server listens by -e (setf swank::*loopback-interface* "0.0.0.0"). This is because docker environment often makes turbulance of IP addresses. We ensure connecting to implementation by fixing listening address to 0.0.0.0.

By the way SWANK has no authentication mechanism so everyone can connect to socket of SWANK server. You should use SSH if you have to secure connection, but it's okay because this is development purpose.

Next we boot SWANK server listening 6005 port by -e (swank:create-server :port 6005 :dont-close t :style :spawn). :dont-close should keep connection but I'm not sure. :style :spawn designates type of connection: one spawned process per connection. It is not needed if you connect SWANK server from exactly one client.

Following processes have no business with SWANK.

-l bundle-libs/bundle.lisp is to load dependent local library. qlot can freeze all dependent libraries into bundle-libs executing qlot bundle and bootstrapping file to load libraries is bundle.lisp. It should be used by ros -l option. -l simply loads and executes specified file.

-S . configures ASDF default system repository (asdf:*central-registry*) into current directory (this time it's project-root). ASDF can find system such that its ASD file is placed at specified directory.

At last we boot clack application by ~/.roswell/bin/clackup --server :woo --address 0.0.0.0 --port 5000 app.lisp. app.lisp is called clack aplication alike perl's app.psgi. This file is created automatically when Caveman project is created.

Connecting from Emacs

It's time to flight. Boot project with docker-compose and connect from Emacs.

docker-compose up

After M-x slime-connect you will get the prompt Host: to specify localhost. Then you will get the prompt Port: to specify 6005 configured above. After all following output are made into minibuffer and you can see working REPL window.

Connecting to Swank on port 6005..
Connected. Take this REPL, brother, and may it serve you well.

You can move into your package because living codes are already loaded.

;; there is Caveman 2 project named :hogeapplication
CL-USER> (in-package :hogeapplication.web)
#<PACKAGE "HOGEAPPLICATION.WEB">

HOGEAPPLICATION.WEB> *web*
#<<WEB> {12345678}>

HOGEAPPLICATION.WEB> (render #P"index.html")
;; -- Template is rendered exactly same as you coded in hogeapplication.web package --

HOGEAPPLICATION.WEB> (clear-routing-rules *web*)
;; -- All of routing information is gone, application is made to say 404. try this! --

Conclusion

Now we can read/modify symbols inside living application because:

  • We booted SWANK server at Dockerfile and configured potr exposure setting
  • We connected into the implementation running over container orchestrated by docker-compose, from Emacs on host machine

We can use precise autocompletion along living application because package information is provided to SLIME from SWANK.

This post is self-translation of:

blog.3qe.us

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?