Writing Intelligent Keyboard Shortcuts in Elisp
In my quest to get better aquainted with my new editor I decided to make some intelligent keyboard shortcuts for my blogging repository. I decided to have one keyboard shortcut that could publish the current draft (with jekyll publish) and one to create a new draft (with jekyll draft).
In my vim days I would probably have settled for the simple version of having a couple of keybindings in a .exrc file in the root of the project.
This gist would allow me to quickly run publish on a draft - however if I tried to run it on a non-draft file in the project, it would still run the command. (And who knows what sideeffects that may have?)
It also allows me to quickly create a new draft, by just starting a ex command allready having typed !jekyll draft - and then letting me finish the command and pressing enter.
Doing the same thing in emacs is verbose. There I admitted it.
However with this verbosity comes great power.
With writing everything in lisp I have the full power of an awesome programming language at my disposal (and frankly VimL sucks by comparison). Writing this “plugin” was maybe more an excercise in writing lisp than it was an aid in my blog writing - to be honest, it took me longer to write this code, than it took to write the last three posts combined - but that was also a point. By doing something with a limited scope, and a known outcome - and something I have done a couple of times before in different editors/languages/blogengines, I knew that most of the pitfalls that might come up, would be manageable.
First of all the obscurity of the .dir-locals.el file in general was mind boggling at first. I tried out few peoples code (mostly found on stack overflow) just to get something to load when opening a project. The amount of open parens and the “commands” nil, eval and progn is boilerplate code for me atm - I have not looked into why I have written it.
Second, the fact that I use eval either gives me a warning everytime I load the file (which looks like is done each time a new buffer in the project is opened) - or I have to mark it as ok, which in turn copies almost all of the code verbatim into my .spacemacs file. The former is annoying, the latter is making me think that I could just add it to my layer (functions and keyboard shortcuts) - however this would not be sharable with other users of emacs on a team (not that my blog has one - but I have other projects you know, this is not all I do).
Third there are a couple of weird stuff that just took me a while to wrap my head around.
It is weird to me that the current working directory in emacs always is the directory of the file in the current buffer. From vim I am used to cwd being the project root. This means that if I just did file operations without making sure the correct path was set somehow, all file operations would be done relative to where the current open file is located. e.g. if I have one of my css files open in projectroot/assets/css/somefile.css and tried to create a new blog post from there, it would have made the draft in projectroot/assets/css/_drafts/title-of-the-post.md … Weird!
Well projectile luckily has a method to get the current root directory for the project.
There is a huge difference between doing
(concat projectile-project-root "file.ext") and
(concat (projectile-project-root) "file.ext" )
Sometimes I try to call something that is just a string variable. And sometimes I try to read something like a variable, which was in fact a function.
That thing where you can call interactive from within a function and have that set the params of the function if they were not provided from the outside. That feels a bit unnatural for me. However it has the great upside that the same code can be used for calling directly from the editor (and then showing you the prompt to enter the value) or from another function, which will the just provide the value needed.
This was the longest two hours of my life. I could not for the life of me understand why when creating a new post (which I could see on disk was there), that it opened an empty buffer, which looked like it had the correct filename - however I could still open the real file in a buffer next to it.
This took me forever - until I realized I had to go js-frontend-developer-ninja on lisps as (you know, console.log everthing) and just (message) and wrapping the filename in something… And damn! There was of course a newline at the end of the terminal output. (Should have known right?)
Well easy-peasy: regex replace that - and of we go.
I have a “secret” opengl based c++ project that I work on pretty much daily now. The project can render scenes based on input files, and these files can vary a great deal. I have a lot of these file in my project at the moment. Until I switched to emacs, I ran my tests manually in the terminal and either scrolled my way through the debug output, or piped it to a file and opened that in vim.
Now I have a keybinding that runs the current input file as an argument to the latest binary asyncronously, sending the output live directly into a buffer in emacs. This lets me watch the output on one screen, the log in another screen, and the original code in a third screen. All without having to anything else than press in my editor.