Please remove this {{Draft}}template, when you're sure the page is complete and correct.
|
Contents
- 1 Introduction
- 2 Files in support of Emacs in a Mageia system
- 3 Elisp code for customization
- 3.1 Common issues
- 3.2 Customizing non-functional properties
- 3.3 Customization of file-type specific behaviour
- 3.4 Customization of keyboard shortcuts
- 3.5 Customizing of global Emacs variables
- 3.6 Customization of the menu bar
- 3.6.1 Naming menus and menu-items, finding the name of an item to be modified
- 3.6.2 The function "menu-item" - advanced issues
- 3.6.3 Deleting an entire menu from the menu-bar, deleting one of its menu-items
- 3.6.4 Adding a new item to a menu of the menu-bar
- 3.6.5 Adding a new menu to the menu-bar
- 3.6.6 Example: putting things together
- 3.7 Debugging customization code
- 4 Documentation, references
- 5 Conclusions
- 6 Appendix: examples of Elisp code
Introduction
Emacs and Xemacs in Mageia
Up to and including Mageia-5, both Xemacs and Emacs packages had been part of the distribution. Future releases will probably not offer an Xemacs package any more - (1) upstream Xemacs is practically un-maintained: the latest stable release dates from 2009, and the version available in Mageia-5 suffers from a severe bug - https://bugs.mageia.org/show_bug.cgi?id=11254 , and (2) Emacs is a perfectly valid and properly maintained alternative. Therefore, it is advisable for users of Xemacs to consider rapidly migrating to Emacs.
Customization of Emacs
Just as with Xemacs, the control of the user interface of Emacs is implemented by "Elisp" code ("Emacs Lisp", a clone of the Lisp language, introduced in the late 1950's - a big compliment, not a negative comment). There exist practically no differences between Elisp in Xemacs and Emacs. Nevertheless, the libraries of Elisp modules that are required in support of such code differ between Xemacs and Emacs: the code written for personalizing the user interface of Xemacs needs to be adapted in order to implement a corresponding user interface for Emacs.
(X)emacs claims to be self-documenting. Much information in support of customization can be obtained by using the on-line help facilities offered in Emacs. However, on-line help is only available where the author of the corresponding code had included pertinent information into the source-code - and this is not everywhere the case. For instance, some of the most important library functions used for implementing the menu-bar are not well documented.
Scope of this wiki-page
The present wiki-page was originally aimed at supporting the migration from Xemacs to Emacs. But, drafting the page resulted increasingly in a kind of "quick guide to customizing Emacs" - and that is what the wiki-page now formally is. Nevertheless, the information relative to the migration from Xemacs to Emacs has been kept on board.
There exists ample documentation on Emacs and Elisp - this guide assumes that the reader will refer to that documentation whenever necessary, or has already some basic reading knowledge.
This wiki-page tries to be complementary to the already existing documentation: it selects and presents information that is relevant to customization. The structure of the text tries to compromise between a document that is agreeable to read, and the ease of finding particular pieces of information as need arises during the process of customization.
The page focuses on the creation of simple customization code and on customizing Emacs for a well defined usage profile. Customization can also produce sophisticated extensions of Emacs functionality and enhance portability, but that is beyond the scope of this page.
In its persent - preliminary - state, this page is excessively long, and it is nevertheless lacking in practical advice and examples ready for copy-pasting into a user's initialisation file. It is considered to create an additional page with a collection of code snippets and practical advice on system integration; the present page would be confined to the discussion of concepts and methods for creating initialisation code.
Files in support of Emacs in a Mageia system
Library modules
No user ever needs to look at the code that implements Emacs. However, if you want to customize the behavior of emacs, you need to put together - at least small - pieces of Elisp code. This can be substantially eased by consulting Elisp code that exists as part of emacs.
You find the library of Elips modules at "/usr/share/emacs/24.3/lisp/...". By default, the Emacs package of Mageia only installs compiled modules (xxx.elc files). If you want to consult the Elisp source-code, you also need to install the package emacs-el and than to de-compress the thus loaded xxx.el.gz files with gunzip.
A reminder: you can use the Mageia control center (its drakrpm component) to find the absolute paths of the files installed by the package (just open the "Files:" tab of the "Software management" window).
Initialization files
Events during the initialization of Emacs
The precise sequence of events that happen when emacs is launched is documented in this Section of the emacs manual. In particular:
- first, emacs loads and executes all executable .el or .elc files that exist in the directory /etc/emacs/site-start.d/ - the site initialization code,
- it then loads and executes a user initialization file at $HOME/.emacs or $HOME/.emacs.d/init.el - if such a file exists.
Keep this sequence of actions in mind when you write customization code, and remember that user initialization code may modify the results of site initialization.
Placing the initialization files
The Emacs documentation suggests $HOME/.emacs as the location for the user initialization file. However, this is not a good place: Xemacs considers .emacs as the potential name of its directory with user customization files, and Xemacs suggests to modify the contents of that file - a risk that can be avoided by placing the user initialization file of Emacs at $HOME/.emacs.d/init.el, an alternative location that is also supported.
In case you create site-initialization code (code that will be run for each user when emacs is launched), that code should be stored as files in the directory /etc/emacs/site-start/.
The following lines describe an alternative approach that is well suited for Emacs on a single-user machine:
- store the bulk of the initialization code as shared setup files in some protected (belonging to root) directory,
- in $HOME/.emacs.d/init.el, do not much more than simply loading these files.
This approach offers several advantages:
- init.el remains small: it is easy to safely switch between vanilla Emacs and customized Emacs (the term "vanilla Emacs" is used throughout this wiki-page to refer to Emacs as it is installed from the Mageia repositories),
- the initialization modules can be shared between users (a common user and root, for instance - I have different background colors for "user Emacs" and "root Emacs" without needing to modify the distro-provided Emacs, just as I do for command-line terminal emulator windows),
- the directory with the initialization code can be put into a separate file-system, allowing to share a single copy between several OS partitions (useful when - for instance - you have several releases of Mageia in co-existence).
Potential conflicts between Xemacs and Emacs
Emacs and Xemacs are still very close to their common origin and the name-spaces still have some overlap. There exist some slight incompatibilities:
- The xemacs-extras package is in conflict with the emacs package: it is not possible to have a system that has, both, the emacs and the xemacs-extras packages installed. If, during migration, it is desirable to have Xemacs and Emacs in co-existance, the package xemacs-extra needs to be un-installed.
- As already mentioned, Xemacs considers the directory $HOME/.emacs as its initialization directory and risks to destroy a file at that location.
- Like Emacs, Xemacs places its site initialization files into the directory /etc/emacs/site-startd.d/. Although Emacs probably will not load these Xemacs files, errors might be thrown when that directory is submitted to a global re-compilation.
Elisp code for customization
Common issues
Name-space considerations
All functions and global (non-buffer-specific) variables in Emacs use a single name-space. If customization introduces function or variable names, be careful that their names do not coincide with existing names! A collision of a function name, for instance, would result in the re-definition of the existing Emacs function. The best method to avoid such collisions is to systematically use "my-..." or "nn-..." for all function and global variable names created for customization ("nn" being your initials).
Self documentation
Customization means writing code for personal use, and documentation becomes a secondary issue. Nevertheless, sticking to Emacs coding conventions on documentation is a good thing to do - it helps when code needs to be re-viewed some time in the far future, or when code is used by other users.
Documentation to provide in-function headers
A function is documented in the header of the function definition:
- you compose documentation as a single string,
- you add that string as a third list-item in the header of the function definition (after the name of the function and after list of arguments, see the definition of the function my-setup-frame).
Newline characters in the documentation are non-transparent: Emacs treats them as part of the documentation string, just as white-spaces that follow the newline. The documentation string will be displayed as part of the information Emacs provides in response to a "<Control-h> f" key-sequence
Documentation to provide with the definition of variables
A variable is documented as part of the defvar command:
- again, you compose documentation as a single string,
- you add that string as a third list item in that command (after the variable name and the default value, as illustrated here).
The documentation string will be displayed as part of the information Emacs provides in response to a "<Control-h> v" key-sequence
Documentation of items in the menu-bar
This will be discussed in the Section on the customization of the menu-bar.
Customizing non-functional properties
Customizing the menu-bar and keyboard shortcuts controls how the functions of Emacs behave when they are executed. The initialization code discussed in this Section determines "the look" of the user-interface - the "Emacs eye candy".
An extensive and well-documented example of a user customization file can be found at http://www.mygooglest.com/fni/dot-emacs.html. Large parts of that code concern items discussed in the present Section. But, as opposed to this example, the code for customizing Emacs properties can be kept very short. The following paragraphs illustrate the customization of some aspects that frequently appear in customization code.
Frame properties
Customization can define a set of properties that will be applied to all frames:
(modify-all-frames-parameters (list (cons 'height 25) (cons 'width 80) (cons 'font "DejaVu Sans Mono-10.5") (cons 'background-color my-default-background) (cons 'foreground-color "white") (cons 'cursor-color "red") ) )
This code is self-explaining (my-default-background is a global variable that must already have been defined: storing the background color in a variable facilitates having different backgrounds for root and for ordinary users). The manual of Emacs provides information on all frame properties that are supported in Emacs:
(defvar my-default-background (if (eq (user-uid) 0) "#5A0000" "#005538" ) "Default background colour of frames")
This call to "modify-all-frames-parameters" is not quite sufficient for a decent display of customized frames: frames will still have small black borders at their left and right sides - in Emacs terminology the "fringes", zones reserved for the display of curly arrows for marking wrapped lines; the fringes can be made invisible by making their background identical to the default frame-background (the foreground should be a contrasting color). But the background color of the fringes does not figure in the list of parameters recognized by "modify-all-frames-parameters". This problem can be dealt with by defining a "hook" - a function that is automatically called whenever a frame is created (the function-call to make the cursor blink acts on yet another feature that is not supported as a frame property):
(defun my-setup-frame () (set-face-foreground 'fringe "yellow") (set-face-background 'fringe my-default-background) (blink-cursor-mode 0) )
Calling the function just after the creation of the frame can be achieved by defining the after-make-frame-functions "hook" of Emacs:
(add-hook 'after-make-frame-functions (lambda ($new-frame) (progn (select-frame $new-frame) (my-setup-frame) ) ) )
Defining this hook when the user initialization code is executed comes too late for the frame which had been automatically created during the initialization of Emacs. This can easily be corrected by adding anywhere in the top-level of the user initialization code:
(my-setup-frame)
This example uses the lambda function: the definition of the hook requires an argument that is the name of a function: rather than explicitly defining a function and referring to it by its name, lambda is used to define the function "in-line" without cluttering the code with only locally needed functions.
Tabbing and indentation
The handling of tabs and of indentation is a quite complex issue. It depends
- on the mode of the current buffer (please refer to),
- and on preferences of the user with respect to tabbing and indentation.
The first issue is handled automatically by Emacs (it loads a particular module for each mode), the second one is controlled by variables which the user can set according to his preferences. The following list enumerates some frequently used variables (to find information on these variables, use the Emacs-help "<Control-h> v" key sequence - if necessary from a buffer that is in the mode for which the variable is defined):
Mode-independent variables | |
tab-width | |
indent-tabs-mode | |
tab-stop-list | |
Variables used for c-perl mode - using perl as an example for mode-specific issues | |
cperl-close-paren-offset | |
cperl-continued-statement-offset | |
cperl-indent-level | |
cperl-indent-parens-as-block | |
cperl-tab-always-indent |
The value of these variables can be set by instructions like
(setq-default <name-of-variable> <default-value>)
or
(custom-set-variables '(<name-of-variable-1> <value-1>) '(<name-of-variable-2> <value-2>) etc. '(<name-of-variable-n> <value-n>))
To understand the handling of tabbing support by Emacs, some comments on the the function "defvaralias" may help: defvaralias allows to define an alias for a variable - a secondary name of the variable. The alias always has the same value as the original.
Emacs considers the variable "tab-width " as a global variable - a varible which is not mode-specific. The function calls
(defvaralias 'c-basic-offset 'tab-width) (defvaralias 'cperl-indent-level 'tab-width)
permit to associate the value of tab-width to mode-specific aliases which are defined in the modules that handle that code.
General aspect and behavior of Emacs
Many properties of Emacs are controlled by globally defined control variables. Most of these variables are preset to "reasonable" values and do not require adjustment. The preceding paragraphs are an illustration on some particularly important issues where customization may be indicated. The following list provides some additional examples on the customization of control-variables with of more than anecdotal interest
Mouse-wheel scrolling
By default, mouse-wheel scrolling uses increments of 4 lines, which causes annoyingly large jumps. The following code sets Emacs for scrolling by 1-line increments, which become bigger when the scolling wheel is moved faster:
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1))) ;; one line at a time (setq mouse-wheel-progressive-speed 1) ;; accelerate scrolling
The Emacs wiki provides some detailed information on the control of scrolling.
Start-up screen visibility
By default and immediately after launch, Emacs will display an "Emacs welcome screen" window. The display of this window can be controlled by the function call:
(setq inhibit-startup-screen 1)
If the argument is non-nil, the display will be suppressed
Presence of a toolbar
Vanilla Emacs has a toolbar at the top of each frame. The presence of a toolbar can be suppressed by the function call:
(tool-bar-mode -1)
With a positive argument, the toolbar will be present, a negative argument specifies suppression.
Enabling the case-converion functions
Vanilla Emacs comes with the "case-conversion" functions (switching or toggling a region between upper- and lower-case) disabled. The following function calls provide control over this feature:
(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)
If the argument is nil, the functions "upcase-region", resp. "downcase-region", are enabled, otherwise the are not.
Reference [CF ] contains a long list of setup commands that can be used as a repertory with additional candidates for your customization file.
Customization of file-type specific behaviour
Whenever Emacs opens a file, it determines the type of the file and associates a corresponding mode to the buffer in which the file will be edited (Emacs derives the type from the name of the file, for instance by checking for a suffix).
The mode of a buffer customizes the behaviour of Emacs for this specific buffer: this determines - for instance - how tabbing, indentation, syntax-checking and highlighting is handled (the mode of a buffer appears at the end of the buffer's mode-line). To obtain a list of modes that are currently loaded in your Emacs, type
ESC-x *-mode TAB
Some of the details of mode-specific handling may be quite arbitrary: the choice made by Emacs may be different from the user's personal preferences. This can be arranged by including a "mode-hook" into an initialisation file.
Mode hooks
A mode-hook is a function-like block of statements, specified in an initialisation file; it will be automatically executed whenever a buffer using this mode is initialized (the name of a mode hook is normally the name of the mode, converted to lower-case with spaces substituted by underscores, appended with "-hook").
The following code illustrates such a mode hook:
(add-hook 'change-log-mode-hook (lambda () (progn (setq tab-width 4) (auto-fill-mode -1) ))
This particular hook is needed when a file with the name of - for instance - CHANGELOG is edited. Emacs decides that, for editing such a file, the "Change Log" mode is required; this mode will force the tab-width to 8 and will automatically break lines that are longer than 72 characters - even if the initialisation file had established global settings that are different (this line-breaking feature is set by calling the function "auto-fill-mode" with a positive argument, cancelled by a negative argument).
There is one particular case which is worth-while mentioning: Emacs automatically enables line-breaking for files opened in text-mode or any of its sub-modes - which can be very annoying. But here, a mode-hook with "(auto-fill-mode -1)" does not help - very likely because the implementation of text-mode itself uses a hook to establish line breaking, and because that hook happens to be executed after any user-defined hook. The solution is to include the following line into the user's emacs initialisation code (at the top level, not in a hook):
(remove-hook 'text-mode-hook #'turn-on-auto-fill)
This line very simply cancels the un-desired hook of the text-mode module (the "auto-fill-mode" line in the change-log hook therefore is without effect, is is just kept as an illustration).
It does not make sense to recommend a list of mode-hooks to be included in a user's initialisation code. The best approach is to start by implementing the logically evident customisation code without mode hooks, and then to create mode-hooks whenever the need for one appears.
Customization of keyboard shortcuts
The function "global-set-key" allows to bind a key - or a combination of keys - to a function. The function is called when such a keyboard event happens; it can be one of the functions provided by vanilla Emacs, or a function created by the user. Instead of a full explanation of how to define combinations of keys, have a look at a series of typical cases (and at) :
Bind the function "undo" to the "f4" function-key
(global-set-key (kbd "<f4>") 'undo)
For a list of names of key recognized by Emacs, such as <f4>, please refer to.
Bind the function "find-file" to the <Control><keypad-Enter> key-combination
(global-set-key (kbd "C-<kp-enter>") 'find-file)
"C-<kp-enter>" stands for a simultaneously pressing the "Control"-key and the "keypad-enter" key. Emacs refers to combinations with modifier keys as:
- "C-..." ("Control"),
- "S-..." ("Shift"), and
- "M-..." ("Meta", the "Alt"-key");
these three modifier keys can be used in any combination - for instance "C-S-<kp-enter>.
Bind the function "save-buffer" to the sequence "Control-x" - "Control-s"
(global-set-key (kbd "C-x C-s") 'save-buffer)
Emacs defines "Control-x" as a prefix-key, a key to be used as the first key in a 2-key sequence (lower-case characters represent the corresponding alphabetic key).
There exist other forms for representing key-combinations, but the one illustrated here is most commonly used.
Customizing of global Emacs variables
Many properties of Emacs are controlled by globally defined control variables. Most of these variables are preset to "reasonable" values and do not require adjustment.
For some features, the default behaviour of Emacs can be annoying, and adjusting the corresponding variables can be advised.
Enhancing the menu-bar of Emacs is probably the most rewarding topic for customization; this is a slightly complex issue and will be discussed at a higher level of detail than the rest of this wiki-page. The following paragraphs document the basic operations necessary for modifying the menu-bar and its menus. Some complementary information can be found here. Emacs supports various alternatives of coding menu-bar customization; the approach documented here corresponds to the approach used in the module menu-bar.el.
One item, however, is missing: there are no simple instructions for modifying the order of already existing menus, nor for inserting a new menu at some place different from the left side of the menu-bar (but there is always the crude solution: delete all menus and re-create them in the desired order - this is possible without excessive loss of performance).
One issue to be aware of: Emacs assembles menu-items in an inverted sequence: the first menu-item you have defined will appear at the bottom of the menu, the last one at the top.
Emacs uses several terms for referring to menus and menu items:
Menu- and menu-item labels
A label is an "external" tag - visible at the level of the user interface. It is perceived by the user as the "name of the menu", but the label is practically not used within Emacs when it addresses a menu or a menu-item. A label is normally specified as short text string, placed between two double-quotes ("). In the present discussion, "<menu-label>" or "<item-label>" will be used when talking about the label.
Menu- and menu-item names
When, for instance in a function call, Emacs refers to a menu or a menu-item, it will use an "internal name", a symbol defined to represent that entity. The following discussion will use "<my-menu>" to refer to that internal name.
Keymap of a menu- or a menu-item
Menus and menu-items practically always are associated with a keymap see in the Emacs manual. Emacs sometimes refers to a menu or a menu-item by specifying the keymap rather than the name. The following discussion uses "<menu-keymap>" or "<item-keymap> when it refers to a keymap.
Finding the name of an existing menu or menu-item
When customization intends to modify an existing menu or menu-item, a function call needs to be made with the name of the menu or the menu-item as an argument, a name that had been defined as part of the implementation of Emacs. That means that somehow that name must be detected.
An efficient method for finding the name is to search for a corresponding text-string in the source-code of the module menubar.el - practically all menus in the menu-bar of vanilla Emacs and all their items are defined in that module.
New menu-items are created by calling the function "define-key. For most types of menu-items, this function - in turn - will call the function "menu-item".
The following paragraphs discuss some detailed aspects of the function "menu-item". Note that these details only need to be considered in particular cases, normally you can simply copy the sample code proposed in the following sections.
Item properties
The function "menu-item" permits to specify particular properties of the item by appending additional pairs of arguments after the leading two mandatory arguments. The first item of the pair is a key, the second one a double-quoted string or a function call:
:enable | Elisp command that evaluates to true or false : controls whether the menu-item is active (enabled) or not; |
:help | help-text: will pop up when the mouse hovers over the menu item; |
:keys | text-string that can enumerate shortcut keys: right-adjusted comment, appended to the label of the menu-item used to display keyboard shortcuts that exist for the menu item; |
:button | description of a button in a button-item to be added: add a check-button or a radio-button - please refer to or see the implementation of "Line Wrapping" in the "Options" menu (module menu-bar.el). |
Note: the two mandatory arguments are
- the first argument must be the label of the item, a short string placed between double-quotes,
- the second argument determines what Emacs has to do when there is a hit on the item; this can be the name of a function to call, the name of a sub-menu to open, or the name of the control-variable of a toggle-item; this name must always be quoted (see the Section on quoting),
Quoting and backquoting
The call off the function "menu-item" must be "quoted": that means that the function is not called at the time the Elisp interpreter reads the code (i.e. when the menu-item is defined), but that calling and the evaluation of arguments is deferred to some later time - the time when there is a hit on the menu-item.
Back-quoting is a special case of quoting: in a back-quoted list, the handling of each item can be individually determined and depends on whether the item is preceded by a comma ( , ) or not:
- items that are not preceded by a comma are quoted (their evaluation is deferred),
- items that follow a comma are not quoted - evaluated immediately.
Plain quoting uses the ' quote character, back-quoting uses `. For more information on back-quoting refer to and also to. In the normal case,
- button menu-items are defined with plain quoting,
- sub-menu header items always with back-quoting; the argument (the name of the sub-menu) is preceded by a comma.
But in particular situations, also button menu-items can be back-quoted. This is rather a mess - probably the best approach when back-quoting might be required is to look at existing examples (for instance the menu-bar.el library module) and to resort to trial and error. For instance, have a look at the "Copy" item of the Edit menu in the module menu-bar.el, and try to understand why that item needs back-quoting with a comma!
In-line coding of the target function
Instead of providing the (quoted) name of a target function, it is possible to specify the code of the target function in-line, using the "lambda" function. This is illustrated in the following code snippet:
(bindings--define-key <my-menu> [<item-keymap>] `(menu-item "<item-label>" ,(lambda () (interactive) ( ... ))
Note that "menu-item" is called with back-quoting, and that the argument with the lambda function is preceded by a comma. This way of coding the target function offers additional flexibility - for instance, it permits to implement target functions that are called with arguments. The creation of a sub-menu for manually setting the mode of a buffer is an example where this can be very useful.
Deleting an entire menu
A menu is deleted by setting its key-map to nil. For instance, if <my-menu> is a menu in the menu-bar, it will be destroyed by the command
(define-key global-map [menu-bar <my-menu>] nil)
This can, for instance, be used to delete the "Edit" menu that comes with vanilla Emacs - an action that is necessary before a totally new Edit menu can be defined.
Deleting a menu-item
The menu-item <my-item> of <my-menu> can be destroyed by the command
(define-key global-map [menu-bar <my-menu> <my-item>] nil)
- or by
(define-key menu-bar-<my-menu> [<my-item>] nil)
This is also valid if the menu-item is the header of a sub-menu: the command will destroy the header and the sub-menu it controls.
Adding a "button" menu-item to a new menu
While <my-menu> is being created, a new button-item can be added by the command
(bindings--define-key <my-menu> [<item-keymap>] '(menu-item "<item-label>" <item-function>))
This will create a menu-item with the label <item-label> that invokes the target function <item-function> when a hit on the menu-item occurs; "define-key" requires that a unique key <item-keymap> is specified, even if that key is not used.
Adding a "check-button" menu-item to a new menu
While <my-menu> is being created, a new check-button-item can be added by the command
(bindings--define-key <my-menu> [<item-keymap>] (menu-bar-make-mm-toggle <control-variable> "<item-label>" "<help-text>" ))
Some documentation on the function menu-bar-make-mm-toggle can be found here: the variable <control-variable> must have been defined, it represents the state of the check-button. For this to work, you must also define a function that has the same name as the variable; the function must (1) read the old value from the variable, (2) compute the new value and (3) update the variable. On a hit on the check-button menu-item, Emacs will automatically execute this function.
Adding a "sub-menu header" item to a new menu
This is very similar to the creation of a "button" menu-item:
(bindings--define-key <my-menu> [<item-keymap>] `(menu-item "<item-label>" ,<sub-menu-name> ))
Inserting a menu-item into an existing menu
A new menu-item can be inserted into an existing menu <my-menu> by the command
(define-key-after <my-menu> [<item-keymap>] (list 'menu-item "<item-label>" <item-function>) '<predecessor-item>)
The new item is inserted after the item <predecessor-item> of the target menu. The call to "define-key-after" uses the function "menu-item" which is described in the preceding paragraph.
The creation of a new menu requires two distinct steps to be coded:
- defining the new menu and its menu-items,
- inserting this menu into the menu-bar.
The menu <my-menu> is defined by defining its key-map - <my-menu-map> - and filling it with menu-items; each "<menu-item-..>" is created by a call to the function "menu-item" or to the function "menu-bar-make-mm-toggle", as discussed in the preceding paragraphs.
(defvar <my-menu-map> (let ((<my-menu> (make-sparse-keymap "<name-string>"))) <menu-item-1> <menu-item-2> ... ))
<name-string> is a name given to the menu being created; that name is not used if the menu is part of the menu-bar (hence in the context discussed here) - it is needed when the menu is used as a popup menu.
Once the menu is completely defined, it is inserted at the left side of the menu-bar, using the command
(bindings--define-key global-map [menu-bar <my-menu>] (cons "<menu-name>" <my-menu-map>))
Example: putting things together
The preceding paragraphs have a certain aspect of a dry repertory of functions and commands. This wiki-page therefore proposes a small example with Elisp code that can be added to customize vanilla Emacs. That should help to apply this documentation to writing Elisp for a fully functional user initialization and setup file.
To keep this wiki-page at a manageable size, the example cannot contain any effectively usable examples - it does not go beyond demo customization :
- the user initialization file just loads a setup module that, in turn, contains the bulk of the initialization code (this illustrates how such a module is loaded); for practical reasons, that setup module is placed into the home directory of the user,
- the setup module
- executes some basic setup functions (tabs, initialization of frames),
- defines the variables needed,
- defines some keyboard shortcuts,
- adds a short menu ("Demo") at the left side of the menu-bar,
- the menu-items are "one of each" for the different types discussed in the present wiki-page,
- when an item is hit, it simply triggers the display of short message.
The insertion of a new menu-item at a place within an existing menu is illustrated differently: a "simple compare" function is coded, a button menu-item that calls that function is then inserted into the "Tools" menu - right after the existing "Compare (Ediff)" sub-menu header menu-item.
The code of the user initialization file ($HOME,.emacs.d/init.el) and of the library setup file ($HOME/emacs_setup.el) is presented in the Appendix. The following screen-shot illustrates the "look" of the thus customized editor.
In case you are interested in fully operational customization files in order to find "inspiration" or pieces of code to be copy/pasted into your new menu-bar, it is worth while to do some searching in the internet, several such examples exist.
Debugging customization code
By experience, debugging of Emacs customization code is seldom a problem. A couple of hints may be helpful to be aware of the techniques available:
- Always keep a window with an instance of a correctly running Emacs on your desktop; using this window for correcting broken modules is faster than reverting to vanilla Emacs or using - for instance - kwrite.
- Always give your modules a file-name with the .el suffix; in consequence, Emacs will do the editing of your new module in elisp mode; use the Emacs-Lisp menu, i.e.the "byte-compile" feature, to check your modules against syntax errors.
- You can display intermediate values by using
(message <string-to-print>) (sit-for 2)
- The "sit-for" is necessary to avoid Emacs from just "flashing" the message and to give you a chance tor read it (2 is an arbitrary value - the number of seconds Emacs will wait); be aware that Emacs expects a string for the argument of "message", and that you have to convert numeric values - for instance - to strings when you call "message". The Emacs manual describes a series of functions for string conversion.
- The Emacs manual offers extensive instructions on the debugging of Elips code, but these instructions go beyond what is needed for the simple task of creating customization code.
- When you launch Emacs while some module with customization code is broken, execution of the customization code is abandoned. Emacs will invite you to re-launch Emacs from a command string that specifies a debugging option; however, normally the debugging option does not provide very valuable information beyond the information already available in the current buffers of Emacs.
$ emacs --debug-init
Documentation, references
The most important source for obtaining concise information on features of (X)Emacs is the on-line help facility:
- <Control-h> a <search-key> provides an "apropos" list of Emacs commands and variables,
- <Control-h> f <function-name> will display information on a function,
- <Control-h> v <variable-name> will display information on a variable.
See http://www.emacswiki.org/emacs/SelfDocumentation for an extensive list of help commands. However, the information accessible through these help commands needs to be implemented by the author of the corresponding code as text that is glued into the corresponding code as "self-documentation" - it happens that this text is not provided.
Here are some more manuals and references with documentation on Emacs and Elisp (several documents are available both in an html and a pdf version):
General documentation on Emacs and Elisp | ||
[EM] | User manual : | http://www.gnu.org/software/emacs/manual |
[LM] | Elisp reference manual : | http://www.gnu.org/software/emacs/manual/html_node/elisp/index.html |
[EW] | Emacs wiki : | http://www.emacswiki.org/emacs |
[ET] | Tutorial : | http://www.linuxjournal.com/node/2821/print |
Description of selected customization commands | ||
[CE] | Selection of commands in User manual : | http://www.gnu.org/software/emacs/manual/html_node/emacs/Init-Examples.html |
[CC] | Some customization commands : | http://www.nongnu.org/emacsdoc-fr/manuel/init-file.html |
[CF] | More customization commands : | https://ryuslash.org/dotfiles/emacs/init.html |
[EI] | Introduction to the .emacs file | http://www.math.utah.edu/docs/info/emacs-lisp-intro_17.html |
Examples of user initialization files | ||
[CD] | Very extensive customization file: | http://www.mygooglest.com/fni/dot-emacs.html |
Web-searches for specific keywords on Elisp issues outside the official documentation provide amazingly scarce information.
Conclusions
Today, the quality of Xemacs and Emacs are at about the same level, the solidity of Emacs and the prospect of support at mid-term is certainly superior to that of Xemacs.
Customization of the user-interface is very simple. The menu-bar of Emacs is quit malleable, the functions needed for modifying menus are explained in this page: it is easy to adapt the "look-and-feel" of the user interface to your individual habits, and the functionality of Emacs can easily be extended beyond what "vanilla Emacs" makes available.
Migration from Xemacs is not a major effort; my experience - and I use a quite substantial amount of customization - has been that this effort can be completed in 1 or 2 days of work. There were two issues where features of Xemacs do not have a one-to-one equivalent in Emacs: (1) the concept of "Extents" (managing marked regions of text) is lacking in Emacs, and (2) after certain file-opening operations, an auxiliary window with directory list information tends to "hang around" in Emacs, and is difficult to get rid of automatically. Both issues are perfectly secondary.
Appendix: examples of Elisp code
Emacs user initialisation file
This file must be placed at $HOME/.emacs.d/init.el
;; Load demo customization file ;; ---------------------------- ( load-file (substitute-in-file-name "$HOME/emacs-setup.el") ) ;; For expert users: ;; - suppress startup screen if emacs is called with an argmuent (a file name) ;; (e.g. launched by a hit on the file-manager icon of the target-file). (if (> (length command-line-args) 1) (setq inhibit-startup-screen 1) ()) ;; - inhibit the toolbar (tool-bar-mode -1) ;; Enable case conversion functions (put 'upcase-region 'disabled nil) (put 'downcase-region 'disabled nil)
Setup module
This file must be placed at $HOME/emacs-setup.el (i.e. the path specified in .emacs.d/init.el)
;; Emacs wiki-guide customisation file ;; =================================== ;; General setup, key bindings ;; =========================== ;; Initial setup, keyboard shortcuts (setq-default tab-width 4) (setq tab-width 4) (defvaralias 'c-basic-offset 'tab-width) (defvaralias 'cperl-indent-level 'tab-width) (defalias 'perl-mode 'cperl-mode) ;; Initialization of frames ;; Set the standard properties of a newly created buffer ;; ----------------------------------------------------- (defvar my-default-background (if (eq (user-uid) 0) "#5A0000" "#005538" ) "Default frame background colour") (modify-all-frames-parameters (list (cons 'height 25) (cons 'width 80) (cons 'font "DejaVu Sans Mono-10.5") (cons 'background-color my-default-background) (cons 'foreground-color "#ffffff") (cons 'cursor-color "red") )) (defun my-setup-frame () "Initialise a just created buffer: - set the background of the fringe to the background colour of frames - set the foreground of the fringe to yellow - make the cursor non-blinking." (set-face-foreground 'fringe "yellow") (set-face-background 'fringe my-default-background) (blink-cursor-mode 0) ) (my-setup-frame) (add-hook 'after-make-frame-functions (lambda ($new-frame) (progn (select-frame $new-frame) (my-setup-frame) )) ) ;; Key-bindings ;; ------------ (global-set-key (kbd "<f4>") 'undo) ; undo (global-set-key (kbd "C-u") 'undo) ; undo ;; Create and attach the "Demo" menu ;; ================================= ;; Action functions and control variables ;; -------------------------------------- ( defun my-plain-button () (interactive) (message "Hit on plain-button demo-button") (sit-for 1 ) ) (defvar my-toggle-button nil "*State of toggle demo menu-item") (defun my-toggle-button () (interactive) (setq my-toggle-button (not my-toggle-button) ) (let (temp) ( if my-toggle-button (setq temp "t") (setq temp "nil") ) (message (concat "Value of toggle demo button is now " temp ) ) ) (sit-for 1) ) ;; Action function for "simple compare" (defun my-launch-compare () (interactive) (let ( ($window1 (get-buffer-window)) $window2) (if (one-window-p t) (progn (goto-char (point-min)) (find-file-other-window (read-file-name "Compare with : ")) (setq $window2 (get-buffer-window)) (goto-char (point-min)) ) (progn (setq $window2 (next-window)) ) ) (compare-windows nil) (add-text-properties (point-min) (+ (point-max) 1) '(face "isearch-fail")) ) ) ;; Tools menu: add "Simple compare" button after compare button (define-key-after menu-bar-tools-menu [tools-compare] '(menu-item "Simple Compare" my-launch-compare ) 'compare ) ;; Sub-menu for case-modifications (defvar my-modify-case-menu (let ((menu (make-sparse-keymap "Modify case"))) ; *** Convert to lower case (bindings--define-key menu [case-b] '(menu-item "Convert region to Lower Case" downcase-region :help "Convert case in region (mark <-> cursor) to lower case)) ; *** Convert to upper case (bindings--define-key menu [case-c] '(menu-item "Convert region to Upper Case" upcase-region :help "Convert case in region (mark <-> cursor) to upper-case)) menu )) ;; Main demo menu ;; -------------- (defvar my-demo-menu (let ((menu (make-sparse-keymap "Demo"))) ; *** Modify-case sub-menu header (bindings--define-key menu [edit-a] `(menu-item "Modify case", my-modify-case-menu)) ; *** Plain button (bindings--define-key menu [edit-b] '(menu-item "Plain button" my-plain-button :help "Demonstrate plain button-item")) ; *** Undo button with keyboard shortcuts (bindings--define-key menu [edit-c] '(menu-item "Undo button" undo :help "Demonstrate keyboard shortcuts" :keys "C-u <f4>")) ; *** Separator below checkbutton (bindings--define-key menu [edit-d] menu-bar-separator) ; *** Toggle button (bindings--define-key menu [edit-e] (menu-bar-make-mm-toggle my-toggle-button "Toggle button" "Demonstrate check-button menu-item" ) ) menu )) ;; Attach the demo menu at left side of the menu-bar (bindings--define-key global-map [menu-bar my-demo] (cons "Demo" my-demo-menu))