CS010 Practice 8

Customizing Emacs and More Pointer Practice

Help in Emacs

So far the only help command we have looked at in Emacs is the info command. Here are three more commands that you may find useful:

Apropos is bound to C-h a. It prompts you for a string:

Apropos command (regexp):

Type in a word and it will show you all the commands that have that word in their names along with the key sequence they are bound to and a one line description. The list appears in a separate buffer and Emacs moves your cursor into that buffer.

As you should have figured out by now, knowing the key bindings for commands allows you to enter the commands more quickly, but it takes a lot of effort to learn what the commands are. Use C-h b to see a list of all the key bindings. Again, these will be displayed in a separate buffer. You could print out a list by clicking with the mouse in that buffer and then use M-x print-buffer.

One more useful help command allows you to get help specifically about your current mode. If you are editing a C file and in C mode, you can find out more about C mode by typing C-h m. This creates a buffer with help on your current mode. You can read it online or you could again click in the buffer and use the print-buffer command to get a printed copy.

Compilng in Emacs

Last time we saw how to run the debugger while still in Emacs. We can also compile our code from within Emacs. To do this type M-x compile. The message buffer at the bottom of Emacs will say:

Compile command: make 

Hit backspace to erase the word make and type your usual compilation command

Compile command: gcc -Wall -g -o match match.c

If you have not saved all of your changes, Emacs will put out a message asking you if you want to save the buffer first:

Save file /home/cs-students/jcool/match.c? (y, n, !, q, C-r or C-h)

You should type y. Emacs will save the buffer and continue with the compilation. Now Emacs will create a buffer called *compilation*. It will display error messages in this buffer. If you click on an error message in this buffer with your mouse and then hit the return key, Emacs will scroll the error message to the top line of the buffer, move the cursor to the buffer containing the file with the error, placing the cursor on the line where the error occurred. This makes it very easy to find the lines with errors. You can go back to the *compilation* buffer and repeat this for another error message.

After you compile once, subsequent compile commands will print the last compile command that you executed. If you want to compile the same file again, just hit the return key. If not, just edit the line.

Customizing Emacs

One of the nice things about Emacs is that it is highly customizable. One way that it can be customized is by changing modes within Emacs.

Major and minor modes

Emacs provides major and minor modes. Major modes are based upon the type of information that is in the buffer. We have seen 3 major modes so far: C mode, gdb mode, and compilation mode. In each case, the status line indicates what mode we are in. This is approximately in the middle of the line and is enclosed in parentheses. A major mode typically redefines some key bindings to do something interesting for that mode. For example, in compilation mode, the return key is redefined to show us where an error message occurs in our program.

Emacs also has minor modes. Minor modes allow some tweaking of the major mode. We have seen two minor modes in C mode: auto-newline-insertion mode and hungry-delete mode. We can turn these on and off individually and still stay in C mode. These update the status line to show us what minor modes we are in as well.

C mode offers several styles of indentation, including the ones show in King on pages 79-80. If you don't like the default style, you can change it using the c-set-style command (remember to precede it with M-x). It will prompt you with:

Which C indentation style? gnu

It will display gnu because that is its default style. Erase gnu and type ?. You will see a list of indentation styles. Pick one. When you are back in your C buffer, type C-c C-q. It will reformat your current function using that style. Play around until you find a style you like.

You might also want to control how many spaces a line indents when it auto-indents. To do that, use the set-variable command. You will get the prompt:

Set variable:

Type c-basic-offset and return. Now you will get the prompt:

Set c-basic-offset to value:

Type the amount you want and hit return. Back in your C buffer, use C-c C-q to reformat again.

.local_emacs

If you play around with these two commands you will find a setting that you like and you will want to use again. If you don't save it somehow, you will need to re-enter those commands everytime you start Emacs. The way to save these settings is by defining a file called .local_emacs and placing some commands in that file to set up your environment. This file uses a language called Lisp to define the customizations. Lisp has a rather unusual syntax, but it is generally easy enough to copy what somebody else does and make some modifications. Here is what you would put in your .local_emacs file to use k&r indentation, an indent of 2 spaces, auto-newline-insert mode and hungry delete mode:

;;; These are my customizations for C mode
   
;;; Define a function named my-c-mode-hook to perform my customizations
(defun my-c-mode-hook ()
  ;; Use k&r style formatting
  (c-set-style "k&r")
   
  ;; Use 2 as the amount for indentation
  (setq c-basic-offset 2)
   
  ;; Turn on auto-newline-insert mode
  (c-toggle-auto-state 1)
   
  ;; Turn on hungry-delete mode
  (c-toggle-hungry-state 1)
)
   
;; Install my-c-mode-hook so these commands are run whenever I enter C mode.
(add-hook 'c-mode-hook 'my-c-mode-hook)
   

Lisp syntax is:

(function-name arguments)

First we define a function using defun. The name of the new function is my-c-mode-hook. The new function takes no parameters. The indented text is the statements in the function. The first executes the c-set-style function to change the style. The next changes the value of the c-basic-offset variable by calling the setq function. The second argument to this function indicates what value to assign. The next 2 statements turn on the minor modes. If you don't want to turn them on, just leave those statements out. Then outside the function, the add-hook statement indicates that our new function should be called whenever we enter C mode. The syntax is certainly bizarre. Becoming an expert in Emacs Lisp programming is definitely difficult. Fortunately, we usually have sample code we can follow to figure out what to do. In fact, the code above is just a modification of the code provided on the C mode info page under the "Sample .emacs file" link.

Binding commands to keys

Another type of statement that you can put in your .local_emacs file is one that will bind a command to a control sequence. For example, I use this to define a keyboard shortcut for starting a shell inside Emacs so that I don't need to say "ESC X shell" whenever I want to start a shell.

 (define-key esc-map "-" 'shell)

This allows me to start a shell by typing ESC -. Define-key does the key binding. esc-map indicates that the command begins with the escape key. - is the character following the escape key. Shell is the Emacs function bound to the key sequence.

Font Colors

If you like the font coloring in C mode, you can enable it for all modes that support colors by adding this line to your .local_emacs:

 (global-font-lock-mode 1)

There's much, much more to customizing Emacs. Mostly, what happens is that somebody else tells you about something cool and shows you how to set it up. Some people write very sophisticated Lisp packages to customzie Emacs, so what ti can do grows daily! The cool thing is that each user or Emacs can install their own Lisp packages. These are all extensions to Emacs. Everybody still uses the same Emacs program just different sets of customizations.

Exercises

For these exercises, I would like you to modify the vector program we developed during class. You can copy the program from /home/facutly/freund/shared/cs010/vector.c

  1. Write a function that returns the position of a value in the vector, returning -1 if the desired value is not in the vector. It should have this signature:
    int indexOf (Vector *v, void *value)
  2. Write a function to insert an element at a specific position within the vector. The position specified must either be a position already containing an element or 1 beyond the last element. If the index contains an existing element, everything after it should slide up in the array (the opposite of removeElementAt). If it is the value one beyond the end of the array, it should behave like addElement. Note that in either case, it may be necessary to increase the size of the contents array. If the index given is negative or more than one beyond the last index, it should print an error message and do nothing. The signature for this function should be:
    void insertElementAt (Vector *v, void *value, int index)
  3. As noted in class, there should be a freeVector function that frees the memory that was allocated for the array. This function should have the following signature:
    void freeVector (Vector *vptr)

    Should this function also free the memory used by the pointers stored in the vector? Since we don't know how the vector is being used, we don't know if there are other accesses to those values, so don't free them in freeVector. This is consistent with the behavior of setElementAt and removeElementAt. We'll leave it up to the caller of freeVector to decide if they need to deallocate the elements in the vector as well.


Return to CS 010 Home Page