вторник, 26 ноября 2013 г.

Emacs Keymaps and Packaging

Abstract


We currently have nice piece of code, able to remove trailing spaces for .java files. We might add some other functionality into that code. For example, I have pieces of code which add spaces after if statements just before opened brace when I save .js file, and removes them when I open one. This makes my comrade developers feel like code is so-called "readable", and I can keep programming in my favourite manner.
To be able to spread code amongst multiple computers and environments, we will have to either copy files all the time, or use Emacs packaging system. This will also provide us an ability to share our library. So your friends will be able to use your packages in their own environments and be up to date with them.
Also, we will look at keymaps - short cuts for interactive functions. These are widely used, and in case you want to make your environment look like Intellij Idea, you can use them. Personally, I think that less used functions should persist in M-x calls family. This does not pollute your short cuts, which are pretty tight, and long short cuts look not so sexy as expected. Another reason is that, maybe, long command looks much more understandable and easy to search.

Keymap

Lets start by linking our function to some short cut. Emacs allows setting short cuts on global, local and per-mode keymap level.
Local keymap is something user might define for particular buffer.
Global keymap can be changed while emacs is loaded, or when some special process is ongoing. I think Compilation is a good example, but I yet study it. Basically, it overloads some global short cuts to allow user to navigate around errors and warnings of compilation process.
As soon as we will create package for our custom code - we will introduce
mode-local keymap and fill it.

Bind on Emacs start

First, let's try binding our function on start up:
01 (global-set-key [?\C-x ?t] 'kill-whitepaces)
now, you can call this function any time, by just clicking Ctrl+x t.
Usually, we need this function in, say, java files. so we can add this to java hook:
01 (defun special-java-functions ()
02   (kill-whitespaces)
03   (local-set-key [?\C-x ?t] 'kill-whitespaces))
04
05 (add-hook 'java-mode-hook 'special-java-functions)
This brings us to the point, where we open java files, and automatically clean it. But if something strange happens - we still have short cut to remove trailing spaces.

Packages

Now, we want to spread our code around our own Emacs instances at home, and on the workplace, and maybe even in our cellphone. Further, other people might think that your code is not so useless, and wish you share it. Here packages come to help us.
All packages are stored in some repository. There is a default one - http://elpa.gnu.org/. To view list of available packages you can type M-x package-list-packages. Our goal is to have our own package in this list.

Upload package manager

To help us working with package repository, Emacs has package-x library. If you wish to update your repositories from some computer - load it in your configuration file:
01 (require 'package-x)
Remember, you will need this package only on development computer.
Now we need to create a repository. Usually, this is some remote directory, or http service. We will use local directory for now. Lets say it's name is /home/xaero/.emacs-own-packages/. To tell Emacs, that we want to use it, we should add next line to initialization file:
01 (add-to-list 'package-archives '("home packages" . "/home/xaero/.emacs-own-packages/"))
Note: You do not need package-x to work with package-archives alist.
This tells Emacs, that there is a repository, we name it "home packages", that we are interested in.
To tell Emacs, that we also want to upload new or newer packages into the same repository, we need to set package-archive-upload-base variable:
01 (setq package-archive-upload-base "/home/xaero/.emacs-own-packages/")
Now, when you will upload your packages, they will go into this directory.

Http resources

To make a read only package archive, you have to have ssh access to some remote host, and you have to set package-archive-upload-base to, say, ssh://remote.host.com:local/path/to/packages/. Emacs will be able to use it.
To make it reachable from the internet, you can set it available through http server from the http://public.host.com/packages/, and add this address into package-archives alist.
As we can see - URLs are different, but rights are not the same as well. If you will give access to ssh to some people - they will be able to modify your packages, while all the other people will just download and browse them.

Basic package

Package name should be short and verbose. Let's say whitespace manager, and shorten it to wt-manager. Single file package usually is held in single .el file with the same name as package. in our case - it is wt-manager.el :

01 ;;; wt-manager.el --- Manage file whitespaces
02
03 ;; Copyright (C) 2013 dimalev, Inc.
04
05 ;; Name: wt-manager
06 ;; Author: D. Lev. <ldimat@gmail.com>
07 ;; Version: 0.1
08
09 ;;; Commentary:
10
11 ;; Basic tools to manage annoying or required whitespaces.
12
13 ;;; Code:
14
15 ;;;###autoload
16 (defun kill-whitespaces ()
17   "Let's doc it"
18   (interactive)
19   (save-excursion
20     (goto-char 1)
21     (replace-regexp "[\s\t]+$" "")))
22
23 ;;; wt-manager.el ends here

Comment on line 23 is very important - Emacs will fail if it does not find one. Also characters after the end of file are not welcome. Most of other lines speak for themselves, except for line 15, which is autoload marker.
Emacs does not load all the extensions at the same time. It uses lazy load. For most of packages it loads only cover of the package, like main functions, which are meant to be called. As soon as you call one of such functions - it is loaded, and all the dependant code is loaded as well. So basically, if you will not mark some function for autoload - it will stay invisible, until the package is loaded after calling some function which is marked for autoload.

Upload and download

Now, just push this buffer into your repository (this is just a folder for us) by calling:
M-x package-upload-buffer
You can use package-upload-file as well, this does not change much, just that usually you will use file for multi-file packages. You can search information on multi-file packages and how to cook them on emacs wiki.
In case if you cannot find such a function - make sure you required package-x in init file, and reloaded your Emacs instance.
If everything is ok - then in your /home/xaero/.emacs-own-packages/ directory you can find your file with version suffix - like wt-manager-0.1.el as well as some other repository-related files.
Now, we have to download/upgrade our library in Emacs - go and type M-x package-list-packages and then search for wt-manager! To mark package for installation -  click i on it and then commit your choice by clicking x. All the files will be installed (you might be warned about replace-regexp used only in interactive environment, but come on - we are in it. I will fight this later), and you will be able to use kill-whitespaces function!

Multi-file packages

This is much more convenient way of distributing your code, so go and study how to use it: Multi-File Packages on Emacs Wiki

Links

[1] Packages on Emacs Wiki
[2] Keymaps on Emacs Wiki
[3] Key binding conventions

Комментариев нет:

Отправить комментарий